diff --git a/.claude/agents/claude-md-auditor.md b/.claude/agents/claude-md-auditor.md new file mode 100644 index 000000000..ae4a3fc87 --- /dev/null +++ b/.claude/agents/claude-md-auditor.md @@ -0,0 +1,49 @@ +--- +name: claude-md-auditor +description: Independent auditor that judges a pending git commit or push against every applicable bullet in CLAUDE.md (Workflow section). MUST be invoked before retrying a `git commit` or `git push` that was blocked by .claude/hooks/preflight-claude-md.sh. Reads CLAUDE.md and the diff cold in fresh context, writes a structured verdict to .claude/.last-audit.json. The hook only allows the next retry when the verdict file is PASS and matches the current branch + HEAD. +tools: Read, Bash, Write +--- + +You are the CLAUDE.md compliance auditor for the boxlite3 repository. + +The parent agent must tell you the exact git command they are about to retry +(e.g. `git commit -m "..."` or `git push origin `). Treat that as the +"target command" below. + +## Procedure + +1. Read `CLAUDE.md` from the repo root. Locate the `## Workflow` section. +2. Capture current repo state: + - `git branch --show-current` + - `git rev-parse HEAD` +3. Capture the diff that is about to land: + - If the target command starts with `git commit`: `git diff --cached`. + - If it starts with `git push`: `git diff origin/main...HEAD`. +4. For each Workflow phase (Understand / Research / Design / Implement / Test / + Verify / Cross-cutting): + - Identify which bullets are applicable to this diff. Skip ones that don't + apply (e.g. concurrency rules for a pure docs change). + - Judge PASS or FAIL against what the diff actually shows. Be skeptical: + missing tests, scope creep, undocumented new dependencies, secrets, + weakened assertions, comments that restate code, etc. +5. Write `.claude/.last-audit.json` with EXACTLY this shape (no extra fields): + ```json + { + "branch": "", + "head": "", + "command_kind": "commit" | "push", + "verdict": "PASS" | "FAIL", + "findings": [": ", "..."] + } + ``` + On PASS, `findings` is an empty array. +6. Reply to the parent agent with the verdict and findings. + +## Constraints + +- Only judge — do not propose fixes, do not edit code, do not retry the git + command yourself. +- Do not skip phases. If a phase has no applicable bullets for this diff, say so + explicitly in your reply (not in findings). +- The hook reads only the JSON file; your chat reply is for the parent agent's + benefit. Both must agree. diff --git a/.claude/hooks/preflight-claude-md.sh b/.claude/hooks/preflight-claude-md.sh new file mode 100755 index 000000000..0d1c77bbd --- /dev/null +++ b/.claude/hooks/preflight-claude-md.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# PreToolUse hook: gate `git commit` / `git push` on a fresh verdict from the +# claude-md-auditor subagent (see .claude/agents/claude-md-auditor.md). +# +# The hook itself does not call any model; it only reads the verdict artifact +# the subagent writes at .claude/.last-audit.json and checks that the verdict +# is PASS, recent, and bound to the current branch + HEAD. +# +# Flow on a denied attempt: +# 1. Hook denies the git tool call. +# 2. Reason text instructs the parent agent to invoke the claude-md-auditor +# subagent via the Task tool, then retry the same git command. +# 3. Subagent writes .claude/.last-audit.json. +# 4. Parent retries -> hook reads the artifact and allows on PASS. +# +# Wired in .claude/settings.json under hooks.PreToolUse with matcher "Bash". +# +# Design notes +# ------------ +# * Matcher scope: settings.json registers this hook on the broad `Bash` +# matcher, not a narrower `Bash:git*` pattern, because Claude Code's +# PreToolUse matchers are tool-name-only — there's no built-in way to filter +# on the bash command itself. The script does the actual filtering via the +# case match below and exits 0 immediately on non-target commands, so the +# per-invocation cost on unrelated bash calls is one jq parse + one regex. +# +# * One-shot consumption: the audit file is `rm -f`'d on the allow path +# (intentional, see end of script). This forces a fresh audit on every +# subsequent git commit/push — even at the same HEAD — so re-staged content +# between commits can't ride on the previous audit. The cost is that +# commit-then-push of the same HEAD must re-audit; the user has accepted +# this trade-off to avoid stale-audit-passes-new-content failure modes. +# +# Tests: bash .claude/hooks/preflight-claude-md.test.sh +set -euo pipefail + +payload="$(cat)" +command="$(printf '%s' "$payload" | jq -r '.tool_input.command // ""')" + +# Match when the command actually IS a `git commit` / `git push` invocation — +# at the start of the command OR at the start of any chain segment (after &&, +# ||, ;, |, &, $(, (, `). This catches the chained-command case +# (`cat foo && git commit ...`) that an anchor-only matcher misses, while still +# rejecting literal mentions of "git commit" inside string arguments (e.g. +# `echo "git commit"`), which don't sit at the start of a chain segment. +work="${command#"${command%%[![:space:]]*}"}" +if [[ "$work" =~ (^|[[:space:]]*(\&\&|\|\||;|\||\&|\$\(|\(|\`)[[:space:]]*)([A-Za-z_][A-Za-z0-9_]*=[^[:space:]]+[[:space:]]+)*git[[:space:]]+(commit|push)([[:space:]]|$) ]]; then + case "${BASH_REMATCH[4]}" in + commit) kind="commit" ;; + push) kind="push" ;; + *) exit 0 ;; + esac +else + exit 0 +fi + +repo_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)" +project_dir="${CLAUDE_PROJECT_DIR:-$repo_root}" +branch="$(git -C "$repo_root" branch --show-current 2>/dev/null || echo '?')" +head="$(git -C "$repo_root" rev-parse HEAD 2>/dev/null || echo '?')" +audit_file="$project_dir/.claude/.last-audit.json" +max_age_seconds=600 + +deny() { + jq -nc --arg r "$1" '{ + hookSpecificOutput: { + hookEventName: "PreToolUse", + permissionDecision: "deny", + permissionDecisionReason: $r + } + }' + exit 0 +} + +invoke_instruction="Invoke the claude-md-auditor subagent now: + Task(subagent_type='claude-md-auditor', + description='CLAUDE.md audit', + prompt='Audit the pending \`${command}\` on branch ${branch}.') +The subagent will write its verdict to .claude/.last-audit.json. Retry the +same git command after it reports PASS." + +if [[ ! -r "$audit_file" ]]; then + deny "No CLAUDE.md audit found for this change. + +${invoke_instruction}" +fi + +audit_branch="$(jq -r '.branch // ""' "$audit_file" 2>/dev/null || echo '')" +audit_head="$(jq -r '.head // ""' "$audit_file" 2>/dev/null || echo '')" +audit_kind="$(jq -r '.command_kind // ""' "$audit_file" 2>/dev/null || echo '')" +audit_verdict="$(jq -r '.verdict // ""' "$audit_file" 2>/dev/null || echo '')" + +# File mtime as freshness signal — portable across BSD (stat -f %m) and GNU +# (stat -c %Y) without parsing self-reported timestamps. +audit_mtime="$(stat -f '%m' "$audit_file" 2>/dev/null || stat -c '%Y' "$audit_file" 2>/dev/null || echo 0)" +now_epoch="$(date +%s)" +age=$(( now_epoch - audit_mtime )) + +if [[ "$audit_branch" != "$branch" ]] || \ + [[ "$audit_head" != "$head" ]] || \ + [[ "$audit_kind" != "$kind" ]] || \ + (( age > max_age_seconds )); then + deny "Existing audit does not match current state: + audit.branch=${audit_branch} current=${branch} + audit.head=${audit_head} current=${head} + audit.command_kind=${audit_kind} current=${kind} + audit age: ${age}s (max ${max_age_seconds}s) + +Re-audit is required. +${invoke_instruction}" +fi + +if [[ "$audit_verdict" != "PASS" ]]; then + findings="$(jq -r '.findings[]? | " - " + .' "$audit_file" 2>/dev/null || echo '')" + deny "CLAUDE.md audit FAILED on branch '${branch}': + +${findings} + +Address each finding, then re-invoke claude-md-auditor before retrying \`${command}\`." +fi + +# Verdict is PASS, recent, and matches current state — let the git command run. +# Consume the audit file so the next commit/push always re-audits, even if HEAD +# hasn't changed (e.g., user re-stages different content before the next commit). +rm -f "$audit_file" +exit 0 diff --git a/.claude/hooks/preflight-claude-md.test.sh b/.claude/hooks/preflight-claude-md.test.sh new file mode 100755 index 000000000..1acd8808d --- /dev/null +++ b/.claude/hooks/preflight-claude-md.test.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Tests for .claude/hooks/preflight-claude-md.sh +# +# Covers the two areas CLAUDE.md flags for required tests on this change: +# 1. Command matcher (parsing + branching): direct invocation vs. chain +# segments vs. literal-string mentions inside arguments. +# 2. Gate logic (branching + boundary validation): missing / mismatched / +# stale / FAIL / consumed audit-file paths. +# +# Run with: bash .claude/hooks/preflight-claude-md.test.sh +# Exits non-zero on any failure. +set -uo pipefail + +REPO_ROOT="$(git rev-parse --show-toplevel)" +HOOK="$REPO_ROOT/.claude/hooks/preflight-claude-md.sh" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +# Redirect the hook's audit-file lookup into TMP so tests don't touch the real +# .claude/.last-audit.json. The hook still uses git from the real repo for +# branch/HEAD detection — that's fine, we read the same values for assertions. +export CLAUDE_PROJECT_DIR="$TMP" +mkdir -p "$TMP/.claude" + +BRANCH="$(git -C "$REPO_ROOT" branch --show-current)" +HEAD_SHA="$(git -C "$REPO_ROOT" rev-parse HEAD)" + +pass=0 +fail=0 + +run() { + local desc="$1" cmd="$2" expect="$3" out decision + out=$(printf '%s' "$cmd" | jq -Rs '{tool_input:{command:.}}' | "$HOOK") + if [[ -z "$out" ]]; then + decision="passthrough" + else + decision=$(printf '%s' "$out" | jq -r '.hookSpecificOutput.permissionDecision' 2>/dev/null || echo "parse_error") + fi + if [[ "$decision" == "$expect" ]]; then + pass=$((pass + 1)) + printf ' PASS %s\n' "$desc" + else + fail=$((fail + 1)) + printf ' FAIL %s (got=%s expected=%s)\n' "$desc" "$decision" "$expect" + fi +} + +write_audit() { + local verdict="$1" findings_json="$2" kind="$3" + jq -nc --arg b "$BRANCH" --arg h "$HEAD_SHA" \ + --arg v "$verdict" --arg k "$kind" \ + --argjson f "$findings_json" \ + '{branch:$b, head:$h, command_kind:$k, verdict:$v, findings:$f}' \ + > "$TMP/.claude/.last-audit.json" +} + +GC='git commit' +GP='git push' + +echo "## Matcher: should pass through (not a git commit/push invocation)" +rm -f "$TMP/.claude/.last-audit.json" +run "ls" "ls" "passthrough" +run "echo with literal mention" "echo \"$GC\"" "passthrough" +run "grep with literal mention" "grep \"$GC\" file" "passthrough" +run "git status (different verb)" "git status" "passthrough" +run "git log (different verb)" "git log --oneline -5" "passthrough" + +echo +echo "## Matcher: should gate (real git commit/push invocation)" +run "direct commit" "$GC -m wip" "deny" +run "direct push" "$GP origin main" "deny" +run "chained with &&" "cd x && $GC -m wip" "deny" +run "chained with ||" "true || $GC -m foo" "deny" +run "chained with ;" "echo done; $GC" "deny" +run "env var prefix" "FOO=bar $GC -m x" "deny" +run "command substitution" "out=\$($GC -m foo)" "deny" +run "push after &&" "cat x && $GP origin main" "deny" + +echo +echo "## Gate logic: audit file states" +write_audit "PASS" "[]" "commit" +run "PASS verdict matches → allow" "$GC -m foo" "passthrough" +run "verdict consumed on allow" "$GC -m foo" "deny" + +write_audit "FAIL" '["Test: missing"]' "commit" +run "FAIL verdict → deny" "$GC -m foo" "deny" + +write_audit "PASS" "[]" "commit" +# Mutate head field to simulate a stale-by-HEAD audit +jq --arg h "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" '.head=$h' \ + "$TMP/.claude/.last-audit.json" > "$TMP/.claude/x.json" \ + && mv "$TMP/.claude/x.json" "$TMP/.claude/.last-audit.json" +run "HEAD mismatch → deny" "$GC -m foo" "deny" + +write_audit "PASS" "[]" "commit" +touch -t 202001010000 "$TMP/.claude/.last-audit.json" +run "stale mtime (>max_age) → deny" "$GC -m foo" "deny" + +write_audit "PASS" "[]" "commit" +run "kind mismatch (commit vs push)" "$GP origin main" "deny" + +echo +echo "RESULT: $pass passed, $fail failed" +exit $(( fail > 0 ? 1 : 0 )) diff --git a/.claude/hooks/preflight-pr-review.sh b/.claude/hooks/preflight-pr-review.sh new file mode 100755 index 000000000..355a18f54 --- /dev/null +++ b/.claude/hooks/preflight-pr-review.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash +# PreToolUse hook: gate `gh pr create` / `gh pr edit` / `gh pr ready` on a +# user-TYPED acknowledgment that they have reviewed the PR. +# +# `gh pr create --draft` (and `-d`) is intentionally excluded — draft PRs are +# not yet requesting review, so no ack is required. +# +# Flow on a denied attempt: +# 1. Hook denies the gh tool call. +# 2. Reason text instructs the parent agent to obtain a TYPED confirmation +# from the human (not a yes/no click) and persist it verbatim to +# .claude/.pr-reviewed.json bound to current branch + HEAD. +# 3. Parent retries -> hook validates the marker and allows on match. +# +# Wired in .claude/settings.json under hooks.PreToolUse with matcher "Bash". +# +# Design notes +# ------------ +# * Matcher scope: same reason as preflight-claude-md.sh — PreToolUse matchers +# are tool-name-only. This script does the actual `gh pr ` filtering +# and exits 0 immediately on unrelated bash calls. +# +# * Draft detection caveat: the `--draft` / `-d` check matches anywhere in the +# raw command string. A title like `--title "[draft] foo"` would falsely +# skip the ack. The user has accepted this — quoting `--draft` inside an +# arbitrary string is rare and the failure mode is "let one PR through +# without ack," not a destructive action. +# +# * One-shot consumption: the marker file is `rm -f`'d on the allow path so +# each successive gh pr command forces a fresh ack, even at the same HEAD. +# Mirrors the trade-off in preflight-claude-md.sh. +# +# Tests: bash .claude/hooks/preflight-pr-review.test.sh +set -euo pipefail + +payload="$(cat)" +command="$(printf '%s' "$payload" | jq -r '.tool_input.command // ""')" + +# Match `gh pr create|edit|ready` at start of command OR at start of any chain +# segment (after &&, ||, ;, |, &, $(, (, `). Same shape as the git matcher in +# preflight-claude-md.sh so chained invocations are caught. +# +# Scan only the FIRST physical line of the command. Multi-line commands almost +# always put the gh invocation on the first line; this excludes heredoc bodies +# (e.g. a `git commit -m "$(cat </dev/null || echo '?')" +head="$(git -C "$repo_root" rev-parse HEAD 2>/dev/null || echo '?')" +marker_file="$project_dir/.claude/.pr-reviewed.json" +max_age_seconds=600 + +deny() { + jq -nc --arg r "$1" '{ + hookSpecificOutput: { + hookEventName: "PreToolUse", + permissionDecision: "deny", + permissionDecisionReason: $r + } + }' + exit 0 +} + +# ───────────────────────────────────────────────────────────────────────────── +# TODO(user, learning-mode): author the ack instructions Claude reads on every +# deny. This is the actual UX of the gate — keep it tight, unambiguous, and +# anti-cheating. +# +# Constraints the message MUST satisfy: +# • Direct Claude to obtain a TYPED reply from the human (not AskUserQuestion +# click, not Claude inferring "the user already said yes earlier"). +# • Specify the exact shape the user must type. The hook validates the +# marker's `.message` field with the regex defined in REQUIRED_MESSAGE_RE +# below — keep the two in sync. +# • Tell Claude where to write the marker (path shown via ${marker_file}) +# and what JSON shape: { branch, head, message }. +# • Forbid Claude from fabricating the message or paraphrasing the user. +# +# Variables available for interpolation: +# ${command} the exact gh command being gated +# ${subcmd} create | edit | ready +# ${branch} current branch +# ${head} current HEAD sha +# ${marker_file} absolute path Claude must write to +# +# Required regex the user's typed message must match (keep in sync with the +# required phrase shown in ack_instruction below): +REQUIRED_MESSAGE_RE='^reviewed:[[:space:]]+[^[:space:]]' + +ack_instruction="PR-review acknowledgment required before: ${command} + +Branch: ${branch} HEAD: ${head:0:12} + +Invoke the AskUserQuestion tool. The user will pick 'Other' and type their +acknowledgment into the side-panel text field; their typed text comes back +on the question's 'notes' annotation (not the 'answers' field). + +Use this AskUserQuestion payload: + question: 'PR-review acknowledgment for: ${command} + Pick \"Other\" and type your acknowledgment in this exact shape: + reviewed: ' + header: 'PR review' + options: + - label: 'Abort — do not file this PR' + description: 'Cancel the gh command and return to chat.' + - label: 'Show me the diff first, then re-ask' + description: 'Run git diff main...HEAD --stat + git log main..HEAD, + display output, then re-invoke AskUserQuestion.' + multiSelect: false + +After AskUserQuestion returns: + • If the user's typed 'notes' text matches ${REQUIRED_MESSAGE_RE}: + write it verbatim to: + ${marker_file} + as: + { \"branch\": \"${branch}\", + \"head\": \"${head}\", + \"message\": \"\" } + then retry the same gh command. + • If the user picked 'Abort — do not file this PR': + do NOT write marker, do NOT retry; tell the user the PR was aborted + and ask what they want to do instead. + • If the user picked 'Show me the diff first, then re-ask': + run \`git diff main...HEAD --stat && git log main..HEAD --oneline\`, + show the output, then re-invoke the SAME AskUserQuestion. + • If the user typed text in 'Other' but it does not match the regex: + re-invoke the SAME AskUserQuestion with a one-line note explaining + the required shape. Do NOT write the marker. Do NOT retry yet. + +You MUST NOT fabricate, paraphrase, or pre-fill the summary. Only the user's +verbatim typed text from the AskUserQuestion 'Other' field is acceptable as +the ack." +# ───────────────────────────────────────────────────────────────────────────── + +if [[ ! -r "$marker_file" ]]; then + deny "No PR-review acknowledgment on file for: ${command} + +${ack_instruction}" +fi + +marker_branch="$(jq -r '.branch // ""' "$marker_file" 2>/dev/null || echo '')" +marker_head="$(jq -r '.head // ""' "$marker_file" 2>/dev/null || echo '')" +marker_message="$(jq -r '.message // ""' "$marker_file" 2>/dev/null || echo '')" + +marker_mtime="$(stat -f '%m' "$marker_file" 2>/dev/null || stat -c '%Y' "$marker_file" 2>/dev/null || echo 0)" +now_epoch="$(date +%s)" +age=$(( now_epoch - marker_mtime )) + +if [[ "$marker_branch" != "$branch" ]] || \ + [[ "$marker_head" != "$head" ]] || \ + (( age > max_age_seconds )); then + deny "Existing PR-review acknowledgment does not match current state: + marker.branch=${marker_branch} current=${branch} + marker.head=${marker_head} current=${head} + marker age: ${age}s (max ${max_age_seconds}s) + +Re-acknowledgment required. +${ack_instruction}" +fi + +if [[ ! "$marker_message" =~ $REQUIRED_MESSAGE_RE ]]; then + deny "PR-review acknowledgment message is missing or malformed. +Found: ${marker_message:-} +Required to match: ${REQUIRED_MESSAGE_RE} + +${ack_instruction}" +fi + +# Marker is valid for this exact branch+HEAD. Consume it so the next +# gh pr create/edit/ready forces a fresh ack. +rm -f "$marker_file" +exit 0 diff --git a/.claude/hooks/preflight-pr-review.test.sh b/.claude/hooks/preflight-pr-review.test.sh new file mode 100755 index 000000000..77eaac762 --- /dev/null +++ b/.claude/hooks/preflight-pr-review.test.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# Tests for .claude/hooks/preflight-pr-review.sh +# +# Covers: +# 1. Command matcher: gh pr create / edit / ready vs. unrelated bash, +# chained invocations, draft exclusion. +# 2. Gate logic: missing / mismatched / stale / malformed-message / +# consumed marker paths. +# +# Run with: bash .claude/hooks/preflight-pr-review.test.sh +# Exits non-zero on any failure. +set -uo pipefail + +REPO_ROOT="$(git rev-parse --show-toplevel)" +HOOK="$REPO_ROOT/.claude/hooks/preflight-pr-review.sh" +TMP="$(mktemp -d)" +trap 'rm -rf "$TMP"' EXIT + +export CLAUDE_PROJECT_DIR="$TMP" +mkdir -p "$TMP/.claude" + +BRANCH="$(git -C "$REPO_ROOT" branch --show-current)" +HEAD_SHA="$(git -C "$REPO_ROOT" rev-parse HEAD)" + +pass=0 +fail=0 + +run() { + local desc="$1" cmd="$2" expect="$3" out decision + out=$(printf '%s' "$cmd" | jq -Rs '{tool_input:{command:.}}' | "$HOOK") + if [[ -z "$out" ]]; then + decision="passthrough" + else + decision=$(printf '%s' "$out" | jq -r '.hookSpecificOutput.permissionDecision' 2>/dev/null || echo "parse_error") + fi + if [[ "$decision" == "$expect" ]]; then + pass=$((pass + 1)) + printf ' PASS %s\n' "$desc" + else + fail=$((fail + 1)) + printf ' FAIL %s (got=%s expected=%s)\n' "$desc" "$decision" "$expect" + fi +} + +write_marker() { + local message="$1" + jq -nc --arg b "$BRANCH" --arg h "$HEAD_SHA" --arg m "$message" \ + '{branch:$b, head:$h, message:$m}' \ + > "$TMP/.claude/.pr-reviewed.json" +} + +echo "## Matcher: should pass through (not a gated gh pr invocation)" +rm -f "$TMP/.claude/.pr-reviewed.json" +run "ls" "ls" "passthrough" +run "gh pr list (different subcmd)" "gh pr list" "passthrough" +run "gh pr view (different subcmd)" "gh pr view 123" "passthrough" +run "gh issue create (different verb)" "gh issue create -t foo" "passthrough" +run "echo literal mention" "echo 'gh pr create'" "passthrough" +run "git push (other hook's domain)" "git push origin main" "passthrough" +run "heredoc body mentions trigger" $'git commit -m "$(cat <<\'EOF\'\nbody mentions `gh pr create`\nEOF\n)"' "passthrough" +run "multiline w/ backtick trigger" $'git commit -m "fix bug"\n# `gh pr create`' "passthrough" + +echo +echo "## Matcher: draft exclusion (only on create)" +run "gh pr create --draft" "gh pr create --draft -t wip" "passthrough" +run "gh pr create -d short flag" "gh pr create -d -t wip" "passthrough" + +echo +echo "## Matcher: should gate" +run "gh pr create direct" "gh pr create -t foo" "deny" +run "gh pr edit direct" "gh pr edit 42 --body foo" "deny" +run "gh pr ready direct" "gh pr ready 42" "deny" +run "chained with &&" "cd x && gh pr create -t foo" "deny" +run "chained with ;" "echo done; gh pr create -t foo" "deny" +run "command substitution" "out=\$(gh pr create -t foo)" "deny" +run "env var prefix" "FOO=bar gh pr create -t foo" "deny" + +echo +echo "## Gate logic: marker file states" +write_marker "reviewed: refactor auth middleware" +run "valid marker → allow" "gh pr create -t foo" "passthrough" +run "marker consumed on allow" "gh pr create -t foo" "deny" + +write_marker "reviewed: edit body fix" +run "valid marker for edit → allow" "gh pr edit 42" "passthrough" + +write_marker "yes" +run "malformed message → deny" "gh pr create -t foo" "deny" + +write_marker "" +run "empty message → deny" "gh pr create -t foo" "deny" + +write_marker "reviewed: stale" +jq --arg h "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" '.head=$h' \ + "$TMP/.claude/.pr-reviewed.json" > "$TMP/.claude/x.json" \ + && mv "$TMP/.claude/x.json" "$TMP/.claude/.pr-reviewed.json" +run "HEAD mismatch → deny" "gh pr create -t foo" "deny" + +write_marker "reviewed: branch test" +jq --arg b "some-other-branch" '.branch=$b' \ + "$TMP/.claude/.pr-reviewed.json" > "$TMP/.claude/x.json" \ + && mv "$TMP/.claude/x.json" "$TMP/.claude/.pr-reviewed.json" +run "branch mismatch → deny" "gh pr create -t foo" "deny" + +write_marker "reviewed: old" +touch -t 202001010000 "$TMP/.claude/.pr-reviewed.json" +run "stale mtime (>max_age) → deny" "gh pr create -t foo" "deny" + +echo +echo "RESULT: $pass passed, $fail failed" +exit $(( fail > 0 ? 1 : 0 )) diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..cc475f6d2 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,19 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/preflight-claude-md.sh" + }, + { + "type": "command", + "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/preflight-pr-review.sh" + } + ] + } + ] + } +} diff --git a/.claude/skills/adversarial-iteration/SKILL.md b/.claude/skills/adversarial-iteration/SKILL.md new file mode 100644 index 000000000..dea8cd33f --- /dev/null +++ b/.claude/skills/adversarial-iteration/SKILL.md @@ -0,0 +1,188 @@ +--- +name: adversarial-iteration +description: > + Use when iterating on a fix after an adversarial code review (Codex, human + reviewer) surfaces design-level findings — not typos, lint, or formatter work. + Frames the review/fix loop as implement -> [review -> REFLECT -> reproducer -> + fix -> verify -> handoff] where each round runs in a fresh agent to prevent + context accumulation. Provides a four-item cheap-checks list (timeline draw, + upstream contract read, full lifecycle trace, variant enumeration) that catches + recurring failure classes. No git commits during the loop — all changes commit + together after zero-finding termination. Trigger when a review pass surfaces + 2+ findings on a single change, when sibling bugs of an "already-fixed" issue + keep appearing across rounds, or when the user asks "why did I miss this?". + Do NOT use for single-line fixes where the cause is obvious from the diff, + or for initial implementation work (use CLAUDE.md rules). +--- + +# Adversarial Iteration + +Work *with* adversarial review feedback instead of patching symptoms. + +## The loop + +``` +implement -> [spawn round agent -> review -> REFLECT -> reproducer -> fix -> verify -> return summary] --+ + ^ | + +-------- next round (fresh agent, clean context, re-reads files from disk) <--------------+ + + terminates when round agent reports ZERO_FINDINGS. +``` + +Step 1 (implement) is manual. Steps 2+ run inside `/loop` autonomously — +no `AskUserQuestion`, `ExitPlanMode`, or other user gates. When multiple +approaches exist, tag one "Recommended", pick it, continue. The user can +interrupt at any point; you never pause to ask. + +If agents are unavailable, run the next review inline and re-read all +changed files before reviewing. + +## REFLECT + +The unique step. Skipping it produces fixes that pass the named test but seed +the next finding in the same class. + +For each finding, answer: + +1. **What did I do wrong?** One sentence, no euphemisms. +2. **Why did I miss it?** Name the assumption encoded without verifying. +3. **Which `CLAUDE.md` rule did I violate?** Be specific. +4. **What cheap check would have caught it?** (see next section) + +Then across all findings: *what's the unifying shape?* Often a single cognitive +failure caused them all. Name it. + +Common shapes: + +- **Solved the named problem, not the invariant.** +- **Yes-manned own design** — round-1 yes-mans the user; round-2 yes-mans yourself. +- **Generic abstraction without per-instantiation audit.** +- **Layer-ownership leak via assumed invariant.** +- **Tunnel vision on per-unit fix, missed system-level cooperation.** + +If reflection produces a new shape, add it to this list. + +## Cheap checks (do all four before writing fix code) + +1. **Timeline draw** — For concurrent state changes, draw the interleaved + timeline. Find the check-act gap. Two cooperating booleans is almost always + wrong; use `Once`, `compare_exchange`, or a single owner. + +2. **Upstream contract read** — For any value flowing in, read the producer's + contract (grep, 5 lines). Don't assume from the type name. Ask: does this + transformation belong in this layer? + +3. **Full lifecycle trace** — For any fix depending on component cooperation, + trace init through teardown. List every component and what happens to + in-flight work. Especially: what stops first? + +4. **Variant enumeration** — For any generic covering N types, list each + type's destructor/serializer/contract. Verify the generic satisfies every + one. If they differ non-trivially, prefer N type-specific wrappers or + parametrize over a closure. + +## Reproducer test + +Write the test **before** the fix. Verify it FAILS on unfixed code — if it +passes, it's a tautology. The test must touch the *system* the bug lives in, +not just a helper in isolation. Do not commit yet; all changes commit together +after the loop terminates. + +## Plan-mode discipline + +Draft a plan (in the response, not via `ExitPlanMode`) referencing: + +1. The REFLECT root-cause shape this fix addresses. +2. The reproducer test (file + function) and its assertion. +3. Which cheap check identified the design constraint. +4. The chosen primitive and why it's the smallest satisfying change. +5. Adjacent contracts that also need tests. + +## Verify + +- Reproducer flips red -> green (necessary, not sufficient). +- Adjacent-contract tests from REFLECT also pass. +- If the next review surfaces a sibling of the same finding, REFLECT wasn't + deep enough — go back to REFLECT, don't just patch. + +## Round handoff (MANDATORY — blocks next round) + +Do NOT commit during the loop. All changes accumulate as working-tree edits +and are committed once after the loop terminates with zero findings. + +After verify passes, do these IN ORDER before spawning the next round. +Skipping any one is a protocol violation. + +### 1. Audit comments + +`grep -rn 'round\|TODO.*round\|HACK\|FIXME\|debugging\|TEMP' `. +Remove round-N narrative, debugging scaffolding, and stale TODOs from the +working tree before the next agent reads those files. + +### 2. Build round summary + +Construct this structured summary for the orchestrator to carry forward: + +``` +### Round N complete +- Findings: +- Root-cause shape: +- Fixes: +- Files changed: +- Tests: pass/fail +- Remaining risk: +``` + +### 3. Spawn fresh agent for next round + +Read [references/round-agent-prompt.md](references/round-agent-prompt.md) +for the agent prompt template. Populate it with: + +- The round number +- All accumulated prior round summaries +- The list of changed files + +Spawn a **general-purpose agent** (not codex:rescue — the round agent needs +Read/Write/Edit/Bash for the full REFLECT->fix cycle). The fresh context +window is the compaction mechanism — no `/compact` needed. + +The agent runs its full round (review -> REFLECT -> reproducer -> fix -> +verify) and returns a structured round summary. The orchestrator evaluates +whether the summary contains `ZERO_FINDINGS`. + +## Termination + +The loop ends when **all three** hold: + +1. Latest round agent reports `ZERO_FINDINGS` after re-reading all changed files. +2. Every finding has a reproducer test, verified green. +3. Every adjacent-contract test from each REFLECT passes. + +No other exit. Sibling findings recurring -> previous REFLECT was too shallow, +go back to REFLECT. Fix getting expensive -> plan the architectural change as +this iteration's step, same loop. Never propose "stop here, accept risk." + +## Anti-patterns + +- **Reflection-as-apology.** "Should have been more careful" is not a root + cause. "Never read `ExecStdout::next`" is. +- **Test polarity flip.** If the test was green on broken code, it's + confirmation bias, not a reproducer. +- **One reproducer, no class coverage.** If reflection identified 6 types and + only one has a test, the next review finds the other 5. +- **Treating review as checklist.** The review surfaces symptoms; REFLECT finds + the design choice that produced them all. +- **Skipping agent spawn.** Running the next review in the same context instead + of spawning a fresh agent. Accumulated context degrades review quality and + wastes tokens. The pull to "just run the review here" is strong — resist it. + A new agent re-reads files from disk, not from stale cached content. +- **Bloated round summaries.** Including full diffs or debug traces in the + round summary defeats the purpose. The summary is compressed signal, not a + transcript. Five lines per round, not fifty. + +## This project's codebase + +- Adversarial review: `/codex:adversarial-review` +- Loop runner: `/loop` (dynamic-pacing, omit interval) +- Commit pattern: all changes commit together after zero-finding termination; + conversation history is the iteration audit trail. diff --git a/.claude/skills/adversarial-iteration/references/round-agent-prompt.md b/.claude/skills/adversarial-iteration/references/round-agent-prompt.md new file mode 100644 index 000000000..c455a2003 --- /dev/null +++ b/.claude/skills/adversarial-iteration/references/round-agent-prompt.md @@ -0,0 +1,92 @@ +# Round Agent Prompt Template + +Construct the agent prompt by filling in the bracketed sections below. +Spawn as a general-purpose agent with `mode: "auto"`. + +--- + +You are running round [N] of an adversarial iteration loop on a codebase. + +## Prior round summaries + +[Paste accumulated round summaries here, one block per prior round. +If this is round 1, write: "This is the first review round."] + +## Files changed so far + +[List file paths from all prior rounds. Example: +- src/cli/src/commands/serve/handlers/executions.rs +- apps/runner/pkg/boxlite/exec_manager.go] + +You MUST re-read every file in this list from disk before reviewing. +Do NOT rely on any cached content — you have a fresh context. + +## Your task + +1. **Re-read** all files listed above using the Read tool. + +2. **Review**: The orchestrator (parent) has already obtained adversarial + review findings and pasted them in the section below titled + "Codex findings for this round." Do NOT attempt to spawn + `codex:codex-rescue` or any nested Agent — the Agent tool is not + available inside subagents in this harness, and the codex companion + runs as a background bash task whose Monitor events fire on the + parent, not back into you. (Trying it anyway burns a 15-min turn + waiting for an event that will never reach you.) + + If the "Codex findings" section is empty or absent, treat that as + zero findings and skip to step 3. + +3. **If zero findings**: Return this exact summary and stop: + ``` + ### Round [N] complete + - Findings: none + - Status: ZERO_FINDINGS + ``` + +4. **If findings exist**, follow the full protocol: + + a. **REFLECT** on each finding: + - What did I do wrong? (one sentence) + - Why did I miss it? (name the unverified assumption) + - Which CLAUDE.md rule was violated? + - What cheap check catches it? + - Across all findings: what is the unifying shape? + + b. **Cheap checks** (all four, before writing fix code): + - Timeline draw (concurrent state) + - Upstream contract read (grep producer, 5 lines) + - Full lifecycle trace (init through teardown) + - Variant enumeration (list each type's contract) + + c. **Reproducer test**: Write BEFORE the fix. Verify it FAILS. + + d. **Plan**: Reference REFLECT shape, reproducer, cheap check, + chosen primitive, adjacent contracts. + + e. **Fix**: Implement the smallest change that satisfies the constraint. + + f. **Verify**: Reproducer flips red to green. Adjacent tests pass. + + g. **Audit**: `grep -rn 'round\|HACK\|FIXME\|debugging\|TEMP'` + on changed files. Remove any round-N narrative. + + h. **Return** structured summary: + ``` + ### Round [N] complete + - Findings: [list] + - Root-cause shape: [from REFLECT] + - Fixes: [what changed and why] + - Files changed: [paths] + - Tests: pass/fail + - Remaining risk: [if any] + ``` + +## Rules + +- Do NOT commit. All changes stay as working-tree edits. +- Do NOT ask the user questions. Pick "Recommended" and continue. +- Do NOT skip REFLECT. It is the difference between fixing symptoms + and fixing the design. +- Re-read every file in the "files changed" list from disk before reviewing. +- Keep the returned summary under 10 lines. It must be signal, not transcript. diff --git a/.codex/hooks.json b/.codex/hooks.json new file mode 100644 index 000000000..c3e3de3aa --- /dev/null +++ b/.codex/hooks.json @@ -0,0 +1,21 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "^Bash$", + "hooks": [ + { + "type": "command", + "command": "/usr/bin/env bash \"$(git rev-parse --show-toplevel)/.claude/hooks/preflight-claude-md.sh\"", + "statusMessage": "Checking CLAUDE.md audit" + }, + { + "type": "command", + "command": "/usr/bin/env bash \"$(git rev-parse --show-toplevel)/.claude/hooks/preflight-pr-review.sh\"", + "statusMessage": "Checking PR review acknowledgement" + } + ] + } + ] + } +} diff --git a/.dockerignore b/.dockerignore index 5bdda4f08..942fcb6f4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,6 +12,11 @@ apps/.nx # Build outputs target +dist +apps/dist +coverage +apps/coverage +*.log # Git .git diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..4fdbad003 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,11 @@ +# Code ownership for boxlite-ai/boxlite. +# Order matters: the LAST matching pattern for a path wins. +# Referenced teams must have explicit repo access or the rule is ignored by GitHub. + +# All code is owned by the reviewers team. +* @boxlite-ai/reviewers + +# Legal and contributor policy changes require project owner review. +/CONTRIBUTING.md @DorianZheng +/docs/legal/ @DorianZheng +/.github/CODEOWNERS @DorianZheng diff --git a/.github/assets/boxlite-banner-dark.png b/.github/assets/boxlite-banner-dark.png new file mode 100644 index 000000000..13d1f7fe7 Binary files /dev/null and b/.github/assets/boxlite-banner-dark.png differ diff --git a/.github/assets/boxlite-banner-light.png b/.github/assets/boxlite-banner-light.png new file mode 100644 index 000000000..5b6ef9e38 Binary files /dev/null and b/.github/assets/boxlite-banner-light.png differ diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 7042cac9a..49f612d62 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -129,53 +129,46 @@ Runs code quality checks. 4. `node` - Run Node lint and format checks via `make lint:node` and `make fmt:check:node` 5. `c` - Run C SDK lint and format checks via `make lint:c` and `make fmt:check:c` -### `e2e-test.yml` +### `codeql.yml` -Runs VM-based E2E integration tests on an ephemeral AWS EC2 self-hosted runner. +Runs CodeQL code scanning (advanced setup) across all analyzed languages. -**Why:** GitHub-hosted runners (including larger paid runners) do not support `/dev/kvm`. BoxLite integration tests need real VMs via libkrun. +**Why advanced setup:** CodeQL *default setup* does not analyze pull requests +from forks, so the `code_scanning` ruleset rule ("Require code scanning +results") permanently blocks fork PRs. Advanced setup runs on `pull_request`, +so fork PRs in this public repo are scanned and the gate is satisfiable without +an admin bypass. -**Architecture:** Three-job ephemeral pattern: -1. `start-runner` (ubuntu-latest) — launches an AWS EC2 c8i.2xlarge instance, registers an ephemeral GitHub Actions runner -2. `e2e-tests` (self-hosted) — builds runtime, runs all integration test suites -3. `stop-runner` (ubuntu-latest, `if: always()`) — terminates instance, deregisters runner +**Bootstrap guard:** GitHub rejects advanced CodeQL uploads while default setup +is enabled. The workflow is dormant until repository variable +`CODEQL_ADVANCED_SETUP_ENABLED` is set to `true`. **Triggers:** -- Push to `main` (path-filtered to `src/`, `sdks/`, `Cargo.*`) -- Pull request with `e2e` label (cost-gated) -- Manual dispatch (`workflow_dispatch`) - -**Cost:** ~$0.34/hr (c8i.2xlarge). Typical run: 15-25 min → ~$0.09-0.14 per run. - -**Safety mechanisms:** -- `--ephemeral` runner auto-deregisters after one job -- `if: always()` ensures cleanup on failure/cancellation -- 45-minute self-destruct timer on the instance (EC2 self-termination) -- Runner deregistration API call (belt-and-suspenders) -- 35-minute job timeout prevents runaway tests -- `instance-initiated-shutdown-behavior: terminate` auto-cleans on shutdown +- Push to `main` +- Pull requests against `main` (including fork PRs) +- Manual dispatch +- Weekly schedule (Mondays 03:31 UTC) -**Authentication:** GitHub OIDC → AWS STS (no stored AWS credentials). +**Jobs:** +1. `analyze` - Matrix over `actions`, `c-cpp`, `go`, `javascript-typescript`, `python`, `rust`. All use `build-mode: none` (source-only, no compile) except `go`, which requires `autobuild` (Go's extractor must observe a build). Uses `github/codeql-action@v4`. -**Required secret:** -- `GH_PAT` - GitHub PAT with `repo` scope (for runner registration API) +**Activation sequence:** +1. Merge this workflow while `CODEQL_ADVANCED_SETUP_ENABLED` is unset or `false`, so default setup remains the active scanner. +2. Disable CodeQL default setup. +3. Set repository variable `CODEQL_ADVANCED_SETUP_ENABLED=true`. +4. Trigger a new push, pull request update, or manual dispatch and verify CodeQL analysis uploads successfully. +5. Roll back by setting `CODEQL_ADVANCED_SETUP_ENABLED=false` and re-enabling default setup. -**Required variables** (Settings → Variables → Actions): -- `AWS_ACCOUNT_ID` - AWS account ID -- `AWS_SUBNET_ID` - Subnet with auto-assign public IP -- `AWS_SECURITY_GROUP_ID` - Security group allowing outbound HTTPS +### `e2e-local.yml` -**Required AWS resources** (provisioned by `scripts/ci/setup-aws-oidc.sh`): -- OIDC identity provider (`token.actions.githubusercontent.com`) -- IAM role `boxlite-e2e-github-actions` with trust policy for this repo -- IAM instance profile `boxlite-e2e-runner` with `ec2:TerminateInstances` on self -- Subnet with internet access + security group (outbound 443) +Runs VM-based integration tests on a persistent, self-hosted AWS EC2 runner — GitHub-hosted +runners can't expose `/dev/kvm`, which BoxLite's libkrun microVMs need. The instance is +started before a run and stopped (not terminated) after, so caches persist. AWS auth is +GitHub OIDC → STS; runner registration is a GitHub App. A pull request must carry the +`e2e-local` label (the cost gate) to run. -**Jobs:** -1. `should-run` - Gate check (label present on PR?) -2. `start-runner` - Launch EC2 c8i.2xlarge, register runner, wait for online -3. `e2e-tests` - Build runtime, run Rust/CLI/Python/Node/C integration tests -4. `stop-runner` - Terminate instance, deregister runner +See the **[E2E Local CI runbook](../../docs/ci/e2e-local.md)** for the jobs, the instance, +one-time provisioning (`scripts/ci/setup-ci-runner.sh`), and troubleshooting. ## Trigger Behavior @@ -227,7 +220,7 @@ Additional platforms (darwin-x64, linux-arm64-gnu) can be added to `config.yml` - `CARGO_REGISTRY_TOKEN` - crates.io API token for publishing Rust crates - `PYPI_API_TOKEN` - PyPI API token for publishing Python wheels - `NPM_TOKEN` - npm access token for publishing Node.js packages -- `GH_PAT` - GitHub PAT with `repo` scope (for self-hosted runner registration) +- `GH_APP_PRIVATE_KEY` - GitHub App private key for self-hosted E2E runner registration (see the [E2E Local CI runbook](../../docs/ci/e2e-local.md)) Set these in repository Settings → Secrets and variables → Actions. diff --git a/.github/workflows/api-client-drift.yml b/.github/workflows/api-client-drift.yml new file mode 100644 index 000000000..88871987b --- /dev/null +++ b/.github/workflows/api-client-drift.yml @@ -0,0 +1,103 @@ +name: API client drift + +# Regenerates the OpenAPI spec and the generated clients +# (apps/libs/api-client, apps/api-client-go, apps/libs/analytics-api-client) +# and fails if the committed clients differ — the committed clients must +# always match their specs (apps/api source for the product clients, +# apps/libs/analytics-api-client/swagger.json for the analytics client). +# Also guards the contract boundary: the cloud spec must not re-grow +# box-operation paths, and openapi/box.openapi.yaml must stay tenancy-free. +# Ported from daytonaio/daytona's `format-lint-api-clients` PR check. + +on: + pull_request: + paths: + - 'apps/**' + - 'openapi/box.openapi.yaml' + - '.github/workflows/api-client-drift.yml' + +permissions: + contents: read + +jobs: + check-generated-clients: + name: Regenerate API clients and diff + runs-on: ubuntu-latest + services: + # generate-openapi.ts boots the full NestJS app; SKIP_CONNECTIONS=true + # defers DB/queue connections but bootstrap still issues Redis commands + # (notification pub/sub), so a real Redis avoids a retry-exhaustion race. + redis: + image: redis:7 + ports: + - 6379:6379 + env: + REDIS_HOST: localhost + defaults: + run: + working-directory: apps + steps: + - uses: actions/checkout@v5 + + # corepack must be enabled before setup-node's yarn cache probe runs + - name: Enable corepack (yarn 4) + run: corepack enable + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: yarn + cache-dependency-path: apps/yarn.lock + + - uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: temurin + + - name: Install dependencies + run: yarn install --immutable + + - name: Prepare offline app env + run: | + printf 'DEFAULT_PACKAGE_VERSION=0.0.0-dev\n' > .env + cp api/.env.example api/.env + + - name: Pin openapi-generator version + run: | + openapi_version="$(node -e "console.log(require('./openapitools.json')['generator-cli'].version)")" + yarn openapi-generator-cli version-manager set "$openapi_version" + + - name: Regenerate API clients + run: | + yarn nx run api-client:generate:api-client + yarn nx run api-client-go:generate:api-client + yarn nx run analytics-api-client:generate:api-client + + - name: Contract boundary guard (single-owner contracts) + run: | + # The exported cloud spec must not re-grow box-operation endpoints — + # those belong to openapi/box.openapi.yaml (served by boxlite-rest). + spec=dist/apps/api/openapi.json + if [ ! -f "$spec" ]; then + echo "::error::$spec not found; the openapi export step must run before this guard." + exit 1 + fi + if jq -r '.paths | keys[]' "$spec" | grep -E '/toolbox/|/exec|/files|/workspace'; then + echo "::error::Cloud API spec contains box-operation paths. Box operations belong to the Box API contract (openapi/box.openapi.yaml), served by apps/api/src/boxlite-rest." + exit 1 + fi + # The Box API dialect must stay tenancy-free so one SDK dialect works + # against local, self-hosted, and cloud servers. + if grep -nE 'organizationId|/organizations|billing' ../openapi/box.openapi.yaml; then + echo "::error::Box API spec contains cloud/tenancy concepts. Those belong to the cloud API (apps/api controllers outside boxlite-rest)." + exit 1 + fi + + - name: Fail on drift + run: | + if [ -n "$(git status --porcelain -- libs/api-client api-client-go libs/analytics-api-client)" ]; then + git status --porcelain -- libs/api-client api-client-go libs/analytics-api-client + git diff -- libs/api-client api-client-go libs/analytics-api-client | head -200 + echo "::error::Generated API clients are stale. Run 'yarn nx run api-client:generate:api-client && yarn nx run api-client-go:generate:api-client && yarn nx run analytics-api-client:generate:api-client' in apps/ and commit the result." + exit 1 + fi diff --git a/.github/workflows/build-runner-binary.yml b/.github/workflows/build-runner-binary.yml index 4fe736f4b..9c368c972 100644 --- a/.github/workflows/build-runner-binary.yml +++ b/.github/workflows/build-runner-binary.yml @@ -108,12 +108,17 @@ jobs: VERSION: ${{ steps.version.outputs.version }} run: | tar czf "boxlite-runner-v${VERSION}-linux-amd64.tar.gz" -C /tmp boxlite-runner + # Integrity manifest the runner's user-data verifies before installing + # the binary (apps/infra/sst.config.ts buildRunnerUserData). + sha256sum "boxlite-runner-v${VERSION}-linux-amd64.tar.gz" > "boxlite-runner-v${VERSION}-linux-amd64.tar.gz.sha256" - name: Upload artifact uses: actions/upload-artifact@v4 with: name: runner-linux-amd64 - path: boxlite-runner-v*-linux-amd64.tar.gz + path: | + boxlite-runner-v*-linux-amd64.tar.gz + boxlite-runner-v*-linux-amd64.tar.gz.sha256 if-no-files-found: error retention-days: ${{ needs.config.outputs.artifact-retention-days }} @@ -145,5 +150,7 @@ jobs: uses: softprops/action-gh-release@v2 with: tag_name: v${{ steps.version.outputs.version }} - files: dist/boxlite-runner-v*.tar.gz + files: | + dist/boxlite-runner-v*.tar.gz + dist/boxlite-runner-v*.tar.gz.sha256 fail_on_unmatched_files: true diff --git a/.github/workflows/build-runtime.yml b/.github/workflows/build-runtime.yml index 984f673f1..382854725 100644 --- a/.github/workflows/build-runtime.yml +++ b/.github/workflows/build-runtime.yml @@ -1,18 +1,32 @@ -# Build BoxLite core runtime, upload artifacts, and publish to crates.io. +# Build BoxLite core runtime + CLI, upload artifacts, and publish to crates.io. # -# This workflow builds the Rust runtime and populates the shared cache. -# On push to main: chains after Warm Caches workflow for sccache hits. -# On release: builds, uploads runtime tarballs to GitHub Release, and publishes -# Rust crates to crates.io (requires CARGO_REGISTRY_TOKEN secret). +# This workflow builds the Rust runtime and the `boxlite` CLI binary, then +# populates the shared sccache cache. +# +# Triggers: +# - push to main (via Warm Caches workflow_run): sccache warm-up only, no publish. +# - release: published: builds and uploads +# - boxlite-runtime-v-.tar.gz (for `cargo install` consumers) +# - boxlite-cli-v-.tar.gz (self-contained CLI binary; +# runtime embedded via include_bytes!) +# - per-file .sha256 sidecars +# - SHA256SUMS (combined integrity manifest) +# to the GitHub Release, then publishes Rust crates to crates.io +# (requires CARGO_REGISTRY_TOKEN). +# - workflow_dispatch: manual build, no publish. +# +# Note: SDK workflows (build-wheels, build-node, build-c) also gate on +# `release: published`. To ship a release, push the tag and then create +# the release (e.g. `gh release create v0.9.4 --generate-notes +# --verify-tag`); the user-token-created release.published event fans out +# to every workflow. Tag-only pushes do NOT publish — see the deferred +# RELEASE_PAT alternative if that UX is wanted. # # Platform-specific considerations: # - Linux: Uses manylinux_2_28 container for glibc compatibility # - Guest binary built on host first (Ubuntu has musl-tools, manylinux doesn't) # - Shim/libkrun built in container (needs glibc 2.28) # - macOS: Builds directly on runner (ARM64 only) -# -# Note: SDK workflows (build-wheels, build-node) are self-contained -# and trigger independently on release. name: Build Runtime on: @@ -181,20 +195,110 @@ jobs: if: github.event_name == 'release' permissions: contents: write + id-token: write + attestations: write steps: + - name: Checkout (for scripts/release/install.sh.template) + uses: actions/checkout@v4 + - name: Download all build artifacts uses: actions/download-artifact@v4 with: path: dist merge-multiple: true + # Per-tarball sidecars come first so the install.sh render step can read + # them to embed checksums for the fast path. + - name: Generate per-tarball .sha256 sidecars + working-directory: dist + run: | + for f in boxlite-runtime-*.tar.gz boxlite-cli-*.tar.gz; do + sha256sum "$f" > "$f.sha256" + done + ls -1 *.sha256 + + - name: Render install.sh for this release + working-directory: dist + env: + REF_NAME: ${{ github.ref_name }} + run: | + # github.ref_name on a tag push is the bare tag (e.g. "v0.9.4"); + # on a release event, the release's tag_name is exposed the same way. + VERSION="$REF_NAME" + case "$VERSION" in + v*) ;; + *) VERSION="v$VERSION" ;; # normalize for safety + esac + + sha_for() { + awk '{print $1}' < "boxlite-cli-${VERSION}-$1.tar.gz.sha256" + } + DARWIN_ARM64=$(sha_for aarch64-apple-darwin) + LINUX_X64=$(sha_for x86_64-unknown-linux-gnu) + LINUX_ARM64=$(sha_for aarch64-unknown-linux-gnu) + + sed \ + -e "s|__VERSION__|${VERSION}|g" \ + -e "s|__SHA_AARCH64_APPLE_DARWIN__|${DARWIN_ARM64}|g" \ + -e "s|__SHA_X86_64_UNKNOWN_LINUX_GNU__|${LINUX_X64}|g" \ + -e "s|__SHA_AARCH64_UNKNOWN_LINUX_GNU__|${LINUX_ARM64}|g" \ + ../scripts/release/install.sh.template > install.sh + chmod +x install.sh + + # Sanity-check substitution succeeded. + if grep -q '__VERSION__\|__SHA_' install.sh; then + echo "ERROR: install.sh still contains placeholders" >&2 + exit 1 + fi + # Gate the release on a rendered-script syntax check; a broken + # installer would otherwise only surface on user machines. + sh -n install.sh + head -25 install.sh + + # install.sh sidecar lives in the same integrity envelope as the + # tarballs: users who pipe `curl ... install.sh | sh` are running this + # script as their own user, so the wrapper itself must be verifiable + # (sha256 + sigstore attestation), not just its tarball payload. + - name: Generate install.sh.sha256 sidecar + working-directory: dist + run: | + sha256sum install.sh > install.sh.sha256 + cat install.sh.sha256 + + - name: Generate SHA256SUMS + working-directory: dist + run: | + sha256sum boxlite-runtime-*.tar.gz boxlite-cli-*.tar.gz install.sh > SHA256SUMS + cat SHA256SUMS + + # Final gate against installer regressions before the release goes + # public. The lint workflow also runs this on PRs touching the + # template; this re-run validates the actually-rendered dist/install.sh + # (placeholders substituted, real digests embedded). + - name: Validate rendered installer + env: + BOXLITE_INSTALL_TEMPLATE: dist/install.sh + run: bash scripts/release/test_install.sh + + - name: Attest build provenance + uses: actions/attest-build-provenance@v2 + with: + subject-path: | + dist/boxlite-runtime-*.tar.gz + dist/boxlite-cli-*.tar.gz + dist/install.sh + - name: Upload to release uses: softprops/action-gh-release@v2 with: files: | dist/boxlite-runtime-*.tar.gz dist/boxlite-cli-*.tar.gz + dist/*.sha256 + dist/SHA256SUMS + dist/install.sh + dist/install.sh.sha256 fail_on_unmatched_files: true # Publish Rust crates to crates.io after runtime tarballs are on GitHub Release. diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..24af84a70 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,75 @@ +# CodeQL advanced setup. +# +# Replaces CodeQL "default setup", which does not analyze pull requests from +# forks. Advanced setup runs on `pull_request`, so fork PRs are scanned and the +# "Require code scanning results" ruleset rule can be satisfied without an admin +# bypass. +# +# GitHub rejects advanced CodeQL uploads while default setup is enabled, so keep +# this workflow dormant until repository variable CODEQL_ADVANCED_SETUP_ENABLED +# is set to "true" after default setup is disabled. +# +# Languages use `build-mode: none` (source-only analysis, no compile step) where +# CodeQL supports it. Go does not support `none` — its extractor must observe a +# build — so Go uses `autobuild` (the same mode default setup used successfully +# for this repo). +name: CodeQL + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + schedule: + # Weekly full scan of the default branch (Mondays 03:31 UTC). + - cron: "31 3 * * 1" + +# Cancel in-progress analysis when a PR is updated. +concurrency: + group: codeql-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + analyze: + if: vars.CODEQL_ADVANCED_SETUP_ENABLED == 'true' + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + permissions: + # Required to upload code scanning results. + security-events: write + # Required by the CodeQL action to read the repository and Actions runs. + contents: read + actions: read + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: c-cpp + build-mode: none + - language: go + build-mode: autobuild + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + - language: rust + build-mode: none + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + + - name: Initialize CodeQL + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 + with: + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/e2e-local.yml b/.github/workflows/e2e-local.yml new file mode 100644 index 000000000..0ce93cc70 --- /dev/null +++ b/.github/workflows/e2e-local.yml @@ -0,0 +1,504 @@ +# Run VM-based E2E integration tests on a persistent AWS EC2 runner. +# +# GitHub-hosted runners do not support nested virtualization / KVM. +# This workflow starts a pre-configured EC2 instance, runs integration tests, +# then stops it. The instance is never terminated — build caches persist. +# +# Architecture: +# start-runner (ubuntu-latest) → e2e-tests (self-hosted) → stop-runner (ubuntu-latest) +# Start stopped EC2 Build + run tests Stop EC2 +# Wait for runner online 50-min timeout if: always() +# +# Concurrency: only ONE e2e run at a time (single persistent instance). +# Queued runs wait; in-progress runs are cancelled by newer pushes. +# +# Cost: c8i.4xlarge on-demand rate only while running + ~$4/mo EBS storage. +# Subsequent runs skip setup/compilation (cached) → ~5-10 min → ~$0.03/run. +# +# Authentication: +# AWS: GitHub OIDC → AWS STS (no stored AWS credentials) +# GitHub: GitHub App → short-lived installation token (no PAT) +# +# Setup: ./scripts/ci/setup-ci-runner.sh +# Runbook: docs/ci/e2e-local.md +name: E2E local + +on: + push: + branches: [main] + paths: + - 'src/boxlite/**' + - 'src/shared/**' + - 'src/cli/**' + - 'src/guest/**' + - 'sdks/**' + - '**/Cargo.toml' + - 'Cargo.lock' + - '.github/workflows/e2e-local.yml' + pull_request: + types: [labeled, synchronize] + branches: [main] + paths: + - 'src/boxlite/**' + - 'src/shared/**' + - 'src/cli/**' + - 'src/guest/**' + - 'sdks/**' + - '**/Cargo.toml' + - 'Cargo.lock' + workflow_dispatch: + inputs: + debug: + description: 'Open SSH session on failure (via tmate)' + type: boolean + default: false + +# Single global concurrency group — only one e2e run at a time. +# The persistent instance can only serve one job. Newer pushes cancel older runs. +concurrency: + group: e2e-runner + cancel-in-progress: true + +env: + AWS_REGION: us-east-1 + AWS_ROLE_ARN: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/boxlite-e2e-github-actions + EC2_INSTANCE_ID: ${{ vars.EC2_E2E_INSTANCE_ID }} + EC2_INSTANCE_TYPE: c8i.4xlarge + EC2_AMI_ID: ami-05cf1e9f73fbad2e2 + EC2_SUBNET_IDS: ${{ vars.AWS_SUBNET_IDS || vars.AWS_SUBNET_ID }} + EC2_SECURITY_GROUP_ID: ${{ vars.AWS_SECURITY_GROUP_ID }} + RUNNER_VERSION: '2.334.0' + RUNNER_LABEL: boxlite-e2e + +jobs: + # ========================================================================= + # Gate: PRs require 'e2e-local' label (only maintainers can add it) + # ========================================================================= + should-run: + runs-on: ubuntu-latest + outputs: + run: ${{ steps.check.outputs.run }} + steps: + - name: Check trigger conditions + id: check + run: | + if [ "${{ github.event_name }}" = "pull_request" ]; then + LABELS='${{ toJson(github.event.pull_request.labels.*.name) }}' + if echo "$LABELS" | jq -e 'index("e2e-local")' > /dev/null 2>&1; then + echo "run=true" >> "$GITHUB_OUTPUT" + else + echo "run=false" >> "$GITHUB_OUTPUT" + fi + else + echo "run=true" >> "$GITHUB_OUTPUT" + fi + + # ========================================================================= + # Job 1: Start the persistent EC2 instance and wait for runner + # ========================================================================= + start-runner: + name: Start E2E Runner + needs: should-run + if: needs.should-run.outputs.run == 'true' + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + outputs: + instance-id: ${{ steps.ensure-instance.outputs.instance-id }} + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v1 + with: + app-id: ${{ vars.GH_APP_ID }} + private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} + + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ env.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + role-session-name: e2e-start-${{ github.run_id }} + + - name: Start or create EC2 instance + id: ensure-instance + run: | + INSTANCE_ID="${{ env.EC2_INSTANCE_ID }}" + + # Fallback: discover by tag if variable is empty + if [ -z "$INSTANCE_ID" ]; then + INSTANCE_ID=$(aws ec2 describe-instances \ + --filters "Name=tag:Name,Values=boxlite-e2e" "Name=instance-state-name,Values=running,stopped,stopping,pending" \ + --query "Reservations[0].Instances[0].InstanceId" --output text 2>/dev/null || echo "") + [ "$INSTANCE_ID" = "None" ] && INSTANCE_ID="" + fi + + # Check if instance exists and get its state + if [ -n "$INSTANCE_ID" ]; then + STATE=$(aws ec2 describe-instances \ + --instance-ids "$INSTANCE_ID" \ + --query "Reservations[0].Instances[0].State.Name" --output text 2>/dev/null || echo "not-found") + else + STATE="not-found" + fi + + if [ "$STATE" = "running" ]; then + echo "Instance already running" + echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" + + elif [ "$STATE" = "stopped" ]; then + echo "Starting instance..." + aws ec2 start-instances --instance-ids "$INSTANCE_ID" + aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" + echo "Instance started" + echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" + + elif [ "$STATE" = "stopping" ] || [ "$STATE" = "pending" ]; then + echo "Instance in transitional state ($STATE), waiting..." + aws ec2 wait instance-stopped --instance-ids "$INSTANCE_ID" 2>/dev/null || \ + aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" 2>/dev/null || true + # Re-check and start if stopped + STATE=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \ + --query "Reservations[0].Instances[0].State.Name" --output text) + if [ "$STATE" = "stopped" ]; then + aws ec2 start-instances --instance-ids "$INSTANCE_ID" + aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" + elif [ "$STATE" != "running" ]; then + echo "::error::Instance stuck in state: $STATE" + exit 1 + fi + echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" + + else + echo "Instance not found or terminated. Creating new one..." + + # Generate runner registration token for user-data + REG_TOKEN=$(curl -sf -X POST \ + -H "Authorization: token ${{ steps.app-token.outputs.token }}" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/actions/runners/registration-token" \ + | jq -r .token) + + # Build user-data + cat > /tmp/user-data.sh << 'USERDATA' + #!/bin/bash + set -euo pipefail + export HOME=/root + modprobe kvm_intel 2>/dev/null || modprobe kvm_amd 2>/dev/null || true + chmod 666 /dev/kvm 2>/dev/null || true + usermod -aG kvm root 2>/dev/null || true + # User-data only installs the actions-runner. Build deps + # (apt + rustup + cargo-nextest) are installed by the + # e2e-tests job's "Install build dependencies" step so they + # stream to the GitHub Actions log and don't race the + # runner-online poll. + apt-get update -qq && apt-get install -y -qq curl jq tar + RUNNER_VERSION="__RUNNER_VERSION__" + mkdir -p /opt/actions-runner && cd /opt/actions-runner + if [ ! -f ./config.sh ]; then + curl -fsSL "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" | tar xz + fi + RUNNER_ALLOW_RUNASROOT=1 ./config.sh \ + --url "https://github.com/__REPO__" \ + --token "__REG_TOKEN__" \ + --name "boxlite-e2e" \ + --labels "self-hosted,linux,x64,kvm,boxlite-e2e" \ + --unattended --disableupdate --replace || true + ./svc.sh install root || true + ./svc.sh start || true + USERDATA + + sed -i "s|__REPO__|${{ github.repository }}|g" /tmp/user-data.sh + sed -i "s|__RUNNER_VERSION__|${{ env.RUNNER_VERSION }}|g" /tmp/user-data.sh + sed -i "s|__REG_TOKEN__|${REG_TOKEN}|g" /tmp/user-data.sh + + # Launch instance — try each subnet (AZ) until one has capacity + INSTANCE_ID="" + IFS=',' read -ra SUBNET_LIST <<< "${{ env.EC2_SUBNET_IDS }}" + for SUBNET in "${SUBNET_LIST[@]}"; do + SUBNET=$(echo "$SUBNET" | xargs) + echo "Attempting launch in subnet $SUBNET..." + if RESULT=$(aws ec2 run-instances \ + --instance-type "${{ env.EC2_INSTANCE_TYPE }}" \ + --image-id "${{ env.EC2_AMI_ID }}" \ + --subnet-id "$SUBNET" \ + --security-group-ids "${{ env.EC2_SECURITY_GROUP_ID }}" \ + --iam-instance-profile Name=boxlite-e2e-runner \ + --cpu-options "NestedVirtualization=enabled" \ + --user-data file:///tmp/user-data.sh \ + --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":50,"VolumeType":"gp3","DeleteOnTermination":false}}]' \ + --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=boxlite-e2e},{Key=Purpose,Value=boxlite-e2e}]" \ + --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \ + --query "Instances[0].InstanceId" --output text 2>&1); then + INSTANCE_ID="$RESULT" + echo "Launched $INSTANCE_ID in subnet $SUBNET" + break + else + echo "::warning::Subnet $SUBNET unavailable: $RESULT" + fi + done + + if [ -z "$INSTANCE_ID" ]; then + echo "::error::Failed to launch instance in any availability zone" + exit 1 + fi + + echo "Created instance: $INSTANCE_ID" + aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" + echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" + + # Try to save instance ID for faster lookup on the next run. + # Best-effort: tag-based fallback (Name=boxlite-e2e) handles rediscovery + # if this fails (e.g., the auth token lacks variables:write). + if ! gh variable set EC2_E2E_INSTANCE_ID --body "$INSTANCE_ID" -R "${{ github.repository }}"; then + echo "::warning::Could not persist EC2_E2E_INSTANCE_ID. Future runs will rediscover by tag." + fi + fi + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + + - name: Wait for runner to come online + run: | + TIMEOUT=180 + ELAPSED=0 + INTERVAL=10 + + echo "Waiting for runner '${{ env.RUNNER_LABEL }}' to come online..." + + while [ $ELAPSED -lt $TIMEOUT ]; do + STATUS=$(curl -sf \ + -H "Authorization: token ${{ steps.app-token.outputs.token }}" \ + -H "Accept: application/vnd.github+json" \ + "https://api.github.com/repos/${{ github.repository }}/actions/runners" \ + | jq -r '.runners[] | select(.labels[].name == "${{ env.RUNNER_LABEL }}") | .status // empty' \ + 2>/dev/null || echo "") + + if [ "$STATUS" = "online" ]; then + echo "Runner is online" + exit 0 + fi + + echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)" + sleep $INTERVAL + ELAPSED=$((ELAPSED + INTERVAL)) + done + + echo "::error::Runner did not come online within ${TIMEOUT}s" + exit 1 + + # ========================================================================= + # Job 2: Run integration tests on the persistent runner + # ========================================================================= + e2e-tests: + name: E2E Integration Tests + needs: start-runner + runs-on: + - self-hosted + - boxlite-e2e + timeout-minutes: 50 + env: + CARGO_TERM_COLOR: always + CARGO_INCREMENTAL: '0' + HOME: /root + steps: + - name: Clean workspace (persistent runner) + run: | + rm -rf /tmp/boxlite-* 2>/dev/null || true + if [ -d "$GITHUB_WORKSPACE/.git" ]; then + cd "$GITHUB_WORKSPACE" + git clean -ffd 2>/dev/null || true + git checkout -- . 2>/dev/null || true + fi + + - name: Stage prior-run logs for rescue + # If a previous run died mid-step (e.g. Worker ENOSPC), its logs + # are still on this persistent runner under /var/log/boxlite-ci/. + # Move them aside so the upload step below can ship them as a + # separate artifact, then clear them off the runner volume. + # + # Namespace key is "-": reruns of the same + # workflow share github.run_id and only github.run_attempt + # increments, so run_id alone would let attempt N misidentify + # attempt N-1's directory as its own and skip rescuing it. + env: + GH_RUN_ID: ${{ github.run_id }} + GH_RUN_ATTEMPT: ${{ github.run_attempt }} + run: | + set -u + BASE=/var/log/boxlite-ci + STAGE=/tmp/boxlite-rescue + CURRENT="${GH_RUN_ID}-${GH_RUN_ATTEMPT}" + rm -rf "$STAGE" && mkdir -p "$STAGE" + if [ -d "$BASE" ]; then + for d in "$BASE"/*; do + [ -d "$d" ] || continue + [ "$(basename "$d")" = "$CURRENT" ] && continue + mv "$d" "$STAGE/" + done + fi + + - name: Upload rescued prior-run logs + uses: actions/upload-artifact@v4 + with: + name: e2e-local-logs-rescued-${{ github.run_id }}-${{ github.run_attempt }} + path: /tmp/boxlite-rescue/ + retention-days: 7 + if-no-files-found: ignore + + - name: Pre-flight runner cleanup (runner internals only — never caches) + # Housekeeping for actions-runner's own diagnostic logs so they + # don't accumulate over the runner's lifetime. Build/image caches + # (~/.boxlite/, ~/.cargo/, target/) are LEFT ALONE — they are why + # this runner is persistent. + # + # Triggered by run 25726812554 where _diag was the canary for a + # 50→500GB root-volume migration: _diag was the first write to + # hit ENOSPC, but the actual disk fill came from build artifacts + # under _work/.../target/, not from _diag itself. This step is + # preventative housekeeping, not the fix for that incident. + run: | + find /opt/actions-runner/_diag -maxdepth 1 -type f -name 'Worker_*.log' -mtime +1 -delete 2>/dev/null || true + find /opt/actions-runner/_diag -maxdepth 1 -type f -name 'Runner_*.log' -mtime +1 -delete 2>/dev/null || true + + - name: Checkout code + uses: actions/checkout@v5 + with: + submodules: recursive + clean: false + + - name: Verify KVM access + run: | + test -c /dev/kvm && test -r /dev/kvm -a -w /dev/kvm && echo "/dev/kvm is accessible" || { + echo "::error::/dev/kvm not accessible"; exit 1 + } + + - name: Install build dependencies (make setup) + run: | + # First run on a fresh instance: ~5–10 min (apt + rustup + + # cargo-nextest; CI=true so prek install is skipped). + # Subsequent runs reuse the persistent EBS volume and are + # idempotent fast-paths (~10–30 s). + bash scripts/setup/setup-ubuntu.sh + + - name: Disk-pressure cache eviction + # Replaces the prior hard-fail precheck (run 25726812554 post-mortem): + # at >=THRESHOLD_PCT root use, evict e2e caches in place, re-check, + # and only fail if disk is still over threshold afterward. Caches + # re-warm on next run. Runner-internals housekeeping is separate + # (see "Pre-flight runner cleanup" step above). + # + # df parse-contract is validated before any comparison so a silent + # df format change surfaces as ::error::, not a fake "disk OK". + env: + THRESHOLD_PCT: '80' + MIN_FREE_GB: '20' + run: | + set -euo pipefail + + read_df() { + DF_AVAIL=$(df --output=avail -BG / | tail -n 1) + DF_PCENT=$(df --output=pcent / | tail -n 1) + FREE_GB=$(printf '%s' "$DF_AVAIL" | tr -dc '0-9') + USED_PCT=$(printf '%s' "$DF_PCENT" | tr -dc '0-9') + if ! [[ "$FREE_GB" =~ ^[0-9]+$ ]] || ! [[ "$USED_PCT" =~ ^[0-9]+$ ]]; then + echo "::error::Could not parse df output (FREE_GB='${FREE_GB}' USED_PCT='${USED_PCT}') — df contract may have changed." + echo "Raw df --output=avail -BG / tail: ${DF_AVAIL}" + echo "Raw df --output=pcent / tail: ${DF_PCENT}" + exit 1 + fi + } + + read_df + echo "Root volume: ${FREE_GB} GB free (${USED_PCT}% used)" + + if [ "${USED_PCT}" -ge "${THRESHOLD_PCT}" ]; then + echo "── Disk >=${THRESHOLD_PCT}% used — evicting e2e caches ──" + for path in target/boxlite-test /tmp/boxlite-* "$HOME/.boxlite"; do + for resolved in $path; do + [ -e "$resolved" ] || continue + size=$(du -sh "$resolved" 2>/dev/null | cut -f1 || echo "?") + echo " clearing $resolved (was: $size)" + rm -rf "$resolved" 2>/dev/null || true + done + done + read_df + echo "Post-eviction: ${FREE_GB} GB free (${USED_PCT}% used)" + if [ "${USED_PCT}" -ge "${THRESHOLD_PCT}" ]; then + echo "::error::Disk still ${USED_PCT}% used after clearing all e2e caches — manual cleanup needed." + exit 1 + fi + fi + + if [ "${FREE_GB}" -lt "${MIN_FREE_GB}" ]; then + echo "::error::Only ${FREE_GB} GB free on / (floor=${MIN_FREE_GB}) — refusing to run integration tests." + exit 1 + fi + + - name: Run integration tests + env: + GH_RUN_ID: ${{ github.run_id }} + GH_RUN_ATTEMPT: ${{ github.run_attempt }} + run: | + source "$HOME/.cargo/env" 2>/dev/null || true + export PATH="/usr/local/go/bin:$HOME/go/bin:$PATH" + LOG_DIR="/var/log/boxlite-ci/${GH_RUN_ID}-${GH_RUN_ATTEMPT}" + mkdir -p "$LOG_DIR" + # tee to persistent volume so logs survive runner Worker death. + # The next run's Stage step rescues + uploads "$LOG_DIR". + set -o pipefail + make test:integration 2>&1 | tee "${LOG_DIR}/integration.log" + + - name: Upload test logs on failure or cancellation + # `failure()` alone misses cancelled jobs (concurrency cancellation, + # host reboot, timeout — all surface as `cancelled()`, not failure). + # We want logs for every non-success outcome. + if: failure() || cancelled() + uses: actions/upload-artifact@v4 + with: + name: e2e-local-logs-${{ github.run_id }}-${{ github.run_attempt }} + path: | + target/nextest/ + /tmp/boxlite-*/ + !/tmp/boxlite-rescue/ + /var/log/boxlite-ci/${{ github.run_id }}-${{ github.run_attempt }}/ + retention-days: 7 + if-no-files-found: ignore + + - name: Debug via SSH on failure + if: failure() && inputs.debug + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + timeout-minutes: 30 + + # ========================================================================= + # Job 3: Stop the instance (always runs) + # ========================================================================= + stop-runner: + name: Stop E2E Runner + needs: [start-runner, e2e-tests] + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + if: always() && needs.start-runner.outputs.instance-id != '' + steps: + - name: Configure AWS credentials (OIDC) + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ env.AWS_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + role-session-name: e2e-stop-${{ github.run_id }} + + - name: Stop EC2 instance + if: ${{ !(needs.e2e-tests.result == 'failure' && inputs.debug) }} + run: | + echo "Stopping instance ${{ needs.start-runner.outputs.instance-id }}..." + aws ec2 stop-instances --instance-ids "${{ needs.start-runner.outputs.instance-id }}" \ + && echo "Instance stopping" \ + || echo "::warning::Failed to stop instance" + + - name: Skip stop (debug mode — tests failed with SSH active) + if: ${{ needs.e2e-tests.result == 'failure' && inputs.debug }} + run: echo "::warning::Instance left running for SSH debug session. Will auto-stop after 30 min idle." diff --git a/.github/workflows/e2e-stack.yml b/.github/workflows/e2e-stack.yml new file mode 100644 index 000000000..e815588d8 --- /dev/null +++ b/.github/workflows/e2e-stack.yml @@ -0,0 +1,89 @@ +# End-to-end suite — SDK → API → Runner → libkrun VM. +# +# Existing `make test:integration:*` uses the local PyO3 / FFI path +# (`Boxlite.default()`) and bypasses both the NestJS API and boxlite-runner, +# so bugs that surface only on the REST → API → runner chain (e.g. #563's +# exec-stdout drop, #627's attach re-drain) reach production without +# breaking CI. This workflow runs the full path. +# +# Requires nested-KVM. Targets a self-hosted runner labeled `kvm` (a +# common pattern is a long-lived EC2 m5.metal or *.metal-* with the +# stack pre-bootstrapped). On a fresh runner the first run will +# bootstrap; subsequent runs skip bootstrap and just run pytest. + +name: E2E stack + +on: + push: + branches: [main] + paths: + - 'sdks/c/src/exec/**' + - 'sdks/python/src/**' + - 'src/boxlite/src/rest/**' + - 'src/boxlite/src/cli/**' + - 'apps/runner/**' + - 'apps/api/src/**' + - 'scripts/test/e2e/**' + - '.github/workflows/e2e-stack.yml' + pull_request: + branches: [main] + paths: + - 'sdks/c/src/exec/**' + - 'sdks/python/src/**' + - 'src/boxlite/src/rest/**' + - 'src/boxlite/src/cli/**' + - 'apps/runner/**' + - 'apps/api/src/**' + - 'scripts/test/e2e/**' + - '.github/workflows/e2e-stack.yml' + workflow_dispatch: + inputs: + pr_ref: + description: 'Branch to compare against main (two-sided run)' + required: false + +concurrency: + group: e2e-stack-${{ github.ref }} + cancel-in-progress: true + +jobs: + e2e: + runs-on: [self-hosted, kvm] + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + # Bootstrap is idempotent — skips if services are already up. + - name: Bootstrap stack (services + fixture data) + run: make test:e2e:setup + + # Rebuild Python SDK from this checkout so the test exercises the + # code under review, not whatever wheel happened to be installed. + - name: Rebuild Python SDK wheel from this checkout + run: | + cd sdks/python + pip install --break-system-packages --quiet maturin + maturin build --release --quiet + pip install --break-system-packages --force-reinstall \ + ../../target/wheels/boxlite-*.whl + + # `make test:e2e:setup` (above) already builds boxlite-runner from the + # working tree — release pinning would test stale code. Restart the + # service in case bootstrap was a no-op (binary unchanged, e.g. PR + # didn't touch runner code) so any other prereq drift is picked up. + - name: Restart runner to pick up freshly-installed binary + run: | + sudo systemctl restart boxlite-runner + for _ in $(seq 1 30); do + ss -ltn | grep -q ':8080' && break + sleep 1 + done + + - name: Run E2E suite + run: make test:e2e + + - name: Collect logs on failure + if: failure() + run: | + tail -200 /var/log/boxlite-api.log + sudo journalctl -u boxlite-runner --no-pager -n 200 diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml deleted file mode 100644 index e33ac6304..000000000 --- a/.github/workflows/e2e-test.yml +++ /dev/null @@ -1,379 +0,0 @@ -# Run VM-based E2E integration tests on a persistent AWS EC2 runner. -# -# GitHub-hosted runners do not support nested virtualization / KVM. -# This workflow starts a pre-configured EC2 instance, runs integration tests, -# then stops it. The instance is never terminated — build caches persist. -# -# Architecture: -# start-runner (ubuntu-latest) → e2e-tests (self-hosted) → stop-runner (ubuntu-latest) -# Start stopped EC2 Build + run tests Stop EC2 -# Wait for runner online 35-min timeout if: always() -# -# Concurrency: only ONE e2e run at a time (single persistent instance). -# Queued runs wait; in-progress runs are cancelled by newer pushes. -# -# Cost: ~$0.17/hr (c8i.xlarge) only while running + ~$4/mo EBS storage. -# Subsequent runs skip setup/compilation (cached) → ~5-10 min → ~$0.03/run. -# -# Authentication: -# AWS: GitHub OIDC → AWS STS (no stored AWS credentials) -# GitHub: GitHub App → short-lived installation token (no PAT) -# -# Setup: ./scripts/ci/setup-ci-runner.sh -name: E2E Tests - -on: - push: - branches: [main] - paths: - - 'src/boxlite/**' - - 'src/shared/**' - - 'src/cli/**' - - 'src/guest/**' - - 'sdks/**' - - '**/Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/e2e-test.yml' - pull_request: - types: [labeled, synchronize] - branches: [main] - paths: - - 'src/boxlite/**' - - 'src/shared/**' - - 'src/cli/**' - - 'src/guest/**' - - 'sdks/**' - - '**/Cargo.toml' - - 'Cargo.lock' - workflow_dispatch: - inputs: - debug: - description: 'Open SSH session on failure (via tmate)' - type: boolean - default: false - -# Single global concurrency group — only one e2e run at a time. -# The persistent instance can only serve one job. Newer pushes cancel older runs. -concurrency: - group: e2e-runner - cancel-in-progress: true - -env: - AWS_REGION: us-east-1 - AWS_ROLE_ARN: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/boxlite-e2e-github-actions - EC2_INSTANCE_ID: ${{ vars.EC2_E2E_INSTANCE_ID }} - EC2_INSTANCE_TYPE: c8i.4xlarge - EC2_AMI_ID: ami-05cf1e9f73fbad2e2 - EC2_SUBNET_IDS: ${{ vars.AWS_SUBNET_IDS || vars.AWS_SUBNET_ID }} - EC2_SECURITY_GROUP_ID: ${{ vars.AWS_SECURITY_GROUP_ID }} - RUNNER_VERSION: '2.334.0' - RUNNER_LABEL: boxlite-e2e - -jobs: - # ========================================================================= - # Gate: PRs require 'e2e-test' label (only maintainers can add it) - # ========================================================================= - should-run: - runs-on: ubuntu-latest - outputs: - run: ${{ steps.check.outputs.run }} - steps: - - name: Check trigger conditions - id: check - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - LABELS='${{ toJson(github.event.pull_request.labels.*.name) }}' - if echo "$LABELS" | jq -e 'index("e2e-test")' > /dev/null 2>&1; then - echo "run=true" >> "$GITHUB_OUTPUT" - else - echo "run=false" >> "$GITHUB_OUTPUT" - fi - else - echo "run=true" >> "$GITHUB_OUTPUT" - fi - - # ========================================================================= - # Job 1: Start the persistent EC2 instance and wait for runner - # ========================================================================= - start-runner: - name: Start E2E Runner - needs: should-run - if: needs.should-run.outputs.run == 'true' - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - outputs: - instance-id: ${{ steps.ensure-instance.outputs.instance-id }} - steps: - - name: Generate GitHub App token - id: app-token - uses: actions/create-github-app-token@v1 - with: - app-id: ${{ vars.GH_APP_ID }} - private-key: ${{ secrets.GH_APP_PRIVATE_KEY }} - - - name: Configure AWS credentials (OIDC) - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ env.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - role-session-name: e2e-start-${{ github.run_id }} - - - name: Start or create EC2 instance - id: ensure-instance - run: | - INSTANCE_ID="${{ env.EC2_INSTANCE_ID }}" - - # Fallback: discover by tag if variable is empty - if [ -z "$INSTANCE_ID" ]; then - INSTANCE_ID=$(aws ec2 describe-instances \ - --filters "Name=tag:Name,Values=boxlite-e2e" "Name=instance-state-name,Values=running,stopped,stopping,pending" \ - --query "Reservations[0].Instances[0].InstanceId" --output text 2>/dev/null || echo "") - [ "$INSTANCE_ID" = "None" ] && INSTANCE_ID="" - fi - - # Check if instance exists and get its state - if [ -n "$INSTANCE_ID" ]; then - STATE=$(aws ec2 describe-instances \ - --instance-ids "$INSTANCE_ID" \ - --query "Reservations[0].Instances[0].State.Name" --output text 2>/dev/null || echo "not-found") - else - STATE="not-found" - fi - - if [ "$STATE" = "running" ]; then - echo "Instance already running" - echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" - - elif [ "$STATE" = "stopped" ]; then - echo "Starting instance..." - aws ec2 start-instances --instance-ids "$INSTANCE_ID" - aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" - echo "Instance started" - echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" - - elif [ "$STATE" = "stopping" ] || [ "$STATE" = "pending" ]; then - echo "Instance in transitional state ($STATE), waiting..." - aws ec2 wait instance-stopped --instance-ids "$INSTANCE_ID" 2>/dev/null || \ - aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" 2>/dev/null || true - # Re-check and start if stopped - STATE=$(aws ec2 describe-instances --instance-ids "$INSTANCE_ID" \ - --query "Reservations[0].Instances[0].State.Name" --output text) - if [ "$STATE" = "stopped" ]; then - aws ec2 start-instances --instance-ids "$INSTANCE_ID" - aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" - elif [ "$STATE" != "running" ]; then - echo "::error::Instance stuck in state: $STATE" - exit 1 - fi - echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" - - else - echo "Instance not found or terminated. Creating new one..." - - # Generate runner registration token for user-data - REG_TOKEN=$(curl -sf -X POST \ - -H "Authorization: token ${{ steps.app-token.outputs.token }}" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${{ github.repository }}/actions/runners/registration-token" \ - | jq -r .token) - - # Build user-data - cat > /tmp/user-data.sh << 'USERDATA' - #!/bin/bash - set -euo pipefail - export HOME=/root - modprobe kvm_intel 2>/dev/null || modprobe kvm_amd 2>/dev/null || true - chmod 666 /dev/kvm 2>/dev/null || true - usermod -aG kvm root 2>/dev/null || true - apt-get update -qq && apt-get install -y -qq make sudo git curl jq - if [ ! -d /opt/boxlite ]; then - git clone --recursive https://github.com/__REPO__.git /opt/boxlite - fi - cd /opt/boxlite && git pull --recurse-submodules || true - make setup - source "$HOME/.cargo/env" - RUNNER_VERSION="__RUNNER_VERSION__" - mkdir -p /opt/actions-runner && cd /opt/actions-runner - if [ ! -f ./config.sh ]; then - curl -fsSL "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz" | tar xz - fi - RUNNER_ALLOW_RUNASROOT=1 ./config.sh \ - --url "https://github.com/__REPO__" \ - --token "__REG_TOKEN__" \ - --name "boxlite-e2e" \ - --labels "self-hosted,linux,x64,kvm,boxlite-e2e" \ - --unattended --disableupdate --replace || true - ./svc.sh install root || true - ./svc.sh start || true - USERDATA - - sed -i "s|__REPO__|${{ github.repository }}|g" /tmp/user-data.sh - sed -i "s|__RUNNER_VERSION__|${{ env.RUNNER_VERSION }}|g" /tmp/user-data.sh - sed -i "s|__REG_TOKEN__|${REG_TOKEN}|g" /tmp/user-data.sh - - # Launch instance — try each subnet (AZ) until one has capacity - INSTANCE_ID="" - IFS=',' read -ra SUBNET_LIST <<< "${{ env.EC2_SUBNET_IDS }}" - for SUBNET in "${SUBNET_LIST[@]}"; do - SUBNET=$(echo "$SUBNET" | xargs) - echo "Attempting launch in subnet $SUBNET..." - if RESULT=$(aws ec2 run-instances \ - --instance-type "${{ env.EC2_INSTANCE_TYPE }}" \ - --image-id "${{ env.EC2_AMI_ID }}" \ - --subnet-id "$SUBNET" \ - --security-group-ids "${{ env.EC2_SECURITY_GROUP_ID }}" \ - --iam-instance-profile Name=boxlite-e2e-runner \ - --cpu-options "NestedVirtualization=enabled" \ - --user-data file:///tmp/user-data.sh \ - --block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":50,"VolumeType":"gp3","DeleteOnTermination":false}}]' \ - --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=boxlite-e2e},{Key=Purpose,Value=boxlite-e2e}]" \ - --metadata-options "HttpTokens=required,HttpEndpoint=enabled" \ - --query "Instances[0].InstanceId" --output text 2>&1); then - INSTANCE_ID="$RESULT" - echo "Launched $INSTANCE_ID in subnet $SUBNET" - break - else - echo "::warning::Subnet $SUBNET unavailable: $RESULT" - fi - done - - if [ -z "$INSTANCE_ID" ]; then - echo "::error::Failed to launch instance in any availability zone" - exit 1 - fi - - echo "Created instance: $INSTANCE_ID" - aws ec2 wait instance-running --instance-ids "$INSTANCE_ID" - echo "instance-id=$INSTANCE_ID" >> "$GITHUB_OUTPUT" - - # Save instance ID for future runs (must succeed or we'll orphan the instance) - if ! gh variable set EC2_E2E_INSTANCE_ID --body "$INSTANCE_ID" -R "${{ github.repository }}"; then - echo "::error::Failed to save instance ID. Terminating orphaned instance." - aws ec2 terminate-instances --instance-ids "$INSTANCE_ID" - exit 1 - fi - fi - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} - - - name: Wait for runner to come online - run: | - TIMEOUT=180 - ELAPSED=0 - INTERVAL=10 - - echo "Waiting for runner '${{ env.RUNNER_LABEL }}' to come online..." - - while [ $ELAPSED -lt $TIMEOUT ]; do - STATUS=$(curl -sf \ - -H "Authorization: token ${{ steps.app-token.outputs.token }}" \ - -H "Accept: application/vnd.github+json" \ - "https://api.github.com/repos/${{ github.repository }}/actions/runners" \ - | jq -r '.runners[] | select(.labels[].name == "${{ env.RUNNER_LABEL }}") | .status // empty' \ - 2>/dev/null || echo "") - - if [ "$STATUS" = "online" ]; then - echo "Runner is online" - exit 0 - fi - - echo " Waiting... (${ELAPSED}s / ${TIMEOUT}s)" - sleep $INTERVAL - ELAPSED=$((ELAPSED + INTERVAL)) - done - - echo "::error::Runner did not come online within ${TIMEOUT}s" - exit 1 - - # ========================================================================= - # Job 2: Run integration tests on the persistent runner - # ========================================================================= - e2e-tests: - name: E2E Integration Tests - needs: start-runner - runs-on: - - self-hosted - - boxlite-e2e - timeout-minutes: 35 - env: - CARGO_TERM_COLOR: always - CARGO_INCREMENTAL: '0' - HOME: /root - steps: - - name: Clean workspace (persistent runner) - run: | - rm -rf /tmp/boxlite-* 2>/dev/null || true - if [ -d "$GITHUB_WORKSPACE/.git" ]; then - cd "$GITHUB_WORKSPACE" - git clean -ffd 2>/dev/null || true - git checkout -- . 2>/dev/null || true - fi - - - name: Checkout code - uses: actions/checkout@v5 - with: - submodules: recursive - clean: false - - - name: Verify KVM access - run: | - test -c /dev/kvm && test -r /dev/kvm -a -w /dev/kvm && echo "/dev/kvm is accessible" || { - echo "::error::/dev/kvm not accessible"; exit 1 - } - - - name: Run integration tests - run: | - source "$HOME/.cargo/env" 2>/dev/null || true - export PATH="/usr/local/go/bin:$HOME/go/bin:$PATH" - make test:integration - - - name: Upload test logs on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: e2e-test-logs-${{ github.run_id }} - path: | - target/nextest/ - /tmp/boxlite-*/ - retention-days: 7 - if-no-files-found: ignore - - - name: Debug via SSH on failure - if: failure() && inputs.debug - uses: mxschmitt/action-tmate@v3 - with: - limit-access-to-actor: true - timeout-minutes: 30 - - # ========================================================================= - # Job 3: Stop the instance (always runs) - # ========================================================================= - stop-runner: - name: Stop E2E Runner - needs: [start-runner, e2e-tests] - runs-on: ubuntu-latest - permissions: - id-token: write - contents: read - if: always() && needs.start-runner.outputs.instance-id != '' - steps: - - name: Configure AWS credentials (OIDC) - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ env.AWS_ROLE_ARN }} - aws-region: ${{ env.AWS_REGION }} - role-session-name: e2e-stop-${{ github.run_id }} - - - name: Stop EC2 instance - if: ${{ !(needs.e2e-tests.result == 'failure' && inputs.debug) }} - run: | - echo "Stopping instance ${{ needs.start-runner.outputs.instance-id }}..." - aws ec2 stop-instances --instance-ids "${{ needs.start-runner.outputs.instance-id }}" \ - && echo "Instance stopping" \ - || echo "::warning::Failed to stop instance" - - - name: Skip stop (debug mode — tests failed with SSH active) - if: ${{ needs.e2e-tests.result == 'failure' && inputs.debug }} - run: echo "::warning::Instance left running for SSH debug session. Will auto-stop after 30 min idle." diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f8535c6b6..ac822ba3c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,7 @@ name: Lint and Format on: + merge_group: push: branches: [ main ] paths: @@ -17,28 +18,19 @@ on: - 'sdks/c/**' - 'sdks/go/**' - 'make/quality.mk' + - 'make/test.mk' - '.pre-commit-config.yaml' - '.github/workflows/lint.yml' - '.github/workflows/config.yml' + - '.github/workflows/build-runtime.yml' + - 'scripts/release/**' + # No path filter on pull_request: the `Lint (conclusion)` job is a required + # status check, so this workflow must run on EVERY PR and report it. A + # path-skipped workflow leaves the required check stuck "Pending" → PR blocked. + # Per-suite filtering still happens in the `changes` job (jobs skip when + # unaffected); the conclusion job reports success-or-skipped. pull_request: branches: [ main ] - paths: - - '**/*.rs' - - '**/*.py' - - '**/*.ts' - - '**/*.c' - - '**/*.h' - - '**/*.go' - - '**/Cargo.toml' - - 'Cargo.lock' - - 'sdks/python/**' - - 'sdks/node/**' - - 'sdks/c/**' - - 'sdks/go/**' - - 'make/quality.mk' - - '.pre-commit-config.yaml' - - '.github/workflows/lint.yml' - - '.github/workflows/config.yml' jobs: config: @@ -55,11 +47,16 @@ jobs: c: ${{ steps.filter.outputs.c }} go: ${{ steps.filter.outputs.go }} quality: ${{ steps.filter.outputs.quality }} + install_script: ${{ steps.filter.outputs.install_script }} steps: - uses: actions/checkout@v5 - - uses: dorny/paths-filter@v3 + - uses: dorny/paths-filter@v4 id: filter with: + # In a merge queue, diff against the merge-group base/head (no PR base + # exists). Empty on pull_request/push, where paths-filter ignores them. + base: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_sha || '' }} + ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }} filters: | rust: - '**/*.rs' @@ -83,6 +80,10 @@ jobs: - 'make/quality.mk' - '.pre-commit-config.yaml' - '.github/workflows/lint.yml' + install_script: + - 'scripts/release/**' + - '.github/workflows/build-runtime.yml' + - 'make/test.mk' rustfmt: name: Check Rust formatting @@ -270,4 +271,33 @@ jobs: uses: golangci/golangci-lint-action@v6 with: working-directory: sdks/go - args: --timeout=5m --build-tags boxlite_dev \ No newline at end of file + args: --timeout=5m --build-tags boxlite_dev + + install_script: + name: Installer script smoke test + needs: changes + if: ${{ needs.changes.outputs.install_script == 'true' }} + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Run installer script smoke test + run: make test:install-script + + # Single required status check for branch protection / merge queue. + # Passes only if every job above succeeded or was skipped (path-filtered). + # `if: !cancelled()` is required: without it a failed dependency would skip + # this job, and GitHub treats a skipped required check as success. + # NOTE: keep `needs` in sync with the jobs above when adding/removing jobs. + lint-conclusion: + name: Lint (conclusion) + needs: [config, changes, rustfmt, clippy, python, node, c, go, install_script] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + permissions: {} + steps: + - name: All lint jobs passed or were skipped + run: | + jq -C <<< '${{ toJSON(needs) }}' + jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJSON(needs) }}' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 437f63b77..f755ea092 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,6 +11,7 @@ name: Test on: + merge_group: push: branches: [main] paths: @@ -23,18 +24,13 @@ on: - 'Cargo.lock' - '.github/workflows/test.yml' - '.github/workflows/config.yml' + # No path filter on pull_request: the `Test (conclusion)` job is a required + # status check, so this workflow must run on EVERY PR and report it. A + # path-skipped workflow leaves the required check stuck "Pending" → PR blocked. + # Per-suite filtering still happens in the `changes` job (jobs skip when + # unaffected); the conclusion job reports success-or-skipped. pull_request: branches: [main] - paths: - - 'src/boxlite/**' - - 'src/shared/**' - - 'src/cli/**' - - 'src/guest/**' - - 'sdks/**' - - '**/Cargo.toml' - - 'Cargo.lock' - - '.github/workflows/test.yml' - - '.github/workflows/config.yml' env: CARGO_TERM_COLOR: always @@ -58,9 +54,13 @@ jobs: go: ${{ steps.filter.outputs.go }} steps: - uses: actions/checkout@v5 - - uses: dorny/paths-filter@v3 + - uses: dorny/paths-filter@v4 id: filter with: + # In a merge queue, diff against the merge-group base/head (no PR base + # exists). Empty on pull_request/push, where paths-filter ignores them. + base: ${{ github.event_name == 'merge_group' && github.event.merge_group.base_sha || '' }} + ref: ${{ github.event_name == 'merge_group' && github.event.merge_group.head_sha || '' }} filters: | rust: - 'src/boxlite/**' @@ -275,3 +275,20 @@ jobs: - name: Run Go unit tests run: make test:unit:go + + # Single required status check for branch protection / merge queue. + # Passes only if every job above succeeded or was skipped (path-filtered). + # `if: !cancelled()` is required: without it a failed dependency would skip + # this job, and GitHub treats a skipped required check as success. + # NOTE: keep `needs` in sync with the jobs above when adding/removing jobs. + test-conclusion: + name: Test (conclusion) + needs: [config, changes, rust, cli, python, node, go] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + permissions: {} + steps: + - name: All test jobs passed or were skipped + run: | + jq -C <<< '${{ toJSON(needs) }}' + jq --exit-status 'all(.result == "success" or .result == "skipped")' <<< '${{ toJSON(needs) }}' diff --git a/.gitignore b/.gitignore index f37489b98..35c5b0a72 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ node_modules/ .pnpm-store/ package-lock.json yarn.lock +!/apps/yarn.lock npm-debug.log* yarn-debug.log* yarn-error.log* @@ -81,6 +82,7 @@ yarn-error.log* .env .env.* !/openapi/reference-server/.env.example +!/apps/api/.env.example # ============================================================================ # Go SDK @@ -161,10 +163,10 @@ Cargo.lock /apps/infra/.sst/ /apps/infra/sst-env.d.ts /apps/infra/.env* +!/apps/infra/.env.example /apps/snapshot-manager/data/ /apps/runner/pkg/daemon/static/* !/apps/runner/pkg/daemon/static/.gitkeep -/apps/cli/boxlite /apps/daemon/daemon /apps/proxy/proxy /apps/runner/runner @@ -175,3 +177,45 @@ Cargo.lock /apps/libs/computer-use/boxlite-computer-use /runner .claude/scheduled_tasks.lock +.claude/worktrees/ +.claude/.last-audit.json + +# ============================================================================ +# Local dev artifacts (apps/infra-local + dashboard E2E session) +# ============================================================================ +.playwright-mcp/ +dashboard-*.png +dex-*.png +crud-*.png + +# L2 boot artifacts — local-only, not for repo. +# NOTE: gitignore syntax does NOT support inline comments — `pattern # comment` +# is parsed as the literal path `pattern # comment`. Keep comments on their +# own lines. + +# self-symlink workaround for apps/apps/... path resolution in webpack.config.js +apps/apps +# symlink to apps/api/.env (NestJS env loading from cwd=apps/) +apps/.env +# contains secrets (encryption key, runner token) +apps/api/.env +# SWC config from attempted compile switch (reverted to tsc, file is dead) +apps/api/.swcrc +# 115 MB, prebuilt from upstream releases +sdks/go/libboxlite.a +# 73 MB, prebuilt from upstream releases +sdks/go/libboxlite.dylib +# extracted prebuilt archive +sdks/go/boxlite-c-v*/ + +l2-*.png + +# Local dev stack (apps/infra-local) — ALL generated state: data volumes, +# SDK + runner homes, native binaries, logs +/.apps-local/ + +# Session test screenshots (Playwright runs) +regions-*.png +runners-*.png +term-*.png +test-*.png diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..4c50f8337 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "semi": false, + "tabWidth": 2, + "printWidth": 120 +} diff --git a/CLAUDE.md b/CLAUDE.md index 7b9f57a43..79bcf1384 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,750 +1,108 @@ # CLAUDE.md -This document provides project context for AI-assisted development with Claude Code. - ## Project Overview -**BoxLite** is an embeddable virtual machine runtime for secure, isolated code execution environments. - -**Key Concept:** Think "SQLite for sandboxing" - a lightweight library embedded directly in applications without requiring a daemon or root privileges. - -**What it does:** -- Hardware-level VM isolation (KVM on Linux, Hypervisor.framework on macOS) -- Runs OCI containers inside lightweight VMs -- Async-first API for running multiple isolated environments concurrently - -**Primary use cases:** -- AI Agent Sandbox - Safe execution environment for untrusted AI-generated code -- Serverless Multi-tenant Runtime - Isolated environments per customer/request -- Regulated Environments - Hardware-level isolation for compliance - -**For detailed overview, architecture, and use cases, see:** -- [README.md](./README.md) - Project overview, features, quick start -- [docs/architecture/README.md](./docs/architecture/README.md) - Comprehensive architecture documentation +- [README.md](./README.md) — features, quick start, supported platforms +- [docs/architecture/README.md](./docs/architecture/README.md) — components, use cases +- [sdks/python/README.md](./sdks/python/README.md) — Python SDK (3.10+, PyO3 bindings, async API) +- [sdks/node/README.md](./sdks/node/README.md) — Node.js/TypeScript SDK (18+, napi-rs bindings) +- [sdks/go/README.md](./sdks/go/README.md) — Go SDK (1.24+, CGO + prebuilt native library) +- [sdks/c/README.md](./sdks/c/README.md) — C SDK (C11, cbindgen FFI, Simple + Native API) +- [src/cli/README.md](./src/cli/README.md) — `boxlite` CLI quick start and commands reference +- [docs/reference/README.md](./docs/reference/README.md) — SDK API references (Python, Node, Rust, C) and CLI reference ## Tech Stack -**Core:** -- Rust (async, Tokio runtime) -- libkrun hypervisor (KVM/Hypervisor.framework) -- libcontainer (OCI runtime) -- gRPC (tonic) for host-guest communication - -**SDKs:** -- Python (PyO3, Python 3.10+) -- C (FFI, early stage) -- Node.js (napi-rs, Node.js 18+) - -**For complete tech stack details, see:** -- [docs/architecture/README.md](./docs/architecture/README.md#tech-stack) - Detailed component breakdown +- [docs/architecture/README.md#tech-stack](./docs/architecture/README.md#tech-stack) ## Project Structure -**High-level layout:** -``` -src/ - boxlite/ # Core runtime (Rust) - 19 modules - shared/ # Shared types and protocol - cli/ # CLI binary - server/ # Distributed server - ffi/ # FFI layer for SDKs - guest/ # Guest agent (runs inside VM) - test-utils/ # Test utilities - deps/ # Vendored C sys crates - bubblewrap-sys/ - e2fsprogs-sys/ - libgvproxy-sys/ - libkrun-sys/ -sdks/ - python/ # Python SDK (PyO3) - c/ # C SDK (FFI) - node/ # Node.js SDK (napi-rs) -examples/python/ # Python examples (7 categorized subdirectories) -docs/ # Documentation -scripts/ # Build and setup scripts -``` - -**Key modules in `src/boxlite/src/`:** -- `runtime/` - BoxliteRuntime, main entry point -- `litebox/` - LiteBox handle, command execution -- `vmm/` - VM manager (libkrun, shim controller) -- `jailer/` - Security isolation (seccomp, sandbox-exec, namespaces) -- `portal/` - Host-guest gRPC communication -- `images/` - OCI image management -- `net/`, `volumes/`, `disk/` - Networking, storage, disks -- `db/` - SQLite persistence - -**Runtime data directory (`~/.boxlite/`):** -- `images/` - OCI image cache -- `boxes/` - Per-box data (config, disks) -- `db/` - SQLite databases - -**For detailed project structure, see:** -- [CONTRIBUTING.md](./CONTRIBUTING.md#project-structure) - Directory layout and organization -- [docs/architecture/README.md](./docs/architecture/README.md) - Component architecture +- [CONTRIBUTING.md](./CONTRIBUTING.md#project-structure) — directory layout +- [docs/architecture/README.md](./docs/architecture/README.md) — component architecture ## Common Commands -**Quick reference:** -```bash -make setup # Install dependencies (auto-detects OS) -make dev:python # Build Python SDK locally -make test # Run Rust tests -make fmt # Format code -make dist:python # Build portable Python wheel -``` - -**For complete command reference and Makefile targets, see:** -- [Makefile](./Makefile) - All available targets with descriptions (run `make help`) -- [CONTRIBUTING.md](./CONTRIBUTING.md#building-from-source) - Development workflow -- [docs/guides/README.md](./docs/guides/README.md#building-from-source) - Platform-specific build instructions +- `make help` — list all targets ([Makefile](./Makefile)) +- Always use `make` targets for build, test, lint, format, setup, and distribution. Do not run `cargo`, `npm`, `python`, `go`, or `cbindgen` directly when a make target exists — the Makefile encapsulates correct flags, cross-compilation, environment setup, and ordering. ## Code Style -**Rust:** Follow [docs/development/rust-style.md](./docs/development/rust-style.md) which includes: -- [Microsoft Rust Guidelines](https://microsoft.github.io/rust-guidelines) - external reference -- `cargo fmt` for formatting, `cargo clippy` for linting (enforced in CI) -- Async-first (Tokio runtime) -- Error handling via centralized `BoxliteError` enum -- `Send + Sync` for public types - -Key guidelines to internalize: -- **M-PANIC-IS-STOP**: Panics terminate, don't use for error handling -- **M-CONCISE-NAMES**: Avoid "Service", "Manager", "Factory" in type names -- **M-UNSAFE**: Minimize and document all unsafe blocks - -**Python:** -- Async/await for all I/O -- Context managers (`async with`) for automatic cleanup -- Type hints encouraged - -**For complete code style guidelines, see:** -- [docs/development/rust-style.md](./docs/development/rust-style.md) - Rust style guide with Microsoft guidelines -- [src/shared/src/errors.rs](./src/shared/src/errors.rs) - Error handling patterns - -## Workflows - -**Development workflow:** -1. Create feature branch from `main` -2. Make changes, add tests, update docs -3. Run `cargo test && make fmt` -4. Open Pull Request -5. Address code review feedback -6. Squash merge to main - -**Testing:** -- Rust: `cargo test` (unit + integration) -- Python: `pytest` with real VM integration tests - -**For complete workflows, see:** -- [CONTRIBUTING.md](./CONTRIBUTING.md#how-to-contribute) - PR process, testing, release -- [.github/workflows/](../.github/workflows/) - CI/CD pipelines - -## Important Notes - -### Critical for AI Assistants - -**Platform support:** -- ✅ macOS ARM64 (Apple Silicon), Linux x86_64/ARM64, Windows (via WSL2) -- ❌ macOS Intel (not supported) - -**Before building:** -- ⚠️ **MUST** run `git submodule update --init --recursive` (vendored dependencies) -- Check platform requirements in [README.md](./README.md#supported-platforms) - -**Architecture quirks:** -- **Shim process**: boxlite-shim isolates boxes (libkrun does process takeover) -- **Jailer**: OS-level sandbox (seccomp/sandbox-exec) wraps shim for defense-in-depth -- **gRPC communication**: Host-guest communication via vsock (not TCP) -- **~/.boxlite directory**: All runtime data stored here (images, boxes, db) - -**Git workflow:** -- **NEVER use `--no-verify`** to bypass git hooks without explicit user permission. If hooks fail, fix the root cause or ask the user for guidance. - -**When writing code:** -- All I/O is async (Tokio runtime) -- Errors use centralized `BoxliteError` enum (see `src/shared/src/errors.rs`) -- Python SDK requires Python 3.10+ -- Examples: categorized Python examples in `examples/python/` (7 subdirectories) - -**Common pitfalls:** -- Forgetting to initialize submodules → build fails -- Running on Intel Mac → UnsupportedEngine error -- Not handling async/await properly → runtime errors -- Exceeding resource limits → box killed (OOM) - -### Documentation Map - -When users ask questions, direct them to: - -| Question Type | Reference | -|--------------|-----------| -| "How do I install?" | [docs/getting-started/README.md](./docs/getting-started/README.md) | -| "How does it work?" | [docs/architecture/README.md](./docs/architecture/README.md) | -| "What can I configure?" | [docs/reference/README.md](./docs/reference/README.md) | -| "How do I...?" | [docs/guides/README.md](./docs/guides/README.md) | -| "Why is it failing?" | [docs/faq.md](./docs/faq.md) | -| "Python API reference" | [sdks/python/README.md](./sdks/python/README.md) | -| "How to contribute?" | [CONTRIBUTING.md](./CONTRIBUTING.md) | - -### Quick Debugging - -When helping users debug: -1. Enable logging: `RUST_LOG=debug python script.py` -2. Check `~/.boxlite/` disk space and permissions -3. Verify platform support (KVM on Linux, Hypervisor.framework on macOS) -4. See [docs/faq.md](./docs/faq.md#troubleshooting) for common issues - -## Mandatory Code Design Rules - -**CRITICAL: These rules are MANDATORY for all code contributions.** - -### Meta-Principle - -**0. DON'T BE YES MAN** - Challenge assumptions, question designs, identify flaws - -- **Challenge yourself too, not just the user** -- Before implementing: "Does this already exist?" (search first) -- Before adding logic: "Does this layer need to know this?" -- After writing: "What breaks if I delete this?" - -### Core Principles (Must Know) - -**1. Single Responsibility** - One function, one job -**2. Boring Code** - Obvious > clever -**3. Search Before Implement** - grep before writing, read before coding -**4. Only What's Used** - No future-proofing, delete dead code immediately -**5. DRY** - Don't Repeat Yourself (single source of truth) -**6. Explicit Errors** - Self-documenting error messages - -### Supporting Principles (Reference When Needed) - -**7. Minimal Knowledge** - Components only know interfaces, not internals -**8. No Premature Optimization** - Measure first, optimize later -**9. Explicit Paths** - Calculate from known roots, never assume -**10. Prepare Before Execute** - Setup before irreversible operations -**11. Validate Early** - Check preconditions before expensive work -**12. Thoughtful Naming** - Consider 5+ alternatives, choose the clearest -**13. Structured Code** - Organized hierarchy, clear layers, predictable organization -**14. Idiomatic by Default** - Follow each language's standard patterns and libraries first; only diverge with a clear, documented reason -**15. No Sleep for Events** - Never use `sleep` to wait on events (especially > 10ms). Use proper event-driven mechanisms (channels, waitpid, condition variables, futures) instead. - -### Quick Examples - -**DON'T BE YES MAN** - -Critical thinking is mandatory. Challenge assumptions, identify flaws, propose better alternatives. - -``` -// ❌ Yes Man behavior -User: "Let's add a Supervisor component to track metrics" -AI: "Great idea! I'll implement it right away." -[Creates empty struct with no implementation, violates YAGNI] - -// ✅ Critical thinking -User: "Let's add a Supervisor component to track metrics" -AI: "Wait - what data would Supervisor need? The process handle is in ShimController. - Creating a separate component means duplicating PID tracking (violates DRY). - Supervisor has no access to the data it needs. This design won't work. - - Better approach: ShimController already owns the process, it should provide metrics. - Do you want me to implement metrics() in ShimController instead?" -``` - -**Real example from this codebase:** - -- Initial design had empty `Supervisor` struct that did nothing -- Yes-man would have kept it or implemented it wrong -- Critical analysis identified: violates YAGNI, DRY, and "Delete Legacy Code" -- Solution: Delete Supervisor, add metrics to ShimController (owns the process) - -**When to push back:** - -- Design violates any of the 15 golden rules -- User requests create duplicate state/logic -- Implementation requires blocking operations (sleep, sync I/O in async contexts) -- Code adds complexity without clear benefit -- "Future-proofing" that isn't needed now (YAGNI) - -**How to push back:** - -1. Identify specific rule violations -2. Explain why current approach won't work -3. Propose concrete alternative -4. Let user decide - -**Remember:** Respectful disagreement > harmful agreement. Your job is to build good code, not to please. - -**Single Responsibility** - -```rust -// ❌ One function doing everything -fn setup_and_start_vm(image: &str) -> Result { /* ... */ } - -// ✅ Each function has one job -fn pull_image(image: &str) -> Result { /* ... */ } -fn create_workspace(manifest: &Manifest) -> Result { /* ... */ } -fn start_vm(workspace: &Workspace) -> Result { /* ... */ } -``` - -**Boring Code** - -```rust -// ❌ Clever, hard to understand -fn metrics(&self) -> RawMetrics { - self.process.as_ref() - .and_then(|p| System::new().process(Pid::from(p.id()))) - .map(|proc| RawMetrics { cpu: proc.cpu_usage(), mem: proc.memory() }) - .unwrap_or_default() -} - -// ✅ Boring, obvious -fn metrics(&self) -> RawMetrics { - if let Some(ref process) = self.process { - let mut sys = System::new(); - sys.refresh_process(pid); - if let Some(proc_info) = sys.process(pid) { - return RawMetrics { - cpu_percent: Some(proc_info.cpu_usage()), - memory_bytes: Some(proc_info.memory()), - }; - } - } - RawMetrics::default() -} -``` - -**DRY (Don't Repeat Yourself)** - -```rust -// ❌ Duplicated constants -const VSOCK_PORT: u32 = 2695; // host -const VSOCK_PORT: u32 = 2695; // guest - -// ✅ Shared in boxlite-core -use boxlite_core::VSOCK_GUEST_PORT; -``` - -**Search Before Implement** - -BEFORE writing ANY code, search for existing implementations: - -```bash -# ❌ Writing transformation without searching -# (adds duplicate unix→vsock transformation in litebox.rs) - -# ✅ Search first, find existing code -$ grep -r "transform.*guest" src/boxlite/src/ -src/boxlite/src/engines/krun/engine.rs:113:fn transform_guest_args(...) -# → Found it! Use existing code, don't duplicate. -``` - -```rust -// ❌ Duplicate logic in wrong layer -impl BoxInitializer { - fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - // Why does litebox know about krun's unix→vsock transformation? - let guest_transport = match transport { - Transport::Unix { .. } => Transport::vsock(2695), // Already in krun/engine.rs! - ... - }; - } -} - -// ✅ Let existing engine code handle it -impl BoxInitializer { - fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - // Pass transport as-is, krun engine will transform if needed - let uri = transport.to_uri(); - format!("exec boxlite-guest --listen {}", uri) - } -} - -// Engine already has the transformation (discovered via grep) -impl EngineKrun { - fn transform_guest_args(args: Vec) -> Vec { - // Krun-specific: unix:// → vsock:// transformation - } -} -``` - -**Search patterns to try:** - -- Similar functionality: `grep -r "transform.*args" src/` -- Function names: `grep -r "function_name" src/` -- Constants/config: `grep -r "VSOCK_PORT\|2695" src/` -- Layer ownership: `grep -r "GUEST_AGENT" src/` (shows which modules use it) - -**Explicit Path Calculation** - -```rust -// ❌ Assumes relationship -let box_dir = rootfs_dir.join(box_id); - -// ✅ Calculate from known root -let home_dir = rootfs_dir.parent().ok_or(...) ?; -let box_dir = home_dir.join(dirs::BOXES_DIR).join(box_id); -``` - -**Only What's Used** - -```rust -// ❌ Adding "future-proof" code -pub struct Supervisor { - // Empty - we might need this later! -} - -// ❌ Keeping "just in case" code -// fn old_extract_layers() { ... } // Commented out, might need later? - -// ✅ Only implement what's needed now -// If Supervisor isn't used, don't create it -// If old code isn't called, delete it (it's in git history) -``` - -**Prepare Before Execute** - -```rust -// ❌ Setup mixed with critical operation -fn start_vm() -> Result<()> { - let ctx = create_ctx()?; - ctx.start(); // Process takeover - can't recover from errors! -} - -// ✅ All setup before point of no return -std::fs::create_dir_all( & socket_dir) ?; // Can fail safely -let ctx = create_ctx() ?; // Can fail safely -ctx.configure() ?; // Can fail safely -ctx.start(); // Point of no return -``` - -**Explicit Error Context** - -```rust -// ❌ Generic error -std::fs::create_dir_all( & dir) ?; - -// ✅ Self-documenting -std::fs::create_dir_all( & socket_dir).map_err( | e| { -BoxliteError::Storage(format ! ( -"Failed to create socket directory {}: {}", socket_dir.display(), e -)) -}) ?; -``` - -**Thoughtful Naming** - -```rust -// ❌ Unclear, abbreviated -fn proc_data(d: &[u8]) -> Vec { /* ... */ } -fn get_stuff() -> Result { /* ... */ } - -// ✅ Clear, self-documenting (considered alternatives first) -// Alternatives considered: handle_layers, merge_layers, combine_layers, stack_layers -fn extract_and_merge_layers(tarballs: &[PathBuf], dest: &Path) -> Result<()> { /* ... */ } - -// Alternatives considered: fetch_config, load_config, read_config, get_config -fn parse_container_config(manifest: &Manifest) -> Result { /* ... */ } -``` - -**Naming Process**: - -1. Write down 5+ name candidates -2. Consider: verb clarity, noun specificity, context -3. Ask: "If I read this in 6 months, would it be obvious?" -4. Choose the name that needs the least explanation - -**Minimal Knowledge** - -```rust -// ❌ Component knows about other's internals -mod krun_engine { - use crate::networking::constants::GUEST_MAC; - - fn configure_network(&self, socket_path: &str) { - // Engine directly imports networking constant - self.ctx.add_net_path(socket_path, GUEST_MAC); - } -} - -// ✅ Component only knows interface -mod krun_engine { - fn configure_network(&self, socket_path: &str, mac_address: [u8; 6]) { - // Engine receives mac_address as parameter, doesn't know where it comes from - self.ctx.add_net_path(socket_path, mac_address); - } -} - -// Backend provides the configuration -mod gvproxy_backend { - use crate::networking::constants::GUEST_MAC; - - fn endpoint(&self) -> NetworkBackendEndpoint { - NetworkBackendEndpoint::UnixSocket { - path: self.socket_path.clone(), - mac_address: GUEST_MAC, // Backend knows the constant - } - } -} -``` - -**Why this matters:** - -- Loose coupling: Components don't know each other's internals -- Independent evolution: Backend and engine can change independently -- Testability: Components can be tested in isolation - -**Anti-pattern: Knowledge Leaks** - -```rust -// ❌ litebox.rs knowing about krun's socket bridging -impl BoxInitializer { - fn create_guest_entrypoint(&self) -> GuestEntrypoint { - // Why does litebox know about krun's unix→vsock transformation? - let guest_transport = match transport { - Transport::Unix { .. } => Transport::vsock(2695), // ← Krun implementation detail! - ... - }; - } -} - -// ✅ Let the engine handle its own implementation details -impl BoxInitializer { - fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - // Pass transport as-is, let engine transform if needed - let uri = transport.to_uri(); - format!("exec boxlite-guest --listen {}", uri) - } -} - -// Engine handles the transformation -impl EngineKrun { - fn transform_guest_args(args: Vec) -> Vec { - // Krun-specific: unix:// → vsock:// transformation - } -} -``` - -**Ask before adding logic:** - -- Does THIS component need to know this detail? -- Would removing this knowledge make the system more flexible? -- Is this an implementation detail of a different layer? - -**Minimal Knowledge applies to EVERYTHING:** - -- ✅ Code (imports, function calls, direct references) -- ✅ **Comments** (what implementation details are revealed) -- ✅ Documentation (API contracts, design docs) - -```rust -// ❌ Comment reveals implementation details -fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - // Pass transport as-is - krun engine will transform unix:// to vsock:// - // (see krun/engine.rs::transform_guest_args) - // ↑ Why does litebox know krun's transformation logic? - let uri = transport.to_uri(); -} - -// ✅ Comment maintains abstraction -fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - // Engine handles any transport-specific transformations - // ↑ Generic, no implementation details leaked - let uri = transport.to_uri(); -} -``` - -**Why comments matter:** - -- Comments create **documentation coupling** (if implementation changes, comment is wrong) -- Revealing "how" instead of "why" leaks abstractions -- Future readers learn the wrong patterns - -**Structured Code** - -```rust -// ❌ Flat, disorganized -mod rootfs { - pub fn prepare() { ... } - pub fn extract() { ... } - pub fn mount() { ... } - pub fn unmount() { ... } - pub fn process_whiteouts() { ... } - pub struct PreparedRootfs { - ... - } - pub struct SimpleRootfs { - ... - } -} - -// ✅ Hierarchical, organized by responsibility -mod rootfs { - mod operations; // Low-level primitives - mod prepared; // High-level orchestration (uses operations) - mod simple; // Alternative implementation - - pub use operations::{extract_layer_tarball, mount_overlayfs_from_layers}; - pub use prepared::PreparedRootfs; - pub use simple::SimpleRootfs; -} -``` - -**Structure Principles**: - -1. **Clear Hierarchy**: operations → prepared → public API -2. **Separation of Concerns**: Each module has ONE purpose -3. **Progressive Disclosure**: High-level API first, details hidden -4. **Predictable Organization**: Similar things organized similarly -5. **Explicit Dependencies**: Imports show relationships -6. **Testable Isolation**: Each layer can be tested independently - -**File Organization Pattern**: - -``` -src/ - ├── lib.rs // Public API only - ├── errors.rs // Shared error types - ├── feature/ - │ ├── mod.rs // Public interface + re-exports - │ ├── operations.rs // Low-level primitives - │ ├── types.rs // Feature-specific types - │ └── impl.rs // High-level implementation -``` - -### Pre-Submission Checklist - -**Pre-Implementation (BEFORE writing code):** - -- [ ] Searched for similar functionality (`grep -r "pattern" src/`) -- [ ] Read ALL files that would be affected (completely, not skimmed) -- [ ] Identified correct layer for new logic (ownership analysis) -- [ ] Verified no duplicate logic exists -- [ ] Questioned: "Does this component need to know this?" -- [ ] Applied Rule #0 to OWN design (not just user's request) - -**Meta-Principle:** - -- [ ] Design was critically evaluated (not yes-man accepted) - -**Core Principles:** - -- [ ] Each function has single responsibility (one job) -- [ ] Code is boring and obvious (not clever) -- [ ] Only code that's actually used exists (no future-proofing, no dead code) -- [ ] No duplicated knowledge (DRY - single source of truth) -- [ ] Every error has full context (self-documenting) - -**Supporting Principles:** - -- [ ] Components only know interfaces (minimal knowledge / loose coupling) -- [ ] No optimization without measurement -- [ ] Paths calculated from known roots (never assume) -- [ ] Setup completed before irreversible operations -- [ ] Preconditions validated early -- [ ] Names considered carefully (5+ alternatives evaluated) -- [ ] Code has clear hierarchy and predictable organization - -**Guiding principles**: - -- "Does this design actually make sense, or am I just agreeing?" -- "Is this the simplest thing that could possibly work?" -- "If I delete this, can I recreate it from git history?" - -**Post-Coding (AFTER writing code, BEFORE submitting):** - -This is the #1 critical requirement. Every code change MUST pass all of the following: - -- [ ] **Unit & integration tests cover all points** — every new behavior, edge case, and error path must have a corresponding test. This is the number one priority. - - **Tests must exercise actual code** — every test must directly call the real boxlite code it covers. Tests that only verify language/framework behavior (e.g., testing `tokio::select!` with mock sleeps) are not acceptable. - - **Integration tests are mandatory** — don't skip because of VM/hardware dependencies. The test infrastructure handles that (uses real `alpine:latest`, temp dirs). Integration tests validate that code changes actually work end-to-end. - - **Test patterns**: Unit tests in `#[cfg(test)] mod tests` within the source file. Integration tests in `src/boxlite/tests/` or `src/cli/tests/`. -- [ ] **All tests pass** — both new and existing tests. Run: `cargo test -p ` (or the relevant test command for the language) -- [ ] **Clippy clean** — `cargo clippy -p --tests -- -D warnings` (zero warnings) -- [ ] **Format clean** — `cargo fmt --check` for Rust, `ruff format --check` / `ruff check` for Python -- [ ] If any of the above fail, fix before proceeding — do not submit code with failing tests or lint warnings - ---- - -## Lessons from Real Mistakes - -### Case Study: Duplicate Transformation Logic - -**The Mistake:** -Added `unix://` → `vsock://` transformation in `litebox.rs` when it already existed in `krun/engine.rs`. +- [docs/development/rust-style.md](./docs/development/rust-style.md) -**Why it happened:** +## Workflow -- Didn't search before implementing (violated Rule #3) -- Became "yes man" to own design (violated Rule #0) -- Didn't question which layer should own the logic (violated Rule #7) -- Treated rules as post-commit review, not pre-commit design +Every change goes: understand → research → design → implement → test → verify. Leave the code easier to read, test, and change than you found it. Make small, deliberate changes that directly support the task; don't rewrite or reformat unrelated code. -**How rules should have prevented it:** +**Understand** -| Rule | What Should Have Happened | -|----------------------------------|-------------------------------------------------------------| -| **#0 (Don't Be Yes Man)** | "Does this already exist?" → Search first | -| **#3 (Search Before Implement)** | `grep -r "transform.*vsock" src/` → Found in krun/engine.rs | -| **#5 (DRY)** | Check for existing transformation logic | -| **#7 (Minimal Knowledge)** | "Why does litebox know about krun details?" → Wrong layer | +- Read this file, the nearest README/CONTRIBUTING, relevant docs, and the actual source before editing. +- Identify the smallest behavioral change that satisfies the request. +- Check the existing naming, module layout, test style, logging, and error-handling conventions in the affected area. +- Look for nearby tests or scripts that already define expected behavior. +- Reproduce-before-fix: when fixing a bug, write the failing test first, observe it fail, then fix. Do not create tests that don't actually test project code. A test that only exercises stdlib or framework code is not a real test. +- If docs and implementation disagree, capture the conflict and ask before making architectural assumptions. -**What would have caught it:** +**Research** -```bash -# BEFORE writing transformation code: -$ grep -r "transform.*vsock" src/boxlite/src/ -src/boxlite/src/engines/krun/engine.rs:113:fn transform_guest_args -# → Found it! Don't duplicate. +- Cite real `file:line` refs from similar projects. The user routinely asks "research other projects" if this step is skipped. -# BEFORE adding krun-specific logic to litebox: -$ grep -r "GUEST_AGENT_PORT" src/boxlite/src/ -# → Only in krun/ directory -# → This is a krun detail, doesn't belong in litebox.rs -``` +**Design** -**The Fix:** +- Don't be yes-man — challenge assumptions (yours too); ask whether a layer needs to know what you're about to teach it. +- Search before implement — `grep` for existing code first. +- Single responsibility — one function, one reason to change. +- One level of abstraction per function — don't mix orchestration with parsing, validation, persistence, rendering. +- **High cohesion, loose coupling via struct facade:** group related state + behavior into a struct; expose 1-2 `pub` entry points; keep internals + helpers private (minimizes cross-module knowledge). *Anti-pattern:* scattered free `pub fn`s callers must stitch together — leaks call order, helper graph, and shared state into every call site; every new caller re-learns the workflow. *Example:* [`ImageManager`](src/boxlite/src/images/manager.rs) exposes `new`/`pull`/`list`/`load_from_local` and hides `Arc`, blob sources, manifest handling. *Exception:* stateless utilities (e.g., [`jailer/common/`](src/boxlite/src/jailer/common/) async-signal-safe helpers). +- Co-locate related code — fields, methods, and helpers that work together stay in the same file/module. +- DRY when it's the same rule, policy, or transformation. Tolerate small local duplication when an abstraction would hide important local behavior. +- Validation at the boundary — untrusted inputs get checked where they enter; trust internal code. +- Composition over inheritance / framework magic. +- Only what's used — no future-proofing; delete dead code immediately. +- No premature optimization — measure first. -```rust -// ❌ WRONG: litebox.rs duplicating krun logic -fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - let guest_transport = match transport { - Transport::Unix { .. } => Transport::vsock(2695), // Duplicate! - ... - }; -} +**Implement** -// ✅ RIGHT: Pass as-is, let engine handle it -fn create_guest_entrypoint(&self, transport: &Transport) -> GuestEntrypoint { - let uri = transport.to_uri(); // krun/engine.rs transforms later - format!("exec boxlite-guest --listen {}", uri) -} -``` +- Boring code — obvious > clever. Code is read more than written. +- Names reveal intent, domain, and units. Booleans are predicates (`is_ready`, `has_token`, `can_retry`). Avoid `data`/`info`/`tmp`/`thing`/`handle`/`process` outside tiny scopes. Don't reuse one variable for two concepts in the same scope. +- Guard clauses + early returns over deeply nested control flow. +- Short argument lists. Group related values into typed options. Don't use boolean flags that make one function do two workflows — split them. +- Visible side effects: network calls, file writes, process exec, DB mutations should be explicit at the call site. +- Explicit errors — fail fast on missing config / invalid inputs; include operation, resource id, endpoint/status, input shape. Preserve the original cause when wrapping. Never swallow silently. Mask secrets in errors and logs. +- Explicit paths — calculate from known roots, never assume. +- Prepare before execute — setup before irreversible operations. +- No `sleep` for events — channels/waitpid/futures. +- Concurrency: timeouts, retries, cancellation explicit for external work. No unbounded queues/concurrency/memory. Close/release files, sockets, clients, browser handles, subprocesses. Retry loops must be idempotent (or document why safe). +- Security: no secrets in commits, logs, or test fixtures. Validate before SQL/shell/URL/path/HTML/prompt. Avoid shell execution with untrusted input. +- Comments explain *why*, not *what*: non-obvious intent, hidden constraints, deliberate trade-offs. Delete comments that restate the code or preserve dead decisions. Update nearby comments when behavior changes. Don't paste long excerpts from books, tickets, or logs. +- Follow the repository's existing formatter, linter, language level, and module style. Keep diffs focused — no whitespace churn outside touched lines. Add a new dependency only when it materially reduces risk or complexity. -**Key Lesson:** -Rules are not a QA checklist to run after coding. They are a **design thinking framework** to apply BEFORE and DURING coding. Always: +**Test** -1. Search first (`grep`) -2. Read affected files completely -3. Question ownership/layering -4. THEN code +- Two-side verification for reproducer tests. When you add a test alongside a fix, demonstrate it in this order, both manually run: + 1. You must revert **every** production change — every non-test file back to its pre-fix state, only the test remains. Run the test. It must fail, with the failure pointing at the bug — log the observed failure signal (assertion text, hang, panic). **Partial reverts, mental simulation, or "it would obviously fail without the fix" are treated as cheating.** If a full revert is genuinely impractical, stop and surface that — do not paper over it. + 2. Restore the production change in full. Run the test. It must pass. + Without a complete step 1 you've only proved your code works, not that the fix was necessary or that this test would have caught the bug. Don't accept "it passes now" as evidence the test guards the right thing. +- A test is only meaningful when there's something that could go wrong between the data being produced and the assertion being made. If the test builds the value it then asserts on (e.g., `format!`-ing a string and then asserting that the same string contains a substring it just put in), the assertion is tautological — nothing crossed a boundary, so nothing is being tested. The data must come from production code under test, not from the test body itself. +- Add or update tests when behavior changes around branching, parsing, retries, security checks, or boundaries. +- Prefer focused tests that prove the *right* reason for the change. +- Do not create tests that don't actually test project code. A test that only exercises stdlib or framework code is not a real test. +- Temporary tests that don't reference a project symbol must be written to a temporary directory — they are not production tests. +- Never weaken a test to force it green — fix the code under test, not the assertion. ---- +**Verify** (before reporting done) -## How to Use These Rules +- Run the smallest relevant verification first (`make test`, package-scoped test), then broaden if risk justifies. +- Don't claim tests passed unless they actually ran. If verification can't run, state the blocker and the residual risk. -**❌ WRONG: Checklist after coding** +**Cross-cutting** (apply at every phase) -1. Write code -2. Check if it follows rules -3. Fix violations +- Verify external findings against the working tree before acting. `/codex:adversarial-review`, lint, and PR comments work from a snapshot — they may name deleted code. `git grep` and `git diff` first. +- Treat every failure as a class, not an instance: when one surfaces, find and fix every sibling of the same shape in the same pass — grounded in what's actually there, not speculation. A single-site fix to a systemic bug isn't done. +- Honor scope reduction: "drop X" means drop X. Don't bundle adjacent improvements unprompted. -**✅ RIGHT: Active thinking before coding** +**Communication** -1. Search for existing solutions (`grep -r "pattern" src/`) -2. Read affected files completely (don't skim) -3. Analyze ownership/layering ("Who should know this?") -4. Question necessity ("What breaks if I don't add this?") -5. THEN code (following rules) +- Words: as concise and simple as possible, unless explicitly asked otherwise. +- A simple call graph (func name, class name, file name, LOC, short annotation) is the first choice when explaining code. -**The rules are not a QA checklist—they're a design thinking framework.** +Adapted from Clean Code (Robert C. Martin) via the polygala-inc AGENTS.md distillation. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 65060984e..829f32a77 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,7 @@ Key test entry points: 4. Run quality and tests (`make lint && make fmt:check && make test`) 5. Commit with clear messages 6. Open a Pull Request +7. Sign the [BoxLite Contributor License Agreement](./docs/legal/CLA.md) when CLA Assistant asks you to do so ### Code Style @@ -95,4 +96,6 @@ examples/ # Example code ## License -By contributing, you agree that your contributions will be licensed under the Apache License 2.0. +BoxLite is licensed under the Apache License, Version 2.0. + +By contributing, you agree that your contributions will be licensed under the Apache License, Version 2.0. Pull requests must satisfy CLA Assistant using the [BoxLite Contributor License Agreement](./docs/legal/CLA.md). diff --git a/Cargo.lock b/Cargo.lock index 695a1464c..8a31905b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,6 +214,7 @@ checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core 0.5.6", "axum-macros", + "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", @@ -232,8 +233,10 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", + "sha1", "sync_wrapper", "tokio", + "tokio-tungstenite 0.29.0", "tower 0.5.3", "tower-layer", "tower-service", @@ -290,18 +293,36 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bincode" version = "1.3.3" @@ -369,7 +390,7 @@ dependencies = [ [[package]] name = "boxlite" -version = "0.9.3" +version = "0.9.7" dependencies = [ "anyhow", "async-stream", @@ -426,6 +447,7 @@ dependencies = [ "time", "tokio", "tokio-stream", + "tokio-tungstenite 0.24.0", "tokio-util", "tonic", "tower 0.5.3", @@ -442,7 +464,7 @@ dependencies = [ [[package]] name = "boxlite-c" -version = "0.9.3" +version = "0.9.7" dependencies = [ "boxlite", "cbindgen", @@ -452,7 +474,7 @@ dependencies = [ [[package]] name = "boxlite-cli" -version = "0.9.3" +version = "0.9.7" dependencies = [ "anyhow", "assert_cmd", @@ -465,14 +487,19 @@ dependencies = [ "clap", "clap_complete", "comfy-table", + "dialoguer", "dirs 6.0.0", "futures", "gtmpl", "gtmpl_value", "nix 0.30.1", "notify", + "openidconnect", "predicates", + "reqwest", + "rpassword", "rstest", + "secrecy", "serde", "serde_json", "serde_yaml", @@ -481,15 +508,19 @@ dependencies = [ "tempfile", "term_size", "tokio", + "toml 0.8.23", "tower-http", "tracing", + "tracing-appender", "tracing-subscriber", "ulid", + "url", + "webbrowser", ] [[package]] name = "boxlite-guest" -version = "0.9.3" +version = "0.9.7" dependencies = [ "async-stream", "async-trait", @@ -500,7 +531,7 @@ dependencies = [ "libcontainer", "nix 0.29.0", "oci-spec 0.6.7", - "procfs 0.18.0", + "procfs", "rayon", "rtnetlink", "serde", @@ -518,7 +549,7 @@ dependencies = [ [[package]] name = "boxlite-node" -version = "0.9.3" +version = "0.9.7" dependencies = [ "boxlite", "boxlite-shared", @@ -532,7 +563,7 @@ dependencies = [ [[package]] name = "boxlite-python" -version = "0.9.3" +version = "0.9.7" dependencies = [ "boxlite", "futures", @@ -545,7 +576,7 @@ dependencies = [ [[package]] name = "boxlite-shared" -version = "0.9.3" +version = "0.9.7" dependencies = [ "proptest", "prost", @@ -561,7 +592,7 @@ dependencies = [ [[package]] name = "boxlite-shim" -version = "0.9.3" +version = "0.9.7" dependencies = [ "boxlite", "boxlite-shared", @@ -588,6 +619,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.12.1" @@ -601,7 +641,7 @@ dependencies = [ [[package]] name = "bubblewrap-sys" -version = "0.9.3" +version = "0.9.7" dependencies = [ "num_cpus", ] @@ -655,7 +695,7 @@ dependencies = [ "serde_json", "syn 2.0.117", "tempfile", - "toml", + "toml 0.9.12+spec-1.1.0", ] [[package]] @@ -760,6 +800,16 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "comfy-table" version = "7.2.2" @@ -771,6 +821,25 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "unicode-width", + "windows-sys 0.59.0", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const_format" version = "0.2.36" @@ -801,6 +870,16 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -882,6 +961,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.7" @@ -898,14 +989,51 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "400a21f1014a968ec518c7ccdf9b4a4ed0cac8c56ccb6d604f8b91f00110501e" +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "darling" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -922,17 +1050,58 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + [[package]] name = "darling_macro" version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "darling_core", + "darling_core 0.20.11", "quote", "syn 2.0.117", ] +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.5.8" @@ -940,6 +1109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -957,7 +1127,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ - "darling", + "darling 0.20.11", "proc-macro2", "quote", "syn 2.0.117", @@ -973,6 +1143,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "dialoguer" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" +dependencies = [ + "console", + "shell-words", + "tempfile", + "thiserror 1.0.69", + "zeroize", +] + [[package]] name = "difflib" version = "0.4.0" @@ -986,6 +1169,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1052,19 +1236,90 @@ dependencies = [ "litrs", ] +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + [[package]] name = "e2fsprogs-sys" -version = "0.9.3" +version = "0.9.7" dependencies = [ "num_cpus", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "endian-type" version = "0.1.2" @@ -1138,6 +1393,22 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.27" @@ -1192,6 +1463,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1343,6 +1620,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1403,6 +1681,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "gtmpl" version = "0.7.1" @@ -1457,7 +1746,16 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", ] [[package]] @@ -1468,11 +1766,11 @@ checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" [[package]] name = "hashlink" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -1508,6 +1806,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1621,7 +1928,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots", + "webpki-roots 1.0.7", ] [[package]] @@ -1807,6 +2114,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -1891,6 +2199,15 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -1906,6 +2223,55 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.117", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -1994,6 +2360,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "leb128fmt" @@ -2009,15 +2378,14 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libcgroups" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acabc2d6b351af9406d5bddfe86697c3791fda2a6d6d03b90d86af1f0998751e" +version = "0.6.0" +source = "git+https://github.com/youki-dev/youki?rev=4b2f0e00a4a11107f3a338c21c17407d2f664ec9#4b2f0e00a4a11107f3a338c21c17407d2f664ec9" dependencies = [ "fixedbitset", "nix 0.29.0", - "oci-spec 0.8.4", + "oci-spec 0.9.0", "pathrs", - "procfs 0.17.0", + "procfs", "serde", "thiserror 2.0.18", "tracing", @@ -2025,9 +2393,8 @@ dependencies = [ [[package]] name = "libcontainer" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6320ae84435bed00efb3e0a7c2de8a38bba4d619e801d8ab3a8527fcf427709" +version = "0.6.0" +source = "git+https://github.com/youki-dev/youki?rev=4b2f0e00a4a11107f3a338c21c17407d2f664ec9#4b2f0e00a4a11107f3a338c21c17407d2f664ec9" dependencies = [ "caps", "chrono", @@ -2036,13 +2403,14 @@ dependencies = [ "libcgroups", "libseccomp", "nc", + "netlink-packet-core 0.8.1", + "netlink-packet-route 0.26.0", + "netlink-sys", "nix 0.29.0", - "oci-spec 0.8.4", - "once_cell", + "oci-spec 0.9.0", "pathrs", "prctl", - "procfs 0.17.0", - "protobuf", + "procfs", "regex", "rust-criu", "safe-path", @@ -2054,14 +2422,14 @@ dependencies = [ [[package]] name = "libgvproxy-sys" -version = "0.9.3" +version = "0.9.7" dependencies = [ "libc", ] [[package]] name = "libkrun-sys" -version = "0.9.3" +version = "0.9.7" dependencies = [ "libc", "num_cpus", @@ -2078,6 +2446,12 @@ dependencies = [ "windows-link", ] +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + [[package]] name = "libredox" version = "0.1.16" @@ -2110,9 +2484,9 @@ checksum = "60276e2d41bbb68b323e566047a1bfbf952050b157d8b5cdc74c07c1bf4ca3b6" [[package]] name = "libsqlite3-sys" -version = "0.35.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" dependencies = [ "cc", "pkg-config", @@ -2348,6 +2722,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "netlink-packet-core" version = "0.7.0" @@ -2359,6 +2739,15 @@ dependencies = [ "netlink-packet-utils", ] +[[package]] +name = "netlink-packet-core" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" +dependencies = [ + "paste", +] + [[package]] name = "netlink-packet-route" version = "0.19.0" @@ -2369,10 +2758,22 @@ dependencies = [ "byteorder", "libc", "log", - "netlink-packet-core", + "netlink-packet-core 0.7.0", "netlink-packet-utils", ] +[[package]] +name = "netlink-packet-route" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea06a7cec15a9df94c58bddc472b1de04ca53bd32e72da7da2c5dd1c3885edc" +dependencies = [ + "bitflags 2.11.1", + "libc", + "log", + "netlink-packet-core 0.8.1", +] + [[package]] name = "netlink-packet-utils" version = "0.5.2" @@ -2394,7 +2795,7 @@ dependencies = [ "bytes", "futures", "log", - "netlink-packet-core", + "netlink-packet-core 0.7.0", "netlink-sys", "thiserror 2.0.18", ] @@ -2544,12 +2945,48 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.6", + "smallvec", + "zeroize", +] + [[package]] name = "num-conv" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2557,6 +2994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2569,6 +3007,51 @@ dependencies = [ "libc", ] +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.17", + "http", + "rand 0.8.6", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.1", + "objc2", +] + [[package]] name = "oci-client" version = "0.15.0" @@ -2627,6 +3110,23 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "oci-spec" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8445a2631507cec628a15fdd6154b54a3ab3f20ed4fe9d73a3b8b7a4e1ba03a" +dependencies = [ + "const_format", + "derive_builder", + "getset", + "regex", + "serde", + "serde_json", + "strum 0.27.2", + "strum_macros 0.27.2", + "thiserror 2.0.18", +] + [[package]] name = "olpc-cjson" version = "0.1.4" @@ -2650,12 +3150,76 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "openidconnect" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c6709ba2ea764bbed26bce1adf3c10517113ddea6f2d4196e4851757ef2b2" +dependencies = [ + "base64 0.21.7", + "chrono", + "dyn-clone", + "ed25519-dalek", + "hmac", + "http", + "itertools 0.10.5", + "log", + "oauth2", + "p256", + "p384", + "rand 0.8.6", + "rsa", + "serde", + "serde-value", + "serde_json", + "serde_path_to_error", + "serde_plain", + "serde_with", + "sha2", + "subtle", + "thiserror 1.0.69", + "url", +] + [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "papergrid" version = "0.13.0" @@ -2703,7 +3267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fb2311801201fc6fd2e8a9f4841b41eee565e992fbe713731e29e367b8e3f17" dependencies = [ "bitflags 2.11.1", - "itertools", + "itertools 0.14.0", "libc", "memchr", "once_cell", @@ -2724,6 +3288,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2772,6 +3345,27 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -2864,13 +3458,22 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -2904,20 +3507,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "procfs" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" -dependencies = [ - "bitflags 2.11.1", - "chrono", - "flate2", - "hex", - "procfs-core 0.17.0", - "rustix 0.38.44", -] - [[package]] name = "procfs" version = "0.18.0" @@ -2927,21 +3516,10 @@ dependencies = [ "bitflags 2.11.1", "chrono", "flate2", - "procfs-core 0.18.0", + "procfs-core", "rustix 1.1.4", ] -[[package]] -name = "procfs-core" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" -dependencies = [ - "bitflags 2.11.1", - "chrono", - "hex", -] - [[package]] name = "procfs-core" version = "0.18.0" @@ -2989,7 +3567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools", + "itertools 0.14.0", "log", "multimap", "once_cell", @@ -3009,7 +3587,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools", + "itertools 0.14.0", "proc-macro2", "quote", "syn 2.0.117", @@ -3026,9 +3604,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "3.2.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ "once_cell", "protobuf-support", @@ -3037,9 +3615,9 @@ dependencies = [ [[package]] name = "protobuf-codegen" -version = "3.2.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" +checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" dependencies = [ "anyhow", "once_cell", @@ -3052,12 +3630,12 @@ dependencies = [ [[package]] name = "protobuf-parse" -version = "3.2.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" +checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" dependencies = [ "anyhow", - "indexmap 1.9.3", + "indexmap 2.14.0", "log", "protobuf", "protobuf-support", @@ -3068,9 +3646,9 @@ dependencies = [ [[package]] name = "protobuf-support" -version = "3.2.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ "thiserror 1.0.69", ] @@ -3409,6 +3987,26 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "reflink-copy" version = "0.1.29" @@ -3494,21 +4092,72 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 1.0.7", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rpassword" +version = "7.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac5b223d9738ef56e0b98305410be40fa0941bf6036c56f1506751e43552d64" +dependencies = [ + "libc", + "rtoolbox", + "windows-sys 0.61.2", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", ] [[package]] -name = "ring" -version = "0.17.14" +name = "rsqlite-vfs" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", + "hashbrown 0.16.1", + "thiserror 2.0.18", ] [[package]] @@ -3549,8 +4198,8 @@ checksum = "b684475344d8df1859ddb2d395dd3dac4f8f3422a1aa0725993cb375fc5caba5" dependencies = [ "futures", "log", - "netlink-packet-core", - "netlink-packet-route", + "netlink-packet-core 0.7.0", + "netlink-packet-route 0.19.0", "netlink-packet-utils", "netlink-proto", "netlink-sys", @@ -3559,11 +4208,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "rtoolbox" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "rusqlite" -version = "0.37.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" dependencies = [ "bitflags 2.11.1", "fallible-iterator", @@ -3571,13 +4230,14 @@ dependencies = [ "hashlink", "libsqlite3-sys", "smallvec", + "sqlite-wasm-rs", ] [[package]] name = "rust-criu" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4737b28406b3395359f485127073117a11cedc8942738b69ba6ab9a79432acbc" +checksum = "d92037bbe1317b5b615ce32efd5fb63538e3fd57ba424d6f23fe3eb0554294ed" dependencies = [ "anyhow", "libc", @@ -3703,6 +4363,30 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3715,6 +4399,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "seccompiler" version = "0.4.0" @@ -3726,6 +4424,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "serde", + "zeroize", +] + [[package]] name = "semver" version = "1.0.28" @@ -3742,6 +4450,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -3786,6 +4504,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_spanned" version = "1.1.1" @@ -3807,6 +4543,38 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -3820,6 +4588,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.9" @@ -3840,6 +4619,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + [[package]] name = "shlex" version = "1.3.0" @@ -3866,12 +4651,38 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "simd-adler32" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "slab" version = "0.4.12" @@ -3914,6 +4725,34 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlite-wasm-rs" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd578e94101503d97e2b286bbf8db2135035ca24b2ce4cbf3f9e2fb2bbf1eee" +dependencies = [ + "cc", + "js-sys", + "rsqlite-vfs", + "wasm-bindgen", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -4271,6 +5110,34 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite 0.24.0", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + [[package]] name = "tokio-uring" version = "0.4.0" @@ -4312,6 +5179,18 @@ dependencies = [ "vsock", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -4320,13 +5199,22 @@ checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap 2.14.0", "serde_core", - "serde_spanned", + "serde_spanned 1.1.1", "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow 0.7.15", ] +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -4345,6 +5233,20 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "toml_write", + "winnow 0.7.15", +] + [[package]] name = "toml_edit" version = "0.25.11+spec-1.1.0" @@ -4366,6 +5268,12 @@ dependencies = [ "winnow 1.0.2", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "toml_writer" version = "1.1.1+spec-1.1.0" @@ -4463,12 +5371,14 @@ dependencies = [ "futures-util", "http", "http-body", + "http-body-util", "iri-string", "pin-project-lite", "tower 0.5.3", "tower-layer", "tower-service", "tracing", + "uuid", ] [[package]] @@ -4540,6 +5450,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" @@ -4550,12 +5470,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex-automata", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -4564,6 +5487,42 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.8.6", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 1.0.69", + "utf-8", +] + +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", +] + [[package]] name = "typenum" version = "1.20.0" @@ -4659,6 +5618,7 @@ dependencies = [ "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -4667,6 +5627,12 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -4919,6 +5885,31 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webbrowser" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +dependencies = [ + "core-foundation", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.7", +] + [[package]] name = "webpki-roots" version = "1.0.7" @@ -5336,6 +6327,9 @@ name = "winnow" version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] [[package]] name = "winnow" diff --git a/Cargo.toml b/Cargo.toml index de25a6cc3..5d0c45b9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,15 +19,21 @@ exclude = ["build/tmp", "target", ".venv", "examples/*/.venv"] resolver = "2" [workspace.dependencies] -boxlite = { path = "src/boxlite", version = "0.9.3" } -boxlite-shared = { path = "src/shared", version = "0.9.3" } -bubblewrap-sys = { path = "src/deps/bubblewrap-sys", version = "0.9.3" } -e2fsprogs-sys = { path = "src/deps/e2fsprogs-sys", version = "0.9.3" } -libgvproxy-sys = { path = "src/deps/libgvproxy-sys", version = "0.9.3" } -libkrun-sys = { path = "src/deps/libkrun-sys", version = "0.9.3" } +boxlite = { path = "src/boxlite", version = "0.9.7" } +boxlite-shared = { path = "src/shared", version = "0.9.7" } +bubblewrap-sys = { path = "src/deps/bubblewrap-sys", version = "0.9.7" } +e2fsprogs-sys = { path = "src/deps/e2fsprogs-sys", version = "0.9.7" } +libgvproxy-sys = { path = "src/deps/libgvproxy-sys", version = "0.9.7" } +libkrun-sys = { path = "src/deps/libkrun-sys", version = "0.9.7" } + +[patch.crates-io] +# Pull in youki PR #3504 (split intermediate/init readiness channels) which +# fixes the InitReady/IntermediateReady race under concurrent ContainerBuilder +# builds. Not in any tagged release yet (post-v0.6.0). +libcontainer = { git = "https://github.com/youki-dev/youki", rev = "4b2f0e00a4a11107f3a338c21c17407d2f664ec9" } [workspace.package] -version = "0.9.3" +version = "0.9.7" edition = "2024" authors = ["Dorian Zheng "] license = "Apache-2.0" diff --git a/README.md b/README.md index 5999d9bbf..c70191da9 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,23 @@ -# BoxLite [![Discord](https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white)](https://go.boxlite.ai/discord) - -[![GitHub stars](https://img.shields.io/github/stars/boxlite-ai/boxlite?style=social)](https://github.com/boxlite-ai/boxlite) -[![Build](https://github.com/boxlite-ai/boxlite/actions/workflows/build-wheels.yml/badge.svg)](https://github.com/boxlite-ai/boxlite/actions/workflows/build-wheels.yml) -[![Lint](https://github.com/boxlite-ai/boxlite/actions/workflows/lint.yml/badge.svg)](https://github.com/boxlite-ai/boxlite/actions/workflows/lint.yml) -[![codecov](https://codecov.io/gh/boxlite-ai/boxlite/branch/main/graph/badge.svg)](https://codecov.io/gh/boxlite-ai/boxlite) -[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -Compute substrate for AI agents: lightweight enough to live on your laptop, elastic enough to scale into the cloud and unleash unlimited resources. +

+ + + + BoxLite + +

+ +

+ Discord + GitHub stars + Build + Lint + codecov + License +

+ +

+ Compute substrate for AI agents: lightweight enough to live on your laptop, elastic enough to scale into the cloud and unleash unlimited resources. +

## What is BoxLite? @@ -185,7 +196,7 @@ func main() { -## REST API Quick Start +## CLI Quick Start
View guide @@ -193,9 +204,34 @@ func main() { ### Install ```bash -cargo install boxlite-cli +curl -fsSL https://sh.boxlite.ai | sh +``` + +Installs to `$HOME/.local/bin/boxlite`. The runtime is embedded in the +binary — no extra setup. For alternatives (`cargo install boxlite-cli`, +version pinning, custom install dir) and release-artifact verification, +see the [CLI Reference's Installation & Verification section](./docs/reference/cli/README.md#installation--verification). + +### Run + +```bash +boxlite run python:slim python -c "print('Hello from BoxLite!')" ``` +
+ + +## REST API Quick Start + +
+View guide + +### Install + +Install the `boxlite` CLI — see [CLI Quick Start](#cli-quick-start). The +REST server ships with the same binary. For release-artifact verification, +see the [CLI Reference's Installation & Verification section](./docs/reference/cli/README.md#installation--verification). + ### Start the server ```bash @@ -207,12 +243,12 @@ boxlite serve ```bash # Create a box -curl -s -X POST http://localhost:8100/v1/default/boxes \ +curl -s -X POST http://localhost:8100/v1/boxes \ -H 'Content-Type: application/json' \ -d '{"image": "alpine:latest"}' # Run a command (replace BOX_ID from the response above) -curl -s -X POST http://localhost:8100/v1/default/boxes/BOX_ID/exec \ +curl -s -X POST http://localhost:8100/v1/boxes/BOX_ID/exec \ -H 'Content-Type: application/json' \ -d '{"command": "echo", "args": ["Hello from BoxLite!"]}' ``` @@ -284,7 +320,7 @@ For details, see [Architecture](./docs/architecture/). ## Documentation -- API Reference — Coming soon +- [API & CLI Reference](./docs/reference/) — SDK API references (Python, Node.js, Rust, C) and the `boxlite` CLI reference - [Examples](./examples/) — Sample code for common use cases - [Architecture](./docs/architecture/) — How BoxLite works under the hood diff --git a/SECURITY.md b/SECURITY.md index 306c43510..1e7123ada 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -55,8 +55,8 @@ the latest published minor release. We do not back-patch older releases. | Version | Supported | |---------|--------------------| -| 0.8.x | :white_check_mark: | -| < 0.8 | :x: | +| 0.9.x | :white_check_mark: | +| < 0.9 | :x: | ## Scope @@ -105,6 +105,17 @@ destruction, and service disruption while researching. Only interact with accounts and systems you own or for which you have explicit permission. +## Published Advisories + +| Advisory | Severity | Affected | Fixed in | +|----------|----------|----------|----------| +| [GHSA-g6ww-w5j2-r7x3](https://github.com/boxlite-ai/boxlite/security/advisories/GHSA-g6ww-w5j2-r7x3) — read-only volume remount bypass | Critical | `< 0.9.0` (all SDKs) | **0.9.0** | +| [GHSA-f396-4rp4-7v2j](https://github.com/boxlite-ai/boxlite/security/advisories/GHSA-f396-4rp4-7v2j) — OCI layer symlink escape → arbitrary host write | Critical | `< 0.9.0` (all SDKs) | **0.9.0** | + +If you run any boxlite SDK (PyPI, npm, Go, crates.io) or build from source +at a version **before 0.9.0**, upgrade to **0.9.0 or later**. There is no +workaround for affected versions. + --- Thanks for helping keep BoxLite and its users safe. diff --git a/apps/.claude/launch.json b/apps/.claude/launch.json new file mode 100644 index 000000000..1ebbcb629 --- /dev/null +++ b/apps/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "dashboard", + "runtimeExecutable": "npx", + "runtimeArgs": ["nx", "serve", "dashboard"], + "port": 3000 + } + ] +} diff --git a/apps/AGENTS.md b/apps/AGENTS.md new file mode 120000 index 000000000..681311eb9 --- /dev/null +++ b/apps/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/apps/CLAUDE.md b/apps/CLAUDE.md new file mode 100644 index 000000000..23f6a5174 --- /dev/null +++ b/apps/CLAUDE.md @@ -0,0 +1,25 @@ +## Local Dashboard And E2E Commands + +Run these from `apps/` (the nx workspace root), **not** the repository root. + +The dashboard always calls a same-origin `/api` path; Vite proxies `/api` to the +selected backend, so the browser never makes a cross-origin (CORS-gated) request +and no backend has to allow-list `localhost`. The proxy target is chosen by the +command and read by `apps/dashboard/vite.config.mts` via `DASHBOARD_API_PROXY_TARGET` +(see the `API_TARGETS` map in `apps/scripts/start-dashboard.mjs`). Auth follows the +selected API: login uses whatever OIDC issuer that API's `/api/config` reports. + +- `npm run start` / `npm run start:dev` — dashboard against the **dev API** (default). +- `npm run start:local` — dashboard against a **local API** (`/api` → `http://localhost:3001`). +- `npm run start:prod` — dashboard against the **prod API**. Guarded: requires + `--yes-prod`, and prod is unconfigured until the prod stage exists. +- `npm run start -- --api=` — proxy `/api` to **any** API URL (staging, a PR preview, …). +- `npm run start:mock` — MSW mocks; no backend, no login. +- `npm run dev:dex` — full local Dex development. Starts Docker Postgres, Redis, Dex, + and the apps workspace with OIDC pointed at Dex. +- `npm run e2e:local` — the only E2E startup entrypoint; do not use `start`, + `start:dex`, or `serve-slim` directly for E2E. +- The local Dex test account is `admin@boxlite.dev` / `password`. Browser E2E should + log in through Dex when redirected and should not depend on cached cookies. +- `dev:dex` and `e2e:local` require Docker Desktop and create/reuse + `boxlite-local-postgres`, `boxlite-local-redis`, and `boxlite-local-dex`. diff --git a/apps/NOTICE b/apps/NOTICE index 6b8374983..24f8f371f 100644 --- a/apps/NOTICE +++ b/apps/NOTICE @@ -13,7 +13,7 @@ f982c45a42e52c98631002c7dc1df565c44d7aac License map: -- AGPL-3.0 components include apps/api, apps/cli, apps/daemon, +- AGPL-3.0 components include apps/api, apps/daemon, apps/dashboard, apps/infra, apps/libs/computer-use, apps/otel-collector, apps/proxy, apps/runner, apps/snapshot-manager, and apps/ssh-gateway. - Apache-2.0 components include apps/common-go and generated SDK/client diff --git a/apps/api-client-go/.openapi-generator/FILES b/apps/api-client-go/.openapi-generator/FILES index eb2f5b875..61bd90986 100644 --- a/apps/api-client-go/.openapi-generator/FILES +++ b/apps/api-client-go/.openapi-generator/FILES @@ -3,8 +3,9 @@ api/openapi.yaml api_admin.go api_api_keys.go api_audit.go +api_auth.go +api_box.go api_config.go -api_docker_registry.go api_health.go api_jobs.go api_object_storage.go @@ -12,183 +13,119 @@ api_organizations.go api_preview.go api_regions.go api_runners.go -api_sandbox.go -api_snapshots.go -api_toolbox.go api_users.go api_volumes.go api_webhooks.go -api_workspace.go client.go configuration.go go.mod go.sum model_account_provider.go +model_admin_box_item.go +model_admin_box_owner.go model_admin_create_runner.go +model_admin_machine_item.go +model_admin_observability_audit_log.go +model_admin_observability_backend_status_dto.go +model_admin_observability_click_stack_links.go +model_admin_observability_click_stack_source_setup.go +model_admin_observability_commands.go +model_admin_observability_correlation.go +model_admin_observability_external_links.go +model_admin_observability_investigate_response.go +model_admin_observability_layer_signals_dto.go +model_admin_observability_layer_status_dto.go +model_admin_observability_operation.go +model_admin_observability_resource_summary.go +model_admin_observability_s3_object.go +model_admin_observability_source_status.go +model_admin_observability_status_dto.go +model_admin_observability_timeline_event.go +model_admin_observability_x_log.go +model_admin_overview.go +model_admin_overview_boxes.go +model_admin_overview_cluster.go +model_admin_overview_runners.go +model_admin_runner.go +model_admin_runner_item.go +model_admin_user_item.go model_announcement.go model_api_key_list.go model_api_key_response.go model_audit_log.go -model_build_info.go -model_command.go -model_completion_context.go -model_completion_item.go -model_completion_list.go -model_compressed_screenshot_response.go -model_computer_use_start_response.go -model_computer_use_status_response.go -model_computer_use_stop_response.go +model_box.go +model_box_class.go +model_box_desired_state.go +model_box_labels.go +model_box_state.go +model_box_volume.go +model_boxlite_configuration.go model_create_api_key.go -model_create_build_info.go -model_create_docker_registry.go model_create_linked_account.go model_create_organization.go model_create_organization_invitation.go -model_create_organization_quota.go model_create_organization_role.go model_create_region.go model_create_region_response.go model_create_runner.go model_create_runner_response.go -model_create_sandbox.go -model_create_session_request.go -model_create_snapshot.go model_create_user.go model_create_volume.go -model_create_workspace.go -model_boxlite_configuration.go -model_display_info_response.go -model_docker_registry.go -model_download_files.go -model_execute_request.go -model_execute_response.go -model_file_info.go -model_file_status.go -model_git_add_request.go -model_git_branch_request.go -model_git_checkout_request.go -model_git_clone_request.go -model_git_commit_info.go -model_git_commit_request.go -model_git_commit_response.go -model_git_delete_branch_request.go -model_git_repo_request.go -model_git_status.go model_health_controller_check_200_response.go model_health_controller_check_200_response_info_value.go -model_health_controller_check_503_response.go model_job.go model_job_status.go model_job_type.go -model_keyboard_hotkey_request.go -model_keyboard_press_request.go -model_keyboard_type_request.go -model_list_branch_response.go model_log_entry.go -model_lsp_completion_params.go -model_lsp_document_request.go -model_lsp_location.go -model_lsp_server_request.go -model_lsp_symbol.go -model_match.go model_metric_data_point.go model_metric_series.go model_metrics_response.go -model_mouse_click_request.go -model_mouse_click_response.go -model_mouse_drag_request.go -model_mouse_drag_response.go -model_mouse_move_request.go -model_mouse_move_response.go -model_mouse_position.go -model_mouse_scroll_request.go -model_mouse_scroll_response.go model_oidc_config.go model_organization.go +model_organization_box_default_limited_network_egress.go model_organization_invitation.go model_organization_role.go -model_organization_sandbox_default_limited_network_egress.go model_organization_suspension.go -model_organization_usage_overview.go model_organization_user.go model_otel_config.go model_paginated_audit_logs.go +model_paginated_boxes.go model_paginated_jobs.go model_paginated_logs.go -model_paginated_sandboxes.go -model_paginated_snapshots.go model_paginated_traces.go model_poll_jobs_response.go model_port_preview_url.go -model_position.go model_posthog_config.go -model_process_errors_response.go -model_process_logs_response.go -model_process_restart_response.go -model_process_status_response.go -model_project_dir_response.go -model_pty_create_request.go -model_pty_create_response.go -model_pty_list_response.go -model_pty_resize_request.go -model_pty_session_info.go -model_range.go model_rate_limit_config.go model_rate_limit_entry.go model_regenerate_api_key_response.go model_region.go -model_region_quota.go -model_region_screenshot_response.go model_region_type.go -model_region_usage_overview.go -model_registry_push_access_dto.go -model_replace_request.go -model_replace_result.go -model_resize_sandbox.go +model_resize_box.go model_runner.go model_runner_full.go model_runner_health_metrics.go model_runner_healthcheck.go model_runner_service_health.go -model_runner_snapshot_dto.go model_runner_state.go -model_sandbox.go -model_sandbox_class.go -model_sandbox_desired_state.go -model_sandbox_info.go -model_sandbox_labels.go -model_sandbox_state.go -model_sandbox_volume.go -model_screenshot_response.go -model_search_files_response.go model_send_webhook_dto.go -model_session.go -model_session_execute_request.go -model_session_execute_response.go -model_set_snapshot_general_status_dto.go model_signed_port_preview_url.go -model_snapshot_dto.go -model_snapshot_manager_credentials.go -model_snapshot_state.go model_ssh_access_dto.go model_ssh_access_validation_dto.go model_storage_access_dto.go +model_system_role.go model_toolbox_proxy_url.go model_trace_span.go model_trace_summary.go -model_update_docker_registry.go +model_update_box_state_dto.go model_update_job_status.go model_update_organization_default_region.go model_update_organization_invitation.go model_update_organization_member_access.go -model_update_organization_quota.go -model_update_organization_region_quota.go +model_update_organization_name.go model_update_organization_role.go model_update_region.go -model_update_sandbox_state_dto.go -model_url.go model_user.go -model_user_home_dir_response.go model_user_public_key.go model_volume_dto.go model_volume_state.go @@ -196,9 +133,5 @@ model_webhook_app_portal_access.go model_webhook_controller_get_status_200_response.go model_webhook_event.go model_webhook_initialization_status.go -model_windows_response.go -model_work_dir_response.go -model_workspace.go -model_workspace_port_preview_url.go response.go utils.go diff --git a/apps/api-client-go/.openapi-generator/VERSION b/apps/api-client-go/.openapi-generator/VERSION index 5f84a81db..14d6b5dc3 100644 --- a/apps/api-client-go/.openapi-generator/VERSION +++ b/apps/api-client-go/.openapi-generator/VERSION @@ -1 +1 @@ -7.12.0 +7.23.0 diff --git a/apps/api-client-go/api/openapi.yaml b/apps/api-client-go/api/openapi.yaml index bb1b43ac9..7a4f572c9 100644 --- a/apps/api-client-go/api/openapi.yaml +++ b/apps/api-client-go/api/openapi.yaml @@ -19,7 +19,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/BoxLiteConfiguration' + $ref: "#/components/schemas/BoxliteConfiguration" description: BoxLite configuration summary: Get config tags: @@ -42,7 +42,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/ApiKeyList' + $ref: "#/components/schemas/ApiKeyList" type: array description: API keys retrieved successfully. "500": @@ -71,14 +71,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateApiKey' + $ref: "#/components/schemas/CreateApiKey" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/ApiKeyResponse' + $ref: "#/components/schemas/ApiKeyResponse" description: API key created successfully. security: - bearer: [] @@ -106,7 +106,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ApiKeyList' + $ref: "#/components/schemas/ApiKeyList" description: API key retrieved successfully. security: - bearer: [] @@ -171,7 +171,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ApiKeyList' + $ref: "#/components/schemas/ApiKeyList" description: API key retrieved successfully. security: - bearer: [] @@ -230,7 +230,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/OrganizationInvitation' + $ref: "#/components/schemas/OrganizationInvitation" type: array description: List of organization invitations security: @@ -279,7 +279,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/OrganizationInvitation' + $ref: "#/components/schemas/OrganizationInvitation" description: Organization invitation accepted successfully security: - bearer: [] @@ -324,7 +324,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Organization' + $ref: "#/components/schemas/Organization" type: array description: List of organizations security: @@ -343,14 +343,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateOrganization' + $ref: "#/components/schemas/CreateOrganization" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/Organization' + $ref: "#/components/schemas/Organization" description: Organization created successfully security: - bearer: [] @@ -361,9 +361,9 @@ paths: summary: Create organization tags: - organizations - /organizations/{organizationId}/default-region: + /organizations/{organizationId}/name: patch: - operationId: setOrganizationDefaultRegion + operationId: updateOrganizationName parameters: - description: Organization ID explode: false @@ -377,23 +377,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateOrganizationDefaultRegion' + $ref: "#/components/schemas/UpdateOrganizationName" required: true responses: - "204": - description: Default region set successfully + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/Organization" + description: Organization name updated successfully security: - bearer: [] - oauth2: - openid - profile - email - summary: Set default region for organization + summary: Update organization name tags: - organizations - /organizations/{organizationId}: - delete: - operationId: deleteOrganization + /organizations/{organizationId}/default-region: + patch: + operationId: setOrganizationDefaultRegion parameters: - description: Organization ID explode: false @@ -403,20 +407,27 @@ paths: schema: type: string style: simple + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateOrganizationDefaultRegion" + required: true responses: "204": - description: Organization deleted successfully + description: Default region set successfully security: - bearer: [] - oauth2: - openid - profile - email - summary: Delete organization + summary: Set default region for organization tags: - organizations - get: - operationId: getOrganization + /organizations/{organizationId}: + delete: + operationId: deleteOrganization parameters: - description: Organization ID explode: false @@ -427,24 +438,19 @@ paths: type: string style: simple responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Organization' - description: Organization details + "204": + description: Organization deleted successfully security: - bearer: [] - oauth2: - openid - profile - email - summary: Get organization by ID + summary: Delete organization tags: - organizations - /organizations/{organizationId}/usage: get: - operationId: getOrganizationUsageOverview + operationId: getOrganization parameters: - description: Organization ID explode: false @@ -459,83 +465,15 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/OrganizationUsageOverview' - description: Current usage overview - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get organization current usage overview - tags: - - organizations - /organizations/{organizationId}/quota: - patch: - operationId: updateOrganizationQuota - parameters: - - description: Organization ID - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateOrganizationQuota' - required: true - responses: - "204": - description: Organization quota updated successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Update organization quota - tags: - - organizations - /organizations/{organizationId}/quota/{regionId}: - patch: - operationId: updateOrganizationRegionQuota - parameters: - - description: Organization ID - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - - description: ID of the region where the updated quota will be applied - explode: false - in: path - name: regionId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateOrganizationRegionQuota' - required: true - responses: - "204": - description: Region quota updated successfully + $ref: "#/components/schemas/Organization" + description: Organization details security: - bearer: [] - oauth2: - openid - profile - email - summary: Update organization region quota + summary: Get organization by ID tags: - organizations /organizations/{organizationId}/leave: @@ -578,7 +516,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/OrganizationSuspension' + $ref: "#/components/schemas/OrganizationSuspension" required: false responses: "204": @@ -616,14 +554,14 @@ paths: summary: Unsuspend organization tags: - organizations - /organizations/by-sandbox-id/{sandboxId}: + /organizations/by-box-id/{boxId}: get: - operationId: getOrganizationBySandboxId + operationId: getOrganizationByBoxId parameters: - - description: Sandbox ID + - description: Box ID explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -633,7 +571,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Organization' + $ref: "#/components/schemas/Organization" description: Organization security: - bearer: [] @@ -641,42 +579,14 @@ paths: - openid - profile - email - summary: Get organization by sandbox ID - tags: - - organizations - /organizations/region-quota/by-sandbox-id/{sandboxId}: - get: - operationId: getRegionQuotaBySandboxId - parameters: - - description: Sandbox ID - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/RegionQuota' - description: Region quota - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get region quota by sandbox ID + summary: Get organization by box ID tags: - organizations - /organizations/otel-config/by-sandbox-auth-token/{authToken}: + /organizations/otel-config/by-box-auth-token/{authToken}: get: - operationId: getOrganizationOtelConfigBySandboxAuthToken + operationId: getOrganizationOtelConfigByBoxAuthToken parameters: - - description: Sandbox Auth Token + - description: Box Auth Token explode: false in: path name: authToken @@ -689,7 +599,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/OtelConfig' + $ref: "#/components/schemas/OtelConfig" description: OTEL Config security: - bearer: [] @@ -697,12 +607,12 @@ paths: - openid - profile - email - summary: Get organization OTEL config by sandbox auth token + summary: Get organization OTEL config by box auth token tags: - organizations - /organizations/{organizationId}/sandbox-default-limited-network-egress: + /organizations/{organizationId}/box-default-limited-network-egress: post: - operationId: updateSandboxDefaultLimitedNetworkEgress + operationId: updateBoxDefaultLimitedNetworkEgress parameters: - description: Organization ID explode: false @@ -716,18 +626,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/OrganizationSandboxDefaultLimitedNetworkEgress' + $ref: "#/components/schemas/OrganizationBoxDefaultLimitedNetworkEgress" required: true responses: "204": - description: Sandbox default limited network egress updated successfully + description: Box default limited network egress updated successfully security: - bearer: [] - oauth2: - openid - profile - email - summary: Update sandbox default limited network egress + summary: Update box default limited network egress tags: - organizations /organizations/{organizationId}/experimental-config: @@ -786,7 +696,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/OrganizationRole' + $ref: "#/components/schemas/OrganizationRole" type: array description: List of organization roles security: @@ -813,14 +723,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateOrganizationRole' + $ref: "#/components/schemas/CreateOrganizationRole" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/OrganizationRole' + $ref: "#/components/schemas/OrganizationRole" description: Organization role created successfully security: - bearer: [] @@ -886,14 +796,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateOrganizationRole' + $ref: "#/components/schemas/UpdateOrganizationRole" required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/OrganizationRole' + $ref: "#/components/schemas/OrganizationRole" description: Role updated successfully security: - bearer: [] @@ -922,7 +832,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/OrganizationUser' + $ref: "#/components/schemas/OrganizationUser" type: array description: List of organization members security: @@ -958,14 +868,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateOrganizationMemberAccess' + $ref: "#/components/schemas/UpdateOrganizationMemberAccess" required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/OrganizationUser' + $ref: "#/components/schemas/OrganizationUser" description: Access updated successfully security: - bearer: [] @@ -1026,7 +936,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/OrganizationInvitation' + $ref: "#/components/schemas/OrganizationInvitation" type: array description: List of pending organization invitations security: @@ -1053,14 +963,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateOrganizationInvitation' + $ref: "#/components/schemas/CreateOrganizationInvitation" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/OrganizationInvitation' + $ref: "#/components/schemas/OrganizationInvitation" description: Organization invitation created successfully security: - bearer: [] @@ -1095,14 +1005,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateOrganizationInvitation' + $ref: "#/components/schemas/UpdateOrganizationInvitation" required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/OrganizationInvitation' + $ref: "#/components/schemas/OrganizationInvitation" description: Organization invitation updated successfully security: - bearer: [] @@ -1163,7 +1073,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Region' + $ref: "#/components/schemas/Region" type: array description: List of all available regions security: @@ -1190,14 +1100,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateRegion' + $ref: "#/components/schemas/CreateRegion" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/CreateRegionResponse' + $ref: "#/components/schemas/CreateRegionResponse" description: The region has been successfully created. security: - bearer: [] @@ -1264,7 +1174,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Region' + $ref: "#/components/schemas/Region" description: "" security: - bearer: [] @@ -1298,7 +1208,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateRegion' + $ref: "#/components/schemas/UpdateRegion" required: true responses: "200": @@ -1337,7 +1247,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegenerateApiKeyResponse' + $ref: "#/components/schemas/RegenerateApiKeyResponse" description: The proxy API key has been successfully regenerated. security: - bearer: [] @@ -1373,7 +1283,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegenerateApiKeyResponse' + $ref: "#/components/schemas/RegenerateApiKeyResponse" description: The SSH gateway API key has been successfully regenerated. security: - bearer: [] @@ -1384,42 +1294,6 @@ paths: summary: Regenerate SSH gateway API key for a region tags: - organizations - /regions/{id}/regenerate-snapshot-manager-credentials: - post: - operationId: regenerateSnapshotManagerCredentials - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Region ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SnapshotManagerCredentials' - description: The snapshot manager credentials have been successfully regenerated. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Regenerate snapshot manager credentials for a region - tags: - - organizations /users/me: get: operationId: getAuthenticatedUser @@ -1429,7 +1303,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" description: User details security: - bearer: [] @@ -1463,7 +1337,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateUser' + $ref: "#/components/schemas/CreateUser" required: true responses: "201": @@ -1510,7 +1384,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/AccountProvider' + $ref: "#/components/schemas/AccountProvider" type: array description: Available account providers security: @@ -1530,7 +1404,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateLinkedAccount' + $ref: "#/components/schemas/CreateLinkedAccount" required: true responses: "204": @@ -1610,7 +1484,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" description: User details security: - bearer: [] @@ -1631,7 +1505,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Region' + $ref: "#/components/schemas/Region" type: array description: List of all shared regions security: @@ -1643,9 +1517,46 @@ paths: summary: List all shared regions tags: - regions - /sandbox: + /auth/end-session: + get: + description: "Implements OpenID Connect RP-Initiated Logout 1.0 for IdPs (e.g.\ + \ Dex) that do not natively advertise end_session_endpoint. Validates the\ + \ post-logout redirect target, then 302-redirects the browser back to the\ + \ SPA." + operationId: LogoutController_endSession + parameters: + - explode: true + in: query + name: post_logout_redirect_uri + required: false + schema: + type: string + style: form + - explode: true + in: query + name: id_token_hint + required: false + schema: + type: string + style: form + - explode: true + in: query + name: state + required: false + schema: + type: string + style: form + responses: + "302": + description: Redirect to post_logout_redirect_uri + "400": + description: post_logout_redirect_uri not allowed + summary: OIDC RP-initiated logout endpoint + tags: + - auth + /box: get: - operationId: listSandboxes + operationId: listBoxes parameters: - description: Use with JWT to specify the organization ID explode: false @@ -1672,7 +1583,7 @@ paths: example: "{\"label1\": \"value1\", \"label2\": \"value2\"}" type: string style: form - - description: Include errored and deleted sandboxes + - description: Include errored and deleted boxes explode: true in: query name: includeErroredDeleted @@ -1686,54 +1597,21 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" type: array - description: List of all sandboxes - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List all sandboxes - tags: - - sandbox - post: - operationId: createSandbox - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateSandbox' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: The sandbox has been successfully created. + description: List of all boxes security: - bearer: [] - oauth2: - openid - profile - email - summary: Create a new sandbox + summary: List all boxes tags: - - sandbox - /sandbox/paginated: + - box + /box/paginated: get: - operationId: listSandboxesPaginated + operationId: listBoxesPaginated parameters: - description: Use with JWT to specify the organization ID explode: false @@ -1764,7 +1642,7 @@ paths: minimum: 1 type: number style: form - - description: Filter by partial ID match + - description: Filter by partial Box ID or name match explode: true in: query name: id @@ -1816,27 +1694,13 @@ paths: - starting - stopping - error - - build_failed - - pending_build - - building_snapshot - unknown - - pulling_snapshot - archived - archiving - resizing type: string type: array style: form - - description: List of snapshot names to filter by - explode: true - in: query - name: snapshots - required: false - schema: - items: - type: string - type: array - style: form - description: List of regions to filter by explode: true in: query @@ -1932,7 +1796,6 @@ paths: - id - name - state - - snapshot - region - updatedAt - createdAt @@ -1955,20 +1818,20 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PaginatedSandboxes' - description: Paginated list of all sandboxes + $ref: "#/components/schemas/PaginatedBoxes" + description: Paginated list of all boxes security: - bearer: [] - oauth2: - openid - profile - email - summary: List all sandboxes paginated + summary: List all boxes paginated tags: - - sandbox - /sandbox/for-runner: + - box + /box/for-runner: get: - operationId: getSandboxesForRunner + operationId: getBoxesForRunner parameters: - description: Use with JWT to specify the organization ID explode: false @@ -1978,7 +1841,7 @@ paths: schema: type: string style: simple - - description: Comma-separated list of sandbox states to filter by + - description: Comma-separated list of box states to filter by explode: true in: query name: states @@ -1986,10 +1849,10 @@ paths: schema: type: string style: form - - description: Skip sandboxes where state differs from desired state + - description: Skip boxes where state differs from desired state explode: true in: query - name: skipReconcilingSandboxes + name: skipReconcilingBoxes required: false schema: type: boolean @@ -2000,21 +1863,21 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" type: array - description: List of sandboxes for the authenticated runner + description: List of boxes for the authenticated runner security: - bearer: [] - oauth2: - openid - profile - email - summary: Get sandboxes for the authenticated runner + summary: Get boxes for the authenticated runner tags: - - sandbox - /sandbox/{sandboxIdOrName}: - delete: - operationId: deleteSandbox + - box + /box/{boxIdOrName}: + get: + operationId: getBox parameters: - description: Use with JWT to specify the organization ID explode: false @@ -2024,32 +1887,41 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string style: simple + - description: Include verbose output + explode: true + in: query + name: verbose + required: false + schema: + type: boolean + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox has been deleted + $ref: "#/components/schemas/Box" + description: Box details security: - bearer: [] - oauth2: - openid - profile - email - summary: Delete sandbox + summary: Get box details tags: - - sandbox - get: - operationId: getSandbox + - box + /box/{boxIdOrName}/recover: + post: + operationId: recoverBox parameters: - description: Use with JWT to specify the organization ID explode: false @@ -2059,41 +1931,33 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string style: simple - - description: Include verbose output - explode: true - in: query - name: verbose - required: false - schema: - type: boolean - style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox details + $ref: "#/components/schemas/Box" + description: Recovery initiated security: - bearer: [] - oauth2: - openid - profile - email - summary: Get sandbox details + summary: Recover box from error state tags: - - sandbox - /sandbox/{sandboxIdOrName}/recover: + - box + /box/{boxIdOrName}/resize: post: - operationId: recoverSandbox + operationId: resizeBox parameters: - description: Use with JWT to specify the organization ID explode: false @@ -2103,33 +1967,39 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string style: simple + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ResizeBox" + required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - description: Recovery initiated + $ref: "#/components/schemas/Box" + description: Box has been resized security: - bearer: [] - oauth2: - openid - profile - email - summary: Recover sandbox from error state + summary: Resize box resources tags: - - sandbox - /sandbox/{sandboxIdOrName}/start: - post: - operationId: startSandbox + - box + /box/{boxIdOrName}/labels: + put: + operationId: replaceLabels parameters: - description: Use with JWT to specify the organization ID explode: false @@ -2139,34 +2009,39 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string style: simple + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/BoxLabels" + required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox has been started or is being restored from archived - state + $ref: "#/components/schemas/BoxLabels" + description: Labels have been successfully replaced security: - bearer: [] - oauth2: - openid - profile - email - summary: Start sandbox + summary: Replace box labels tags: - - sandbox - /sandbox/{sandboxIdOrName}/stop: - post: - operationId: stopSandbox + - box + /box/{boxId}/state: + put: + operationId: updateBoxState parameters: - description: Use with JWT to specify the organization ID explode: false @@ -2176,138 +2051,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID of the box explode: false in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - - description: Force stop the sandbox using SIGKILL instead of SIGTERM - explode: true - in: query - name: force - required: false - schema: - type: boolean - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox has been stopped - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Stop sandbox - tags: - - sandbox - /sandbox/{sandboxIdOrName}/resize: - post: - operationId: resizeSandbox - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ResizeSandbox' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox has been resized - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Resize sandbox resources - tags: - - sandbox - /sandbox/{sandboxIdOrName}/labels: - put: - operationId: replaceLabels - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxLabels' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxLabels' - description: Labels have been successfully replaced - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Replace sandbox labels - tags: - - sandbox - /sandbox/{sandboxId}/state: - put: - operationId: updateSandboxState - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the sandbox - explode: false - in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -2316,57 +2063,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateSandboxStateDto' - required: true - responses: - "200": - description: Sandbox state has been successfully updated - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Update sandbox state - tags: - - sandbox - /sandbox/{sandboxIdOrName}/backup: - post: - operationId: createBackup - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName + $ref: "#/components/schemas/UpdateBoxStateDto" required: true - schema: - type: string - style: simple responses: "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox backup has been initiated + description: Box state has been successfully updated security: - bearer: [] - oauth2: - openid - profile - email - summary: Create sandbox backup + summary: Update box state tags: - - sandbox - /sandbox/{sandboxIdOrName}/public/{isPublic}: + - box + /box/{boxIdOrName}/public/{isPublic}: post: operationId: updatePublicStatus parameters: @@ -2378,10 +2089,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2399,7 +2110,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" description: Public status has been successfully updated security: - bearer: [] @@ -2409,8 +2120,8 @@ paths: - email summary: Update public status tags: - - sandbox - /sandbox/{sandboxId}/last-activity: + - box + /box/{boxId}/last-activity: post: operationId: updateLastActivity parameters: @@ -2422,10 +2133,10 @@ paths: schema: type: string style: simple - - description: ID of the sandbox + - description: ID of the box explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -2439,10 +2150,10 @@ paths: - openid - profile - email - summary: Update sandbox last activity + summary: Update box last activity tags: - - sandbox - /sandbox/{sandboxIdOrName}/autostop/{interval}: + - box + /box/{boxIdOrName}/autostop/{interval}: post: operationId: setAutostopInterval parameters: @@ -2454,10 +2165,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2475,7 +2186,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" description: Auto-stop interval has been set security: - bearer: [] @@ -2483,55 +2194,10 @@ paths: - openid - profile - email - summary: Set sandbox auto-stop interval - tags: - - sandbox - /sandbox/{sandboxIdOrName}/autoarchive/{interval}: - post: - operationId: setAutoArchiveInterval - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - - description: Auto-archive interval in minutes (0 means the maximum interval - will be used) - explode: false - in: path - name: interval - required: true - schema: - type: number - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Auto-archive interval has been set - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Set sandbox auto-archive interval + summary: Set box auto-stop interval tags: - - sandbox - /sandbox/{sandboxIdOrName}/autodelete/{interval}: + - box + /box/{boxIdOrName}/autodelete/{interval}: post: operationId: setAutoDeleteInterval parameters: @@ -2543,10 +2209,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2565,7 +2231,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" description: Auto-delete interval has been set security: - bearer: [] @@ -2573,45 +2239,10 @@ paths: - openid - profile - email - summary: Set sandbox auto-delete interval - tags: - - sandbox - /sandbox/{sandboxIdOrName}/archive: - post: - operationId: archiveSandbox - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Sandbox has been archived - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Archive sandbox + summary: Set box auto-delete interval tags: - - sandbox - /sandbox/{sandboxIdOrName}/ports/{port}/preview-url: + - box + /box/{boxIdOrName}/ports/{port}/preview-url: get: operationId: getPortPreviewUrl parameters: @@ -2623,10 +2254,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2644,7 +2275,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PortPreviewUrl' + $ref: "#/components/schemas/PortPreviewUrl" description: Preview URL for the specified port security: - bearer: [] @@ -2652,10 +2283,10 @@ paths: - openid - profile - email - summary: Get preview URL for a sandbox port + summary: Get preview URL for a box port tags: - - sandbox - /sandbox/{sandboxIdOrName}/ports/{port}/signed-preview-url: + - box + /box/{boxIdOrName}/ports/{port}/signed-preview-url: get: operationId: getSignedPortPreviewUrl parameters: @@ -2667,10 +2298,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2696,7 +2327,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SignedPortPreviewUrl' + $ref: "#/components/schemas/SignedPortPreviewUrl" description: Signed preview URL for the specified port security: - bearer: [] @@ -2704,10 +2335,10 @@ paths: - openid - profile - email - summary: Get signed preview URL for a sandbox port + summary: Get signed preview URL for a box port tags: - - sandbox - /sandbox/{sandboxIdOrName}/ports/{port}/signed-preview-url/{token}/expire: + - box + /box/{boxIdOrName}/ports/{port}/signed-preview-url/{token}/expire: post: operationId: expireSignedPortPreviewUrl parameters: @@ -2719,10 +2350,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2752,88 +2383,10 @@ paths: - openid - profile - email - summary: Expire signed preview URL for a sandbox port - tags: - - sandbox - /sandbox/{sandboxIdOrName}/build-logs: - get: - deprecated: true - description: This endpoint is deprecated. Use `getBuildLogsUrl` instead. - operationId: getBuildLogs - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - - description: Whether to follow the logs stream - explode: true - in: query - name: follow - required: false - schema: - type: boolean - style: form - responses: - "200": - description: Build logs stream - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get build logs - tags: - - sandbox - /sandbox/{sandboxIdOrName}/build-logs-url: - get: - operationId: getBuildLogsUrl - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID or name of the sandbox - explode: false - in: path - name: sandboxIdOrName - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Url' - description: Build logs URL - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get build logs URL + summary: Expire signed preview URL for a box port tags: - - sandbox - /sandbox/{sandboxIdOrName}/ssh-access: + - box + /box/{boxIdOrName}/ssh-access: delete: operationId: revokeSshAccess parameters: @@ -2845,16 +2398,16 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string style: simple - description: "SSH access token to revoke. If not provided, all SSH access\ - \ for the sandbox will be revoked." + \ for the box will be revoked." explode: true in: query name: token @@ -2867,7 +2420,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Sandbox' + $ref: "#/components/schemas/Box" description: SSH access has been revoked security: - bearer: [] @@ -2875,9 +2428,9 @@ paths: - openid - profile - email - summary: Revoke SSH access for sandbox + summary: Revoke SSH access for box tags: - - sandbox + - box post: operationId: createSshAccess parameters: @@ -2889,10 +2442,10 @@ paths: schema: type: string style: simple - - description: ID or name of the sandbox + - description: ID or name of the box explode: false in: path - name: sandboxIdOrName + name: boxIdOrName required: true schema: type: string @@ -2910,7 +2463,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SshAccessDto' + $ref: "#/components/schemas/SshAccessDto" description: SSH access has been created security: - bearer: [] @@ -2918,10 +2471,10 @@ paths: - openid - profile - email - summary: Create SSH access for sandbox + summary: Create SSH access for box tags: - - sandbox - /sandbox/ssh-access/validate: + - box + /box/ssh-access/validate: get: operationId: validateSshAccess parameters: @@ -2946,7 +2499,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SshAccessValidationDto' + $ref: "#/components/schemas/SshAccessValidationDto" description: SSH access validation result security: - bearer: [] @@ -2954,10 +2507,10 @@ paths: - openid - profile - email - summary: Validate SSH access for sandbox + summary: Validate SSH access for box tags: - - sandbox - /sandbox/{sandboxId}/toolbox-proxy-url: + - box + /box/{boxId}/toolbox-proxy-url: get: operationId: getToolboxProxyUrl parameters: @@ -2969,10 +2522,10 @@ paths: schema: type: string style: simple - - description: ID of the sandbox + - description: ID of the box explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -2982,17 +2535,17 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ToolboxProxyUrl' - description: Toolbox proxy URL for the specified sandbox + $ref: "#/components/schemas/ToolboxProxyUrl" + description: Toolbox proxy URL for the specified box security: - bearer: [] - oauth2: - openid - profile - email - summary: Get toolbox proxy URL for a sandbox + summary: Get toolbox proxy URL for a box tags: - - sandbox + - box /runners: get: operationId: listRunners @@ -3011,7 +2564,7 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/Runner' + $ref: "#/components/schemas/Runner" type: array description: "" security: @@ -3038,14 +2591,14 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateRunner' + $ref: "#/components/schemas/CreateRunner" required: true responses: "201": content: application/json: schema: - $ref: '#/components/schemas/CreateRunnerResponse' + $ref: "#/components/schemas/CreateRunnerResponse" description: "" security: - bearer: [] @@ -3065,7 +2618,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RunnerFull' + $ref: "#/components/schemas/RunnerFull" description: Runner info security: - bearer: [] @@ -3076,13 +2629,13 @@ paths: summary: Get info for authenticated runner tags: - runners - /runners/by-sandbox/{sandboxId}: + /runners/by-box/{boxId}: get: - operationId: getRunnerBySandboxId + operationId: getRunnerByBoxId parameters: - explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -3092,7 +2645,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RunnerFull' + $ref: "#/components/schemas/RunnerFull" description: "" security: - bearer: [] @@ -3100,47 +2653,17 @@ paths: - openid - profile - email - summary: Get runner by sandbox ID + summary: Get runner by box ID tags: - runners - /runners/by-snapshot-ref: - get: - operationId: getRunnersBySnapshotRef + /runners/{id}: + delete: + operationId: deleteRunner parameters: - - description: Snapshot ref - explode: true - in: query - name: ref - required: true - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/RunnerSnapshotDto' - type: array - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get runners by snapshot ref - tags: - - runners - /runners/{id}: - delete: - operationId: deleteRunner - parameters: - - description: Runner ID - explode: false - in: path - name: id + - description: Runner ID + explode: false + in: path + name: id required: true schema: type: string @@ -3189,7 +2712,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Runner' + $ref: "#/components/schemas/Runner" description: "" security: - bearer: [] @@ -3217,7 +2740,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RunnerFull' + $ref: "#/components/schemas/RunnerFull" description: "" security: - bearer: [] @@ -3253,7 +2776,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Runner' + $ref: "#/components/schemas/Runner" description: "" security: - bearer: [] @@ -3289,7 +2812,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Runner' + $ref: "#/components/schemas/Runner" description: "" security: - bearer: [] @@ -3310,7 +2833,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RunnerHealthcheck' + $ref: "#/components/schemas/RunnerHealthcheck" required: true responses: "200": @@ -3324,22 +2847,14 @@ paths: summary: Runner healthcheck tags: - runners - /toolbox/{sandboxId}/toolbox/project-dir: + /preview/{boxId}/public: get: - deprecated: true - operationId: getProjectDir_deprecated + operationId: isBoxPublic parameters: - - description: Use with JWT to specify the organization ID + - description: ID of the box explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -3349,33 +2864,27 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ProjectDirResponse' - description: Project directory retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get sandbox project dir" + type: boolean + description: Public status of the box + summary: Check if box is public tags: - - toolbox - /toolbox/{sandboxId}/toolbox/user-home-dir: + - preview + /preview/{boxId}/validate/{authToken}: get: - deprecated: true - operationId: getUserHomeDir_deprecated + operationId: isValidAuthToken parameters: - - description: Use with JWT to specify the organization ID + - description: ID of the box explode: false - in: header - name: X-BoxLite-Organization-ID - required: false + in: path + name: boxId + required: true schema: type: string style: simple - - explode: false + - description: Auth token of the box + explode: false in: path - name: sandboxId + name: authToken required: true schema: type: string @@ -3385,33 +2894,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UserHomeDirResponse' - description: User home directory retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get sandbox user home dir" + type: boolean + description: Box auth token validation status + summary: Check if box auth token is valid tags: - - toolbox - /toolbox/{sandboxId}/toolbox/work-dir: + - preview + /preview/{boxId}/access: get: - deprecated: true - operationId: getWorkDir_deprecated + operationId: hasBoxAccess parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string @@ -3421,113 +2915,50 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/WorkDirResponse' - description: Work-dir retrieved successfully + type: boolean + description: User access status to the box security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get sandbox work-dir" + summary: Check if user has access to the box tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files: - delete: - deprecated: true - description: Delete file inside sandbox - operationId: deleteFile_deprecated + - preview + /preview/{signedPreviewToken}/{port}/box-id: + get: + operationId: getBoxIdFromSignedPreviewUrlToken parameters: - - description: Use with JWT to specify the organization ID + - description: Signed preview URL token explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: signedPreviewToken required: true schema: type: string style: simple - - explode: true - in: query - name: recursive - required: false - schema: - type: boolean - style: form - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form - responses: - "200": - description: File deleted successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Delete file" - tags: - - toolbox - get: - deprecated: true - operationId: listFiles_deprecated - parameters: - - description: Use with JWT to specify the organization ID + - description: Port number to get box ID from signed preview URL token explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: port required: true schema: - type: string + type: number style: simple - - explode: true - in: query - name: path - required: false - schema: - type: string - style: form responses: "200": content: application/json: schema: - items: - $ref: '#/components/schemas/FileInfo' - type: array - description: Files listed successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] List files" + type: string + description: Box ID from signed preview URL token + summary: Get box ID from signed preview URL token tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/download: + - preview + /volumes: get: - deprecated: true - description: Download file from sandbox - operationId: downloadFile_deprecated + operationId: listVolumes parameters: - description: Use with JWT to specify the organization ID explode: false @@ -3537,42 +2968,34 @@ paths: schema: type: string style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true + - description: Include deleted volumes in the response + explode: true in: query - name: path - required: true + name: includeDeleted + required: false schema: - type: string + type: boolean style: form responses: "200": content: application/json: schema: - format: binary - type: string - description: File downloaded successfully + items: + $ref: "#/components/schemas/VolumeDto" + type: array + description: List of all volumes security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Download file" + summary: List all volumes tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/bulk-download: + - volumes post: - deprecated: true - description: Streams back a multipart/form-data bundle of the requested paths - operationId: downloadFiles_deprecated + operationId: createVolume parameters: - description: Use with JWT to specify the organization ID explode: false @@ -3582,41 +3005,31 @@ paths: schema: type: string style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple requestBody: content: application/json: schema: - $ref: '#/components/schemas/DownloadFiles' + $ref: "#/components/schemas/CreateVolume" required: true responses: "200": content: application/json: schema: - format: binary - type: string - description: A multipart/form-data response with each file as a part + $ref: "#/components/schemas/VolumeDto" + description: The volume has been successfully created. security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Download multiple files" + summary: Create a new volume tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/find: - get: - deprecated: true - description: Search for text/pattern inside sandbox files - operationId: findInFiles_deprecated + - volumes + /volumes/{volumeId}: + delete: + operationId: deleteVolume parameters: - description: Use with JWT to specify the organization ID explode: false @@ -3626,50 +3039,30 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the volume + explode: false in: path - name: sandboxId + name: volumeId required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form - - explode: true - in: query - name: pattern - required: true - schema: - type: string - style: form responses: "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/Match' - type: array - description: Search completed successfully + description: Volume has been marked for deletion + "409": + description: Volume is in use by one or more boxes security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Search for text/pattern in files" + summary: Delete volume tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/folder: - post: - deprecated: true - description: Create folder inside sandbox - operationId: createFolder_deprecated + - volumes + get: + operationId: getVolume parameters: - description: Use with JWT to specify the organization ID explode: false @@ -3679,44 +3072,33 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the volume + explode: false in: path - name: sandboxId + name: volumeId required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form - - explode: true - in: query - name: mode - required: true - schema: - type: string - style: form responses: "200": - description: Folder created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/VolumeDto" + description: Volume details security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Create folder" + summary: Get volume details tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/info: + - volumes + /volumes/by-name/{name}: get: - deprecated: true - description: Get file info inside sandbox - operationId: getFileInfo_deprecated + operationId: getVolumeByName parameters: - description: Use with JWT to specify the organization ID explode: false @@ -3726,1658 +3108,943 @@ paths: schema: type: string style: simple - - explode: false + - description: Name of the volume + explode: false in: path - name: sandboxId + name: name required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/FileInfo' - description: File info retrieved successfully + $ref: "#/components/schemas/VolumeDto" + description: Volume details security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get file info" + summary: Get volume details by name tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/move: - post: - deprecated: true - description: Move file inside sandbox - operationId: moveFile_deprecated + - volumes + /jobs: + get: + description: "Returns a paginated list of jobs for the runner, optionally filtered\ + \ by status." + operationId: listJobs parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Page number of the results + explode: true + in: query + name: page required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true - in: query - name: source - required: true - schema: - type: string - style: form - - explode: true - in: query - name: destination - required: true - schema: - type: string - style: form - responses: - "200": - description: File moved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Move file" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/permissions: - post: - deprecated: true - description: Set file owner/group/permissions inside sandbox - operationId: setFilePermissions_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string + default: 1 + minimum: 1 + type: number style: form - - explode: true + - description: "Maximum number of jobs to return (default: 100, max: 500)" + explode: true in: query - name: owner + name: limit required: false schema: - type: string + default: 100 + maximum: 200 + minimum: 1 + type: number style: form - - explode: true + - description: Filter jobs by status + explode: true in: query - name: group + name: status required: false schema: - type: string + $ref: "#/components/schemas/JobStatus" style: form - - explode: true + - description: "Number of jobs to skip for pagination (default: 0)" + explode: true in: query - name: mode + name: offset required: false schema: - type: string + type: number style: form responses: "200": - description: File permissions updated successfully + content: + application/json: + schema: + $ref: "#/components/schemas/PaginatedJobs" + description: List of jobs for the runner security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Set file permissions" + summary: List jobs for the runner tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/replace: - post: - deprecated: true - description: Replace text/pattern in multiple files inside sandbox - operationId: replaceInFiles_deprecated + - jobs + /jobs/poll: + get: + description: "Long poll endpoint for runners to fetch pending jobs. Returns\ + \ immediately if jobs are available, otherwise waits up to timeout seconds." + operationId: pollJobs parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: "Timeout in seconds for long polling (default: 30, max: 60)" + explode: true + in: query + name: timeout required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + type: number + style: form + - description: "Maximum number of jobs to return (default: 10, max: 100)" + explode: true + in: query + name: limit + required: false schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ReplaceRequest' - required: true + type: number + style: form responses: "200": content: application/json: schema: - items: - $ref: '#/components/schemas/ReplaceResult' - type: array - description: Text replaced successfully + $ref: "#/components/schemas/PollJobsResponse" + description: List of jobs for the runner security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Replace in files" + summary: Long poll for jobs tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/search: + - jobs + /jobs/{jobId}: get: - deprecated: true - description: Search for files inside sandbox - operationId: searchFiles_deprecated + operationId: getJob parameters: - - description: Use with JWT to specify the organization ID + - description: ID of the job explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: jobId required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form - - explode: true - in: query - name: pattern - required: true - schema: - type: string - style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/SearchFilesResponse' - description: Search completed successfully + $ref: "#/components/schemas/Job" + description: Job details security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Search files" + summary: Get job details tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/upload: + - jobs + /jobs/{jobId}/status: post: - deprecated: true - description: Upload file inside sandbox - operationId: uploadFile_deprecated + operationId: updateJobStatus parameters: - - description: Use with JWT to specify the organization ID + - description: ID of the job explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: jobId required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form requestBody: content: - multipart/form-data: + application/json: schema: - $ref: '#/components/schemas/uploadFile_deprecated_request' + $ref: "#/components/schemas/UpdateJobStatus" required: true responses: "200": - description: File uploaded successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Job" + description: Job status updated successfully security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Upload file" + summary: Update job status tags: - - toolbox - /toolbox/{sandboxId}/toolbox/files/bulk-upload: - post: - deprecated: true - description: Upload multiple files inside sandbox - operationId: uploadFiles_deprecated + - jobs + /admin/runners: + get: + operationId: adminListRunners parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Filter runners by region ID + explode: true + in: query + name: regionId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - multipart/form-data: - schema: - items: - $ref: '#/components/schemas/UploadFile' - type: array - required: true + style: form responses: "200": - description: Files uploaded successfully + content: + application/json: + schema: + items: + $ref: "#/components/schemas/AdminRunner" + type: array + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Upload multiple files" + summary: List all runners tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/add: + - admin post: - deprecated: true - description: Add files to git commit - operationId: gitAddFiles_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple + operationId: adminCreateRunner + parameters: [] requestBody: content: application/json: schema: - $ref: '#/components/schemas/GitAddRequest' + $ref: "#/components/schemas/AdminCreateRunner" required: true responses: - "200": - description: Files added to git successfully + "201": + content: + application/json: + schema: + $ref: "#/components/schemas/CreateRunnerResponse" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Add files" + summary: Create runner tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/branches: + - admin + /admin/runners/{id}: delete: - deprecated: true - description: Delete branch on git repository - operationId: gitDeleteBranch_deprecated + operationId: adminDeleteRunner parameters: - - description: Use with JWT to specify the organization ID + - description: Runner ID explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: id required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitDeleteBranchRequest' - required: true responses: - "200": - description: Branch deleted successfully + "204": + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Delete branch" + summary: Delete runner tags: - - toolbox + - admin get: - deprecated: true - description: Get branch list from git repository - operationId: gitListBranches_deprecated + operationId: adminGetRunnerById parameters: - - description: Use with JWT to specify the organization ID + - description: Runner ID explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: id required: true schema: type: string style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/ListBranchResponse' - description: Branch list retrieved successfully + $ref: "#/components/schemas/AdminRunner" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get branch list" + summary: Get runner by ID tags: - - toolbox - post: - deprecated: true - description: Create branch on git repository - operationId: gitCreateBranch_deprecated + - admin + /admin/runners/{id}/scheduling: + patch: + operationId: adminUpdateRunnerScheduling parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - explode: false in: path - name: sandboxId + name: id required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitBranchRequest' - required: true responses: - "200": - description: Branch created successfully + "204": + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Create branch" + summary: Update runner scheduling status tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/clone: + - admin + /admin/box/{boxId}/recover: post: - deprecated: true - description: Clone git repository - operationId: gitCloneRepository_deprecated + operationId: adminRecoverBox parameters: - - description: Use with JWT to specify the organization ID + - description: ID of the box explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitCloneRequest' - required: true responses: "200": - description: Repository cloned successfully + content: + application/json: + schema: + $ref: "#/components/schemas/Box" + description: Recovery initiated security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Clone repository" + summary: Recover box from error state as an admin tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/commit: - post: - deprecated: true - description: Commit changes to git repository - operationId: gitCommitChanges_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitCommitRequest' - required: true + - admin + /admin/overview: + get: + operationId: adminGetOverview + parameters: [] responses: "200": content: application/json: schema: - $ref: '#/components/schemas/GitCommitResponse' - description: Changes committed successfully + $ref: "#/components/schemas/AdminOverview" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Commit changes" + summary: Admin KPI summary tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/history: + - admin + /admin/overview/users: get: - deprecated: true - description: Get commit history from git repository - operationId: gitGetHistory_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form + operationId: adminListUsers + parameters: [] responses: "200": content: application/json: schema: items: - $ref: '#/components/schemas/GitCommitInfo' + $ref: "#/components/schemas/AdminUserItem" type: array - description: Commit history retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get commit history" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/pull: - post: - deprecated: true - description: Pull changes from remote - operationId: gitPullChanges_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitRepoRequest' - required: true - responses: - "200": - description: Changes pulled successfully + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Pull changes" + summary: List all users (cross-org) tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/push: - post: - deprecated: true - description: Push changes to remote - operationId: gitPushChanges_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitRepoRequest' - required: true + - admin + /admin/overview/boxes: + get: + operationId: adminListBoxes + parameters: [] responses: "200": - description: Changes pushed successfully + content: + application/json: + schema: + items: + $ref: "#/components/schemas/AdminBoxItem" + type: array + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Push changes" + summary: List all boxes (cross-org) tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/checkout: - post: - deprecated: true - description: Checkout branch or commit in git repository - operationId: gitCheckoutBranch_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GitCheckoutRequest' - required: true + - admin + /admin/overview/runners: + get: + operationId: adminListRunnersOverview + parameters: [] responses: "200": - description: Branch checked out successfully + content: + application/json: + schema: + items: + $ref: "#/components/schemas/AdminRunnerItem" + type: array + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Checkout branch" + summary: List all runners with full details tags: - - toolbox - /toolbox/{sandboxId}/toolbox/git/status: + - admin + /admin/overview/machines: get: - deprecated: true - description: Get status from git repository - operationId: gitGetStatus_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true - in: query - name: path - required: true - schema: - type: string - style: form + operationId: adminListMachines + parameters: [] responses: "200": content: application/json: schema: - $ref: '#/components/schemas/GitStatus' - description: Git status retrieved successfully + items: + $ref: "#/components/schemas/AdminMachineItem" + type: array + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get git status" + summary: Runner-as-machine resource view tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/execute: - post: - deprecated: true - description: Execute command synchronously inside sandbox - operationId: executeCommand_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ExecuteRequest' - required: true + - admin + /admin/observability/status: + get: + operationId: adminGetObservabilityStatus + parameters: [] responses: "200": content: application/json: schema: - $ref: '#/components/schemas/ExecuteResponse' - description: Command executed successfully + $ref: "#/components/schemas/AdminObservabilityStatusDto" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Execute command" + summary: Get admin observability backend and layer status tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/session: + - admin + /admin/observability/logs: get: - deprecated: true - description: List all active sessions in the sandbox - operationId: listSessions_deprecated + operationId: adminGetObservabilityLogs parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/Session' - type: array - description: Sessions retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] List sessions" - tags: - - toolbox - post: - deprecated: true - description: Create a new session in the sandbox - operationId: createSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Page number (1-indexed) + explode: true + in: query + name: page required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + default: 1 + type: number + style: form + - description: Number of items per page + explode: true + in: query + name: limit + required: false schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateSessionRequest' - required: true - responses: - "200": - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Create session" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/session/{sessionId}: - delete: - deprecated: true - description: Delete a specific session - operationId: deleteSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + default: 100 + type: number + style: form + - description: Telemetry producer layer + explode: true + in: query + name: layer required: false schema: + enum: + - api + - runner + - ec2_host + - box type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: OpenTelemetry service.name filter + explode: true + in: query + name: serviceName + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Organization ID filter + explode: true + in: query + name: orgId + required: false schema: type: string - style: simple - responses: - "200": - description: Session deleted successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Delete session" - tags: - - toolbox - get: - deprecated: true - description: Get session by ID - operationId: getSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: User ID filter + explode: true + in: query + name: userId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Box ID filter + explode: true + in: query + name: boxId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Runner ID filter + explode: true + in: query + name: runnerId + required: false schema: type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Session' - description: Session retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get session" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/session/{sessionId}/exec: - post: - deprecated: true - description: Execute a command in a specific session - operationId: executeSessionCommand_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Machine or host ID filter + explode: true + in: query + name: machineId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Trace ID filter + explode: true + in: query + name: traceId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Request ID filter + explode: true + in: query + name: requestId + required: false schema: type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SessionExecuteRequest' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SessionExecuteResponse' - description: Command executed successfully - "202": - content: - application/json: - schema: - $ref: '#/components/schemas/SessionExecuteResponse' - description: Command accepted and is being processed - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Execute command in session" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/session/{sessionId}/command/{commandId}: - get: - deprecated: true - description: Get session command by ID - operationId: getSessionCommand_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Operation ID filter + explode: true + in: query + name: operationId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Execution ID filter + explode: true + in: query + name: executionId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Job ID filter + explode: true + in: query + name: jobId + required: false schema: type: string - style: simple - - explode: false - in: path - name: commandId - required: true + style: form + - description: "Filter by severity levels (DEBUG, INFO, WARN, ERROR)" + explode: true + in: query + name: severities + required: false + schema: + items: + type: string + type: array + style: form + - description: Search in log body + explode: true + in: query + name: search + required: false schema: type: string - style: simple + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/Command' - description: Session command retrieved successfully + $ref: "#/components/schemas/PaginatedLogs" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get session command" + summary: Get admin-scoped logs tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/session/{sessionId}/command/{commandId}/logs: + - admin + /admin/observability/traces: get: - deprecated: true - description: Get logs for a specific command in a session - operationId: getSessionCommandLogs_deprecated + operationId: adminGetObservabilityTraces parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: commandId - required: true - schema: - type: string - style: simple - - description: Whether to stream the logs + style: form + - description: Page number (1-indexed) explode: true in: query - name: follow + name: page required: false schema: - type: boolean + default: 1 + type: number style: form - responses: - "200": - content: - text/plain: - schema: - type: string - description: Command log stream marked with stdout and stderr prefixes - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get command logs" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/pty: - get: - deprecated: true - description: List all active PTY sessions in the sandbox - operationId: listPTYSessions_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Number of items per page + explode: true + in: query + name: limit required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + default: 100 + type: number + style: form + - description: Telemetry producer layer + explode: true + in: query + name: layer + required: false schema: + enum: + - api + - runner + - ec2_host + - box type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PtyListResponse' - description: PTY sessions retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] List PTY sessions" - tags: - - toolbox - post: - deprecated: true - description: Create a new PTY session in the sandbox - operationId: createPTYSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: OpenTelemetry service.name filter + explode: true + in: query + name: serviceName required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Organization ID filter + explode: true + in: query + name: orgId + required: false schema: type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PtyCreateRequest' - required: true - responses: - "201": - content: - application/json: - schema: - $ref: '#/components/schemas/PtyCreateResponse' - description: PTY session created successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Create PTY session" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/pty/{sessionId}: - delete: - deprecated: true - description: Delete a PTY session and terminate the associated process - operationId: deletePTYSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: User ID filter + explode: true + in: query + name: userId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Box ID filter + explode: true + in: query + name: boxId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Runner ID filter + explode: true + in: query + name: runnerId + required: false schema: type: string - style: simple - responses: - "200": - description: PTY session deleted successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Delete PTY session" - tags: - - toolbox - get: - deprecated: true - description: Get PTY session information by ID - operationId: getPTYSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Machine or host ID filter + explode: true + in: query + name: machineId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Trace ID filter + explode: true + in: query + name: traceId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Request ID filter + explode: true + in: query + name: requestId + required: false schema: type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PtySessionInfo' - description: PTY session retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get PTY session" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/process/pty/{sessionId}/resize: - post: - deprecated: true - description: Resize a PTY session - operationId: resizePTYSession_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Operation ID filter + explode: true + in: query + name: operationId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Execution ID filter + explode: true + in: query + name: executionId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sessionId - required: true + style: form + - description: Job ID filter + explode: true + in: query + name: jobId + required: false schema: type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PtyResizeRequest' - required: true + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/PtySessionInfo' - description: PTY session resized successfully + $ref: "#/components/schemas/PaginatedTraces" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Resize PTY session" + summary: Get admin-scoped traces tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/completions: - post: - deprecated: true - description: The Completion request is sent from the client to the server to - compute completion items at a given cursor position. - operationId: LspCompletions_deprecated + - admin + /admin/observability/traces/{traceId}: + get: + operationId: adminGetObservabilityTraceSpans parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - explode: false in: path - name: sandboxId + name: traceId required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LspCompletionParams' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/CompletionList' - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get Lsp Completions" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/did-close: - post: - deprecated: true - description: The document close notification is sent from the client to the - server when the document got closed in the client. - operationId: LspDidClose_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LspDocumentRequest' - required: true - responses: - "200": - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Call Lsp DidClose" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/did-open: - post: - deprecated: true - description: The document open notification is sent from the client to the server - to signal newly opened text documents. - operationId: LspDidOpen_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Page number (1-indexed) + explode: true + in: query + name: page required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LspDocumentRequest' - required: true - responses: - "200": - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Call Lsp DidOpen" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/document-symbols: - get: - deprecated: true - description: The document symbol request is sent from the client to the server. - operationId: LspDocumentSymbols_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + default: 1 + type: number + style: form + - description: Number of items per page + explode: true + in: query + name: limit required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - explode: true + default: 100 + type: number + style: form + - description: Telemetry producer layer + explode: true in: query - name: languageId - required: true + name: layer + required: false schema: + enum: + - api + - runner + - ec2_host + - box type: string style: form - - explode: true + - description: OpenTelemetry service.name filter + explode: true in: query - name: pathToProject - required: true + name: serviceName + required: false schema: type: string style: form - - explode: true + - description: Organization ID filter + explode: true in: query - name: uri - required: true + name: orgId + required: false schema: type: string style: form - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/LspSymbol' - type: array - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Call Lsp DocumentSymbols" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/start: - post: - deprecated: true - description: Start Lsp server process inside sandbox project - operationId: LspStart_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: User ID filter + explode: true + in: query + name: userId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LspServerRequest' - required: true - responses: - "200": - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Start Lsp server" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/stop: - post: - deprecated: true - description: Stop Lsp server process inside sandbox project - operationId: LspStop_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Box ID filter + explode: true + in: query + name: boxId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Runner ID filter + explode: true + in: query + name: runnerId + required: false schema: type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LspServerRequest' - required: true - responses: - "200": - description: OK - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Stop Lsp server" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/lsp/workspace-symbols: - get: - deprecated: true - description: The workspace symbol request is sent from the client to the server - to list project-wide symbols matching the query string. - operationId: LspWorkspaceSymbols_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Machine or host ID filter + explode: true + in: query + name: machineId required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Request ID filter + explode: true + in: query + name: requestId + required: false schema: type: string - style: simple - - explode: true + style: form + - description: Operation ID filter + explode: true in: query - name: languageId - required: true + name: operationId + required: false schema: type: string style: form - - explode: true + - description: Execution ID filter + explode: true in: query - name: pathToProject - required: true + name: executionId + required: false schema: type: string style: form - - explode: true + - description: Job ID filter + explode: true in: query - name: query - required: true + name: jobId + required: false schema: type: string style: form @@ -5387,476 +4054,490 @@ paths: application/json: schema: items: - $ref: '#/components/schemas/LspSymbol' + $ref: "#/components/schemas/TraceSpan" type: array - description: OK + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Call Lsp WorkspaceSymbols" + summary: Get admin-scoped trace spans tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/start: - post: - deprecated: true - description: "Start all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)" - operationId: startComputerUse_deprecated + - admin + /admin/observability/metrics: + get: + operationId: adminGetObservabilityMetrics parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComputerUseStartResponse' - description: Computer use processes started successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Start computer use processes" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/stop: - post: - deprecated: true - description: "Stop all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)" - operationId: stopComputerUse_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Page number (1-indexed) + explode: true + in: query + name: page required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + default: 1 + type: number + style: form + - description: Number of items per page + explode: true + in: query + name: limit + required: false schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComputerUseStopResponse' - description: Computer use processes stopped successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Stop computer use processes" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/status: - get: - deprecated: true - description: Get status of all VNC desktop processes - operationId: getComputerUseStatus_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + default: 100 + type: number + style: form + - description: Telemetry producer layer + explode: true + in: query + name: layer required: false schema: + enum: + - api + - runner + - ec2_host + - box type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ComputerUseStatusResponse' - description: Computer use status retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get computer use status" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/status: - get: - deprecated: true - description: Get status of a specific VNC process - operationId: getProcessStatus_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: OpenTelemetry service.name filter + explode: true + in: query + name: serviceName required: false schema: type: string - style: simple - - explode: false - in: path - name: processName - required: true + style: form + - description: Organization ID filter + explode: true + in: query + name: orgId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: User ID filter + explode: true + in: query + name: userId + required: false schema: type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ProcessStatusResponse' - description: Process status retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get process status" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/restart: - post: - deprecated: true - description: Restart a specific VNC process - operationId: restartProcess_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Box ID filter + explode: true + in: query + name: boxId required: false schema: type: string - style: simple - - explode: false - in: path - name: processName - required: true + style: form + - description: Runner ID filter + explode: true + in: query + name: runnerId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Machine or host ID filter + explode: true + in: query + name: machineId + required: false schema: type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ProcessRestartResponse' - description: Process restarted successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Restart process" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/logs: - get: - deprecated: true - description: Get logs for a specific VNC process - operationId: getProcessLogs_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Trace ID filter + explode: true + in: query + name: traceId required: false schema: type: string - style: simple - - explode: false - in: path - name: processName - required: true + style: form + - description: Request ID filter + explode: true + in: query + name: requestId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Operation ID filter + explode: true + in: query + name: operationId + required: false schema: type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/ProcessLogsResponse' - description: Process logs retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get process logs" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/errors: - get: - deprecated: true - description: Get error logs for a specific VNC process - operationId: getProcessErrors_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Execution ID filter + explode: true + in: query + name: executionId required: false schema: type: string - style: simple - - explode: false - in: path - name: processName - required: true + style: form + - description: Job ID filter + explode: true + in: query + name: jobId + required: false schema: type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: Filter by metric names + explode: true + in: query + name: metricNames + required: false schema: - type: string - style: simple + items: + type: string + type: array + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/ProcessErrorsResponse' - description: Process errors retrieved successfully + $ref: "#/components/schemas/MetricsResponse" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get process errors" + summary: Get admin-scoped metrics tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/mouse/position: + - admin + /admin/observability/investigate: get: - deprecated: true - description: Get current mouse cursor position - operationId: getMousePosition_deprecated + operationId: adminInvestigateObservability parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/MousePosition' - description: Mouse position retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get mouse position" - tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/mouse/move: - post: - deprecated: true - description: Move mouse cursor to specified coordinates - operationId: moveMouse_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + style: form + - description: Page number (1-indexed) + explode: true + in: query + name: page required: false schema: - type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + default: 1 + type: number + style: form + - description: Number of items per page + explode: true + in: query + name: limit + required: false schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MouseMoveRequest' - required: true + default: 100 + type: number + style: form + - description: Telemetry producer layer + explode: true + in: query + name: layer + required: false + schema: + enum: + - api + - runner + - ec2_host + - box + type: string + style: form + - description: OpenTelemetry service.name filter + explode: true + in: query + name: serviceName + required: false + schema: + type: string + style: form + - description: Organization ID filter + explode: true + in: query + name: orgId + required: false + schema: + type: string + style: form + - description: User ID filter + explode: true + in: query + name: userId + required: false + schema: + type: string + style: form + - description: Box ID filter + explode: true + in: query + name: boxId + required: false + schema: + type: string + style: form + - description: Runner ID filter + explode: true + in: query + name: runnerId + required: false + schema: + type: string + style: form + - description: Machine or host ID filter + explode: true + in: query + name: machineId + required: false + schema: + type: string + style: form + - description: Trace ID filter + explode: true + in: query + name: traceId + required: false + schema: + type: string + style: form + - description: Request ID filter + explode: true + in: query + name: requestId + required: false + schema: + type: string + style: form + - description: Operation ID filter + explode: true + in: query + name: operationId + required: false + schema: + type: string + style: form + - description: Execution ID filter + explode: true + in: query + name: executionId + required: false + schema: + type: string + style: form + - description: Job ID filter + explode: true + in: query + name: jobId + required: false + schema: + type: string + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/MouseMoveResponse' - description: Mouse moved successfully + $ref: "#/components/schemas/AdminObservabilityInvestigateResponse" + description: "" security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Move mouse" + summary: Investigate related observability and platform state from trace or + resource identifiers tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/mouse/click: - post: - deprecated: true - description: Click mouse at specified coordinates - operationId: clickMouse_deprecated + - admin + /audit: + get: + operationId: getAllAuditLogs parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID + - description: Page number of the results + explode: true + in: query + name: page + required: false + schema: + default: 1 + minimum: 1 + type: number + style: form + - description: Number of results per page + explode: true + in: query + name: limit + required: false + schema: + default: 100 + maximum: 200 + minimum: 1 + type: number + style: form + - description: From date (ISO 8601 format) + explode: true + in: query + name: from required: false schema: + format: date-time type: string - style: simple - - explode: false - in: path - name: sandboxId - required: true + style: form + - description: To date (ISO 8601 format) + explode: true + in: query + name: to + required: false schema: + format: date-time type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MouseClickRequest' - required: true + style: form + - description: "Token for cursor-based pagination. When provided, takes precedence\ + \ over page parameter." + explode: true + in: query + name: nextToken + required: false + schema: + type: string + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/MouseClickResponse' - description: Mouse clicked successfully + $ref: "#/components/schemas/PaginatedAuditLogs" + description: Paginated list of all audit logs security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Click mouse" + summary: Get all audit logs tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/mouse/drag: - post: - deprecated: true - description: Drag mouse from start to end coordinates - operationId: dragMouse_deprecated + - audit + /audit/organizations/{organizationId}: + get: + operationId: getOrganizationAuditLogs parameters: - - description: Use with JWT to specify the organization ID + - description: Organization ID explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MouseDragRequest' - required: true + - description: Page number of the results + explode: true + in: query + name: page + required: false + schema: + default: 1 + minimum: 1 + type: number + style: form + - description: Number of results per page + explode: true + in: query + name: limit + required: false + schema: + default: 100 + maximum: 200 + minimum: 1 + type: number + style: form + - description: From date (ISO 8601 format) + explode: true + in: query + name: from + required: false + schema: + format: date-time + type: string + style: form + - description: To date (ISO 8601 format) + explode: true + in: query + name: to + required: false + schema: + format: date-time + type: string + style: form + - description: "Token for cursor-based pagination. When provided, takes precedence\ + \ over page parameter." + explode: true + in: query + name: nextToken + required: false + schema: + type: string + style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/MouseDragResponse' - description: Mouse dragged successfully + $ref: "#/components/schemas/PaginatedAuditLogs" + description: Paginated list of organization audit logs security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Drag mouse" + summary: Get audit logs for organization tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/mouse/scroll: + - audit + /webhooks/organizations/{organizationId}/app-portal-access: post: - deprecated: true - description: Scroll mouse at specified coordinates - operationId: scrollMouse_deprecated + operationId: WebhookController_getAppPortalAccess parameters: - description: Use with JWT to specify the organization ID explode: false @@ -5868,38 +4549,26 @@ paths: style: simple - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MouseScrollRequest' - required: true responses: "200": content: application/json: schema: - $ref: '#/components/schemas/MouseScrollResponse' - description: Mouse scrolled successfully + $ref: "#/components/schemas/WebhookAppPortalAccess" + description: App Portal access generated successfully security: - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Scroll mouse" + summary: Get Svix Consumer App Portal access for an organization tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/keyboard/type: + - webhooks + /webhooks/organizations/{organizationId}/send: post: - deprecated: true - description: Type text using keyboard - operationId: typeText_deprecated + operationId: WebhookController_sendWebhook parameters: - description: Use with JWT to specify the organization ID explode: false @@ -5911,7 +4580,7 @@ paths: style: simple - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string @@ -5920,25 +4589,19 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/KeyboardTypeRequest' + $ref: "#/components/schemas/SendWebhookDto" required: true responses: "200": - description: Text typed successfully + description: Webhook message sent successfully security: - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Type text" + summary: Send a webhook message to an organization tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/keyboard/key: - post: - deprecated: true - description: Press a key with optional modifiers - operationId: pressKey_deprecated + - webhooks + /webhooks/organizations/{organizationId}/messages/{messageId}/attempts: + get: + operationId: WebhookController_getMessageAttempts parameters: - description: Use with JWT to specify the organization ID explode: false @@ -5950,34 +4613,35 @@ paths: style: simple - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/KeyboardPressRequest' + - explode: false + in: path + name: messageId required: true + schema: + type: string + style: simple responses: "200": - description: Key pressed successfully + content: + application/json: + schema: + items: + type: object + type: array + description: List of delivery attempts security: - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Press key" + summary: Get delivery attempts for a webhook message tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/keyboard/hotkey: - post: - deprecated: true - description: Press a hotkey combination - operationId: pressHotkey_deprecated + - webhooks + /webhooks/status: + get: + operationId: WebhookController_getStatus parameters: - description: Use with JWT to specify the organization ID explode: false @@ -5987,36 +4651,21 @@ paths: schema: type: string style: simple - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/KeyboardHotkeyRequest' - required: true responses: "200": - description: Hotkey pressed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/WebhookController_getStatus_200_response" + description: Webhook service status security: - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Press hotkey" + summary: Get webhook service status tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/screenshot: + - webhooks + /webhooks/organizations/{organizationId}/initialization-status: get: - deprecated: true - description: Take a screenshot of the entire screen - operationId: takeScreenshot_deprecated + operationId: WebhookController_getInitializationStatus parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6028,39 +4677,28 @@ paths: style: simple - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string style: simple - - explode: true - in: query - name: show_cursor - required: false - schema: - type: boolean - style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/ScreenshotResponse' - description: Screenshot taken successfully + $ref: "#/components/schemas/WebhookInitializationStatus" + description: Webhook initialization status + "404": + description: Webhook initialization status not found security: - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Take screenshot" + summary: Get webhook initialization status for an organization tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/screenshot/region: - get: - deprecated: true - description: Take a screenshot of a specific region - operationId: takeRegionScreenshot_deprecated + - webhooks + /webhooks/organizations/{organizationId}/initialize: + post: + operationId: WebhookController_initializeWebhooks parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6072,67 +4710,83 @@ paths: style: simple - explode: false in: path - name: sandboxId + name: organizationId required: true schema: type: string style: simple - - explode: true - in: query - name: show_cursor + responses: + "201": + description: Webhooks initialized successfully + "403": + description: User does not have access to this organization + "404": + description: Organization not found + security: + - bearer: [] + summary: Initialize webhooks for an organization + tags: + - webhooks + /object-storage/push-access: + get: + operationId: getPushAccess + parameters: + - description: Use with JWT to specify the organization ID + explode: false + in: header + name: X-BoxLite-Organization-ID required: false schema: - type: boolean - style: form - - explode: true - in: query - name: height - required: true - schema: - type: number - style: form - - explode: true - in: query - name: width - required: true - schema: - type: number - style: form - - explode: true - in: query - name: "y" - required: true - schema: - type: number - style: form - - explode: true - in: query - name: x - required: true - schema: - type: number - style: form + type: string + style: simple responses: "200": content: application/json: schema: - $ref: '#/components/schemas/RegionScreenshotResponse' - description: Region screenshot taken successfully + $ref: "#/components/schemas/StorageAccessDto" + description: Temporary storage access has been generated security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Take region screenshot" + summary: Get temporary storage access for pushing objects + tags: + - object-storage + /health: + get: + operationId: HealthController_live + parameters: [] + responses: + "200": + description: "" + tags: + - Health + /health/ready: + get: + operationId: HealthController_check + parameters: [] + responses: + "200": + content: + application/json: + schema: + $ref: "#/components/schemas/HealthController_check_200_response" + description: The Health Check is successful + "503": + content: + application/json: + schema: + $ref: "#/components/schemas/HealthController_check_200_response" + description: The Health Check is not successful tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/screenshot/compressed: + - Health + /box/{boxId}/telemetry/logs: get: - deprecated: true - description: "Take a compressed screenshot with format, quality, and scale options" - operationId: takeCompressedScreenshot_deprecated + description: Retrieve OTEL logs for a box within a time range + operationId: getBoxLogs parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6142,62 +4796,88 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the box + explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string style: simple - - explode: true + - description: Start of time range (ISO 8601) + explode: true + in: query + name: from + required: true + schema: + format: date-time + type: string + style: form + - description: End of time range (ISO 8601) + explode: true + in: query + name: to + required: true + schema: + format: date-time + type: string + style: form + - description: Page number (1-indexed) + explode: true in: query - name: scale + name: page required: false schema: + default: 1 type: number style: form - - explode: true + - description: Number of items per page + explode: true in: query - name: quality + name: limit required: false schema: + default: 100 type: number style: form - - explode: true + - description: "Filter by severity levels (DEBUG, INFO, WARN, ERROR)" + explode: true in: query - name: format + name: severities required: false schema: - type: string + items: + type: string + type: array style: form - - explode: true + - description: Search in log body + explode: true in: query - name: show_cursor + name: search required: false schema: - type: boolean + type: string style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/CompressedScreenshotResponse' - description: Compressed screenshot taken successfully + $ref: "#/components/schemas/PaginatedLogs" + description: Paginated list of log entries security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Take compressed screenshot" + summary: Get box logs tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/screenshot/region/compressed: + - box + /box/{boxId}/telemetry/traces: get: - deprecated: true - description: Take a compressed screenshot of a specific region - operationId: takeCompressedRegionScreenshot_deprecated + description: Retrieve OTEL traces for a box within a time range + operationId: getBoxTraces parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6207,67 +4887,48 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the box + explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string style: simple - - explode: true - in: query - name: scale - required: false - schema: - type: number - style: form - - explode: true - in: query - name: quality - required: false - schema: - type: number - style: form - - explode: true - in: query - name: format - required: false - schema: - type: string - style: form - - explode: true - in: query - name: show_cursor - required: false - schema: - type: boolean - style: form - - explode: true + - description: Start of time range (ISO 8601) + explode: true in: query - name: height + name: from required: true schema: - type: number + format: date-time + type: string style: form - - explode: true + - description: End of time range (ISO 8601) + explode: true in: query - name: width + name: to required: true schema: - type: number + format: date-time + type: string style: form - - explode: true + - description: Page number (1-indexed) + explode: true in: query - name: "y" - required: true + name: page + required: false schema: + default: 1 type: number style: form - - explode: true + - description: Number of items per page + explode: true in: query - name: x - required: true + name: limit + required: false schema: + default: 100 type: number style: form responses: @@ -6275,22 +4936,21 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CompressedScreenshotResponse' - description: Compressed region screenshot taken successfully + $ref: "#/components/schemas/PaginatedTraces" + description: Paginated list of trace summaries security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Take compressed region screenshot" + summary: Get box traces tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/display/info: + - box + /box/{boxId}/telemetry/traces/{traceId}: get: - deprecated: true - description: Get information about displays - operationId: getDisplayInfo_deprecated + description: Retrieve all spans for a specific trace + operationId: getBoxTraceSpans parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6300,9 +4960,18 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the box + explode: false + in: path + name: boxId + required: true + schema: + type: string + style: simple + - description: ID of the trace + explode: false in: path - name: sandboxId + name: traceId required: true schema: type: string @@ -6312,22 +4981,23 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DisplayInfoResponse' - description: Display info retrieved successfully + items: + $ref: "#/components/schemas/TraceSpan" + type: array + description: List of spans in the trace security: - bearer: [] - oauth2: - openid - profile - email - summary: "[DEPRECATED] Get display info" + summary: Get trace spans tags: - - toolbox - /toolbox/{sandboxId}/toolbox/computeruse/display/windows: + - box + /box/{boxId}/telemetry/metrics: get: - deprecated: true - description: Get list of open windows - operationId: getWindows_deprecated + description: Retrieve OTEL metrics for a box within a time range + operationId: getBoxMetrics parameters: - description: Use with JWT to specify the organization ID explode: false @@ -6337,8030 +5007,4667 @@ paths: schema: type: string style: simple - - explode: false + - description: ID of the box + explode: false in: path - name: sandboxId + name: boxId required: true schema: type: string style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/WindowsResponse' - description: Windows list retrieved successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get windows" - tags: - - toolbox - /snapshots: - get: - operationId: getAllSnapshots - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Page number of the results - explode: true - in: query - name: page - required: false - schema: - default: 1 - minimum: 1 - type: number - style: form - - description: Number of results per page - explode: true - in: query - name: limit - required: false - schema: - default: 100 - maximum: 200 - minimum: 1 - type: number - style: form - - description: Filter by partial name match + - description: Start of time range (ISO 8601) explode: true in: query - name: name - required: false + name: from + required: true schema: - example: abc123 + format: date-time type: string style: form - - description: Field to sort by + - description: End of time range (ISO 8601) explode: true in: query - name: sort - required: false + name: to + required: true schema: - default: lastUsedAt - enum: - - name - - state - - lastUsedAt - - createdAt + format: date-time type: string style: form - - description: Direction to sort by + - description: Filter by metric names explode: true in: query - name: order + name: metricNames required: false schema: - default: desc - enum: - - asc - - desc - type: string + items: + type: string + type: array style: form responses: "200": content: application/json: schema: - $ref: '#/components/schemas/PaginatedSnapshots' - description: Paginated list of all snapshots - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List all snapshots - tags: - - snapshots - post: - operationId: createSnapshot - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateSnapshot' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SnapshotDto' - description: The snapshot has been successfully created. - "400": - description: Bad request - Snapshots with tag ":latest" are not allowed + $ref: "#/components/schemas/MetricsResponse" + description: Metrics time series data security: - bearer: [] - oauth2: - openid - profile - email - summary: Create a new snapshot + summary: Get box metrics tags: - - snapshots - /snapshots/can-cleanup-image: - get: - operationId: canCleanupImage - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: + - box +components: + schemas: + Announcement: + properties: + text: + description: The announcement text + example: New feature available! type: string - style: simple - - description: Image name with tag to check - explode: true - in: query - name: imageName - required: true - schema: + learnMoreUrl: + description: URL to learn more about the announcement + example: https://example.com/learn-more type: string - style: form - responses: - "200": - content: - application/json: - schema: - type: boolean - description: Boolean indicating if image can be cleaned up - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Check if an image can be cleaned up - tags: - - snapshots - /snapshots/{id}: - delete: - operationId: removeSnapshot - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: + required: + - text + type: object + PosthogConfig: + properties: + apiKey: + description: PostHog API key + example: phc_abc123 type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: + host: + description: PostHog host URL + example: https://app.posthog.com type: string - style: simple - responses: - "200": - description: Snapshot has been deleted - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Delete snapshot - tags: - - snapshots - get: - operationId: getSnapshot - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: + required: + - apiKey + - host + type: object + OidcConfig: + properties: + issuer: + description: OIDC issuer + example: https://auth.example.com type: string - style: simple - - description: Snapshot ID or name - explode: false - in: path - name: id - required: true - schema: + clientId: + description: OIDC client ID + example: boxlite-client type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SnapshotDto' - description: The snapshot - "404": - description: Snapshot not found - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get snapshot by ID or name - tags: - - snapshots - /snapshots/{id}/general: - patch: - operationId: setSnapshotGeneralStatus - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: + audience: + description: OIDC audience + example: boxlite-api type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: + endSessionEndpoint: + description: OIDC end-session endpoint. Set when the IdP does not advertise + one via discovery (e.g. Dex) and BoxLite hosts a compatible logout endpoint. + example: https://api.example.com/api/auth/end-session type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SetSnapshotGeneralStatusDto' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SnapshotDto' - description: Snapshot general status has been set - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Set snapshot general status - tags: - - snapshots - /snapshots/{id}/build-logs: - get: - deprecated: true - description: This endpoint is deprecated. Use `getSnapshotBuildLogsUrl` instead. - operationId: getSnapshotBuildLogs - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - - description: Whether to follow the logs stream - explode: true - in: query - name: follow - required: false - schema: - type: boolean - style: form - responses: - "200": - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get snapshot build logs - tags: - - snapshots - /snapshots/{id}/build-logs-url: - get: - operationId: getSnapshotBuildLogsUrl - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Url' - description: The snapshot build logs URL - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get snapshot build logs URL - tags: - - snapshots - /snapshots/{id}/activate: - post: - operationId: activateSnapshot - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SnapshotDto' - description: The snapshot has been successfully activated. - "400": - description: "Bad request - Snapshot is already active, not in inactive\ - \ state, or has associated snapshot runners" - "404": - description: Snapshot not found - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Activate a snapshot - tags: - - snapshots - /snapshots/{id}/deactivate: - post: - operationId: deactivateSnapshot - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Snapshot ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "204": - description: The snapshot has been successfully deactivated. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Deactivate a snapshot - tags: - - snapshots - /workspace: - get: - deprecated: true - operationId: listWorkspaces_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Include verbose output - explode: true - in: query - name: verbose - required: false - schema: - type: boolean - style: form - - description: JSON encoded labels to filter by - explode: true - in: query - name: labels - required: false - schema: - example: "{\"label1\": \"value1\", \"label2\": \"value2\"}" - type: string - style: form - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/Workspace' - type: array - description: List of all workspacees - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] List all workspaces" - tags: - - workspace - post: - deprecated: true - operationId: createWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateWorkspace' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Workspace' - description: The workspace has been successfully created. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Create a new workspace" - tags: - - workspace - /workspace/{workspaceId}: - delete: - deprecated: true - operationId: deleteWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - explode: true - in: query - name: force - required: true - schema: - type: boolean - style: form - responses: - "200": - description: Workspace has been deleted - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Delete workspace" - tags: - - workspace - get: - deprecated: true - operationId: getWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Include verbose output - explode: true - in: query - name: verbose - required: false - schema: - type: boolean - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Workspace' - description: Workspace details - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get workspace details" - tags: - - workspace - /workspace/{workspaceId}/start: - post: - deprecated: true - operationId: startWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - responses: - "200": - description: Workspace has been started - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Start workspace" - tags: - - workspace - /workspace/{workspaceId}/stop: - post: - deprecated: true - operationId: stopWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - responses: - "200": - description: Workspace has been stopped - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Stop workspace" - tags: - - workspace - /workspace/{workspaceId}/labels: - put: - deprecated: true - operationId: replaceLabelsWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxLabels' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/SandboxLabels' - description: Labels have been successfully replaced - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Replace workspace labels" - tags: - - workspace - /workspace/{workspaceId}/backup: - post: - deprecated: true - operationId: createBackupWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Workspace' - description: Workspace backup has been initiated - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Create workspace backup" - tags: - - workspace - /workspace/{workspaceId}/public/{isPublic}: - post: - deprecated: true - operationId: updatePublicStatusWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Public status to set - explode: false - in: path - name: isPublic - required: true - schema: - type: boolean - style: simple - responses: - "201": - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Update public status" - tags: - - workspace - /workspace/{workspaceId}/autostop/{interval}: - post: - deprecated: true - operationId: setAutostopIntervalWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Auto-stop interval in minutes (0 to disable) - explode: false - in: path - name: interval - required: true - schema: - type: number - style: simple - responses: - "200": - description: Auto-stop interval has been set - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Set workspace auto-stop interval" - tags: - - workspace - /workspace/{workspaceId}/autoarchive/{interval}: - post: - deprecated: true - operationId: setAutoArchiveIntervalWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Auto-archive interval in minutes (0 means the maximum interval - will be used) - explode: false - in: path - name: interval - required: true - schema: - type: number - style: simple - responses: - "200": - description: Auto-archive interval has been set - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Set workspace auto-archive interval" - tags: - - workspace - /workspace/{workspaceId}/archive: - post: - deprecated: true - operationId: archiveWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - responses: - "200": - description: Workspace has been archived - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Archive workspace" - tags: - - workspace - /workspace/{workspaceId}/ports/{port}/preview-url: - get: - deprecated: true - operationId: getPortPreviewUrlWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Port number to get preview URL for - explode: false - in: path - name: port - required: true - schema: - type: number - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/WorkspacePortPreviewUrl' - description: Preview URL for the specified port - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get preview URL for a workspace port" - tags: - - workspace - /workspace/{workspaceId}/build-logs: - get: - deprecated: true - operationId: getBuildLogsWorkspace_deprecated - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the workspace - explode: false - in: path - name: workspaceId - required: true - schema: - type: string - style: simple - - description: Whether to follow the logs stream - explode: true - in: query - name: follow - required: false - schema: - type: boolean - style: form - responses: - "200": - description: Build logs stream - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: "[DEPRECATED] Get build logs" - tags: - - workspace - /preview/{sandboxId}/public: - get: - operationId: isSandboxPublic - parameters: - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - type: boolean - description: Public status of the sandbox - summary: Check if sandbox is public - tags: - - preview - /preview/{sandboxId}/validate/{authToken}: - get: - operationId: isValidAuthToken - parameters: - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - description: Auth token of the sandbox - explode: false - in: path - name: authToken - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - type: boolean - description: Sandbox auth token validation status - summary: Check if sandbox auth token is valid - tags: - - preview - /preview/{sandboxId}/access: - get: - operationId: hasSandboxAccess - parameters: - - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - type: boolean - description: User access status to the sandbox - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Check if user has access to the sandbox - tags: - - preview - /preview/{signedPreviewToken}/{port}/sandbox-id: - get: - operationId: getSandboxIdFromSignedPreviewUrlToken - parameters: - - description: Signed preview URL token - explode: false - in: path - name: signedPreviewToken - required: true - schema: - type: string - style: simple - - description: Port number to get sandbox ID from signed preview URL token - explode: false - in: path - name: port - required: true - schema: - type: number - style: simple - responses: - "200": - content: - application/json: - schema: - type: string - description: Sandbox ID from signed preview URL token - summary: Get sandbox ID from signed preview URL token - tags: - - preview - /volumes: - get: - operationId: listVolumes - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Include deleted volumes in the response - explode: true - in: query - name: includeDeleted - required: false - schema: - type: boolean - style: form - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/VolumeDto' - type: array - description: List of all volumes - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List all volumes - tags: - - volumes - post: - operationId: createVolume - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateVolume' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/VolumeDto' - description: The volume has been successfully created. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Create a new volume - tags: - - volumes - /volumes/{volumeId}: - delete: - operationId: deleteVolume - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the volume - explode: false - in: path - name: volumeId - required: true - schema: - type: string - style: simple - responses: - "200": - description: Volume has been marked for deletion - "409": - description: Volume is in use by one or more sandboxes - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Delete volume - tags: - - volumes - get: - operationId: getVolume - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the volume - explode: false - in: path - name: volumeId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/VolumeDto' - description: Volume details - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get volume details - tags: - - volumes - /volumes/by-name/{name}: - get: - operationId: getVolumeByName - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: Name of the volume - explode: false - in: path - name: name - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/VolumeDto' - description: Volume details - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get volume details by name - tags: - - volumes - /jobs: - get: - description: "Returns a paginated list of jobs for the runner, optionally filtered\ - \ by status." - operationId: listJobs - parameters: - - description: Page number of the results - explode: true - in: query - name: page - required: false - schema: - default: 1 - minimum: 1 - type: number - style: form - - description: "Maximum number of jobs to return (default: 100, max: 500)" - explode: true - in: query - name: limit - required: false - schema: - default: 100 - maximum: 200 - minimum: 1 - type: number - style: form - - description: Filter jobs by status - explode: true - in: query - name: status - required: false - schema: - $ref: '#/components/schemas/JobStatus' - style: form - - description: "Number of jobs to skip for pagination (default: 0)" - explode: true - in: query - name: offset - required: false - schema: - type: number - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedJobs' - description: List of jobs for the runner - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List jobs for the runner - tags: - - jobs - /jobs/poll: - get: - description: "Long poll endpoint for runners to fetch pending jobs. Returns\ - \ immediately if jobs are available, otherwise waits up to timeout seconds." - operationId: pollJobs - parameters: - - description: "Timeout in seconds for long polling (default: 30, max: 60)" - explode: true - in: query - name: timeout - required: false - schema: - type: number - style: form - - description: "Maximum number of jobs to return (default: 10, max: 100)" - explode: true - in: query - name: limit - required: false - schema: - type: number - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PollJobsResponse' - description: List of jobs for the runner - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Long poll for jobs - tags: - - jobs - /jobs/{jobId}: - get: - operationId: getJob - parameters: - - description: ID of the job - explode: false - in: path - name: jobId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Job' - description: Job details - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get job details - tags: - - jobs - /jobs/{jobId}/status: - post: - operationId: updateJobStatus - parameters: - - description: ID of the job - explode: false - in: path - name: jobId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateJobStatus' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Job' - description: Job status updated successfully - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Update job status - tags: - - jobs - /docker-registry: - get: - operationId: listRegistries - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/DockerRegistry' - type: array - description: List of all docker registries - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List registries - tags: - - docker-registry - post: - operationId: createRegistry - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/CreateDockerRegistry' - required: true - responses: - "201": - content: - application/json: - schema: - $ref: '#/components/schemas/DockerRegistry' - description: The docker registry has been successfully created. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Create registry - tags: - - docker-registry - /docker-registry/registry-push-access: - get: - operationId: getTransientPushAccess - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the region where the snapshot will be available (defaults - to organization default region) - explode: true - in: query - name: regionId - required: false - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/RegistryPushAccessDto' - description: Temporary registry access has been generated - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get temporary registry access for pushing snapshots - tags: - - docker-registry - /docker-registry/{id}: - delete: - operationId: deleteRegistry - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the docker registry - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "204": - description: The docker registry has been successfully deleted. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Delete registry - tags: - - docker-registry - get: - operationId: getRegistry - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the docker registry - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/DockerRegistry' - description: The docker registry - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get registry - tags: - - docker-registry - patch: - operationId: updateRegistry - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the docker registry - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UpdateDockerRegistry' - required: true - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/DockerRegistry' - description: The docker registry has been successfully updated. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Update registry - tags: - - docker-registry - /docker-registry/{id}/set-default: - post: - operationId: setDefaultRegistry - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the docker registry - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/DockerRegistry' - description: The docker registry has been set as default. - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Set default registry - tags: - - docker-registry - /admin/runners: - get: - operationId: adminListRunners - parameters: - - description: Filter runners by region ID - explode: true - in: query - name: regionId - required: false - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/RunnerFull' - type: array - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: List all runners - tags: - - admin - post: - operationId: adminCreateRunner - parameters: [] - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AdminCreateRunner' - required: true - responses: - "201": - content: - application/json: - schema: - $ref: '#/components/schemas/CreateRunnerResponse' - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Create runner - tags: - - admin - /admin/runners/{id}: - delete: - operationId: adminDeleteRunner - parameters: - - description: Runner ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "204": - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Delete runner - tags: - - admin - get: - operationId: adminGetRunnerById - parameters: - - description: Runner ID - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/RunnerFull' - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get runner by ID - tags: - - admin - /admin/runners/{id}/scheduling: - patch: - operationId: adminUpdateRunnerScheduling - parameters: - - explode: false - in: path - name: id - required: true - schema: - type: string - style: simple - responses: - "204": - description: "" - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Update runner scheduling status - tags: - - admin - /admin/sandbox/{sandboxId}/recover: - post: - operationId: adminRecoverSandbox - parameters: - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/Sandbox' - description: Recovery initiated - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Recover sandbox from error state as an admin - tags: - - admin - /webhooks/organizations/{organizationId}/app-portal-access: - post: - operationId: WebhookController_getAppPortalAccess - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/WebhookAppPortalAccess' - description: App Portal access generated successfully - security: - - bearer: [] - summary: Get Svix Consumer App Portal access for an organization - tags: - - webhooks - /webhooks/organizations/{organizationId}/send: - post: - operationId: WebhookController_sendWebhook - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/SendWebhookDto' - required: true - responses: - "200": - description: Webhook message sent successfully - security: - - bearer: [] - summary: Send a webhook message to an organization - tags: - - webhooks - /webhooks/organizations/{organizationId}/messages/{messageId}/attempts: - get: - operationId: WebhookController_getMessageAttempts - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - - explode: false - in: path - name: messageId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - items: - type: object - type: array - description: List of delivery attempts - security: - - bearer: [] - summary: Get delivery attempts for a webhook message - tags: - - webhooks - /webhooks/status: - get: - operationId: WebhookController_getStatus - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/WebhookController_getStatus_200_response' - description: Webhook service status - security: - - bearer: [] - summary: Get webhook service status - tags: - - webhooks - /webhooks/organizations/{organizationId}/initialization-status: - get: - operationId: WebhookController_getInitializationStatus - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/WebhookInitializationStatus' - description: Webhook initialization status - "404": - description: Webhook initialization status not found - security: - - bearer: [] - summary: Get webhook initialization status for an organization - tags: - - webhooks - /webhooks/organizations/{organizationId}/initialize: - post: - operationId: WebhookController_initializeWebhooks - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - responses: - "201": - description: Webhooks initialized successfully - "403": - description: User does not have access to this organization - "404": - description: Organization not found - security: - - bearer: [] - summary: Initialize webhooks for an organization - tags: - - webhooks - /object-storage/push-access: - get: - operationId: getPushAccess - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/StorageAccessDto' - description: Temporary storage access has been generated - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get temporary storage access for pushing objects - tags: - - object-storage - /audit: - get: - operationId: getAllAuditLogs - parameters: - - description: Page number of the results - explode: true - in: query - name: page - required: false - schema: - default: 1 - minimum: 1 - type: number - style: form - - description: Number of results per page - explode: true - in: query - name: limit - required: false - schema: - default: 100 - maximum: 200 - minimum: 1 - type: number - style: form - - description: From date (ISO 8601 format) - explode: true - in: query - name: from - required: false - schema: - format: date-time - type: string - style: form - - description: To date (ISO 8601 format) - explode: true - in: query - name: to - required: false - schema: - format: date-time - type: string - style: form - - description: "Token for cursor-based pagination. When provided, takes precedence\ - \ over page parameter." - explode: true - in: query - name: nextToken - required: false - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedAuditLogs' - description: Paginated list of all audit logs - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get all audit logs - tags: - - audit - /audit/organizations/{organizationId}: - get: - operationId: getOrganizationAuditLogs - parameters: - - description: Organization ID - explode: false - in: path - name: organizationId - required: true - schema: - type: string - style: simple - - description: Page number of the results - explode: true - in: query - name: page - required: false - schema: - default: 1 - minimum: 1 - type: number - style: form - - description: Number of results per page - explode: true - in: query - name: limit - required: false - schema: - default: 100 - maximum: 200 - minimum: 1 - type: number - style: form - - description: From date (ISO 8601 format) - explode: true - in: query - name: from - required: false - schema: - format: date-time - type: string - style: form - - description: To date (ISO 8601 format) - explode: true - in: query - name: to - required: false - schema: - format: date-time - type: string - style: form - - description: "Token for cursor-based pagination. When provided, takes precedence\ - \ over page parameter." - explode: true - in: query - name: nextToken - required: false - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedAuditLogs' - description: Paginated list of organization audit logs - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get audit logs for organization - tags: - - audit - /health: - get: - operationId: HealthController_live - parameters: [] - responses: - "200": - description: "" - tags: - - Health - /health/ready: - get: - operationId: HealthController_check - parameters: [] - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/HealthController_check_200_response' - description: The Health Check is successful - "503": - content: - application/json: - schema: - $ref: '#/components/schemas/HealthController_check_503_response' - description: The Health Check is not successful - tags: - - Health - /sandbox/{sandboxId}/telemetry/logs: - get: - description: Retrieve OTEL logs for a sandbox within a time range - operationId: getSandboxLogs - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - description: Start of time range (ISO 8601) - explode: true - in: query - name: from - required: true - schema: - format: date-time - type: string - style: form - - description: End of time range (ISO 8601) - explode: true - in: query - name: to - required: true - schema: - format: date-time - type: string - style: form - - description: Page number (1-indexed) - explode: true - in: query - name: page - required: false - schema: - default: 1 - type: number - style: form - - description: Number of items per page - explode: true - in: query - name: limit - required: false - schema: - default: 100 - type: number - style: form - - description: "Filter by severity levels (DEBUG, INFO, WARN, ERROR)" - explode: true - in: query - name: severities - required: false - schema: - items: - type: string - type: array - style: form - - description: Search in log body - explode: true - in: query - name: search - required: false - schema: - type: string - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedLogs' - description: Paginated list of log entries - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get sandbox logs - tags: - - sandbox - /sandbox/{sandboxId}/telemetry/traces: - get: - description: Retrieve OTEL traces for a sandbox within a time range - operationId: getSandboxTraces - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - description: Start of time range (ISO 8601) - explode: true - in: query - name: from - required: true - schema: - format: date-time - type: string - style: form - - description: End of time range (ISO 8601) - explode: true - in: query - name: to - required: true - schema: - format: date-time - type: string - style: form - - description: Page number (1-indexed) - explode: true - in: query - name: page - required: false - schema: - default: 1 - type: number - style: form - - description: Number of items per page - explode: true - in: query - name: limit - required: false - schema: - default: 100 - type: number - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/PaginatedTraces' - description: Paginated list of trace summaries - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get sandbox traces - tags: - - sandbox - /sandbox/{sandboxId}/telemetry/traces/{traceId}: - get: - description: Retrieve all spans for a specific trace - operationId: getSandboxTraceSpans - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - description: ID of the trace - explode: false - in: path - name: traceId - required: true - schema: - type: string - style: simple - responses: - "200": - content: - application/json: - schema: - items: - $ref: '#/components/schemas/TraceSpan' - type: array - description: List of spans in the trace - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get trace spans - tags: - - sandbox - /sandbox/{sandboxId}/telemetry/metrics: - get: - description: Retrieve OTEL metrics for a sandbox within a time range - operationId: getSandboxMetrics - parameters: - - description: Use with JWT to specify the organization ID - explode: false - in: header - name: X-BoxLite-Organization-ID - required: false - schema: - type: string - style: simple - - description: ID of the sandbox - explode: false - in: path - name: sandboxId - required: true - schema: - type: string - style: simple - - description: Start of time range (ISO 8601) - explode: true - in: query - name: from - required: true - schema: - format: date-time - type: string - style: form - - description: End of time range (ISO 8601) - explode: true - in: query - name: to - required: true - schema: - format: date-time - type: string - style: form - - description: Filter by metric names - explode: true - in: query - name: metricNames - required: false - schema: - items: - type: string - type: array - style: form - responses: - "200": - content: - application/json: - schema: - $ref: '#/components/schemas/MetricsResponse' - description: Metrics time series data - security: - - bearer: [] - - oauth2: - - openid - - profile - - email - summary: Get sandbox metrics - tags: - - sandbox -components: - schemas: - Announcement: - properties: - text: - description: The announcement text - example: New feature available! - type: string - learnMoreUrl: - description: URL to learn more about the announcement - example: https://example.com/learn-more - type: string - required: - - text - type: object - PosthogConfig: - properties: - apiKey: - description: PostHog API key - example: phc_abc123 - type: string - host: - description: PostHog host URL - example: https://app.posthog.com - type: string - required: - - apiKey - - host - type: object - OidcConfig: - properties: - issuer: - description: OIDC issuer - example: https://auth.example.com - type: string - clientId: - description: OIDC client ID - example: boxlite-client - type: string - audience: - description: OIDC audience - example: boxlite-api - type: string - required: - - audience - - clientId - - issuer - type: object - RateLimitEntry: - properties: - ttl: - description: Rate limit TTL in seconds - example: 60 - type: number - limit: - description: Rate limit max requests - example: 100 - type: number - type: object - RateLimitConfig: - properties: - failedAuth: - allOf: - - $ref: '#/components/schemas/RateLimitEntry' - description: Failed authentication rate limit - authenticated: - allOf: - - $ref: '#/components/schemas/RateLimitEntry' - description: Authenticated rate limit - sandboxCreate: - allOf: - - $ref: '#/components/schemas/RateLimitEntry' - description: Sandbox create rate limit - sandboxLifecycle: - allOf: - - $ref: '#/components/schemas/RateLimitEntry' - description: Sandbox lifecycle rate limit - type: object - BoxLiteConfiguration: - example: - dashboardUrl: https://dashboard.example.com - rateLimit: "" - analyticsApiUrl: https://analytics.example.com - sshGatewayPublicKey: ssh-gateway-public-key - billingApiUrl: https://billing.example.com - linkedAccountsEnabled: true - version: 0.0.1 - oidc: "" - proxyToolboxUrl: https://proxy.example.com/toolbox - posthog: "" - maxAutoArchiveInterval: 43200 - pylonAppId: pylon-app-123 - proxyTemplateUrl: "https://{{PORT}}-{{sandboxId}}.proxy.example.com" - maintananceMode: false - environment: production - defaultSnapshot: ubuntu:22.04 - sshGatewayCommand: "ssh -p 2222 {{TOKEN}}@localhost" - announcements: - feature-update: - text: New feature available! - learnMoreUrl: https://example.com - properties: - version: - description: BoxLite version - example: 0.0.1 - type: string - posthog: - allOf: - - $ref: '#/components/schemas/PosthogConfig' - description: PostHog configuration - oidc: - allOf: - - $ref: '#/components/schemas/OidcConfig' - description: OIDC configuration - linkedAccountsEnabled: - description: Whether linked accounts are enabled - example: true - type: boolean - announcements: - additionalProperties: - $ref: '#/components/schemas/Announcement' - description: System announcements - example: - feature-update: - text: New feature available! - learnMoreUrl: https://example.com - type: object - pylonAppId: - description: Pylon application ID - example: pylon-app-123 - type: string - proxyTemplateUrl: - description: Proxy template URL - example: "https://{{PORT}}-{{sandboxId}}.proxy.example.com" - type: string - proxyToolboxUrl: - description: Toolbox template URL - example: https://proxy.example.com/toolbox - type: string - defaultSnapshot: - description: Default snapshot for sandboxes - example: ubuntu:22.04 - type: string - dashboardUrl: - description: Dashboard URL - example: https://dashboard.example.com - type: string - maxAutoArchiveInterval: - description: Maximum auto-archive interval in minutes - example: 43200 - type: number - maintananceMode: - description: Whether maintenance mode is enabled - example: false - type: boolean - environment: - description: Current environment - example: production - type: string - billingApiUrl: - description: Billing API URL - example: https://billing.example.com - type: string - analyticsApiUrl: - description: Analytics API URL - example: https://analytics.example.com - type: string - sshGatewayCommand: - description: SSH Gateway command - example: "ssh -p 2222 {{TOKEN}}@localhost" - type: string - sshGatewayPublicKey: - description: Base64 encoded SSH Gateway public key - example: ssh-gateway-public-key - type: string - rateLimit: - allOf: - - $ref: '#/components/schemas/RateLimitConfig' - description: Rate limit configuration - required: - - announcements - - dashboardUrl - - defaultSnapshot - - environment - - linkedAccountsEnabled - - maintananceMode - - maxAutoArchiveInterval - - oidc - - proxyTemplateUrl - - proxyToolboxUrl - - version - type: object - CreateApiKey: - example: - permissions: - - write:registries - - write:registries - name: My API Key - expiresAt: 2025-06-09T12:00:00Z - properties: - name: - description: The name of the API key - example: My API Key - type: string - permissions: - description: The list of organization resource permissions explicitly assigned - to the API key - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - expiresAt: - description: When the API key expires - example: 2025-06-09T12:00:00Z - format: date-time - nullable: true - type: string - required: - - name - - permissions - type: object - ApiKeyResponse: - example: - createdAt: 2024-03-14T12:00:00Z - permissions: - - write:registries - - write:registries - name: My API Key - value: bb_sk_1234567890abcdef - expiresAt: 2025-06-09T12:00:00Z - properties: - name: - description: The name of the API key - example: My API Key - type: string - value: - description: The API key value - example: bb_sk_1234567890abcdef - type: string - createdAt: - description: When the API key was created - example: 2024-03-14T12:00:00Z - format: date-time - type: string - permissions: - description: The list of organization resource permissions assigned to the - API key - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - expiresAt: - description: When the API key expires - example: 2025-06-09T12:00:00Z - format: date-time - nullable: true - type: string - required: - - createdAt - - expiresAt - - name - - permissions - - value - type: object - ApiKeyList: - example: - createdAt: 2024-03-14T12:00:00Z - lastUsedAt: 2024-03-14T12:00:00Z - permissions: - - write:registries - - write:registries - name: My API Key - value: bb_********************def - userId: "123" - expiresAt: 2024-03-14T12:00:00Z - properties: - name: - description: The name of the API key - example: My API Key - type: string - value: - description: The masked API key value - example: bb_********************def - type: string - createdAt: - description: When the API key was created - example: 2024-03-14T12:00:00Z - format: date-time - type: string - permissions: - description: The list of organization resource permissions assigned to the - API key - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - lastUsedAt: - description: When the API key was last used - example: 2024-03-14T12:00:00Z - format: date-time - nullable: true - type: string - expiresAt: - description: When the API key expires - example: 2024-03-14T12:00:00Z - format: date-time - nullable: true - type: string - userId: - description: The user ID of the user who created the API key - example: "123" - type: string - required: - - createdAt - - expiresAt - - lastUsedAt - - name - - permissions - - userId - - value - type: object - OrganizationRole: - example: - createdAt: 2000-01-23T04:56:07.000+00:00 - permissions: - - write:registries - - write:registries - name: name - isGlobal: true - description: description - id: id - updatedAt: 2000-01-23T04:56:07.000+00:00 - properties: - id: - description: Role ID - type: string - name: - description: Role name - type: string - description: - description: Role description - type: string - permissions: - description: Roles assigned to the user - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - isGlobal: - description: Global role flag - type: boolean - createdAt: - description: Creation timestamp - format: date-time - type: string - updatedAt: - description: Last update timestamp - format: date-time - type: string - required: - - createdAt - - description - - id - - isGlobal - - name - - permissions - - updatedAt - type: object - OrganizationInvitation: - example: - organizationId: organizationId - createdAt: 2000-01-23T04:56:07.000+00:00 - invitedBy: invitedBy - role: owner - organizationName: organizationName - assignedRoles: - - createdAt: 2000-01-23T04:56:07.000+00:00 - permissions: - - write:registries - - write:registries - name: name - isGlobal: true - description: description - id: id - updatedAt: 2000-01-23T04:56:07.000+00:00 - - createdAt: 2000-01-23T04:56:07.000+00:00 - permissions: - - write:registries - - write:registries - name: name - isGlobal: true - description: description - id: id - updatedAt: 2000-01-23T04:56:07.000+00:00 - id: id - email: email - expiresAt: 2000-01-23T04:56:07.000+00:00 - status: pending - updatedAt: 2000-01-23T04:56:07.000+00:00 - properties: - id: - description: Invitation ID - type: string - email: - description: Email address of the invitee - type: string - invitedBy: - description: Email address of the inviter - type: string - organizationId: - description: Organization ID - type: string - organizationName: - description: Organization name - type: string - expiresAt: - description: Expiration date of the invitation - format: date-time - type: string - status: - description: Invitation status - enum: - - pending - - accepted - - declined - - cancelled - type: string - role: - description: Member role - enum: - - owner - - member - type: string - assignedRoles: - description: Assigned roles - items: - $ref: '#/components/schemas/OrganizationRole' - type: array - createdAt: - description: Creation timestamp - format: date-time - type: string - updatedAt: - description: Last update timestamp - format: date-time - type: string - required: - - assignedRoles - - createdAt - - email - - expiresAt - - id - - invitedBy - - organizationId - - organizationName - - role - - status - - updatedAt - type: object - CreateOrganization: - example: - defaultRegionId: us - name: My Organization - properties: - name: - description: The name of organization - example: My Organization - type: string - defaultRegionId: - description: The ID of the default region for the organization - example: us - type: string - required: - - defaultRegionId - - name - type: object - Organization: - example: - defaultRegionId: defaultRegionId - sandboxCreateRateLimitTtlSeconds: 2.027123023002322 - suspensionReason: suspensionReason - sandboxLifecycleRateLimit: 9.301444243932576 - suspendedAt: 2000-01-23T04:56:07.000+00:00 - personal: true - suspensionCleanupGracePeriodHours: 0.8008281904610115 - authenticatedRateLimit: 2.3021358869347655 - suspended: true - sandboxCreateRateLimit: 7.061401241503109 - sandboxLifecycleRateLimitTtlSeconds: 4.145608029883936 - createdAt: 2000-01-23T04:56:07.000+00:00 - maxCpuPerSandbox: 6.027456183070403 - sandboxLimitedNetworkEgress: true - suspendedUntil: 2000-01-23T04:56:07.000+00:00 - maxDiskPerSandbox: 5.962133916683182 - createdBy: createdBy - snapshotDeactivationTimeoutMinutes: 5.637376656633329 - name: name - maxMemoryPerSandbox: 1.4658129805029452 - authenticatedRateLimitTtlSeconds: 3.616076749251911 - id: id - experimentalConfig: "{}" - updatedAt: 2000-01-23T04:56:07.000+00:00 - properties: - id: - description: Organization ID - type: string - name: - description: Organization name - type: string - createdBy: - description: User ID of the organization creator - type: string - personal: - description: Personal organization flag - type: boolean - createdAt: - description: Creation timestamp - format: date-time - type: string - updatedAt: - description: Last update timestamp - format: date-time - type: string - suspended: - description: Suspended flag - type: boolean - suspendedAt: - description: Suspended at - format: date-time - type: string - suspensionReason: - description: Suspended reason - type: string - suspendedUntil: - description: Suspended until - format: date-time - type: string - suspensionCleanupGracePeriodHours: - description: Suspension cleanup grace period hours - type: number - maxCpuPerSandbox: - description: Max CPU per sandbox - type: number - maxMemoryPerSandbox: - description: Max memory per sandbox - type: number - maxDiskPerSandbox: - description: Max disk per sandbox - type: number - snapshotDeactivationTimeoutMinutes: - default: 20160 - description: Time in minutes before an unused snapshot is deactivated - type: number - sandboxLimitedNetworkEgress: - description: Sandbox default network block all - type: boolean - defaultRegionId: - description: Default region ID - type: string - authenticatedRateLimit: - description: Authenticated rate limit per minute - nullable: true - type: number - sandboxCreateRateLimit: - description: Sandbox create rate limit per minute - nullable: true - type: number - sandboxLifecycleRateLimit: - description: Sandbox lifecycle rate limit per minute - nullable: true - type: number - experimentalConfig: - description: Experimental configuration - type: object - authenticatedRateLimitTtlSeconds: - description: Authenticated rate limit TTL in seconds - nullable: true - type: number - sandboxCreateRateLimitTtlSeconds: - description: Sandbox create rate limit TTL in seconds - nullable: true - type: number - sandboxLifecycleRateLimitTtlSeconds: - description: Sandbox lifecycle rate limit TTL in seconds - nullable: true - type: number - required: - - authenticatedRateLimit - - authenticatedRateLimitTtlSeconds - - createdAt - - createdBy - - experimentalConfig - - id - - maxCpuPerSandbox - - maxDiskPerSandbox - - maxMemoryPerSandbox - - name - - personal - - sandboxCreateRateLimit - - sandboxCreateRateLimitTtlSeconds - - sandboxLifecycleRateLimit - - sandboxLifecycleRateLimitTtlSeconds - - sandboxLimitedNetworkEgress - - snapshotDeactivationTimeoutMinutes - - suspended - - suspendedAt - - suspendedUntil - - suspensionCleanupGracePeriodHours - - suspensionReason - - updatedAt - type: object - UpdateOrganizationDefaultRegion: - example: - defaultRegionId: us - properties: - defaultRegionId: - description: The ID of the default region for the organization - example: us - type: string - required: - - defaultRegionId - type: object - RegionUsageOverview: - example: - totalCpuQuota: 0.8008281904610115 - regionId: regionId - currentDiskUsage: 2.3021358869347655 - currentMemoryUsage: 5.962133916683182 - currentCpuUsage: 6.027456183070403 - totalMemoryQuota: 1.4658129805029452 - totalDiskQuota: 5.637376656633329 - properties: - regionId: - type: string - totalCpuQuota: - type: number - currentCpuUsage: - type: number - totalMemoryQuota: - type: number - currentMemoryUsage: - type: number - totalDiskQuota: - type: number - currentDiskUsage: - type: number - required: - - currentCpuUsage - - currentDiskUsage - - currentMemoryUsage - - regionId - - totalCpuQuota - - totalDiskQuota - - totalMemoryQuota - type: object - OrganizationUsageOverview: - example: - totalSnapshotQuota: 7.061401241503109 - currentVolumeUsage: 2.027123023002322 - totalVolumeQuota: 3.616076749251911 - currentSnapshotUsage: 9.301444243932576 - regionUsage: - - totalCpuQuota: 0.8008281904610115 - regionId: regionId - currentDiskUsage: 2.3021358869347655 - currentMemoryUsage: 5.962133916683182 - currentCpuUsage: 6.027456183070403 - totalMemoryQuota: 1.4658129805029452 - totalDiskQuota: 5.637376656633329 - - totalCpuQuota: 0.8008281904610115 - regionId: regionId - currentDiskUsage: 2.3021358869347655 - currentMemoryUsage: 5.962133916683182 - currentCpuUsage: 6.027456183070403 - totalMemoryQuota: 1.4658129805029452 - totalDiskQuota: 5.637376656633329 - properties: - regionUsage: - items: - $ref: '#/components/schemas/RegionUsageOverview' - type: array - totalSnapshotQuota: - type: number - currentSnapshotUsage: - type: number - totalVolumeQuota: - type: number - currentVolumeUsage: - type: number - required: - - currentSnapshotUsage - - currentVolumeUsage - - regionUsage - - totalSnapshotQuota - - totalVolumeQuota - type: object - UpdateOrganizationQuota: - example: - sandboxCreateRateLimitTtlSeconds: 4.145608029883936 - sandboxLifecycleRateLimit: 3.616076749251911 - volumeQuota: 2.3021358869347655 - maxSnapshotSize: 5.637376656633329 - authenticatedRateLimit: 7.061401241503109 - sandboxCreateRateLimit: 9.301444243932576 - snapshotQuota: 5.962133916683182 - sandboxLifecycleRateLimitTtlSeconds: 7.386281948385884 - maxCpuPerSandbox: 0.8008281904610115 - maxDiskPerSandbox: 1.4658129805029452 - snapshotDeactivationTimeoutMinutes: 1.2315135367772556 - maxMemoryPerSandbox: 6.027456183070403 - authenticatedRateLimitTtlSeconds: 2.027123023002322 - properties: - maxCpuPerSandbox: - nullable: true - type: number - maxMemoryPerSandbox: - nullable: true - type: number - maxDiskPerSandbox: - nullable: true - type: number - snapshotQuota: - nullable: true - type: number - maxSnapshotSize: - nullable: true - type: number - volumeQuota: - nullable: true - type: number - authenticatedRateLimit: - nullable: true - type: number - sandboxCreateRateLimit: - nullable: true - type: number - sandboxLifecycleRateLimit: - nullable: true - type: number - authenticatedRateLimitTtlSeconds: - nullable: true - type: number - sandboxCreateRateLimitTtlSeconds: - nullable: true - type: number - sandboxLifecycleRateLimitTtlSeconds: - nullable: true - type: number - snapshotDeactivationTimeoutMinutes: - description: Time in minutes before an unused snapshot is deactivated - nullable: true - type: number - required: - - authenticatedRateLimit - - authenticatedRateLimitTtlSeconds - - maxCpuPerSandbox - - maxDiskPerSandbox - - maxMemoryPerSandbox - - maxSnapshotSize - - sandboxCreateRateLimit - - sandboxCreateRateLimitTtlSeconds - - sandboxLifecycleRateLimit - - sandboxLifecycleRateLimitTtlSeconds - - snapshotDeactivationTimeoutMinutes - - snapshotQuota - - volumeQuota - type: object - UpdateOrganizationRegionQuota: - example: - totalCpuQuota: 0.8008281904610115 - totalMemoryQuota: 6.027456183070403 - totalDiskQuota: 1.4658129805029452 - properties: - totalCpuQuota: - nullable: true - type: number - totalMemoryQuota: - nullable: true - type: number - totalDiskQuota: - nullable: true - type: number - required: - - totalCpuQuota - - totalDiskQuota - - totalMemoryQuota - type: object - OrganizationSuspension: - example: - reason: reason - until: 2000-01-23T04:56:07.000+00:00 - suspensionCleanupGracePeriodHours: 0.08008281904610115 - properties: - reason: - description: Suspension reason - type: string - until: - description: Suspension until - format: date-time - type: string - suspensionCleanupGracePeriodHours: - description: Suspension cleanup grace period hours - minimum: 0 - type: number - required: - - reason - - until - type: object - RegionQuota: - example: - organizationId: organizationId - totalCpuQuota: 0.8008281904610115 - regionId: regionId - totalMemoryQuota: 6.027456183070403 - totalDiskQuota: 1.4658129805029452 - properties: - organizationId: - type: string - regionId: - type: string - totalCpuQuota: - type: number - totalMemoryQuota: - type: number - totalDiskQuota: - type: number - required: - - organizationId - - regionId - - totalCpuQuota - - totalDiskQuota - - totalMemoryQuota - type: object - OtelConfig: - example: - headers: - x-api-key: my-api-key - endpoint: endpoint - properties: - endpoint: - description: Endpoint - type: string - headers: - additionalProperties: - type: string - description: Headers - example: - x-api-key: my-api-key - nullable: true - type: object - required: - - endpoint - type: object - OrganizationSandboxDefaultLimitedNetworkEgress: - example: - sandboxDefaultLimitedNetworkEgress: true - properties: - sandboxDefaultLimitedNetworkEgress: - description: Sandbox default limited network egress - type: boolean - required: - - sandboxDefaultLimitedNetworkEgress - type: object - CreateOrganizationRole: - example: - permissions: - - write:registries - - write:registries - name: Maintainer - description: Can manage all resources - properties: - name: - description: The name of the role - example: Maintainer - type: string - description: - description: The description of the role - example: Can manage all resources - type: string - permissions: - description: The list of permissions assigned to the role - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - required: - - description - - name - - permissions - type: object - UpdateOrganizationRole: - example: - permissions: - - write:registries - - write:registries - name: Maintainer - description: Can manage all resources - properties: - name: - description: The name of the role - example: Maintainer - type: string - description: - description: The description of the role - example: Can manage all resources - type: string - permissions: - description: The list of permissions assigned to the role - items: - enum: - - write:registries - - delete:registries - - write:snapshots - - delete:snapshots - - write:sandboxes - - delete:sandboxes - - read:volumes - - write:volumes - - delete:volumes - - write:regions - - delete:regions - - read:runners - - write:runners - - delete:runners - - read:audit_logs - type: string - type: array - required: - - description - - name - - permissions - type: object - OrganizationUser: - example: - organizationId: organizationId - createdAt: 2000-01-23T04:56:07.000+00:00 - role: owner - assignedRoles: - - createdAt: 2000-01-23T04:56:07.000+00:00 - permissions: - - write:registries - - write:registries - name: name - isGlobal: true - description: description - id: id - updatedAt: 2000-01-23T04:56:07.000+00:00 - - createdAt: 2000-01-23T04:56:07.000+00:00 - permissions: - - write:registries - - write:registries - name: name - isGlobal: true - description: description - id: id - updatedAt: 2000-01-23T04:56:07.000+00:00 - name: name - userId: userId - email: email - updatedAt: 2000-01-23T04:56:07.000+00:00 - properties: - userId: - description: User ID - type: string - organizationId: - description: Organization ID - type: string - name: - description: User name - type: string - email: - description: User email - type: string - role: - description: Member role - enum: - - owner - - member - type: string - assignedRoles: - description: Roles assigned to the user - items: - $ref: '#/components/schemas/OrganizationRole' - type: array - createdAt: - description: Creation timestamp - format: date-time - type: string - updatedAt: - description: Last update timestamp - format: date-time - type: string - required: - - assignedRoles - - createdAt - - email - - name - - organizationId - - role - - updatedAt - - userId - type: object - UpdateOrganizationMemberAccess: - example: - role: member - assignedRoleIds: - - assignedRoleIds - - assignedRoleIds - properties: - role: - default: member - description: Organization member role - enum: - - owner - - member - type: string - assignedRoleIds: - default: - - 00000000-0000-0000-0000-000000000001 - description: Array of assigned role IDs - items: - type: string - type: array - required: - - assignedRoleIds - - role - type: object - CreateOrganizationInvitation: - example: - role: member - assignedRoleIds: - - assignedRoleIds - - assignedRoleIds - email: mail@example.com - expiresAt: 2021-12-31T23:59:59Z - properties: - email: - description: Email address of the invitee - example: mail@example.com - type: string - role: - default: member - description: Organization member role for the invitee - enum: - - owner - - member - type: string - assignedRoleIds: - default: - - 00000000-0000-0000-0000-000000000001 - description: Array of assigned role IDs for the invitee - items: - type: string - type: array - expiresAt: - description: Expiration date of the invitation - example: 2021-12-31T23:59:59Z - format: date-time - type: string - required: - - assignedRoleIds - - email - - role - type: object - UpdateOrganizationInvitation: - example: - role: owner - assignedRoleIds: - - assignedRoleIds - - assignedRoleIds - expiresAt: 2021-12-31T23:59:59Z - properties: - role: - description: Organization member role - enum: - - owner - - member - type: string - assignedRoleIds: - description: Array of role IDs - items: - type: string - type: array - expiresAt: - description: Expiration date of the invitation - example: 2021-12-31T23:59:59Z - format: date-time - type: string - required: - - assignedRoleIds - - role - type: object - RegionType: - description: The type of the region - enum: - - shared - - dedicated - - custom - type: string - Region: - example: - organizationId: 123e4567-e89b-12d3-a456-426614174000 - createdAt: 2023-01-01T00:00:00.000Z - snapshotManagerUrl: http://snapshot-manager.example.com - regionType: shared - proxyUrl: https://proxy.example.com - name: us-east-1 - sshGatewayUrl: http://ssh-gateway.example.com - id: "123456789012" - updatedAt: 2023-01-01T00:00:00.000Z - properties: - id: - description: Region ID - example: "123456789012" - type: string - name: - description: Region name - example: us-east-1 - type: string - organizationId: - description: Organization ID - example: 123e4567-e89b-12d3-a456-426614174000 - nullable: true - type: string - regionType: - allOf: - - $ref: '#/components/schemas/RegionType' - description: The type of the region - example: shared - createdAt: - description: Creation timestamp - example: 2023-01-01T00:00:00.000Z - type: string - updatedAt: - description: Last update timestamp - example: 2023-01-01T00:00:00.000Z - type: string - proxyUrl: - description: Proxy URL for the region - example: https://proxy.example.com - nullable: true - type: string - sshGatewayUrl: - description: SSH Gateway URL for the region - example: http://ssh-gateway.example.com - nullable: true - type: string - snapshotManagerUrl: - description: Snapshot Manager URL for the region - example: http://snapshot-manager.example.com - nullable: true - type: string - required: - - createdAt - - id - - name - - regionType - - updatedAt - type: object - CreateRegion: - example: - snapshotManagerUrl: https://snapshot-manager.example.com - proxyUrl: https://proxy.example.com - name: us-east-1 - sshGatewayUrl: ssh://ssh-gateway.example.com - properties: - name: - description: Region name - example: us-east-1 - type: string - proxyUrl: - description: Proxy URL for the region - example: https://proxy.example.com - nullable: true - type: string - sshGatewayUrl: - description: SSH Gateway URL for the region - example: ssh://ssh-gateway.example.com - nullable: true - type: string - snapshotManagerUrl: - description: Snapshot Manager URL for the region - example: https://snapshot-manager.example.com - nullable: true - type: string - required: - - name - type: object - CreateRegionResponse: - example: - proxyApiKey: proxy-api-key-xyz - snapshotManagerUsername: boxlite - snapshotManagerPassword: snapshotManagerPassword - id: region_12345 - sshGatewayApiKey: ssh-gateway-api-key-abc - properties: - id: - description: ID of the created region - example: region_12345 - type: string - proxyApiKey: - description: Proxy API key for the region - example: proxy-api-key-xyz - nullable: true - type: string - sshGatewayApiKey: - description: SSH Gateway API key for the region - example: ssh-gateway-api-key-abc - nullable: true - type: string - snapshotManagerUsername: - description: Snapshot Manager username for the region - example: boxlite - nullable: true - type: string - snapshotManagerPassword: - description: Snapshot Manager password for the region - nullable: true - type: string - required: - - id - type: object - RegenerateApiKeyResponse: - example: - apiKey: api-key-xyz123 - properties: - apiKey: - description: The newly generated API key - example: api-key-xyz123 - type: string - required: - - apiKey - type: object - UpdateRegion: - example: - snapshotManagerUrl: https://snapshot-manager.example.com - proxyUrl: https://proxy.example.com - sshGatewayUrl: ssh://ssh-gateway.example.com - properties: - proxyUrl: - description: Proxy URL for the region - example: https://proxy.example.com - nullable: true - type: string - sshGatewayUrl: - description: SSH Gateway URL for the region - example: ssh://ssh-gateway.example.com - nullable: true - type: string - snapshotManagerUrl: - description: Snapshot Manager URL for the region - example: https://snapshot-manager.example.com - nullable: true - type: string - type: object - SnapshotManagerCredentials: - example: - password: password - username: boxlite - properties: - username: - description: Snapshot Manager username for the region - example: boxlite - type: string - password: - description: Snapshot Manager password for the region - type: string - required: - - password - - username - type: object - UserPublicKey: - example: - name: name - key: key - properties: - key: - description: Public key - type: string - name: - description: Key name - type: string - required: - - key - - name - type: object - User: - example: - createdAt: 2000-01-23T04:56:07.000+00:00 - publicKeys: - - name: name - key: key - - name: name - key: key - name: name - id: id - email: email - properties: - id: - description: User ID - type: string - name: - description: User name - type: string - email: - description: User email - type: string - publicKeys: - description: User public keys - items: - $ref: '#/components/schemas/UserPublicKey' - type: array - createdAt: - description: Creation timestamp - format: date-time - type: string - required: - - createdAt - - email - - id - - name - - publicKeys - type: object - CreateOrganizationQuota: - example: - snapshotQuota: 7.061401241503109 - totalCpuQuota: 0.8008281904610115 - maxCpuPerSandbox: 5.962133916683182 - maxDiskPerSandbox: 2.3021358869347655 - maxMemoryPerSandbox: 5.637376656633329 - volumeQuota: 3.616076749251911 - totalMemoryQuota: 6.027456183070403 - maxSnapshotSize: 9.301444243932576 - totalDiskQuota: 1.4658129805029452 - properties: - totalCpuQuota: - type: number - totalMemoryQuota: - type: number - totalDiskQuota: - type: number - maxCpuPerSandbox: - type: number - maxMemoryPerSandbox: - type: number - maxDiskPerSandbox: - type: number - snapshotQuota: - type: number - maxSnapshotSize: - type: number - volumeQuota: - type: number - type: object - CreateUser: - example: - emailVerified: true - role: admin - name: name - personalOrganizationDefaultRegionId: personalOrganizationDefaultRegionId - id: id - personalOrganizationQuota: - snapshotQuota: 7.061401241503109 - totalCpuQuota: 0.8008281904610115 - maxCpuPerSandbox: 5.962133916683182 - maxDiskPerSandbox: 2.3021358869347655 - maxMemoryPerSandbox: 5.637376656633329 - volumeQuota: 3.616076749251911 - totalMemoryQuota: 6.027456183070403 - maxSnapshotSize: 9.301444243932576 - totalDiskQuota: 1.4658129805029452 - email: email - properties: - id: - type: string - name: - type: string - email: - type: string - personalOrganizationQuota: - $ref: '#/components/schemas/CreateOrganizationQuota' - personalOrganizationDefaultRegionId: - type: string - role: - enum: - - admin - - user - type: string - emailVerified: - type: boolean - required: - - id - - name - type: object - AccountProvider: - example: - displayName: displayName - name: name - properties: - name: - type: string - displayName: - type: string - required: - - displayName - - name - type: object - CreateLinkedAccount: - example: - provider: provider - userId: userId - properties: - provider: - description: The authentication provider of the secondary account - type: string - userId: - description: The user ID of the secondary account - type: string - required: - - provider - - userId - type: object - SandboxState: - description: The state of the sandbox - enum: - - creating - - restoring - - destroyed - - destroying - - started - - stopped - - starting - - stopping - - error - - build_failed - - pending_build - - building_snapshot - - unknown - - pulling_snapshot - - archived - - archiving - - resizing - type: string - SandboxDesiredState: - description: The desired state of the sandbox - enum: - - destroyed - - started - - stopped - - resized - - archived - type: string - SandboxVolume: - example: - mountPath: /data - volumeId: volume123 - subpath: users/alice - properties: - volumeId: - description: The ID of the volume - example: volume123 - type: string - mountPath: - description: The mount path for the volume - example: /data - type: string - subpath: - description: "Optional subpath within the volume to mount. When specified,\ - \ only this S3 prefix will be accessible. When omitted, the entire volume\ - \ is mounted." - example: users/alice - type: string - required: - - mountPath - - volumeId - type: object - BuildInfo: - properties: - dockerfileContent: - description: The Dockerfile content used for the build - example: |- - FROM node:14 - WORKDIR /app - COPY . . - RUN npm install - CMD ["npm", "start"] - type: string - contextHashes: - description: The context hashes used for the build - example: - - hash1 - - hash2 - items: - type: string - type: array - createdAt: - description: The creation timestamp - format: date-time - type: string - updatedAt: - description: The last update timestamp - format: date-time - type: string - snapshotRef: - description: The snapshot reference - example: boxlite-ai/sandbox:latest - type: string - required: - - createdAt - - snapshotRef - - updatedAt - type: object - Sandbox: - example: - memory: 4 - buildInfo: "" - toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox - autoStopInterval: 30 - organizationId: organization123 - createdAt: 2024-10-01T12:00:00Z - networkBlockAll: false - autoDeleteInterval: 30 - public: false - errorReason: The sandbox is not running - runnerId: runner123 - id: sandbox123 - state: creating - class: small - updatedAt: 2024-10-01T12:00:00Z - desiredState: destroyed - networkAllowList: "192.168.1.0/16,10.0.0.0/24" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice - cpu: 2 - recoverable: true - env: - NODE_ENV: production - gpu: 0 - daemonVersion: 1.0.0 - backupCreatedAt: 2024-10-01T12:00:00Z - labels: - boxlite.io/public: "true" - target: local - disk: 10 - name: MySandbox - backupState: None - user: boxlite - autoArchiveInterval: 10080 - snapshot: boxlite-ai/sandbox:latest - properties: - id: - description: The ID of the sandbox - example: sandbox123 - type: string - organizationId: - description: The organization ID of the sandbox - example: organization123 - type: string - name: - description: The name of the sandbox - example: MySandbox - type: string - snapshot: - description: The snapshot used for the sandbox - example: boxlite-ai/sandbox:latest - type: string - user: - description: The user associated with the project - example: boxlite - type: string - env: - additionalProperties: - type: string - description: Environment variables for the sandbox - example: - NODE_ENV: production - type: object - labels: - additionalProperties: - type: string - description: Labels for the sandbox - example: - boxlite.io/public: "true" - type: object - public: - description: Whether the sandbox http preview is public - example: false - type: boolean - networkBlockAll: - description: Whether to block all network access for the sandbox - example: false - type: boolean - networkAllowList: - description: Comma-separated list of allowed CIDR network addresses for - the sandbox - example: "192.168.1.0/16,10.0.0.0/24" - type: string - target: - description: The target environment for the sandbox - example: local - type: string - cpu: - description: The CPU quota for the sandbox - example: 2 - type: number - gpu: - description: The GPU quota for the sandbox - example: 0 - type: number - memory: - description: The memory quota for the sandbox - example: 4 - type: number - disk: - description: The disk quota for the sandbox - example: 10 - type: number - state: - allOf: - - $ref: '#/components/schemas/SandboxState' - description: The state of the sandbox - example: creating - desiredState: - allOf: - - $ref: '#/components/schemas/SandboxDesiredState' - description: The desired state of the sandbox - example: destroyed - errorReason: - description: The error reason of the sandbox - example: The sandbox is not running - type: string - recoverable: - description: Whether the sandbox error is recoverable. - example: true - type: boolean - backupState: - description: The state of the backup - enum: - - None - - Pending - - InProgress - - Completed - - Error - example: None - type: string - backupCreatedAt: - description: The creation timestamp of the last backup - example: 2024-10-01T12:00:00Z - type: string - autoStopInterval: - description: Auto-stop interval in minutes (0 means disabled) - example: 30 - type: number - autoArchiveInterval: - description: Auto-archive interval in minutes - example: 10080 - type: number - autoDeleteInterval: - description: "Auto-delete interval in minutes (negative value means disabled,\ - \ 0 means delete immediately upon stopping)" - example: 30 - type: number - volumes: - description: Array of volumes attached to the sandbox - items: - $ref: '#/components/schemas/SandboxVolume' - type: array - buildInfo: - allOf: - - $ref: '#/components/schemas/BuildInfo' - description: Build information for the sandbox - createdAt: - description: The creation timestamp of the sandbox - example: 2024-10-01T12:00:00Z - type: string - updatedAt: - description: The last update timestamp of the sandbox - example: 2024-10-01T12:00:00Z - type: string - class: - deprecated: true - description: The class of the sandbox - enum: - - small - - medium - - large - example: small - type: string - daemonVersion: - description: The version of the daemon running in the sandbox - example: 1.0.0 - type: string - runnerId: - description: The runner ID of the sandbox - example: runner123 - type: string - toolboxProxyUrl: - description: The toolbox proxy URL for the sandbox - example: https://proxy.app.boxlite.io/toolbox - type: string - required: - - cpu - - disk - - env - - gpu - - id - - labels - - memory - - name - - networkBlockAll - - organizationId - - public - - target - - toolboxProxyUrl - - user - type: object - PaginatedSandboxes: - example: - total: 0.8008281904610115 - totalPages: 1.4658129805029452 - page: 6.027456183070403 - items: - - memory: 4 - buildInfo: "" - toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox - autoStopInterval: 30 - organizationId: organization123 - createdAt: 2024-10-01T12:00:00Z - networkBlockAll: false - autoDeleteInterval: 30 - public: false - errorReason: The sandbox is not running - runnerId: runner123 - id: sandbox123 - state: creating - class: small - updatedAt: 2024-10-01T12:00:00Z - desiredState: destroyed - networkAllowList: "192.168.1.0/16,10.0.0.0/24" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice - cpu: 2 - recoverable: true - env: - NODE_ENV: production - gpu: 0 - daemonVersion: 1.0.0 - backupCreatedAt: 2024-10-01T12:00:00Z - labels: - boxlite.io/public: "true" - target: local - disk: 10 - name: MySandbox - backupState: None - user: boxlite - autoArchiveInterval: 10080 - snapshot: boxlite-ai/sandbox:latest - - memory: 4 - buildInfo: "" - toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox - autoStopInterval: 30 - organizationId: organization123 - createdAt: 2024-10-01T12:00:00Z - networkBlockAll: false - autoDeleteInterval: 30 - public: false - errorReason: The sandbox is not running - runnerId: runner123 - id: sandbox123 - state: creating - class: small - updatedAt: 2024-10-01T12:00:00Z - desiredState: destroyed - networkAllowList: "192.168.1.0/16,10.0.0.0/24" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice - cpu: 2 - recoverable: true - env: - NODE_ENV: production - gpu: 0 - daemonVersion: 1.0.0 - backupCreatedAt: 2024-10-01T12:00:00Z - labels: - boxlite.io/public: "true" - target: local - disk: 10 - name: MySandbox - backupState: None - user: boxlite - autoArchiveInterval: 10080 - snapshot: boxlite-ai/sandbox:latest + required: + - audience + - clientId + - issuer + type: object + RateLimitEntry: properties: - items: - items: - $ref: '#/components/schemas/Sandbox' - type: array - total: - type: number - page: + ttl: + description: Rate limit TTL in seconds + example: 60 type: number - totalPages: + limit: + description: Rate limit max requests + example: 100 type: number - required: - - items - - page - - total - - totalPages type: object - CreateBuildInfo: + RateLimitConfig: properties: - dockerfileContent: - description: The Dockerfile content used for the build - example: |- - FROM node:14 - WORKDIR /app - COPY . . - RUN npm install - CMD ["npm", "start"] - type: string - contextHashes: - description: The context hashes used for the build - example: - - hash1 - - hash2 - items: - type: string - type: array - required: - - dockerfileContent + failedAuth: + allOf: + - $ref: "#/components/schemas/RateLimitEntry" + description: Failed authentication rate limit + authenticated: + allOf: + - $ref: "#/components/schemas/RateLimitEntry" + description: Authenticated rate limit + boxCreate: + allOf: + - $ref: "#/components/schemas/RateLimitEntry" + description: Box create rate limit + boxLifecycle: + allOf: + - $ref: "#/components/schemas/RateLimitEntry" + description: Box lifecycle rate limit type: object - CreateSandbox: + BoxliteConfiguration: example: - memory: 1 - buildInfo: "" - networkAllowList: "192.168.1.0/16,10.0.0.0/24" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice - cpu: 2 - env: - NODE_ENV: production - gpu: 1 - autoStopInterval: 30 - labels: - boxlite.io/public: "true" - target: us - networkBlockAll: false - disk: 3 - autoDeleteInterval: 30 - public: false - name: MySandbox - user: boxlite - class: small - autoArchiveInterval: 10080 - snapshot: ubuntu-4vcpu-8ram-100gb + version: 0.0.1 + posthog: "" + oidc: "" + linkedAccountsEnabled: true + announcements: + feature-update: + text: New feature available! + learnMoreUrl: https://example.com + pylonAppId: pylon-app-123 + proxyTemplateUrl: "https://{{PORT}}-{{boxId}}.proxy.example.com" + proxyToolboxUrl: https://proxy.example.com/toolbox + dashboardUrl: https://dashboard.example.com + maintananceMode: false + environment: production + billingApiUrl: https://billing.example.com + analyticsApiUrl: https://analytics.example.com + sshGatewayCommand: "ssh -p 2222 {{TOKEN}}@localhost" + sshGatewayPublicKey: ssh-gateway-public-key + rateLimit: "" properties: - name: - description: "The name of the sandbox. If not provided, the sandbox ID will\ - \ be used as the name" - example: MySandbox - type: string - snapshot: - description: The ID or name of the snapshot used for the sandbox - example: ubuntu-4vcpu-8ram-100gb - type: string - user: - description: The user associated with the project - example: boxlite - type: string - env: - additionalProperties: - type: string - description: Environment variables for the sandbox - example: - NODE_ENV: production - type: object - labels: - additionalProperties: - type: string - description: Labels for the sandbox - example: - boxlite.io/public: "true" - type: object - public: - description: Whether the sandbox http preview is publicly accessible - example: false - type: boolean - networkBlockAll: - description: Whether to block all network access for the sandbox - example: false - type: boolean - networkAllowList: - description: Comma-separated list of allowed CIDR network addresses for - the sandbox - example: "192.168.1.0/16,10.0.0.0/24" - type: string - class: - description: The sandbox class type - enum: - - small - - medium - - large - example: small - type: string - target: - description: The target (region) where the sandbox will be created - example: us + version: + description: BoxLite version + example: 0.0.1 type: string - cpu: - description: CPU cores allocated to the sandbox - example: 2 - type: integer - gpu: - description: GPU units allocated to the sandbox - example: 1 - type: integer - memory: - description: Memory allocated to the sandbox in GB - example: 1 - type: integer - disk: - description: Disk space allocated to the sandbox in GB - example: 3 - type: integer - autoStopInterval: - description: Auto-stop interval in minutes (0 means disabled) - example: 30 - type: integer - autoArchiveInterval: - description: Auto-archive interval in minutes (0 means the maximum interval - will be used) - example: 10080 - type: integer - autoDeleteInterval: - description: "Auto-delete interval in minutes (negative value means disabled,\ - \ 0 means delete immediately upon stopping)" - example: 30 - type: integer - volumes: - description: Array of volumes to attach to the sandbox - items: - $ref: '#/components/schemas/SandboxVolume' - type: array - buildInfo: + posthog: allOf: - - $ref: '#/components/schemas/CreateBuildInfo' - description: Build information for the sandbox - type: object - ResizeSandbox: - example: - disk: 20 - memory: 4 - cpu: 2 - properties: - cpu: - description: "CPU cores to allocate to the sandbox (minimum: 1)" - example: 2 - minimum: 1 - type: integer - memory: - description: "Memory in GB to allocate to the sandbox (minimum: 1)" - example: 4 - minimum: 1 - type: integer - disk: - description: Disk space in GB to allocate to the sandbox (can only be increased) - example: 20 - minimum: 1 - type: integer - type: object - SandboxLabels: - example: - labels: - environment: dev - team: backend - properties: - labels: + - $ref: "#/components/schemas/PosthogConfig" + description: PostHog configuration + oidc: + allOf: + - $ref: "#/components/schemas/OidcConfig" + description: OIDC configuration + linkedAccountsEnabled: + description: Whether linked accounts are enabled + example: true + type: boolean + announcements: additionalProperties: - type: string - description: Key-value pairs of labels + $ref: "#/components/schemas/Announcement" + description: System announcements example: - environment: dev - team: backend + feature-update: + text: New feature available! + learnMoreUrl: https://example.com type: object - required: - - labels - type: object - UpdateSandboxStateDto: - example: - errorReason: Failed to pull snapshot image - recoverable: true - state: started - properties: - state: - description: The new state for the sandbox - enum: - - creating - - restoring - - destroyed - - destroying - - started - - stopped - - starting - - stopping - - error - - build_failed - - pending_build - - building_snapshot - - unknown - - pulling_snapshot - - archived - - archiving - - resizing - example: started + pylonAppId: + description: Pylon application ID + example: pylon-app-123 type: string - errorReason: - description: Optional error message when reporting an error state - example: Failed to pull snapshot image + proxyTemplateUrl: + description: Proxy template URL + example: "https://{{PORT}}-{{boxId}}.proxy.example.com" + type: string + proxyToolboxUrl: + description: Toolbox template URL + example: https://proxy.example.com/toolbox type: string - recoverable: - description: Whether the sandbox is recoverable - example: true - type: boolean - required: - - state - type: object - PortPreviewUrl: - example: - sandboxId: "123456" - url: "https://{port}-{sandboxId}.{proxyDomain}" - token: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l - properties: - sandboxId: - description: ID of the sandbox - example: "123456" + dashboardUrl: + description: Dashboard URL + example: https://dashboard.example.com type: string - url: - description: Preview url - example: "https://{port}-{sandboxId}.{proxyDomain}" + maintananceMode: + description: Whether maintenance mode is enabled + example: false + type: boolean + environment: + description: Current environment + example: production type: string - token: - description: Access token - example: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l + billingApiUrl: + description: Billing API URL + example: https://billing.example.com type: string - required: - - sandboxId - - token - - url - type: object - SignedPortPreviewUrl: - example: - port: 3000 - sandboxId: "123456" - url: "https://{port}-{token}.{proxyDomain}" - token: jl6wb9z5o3eii - properties: - sandboxId: - description: ID of the sandbox - example: "123456" + analyticsApiUrl: + description: Analytics API URL + example: https://analytics.example.com type: string - port: - description: Port number of the signed preview URL - example: 3000 - type: integer - token: - description: Token of the signed preview URL - example: jl6wb9z5o3eii + sshGatewayCommand: + description: SSH Gateway command + example: "ssh -p 2222 {{TOKEN}}@localhost" type: string - url: - description: Signed preview url - example: "https://{port}-{token}.{proxyDomain}" + sshGatewayPublicKey: + description: Base64 encoded SSH Gateway public key + example: ssh-gateway-public-key type: string + rateLimit: + allOf: + - $ref: "#/components/schemas/RateLimitConfig" + description: Rate limit configuration required: - - port - - sandboxId - - token - - url + - announcements + - dashboardUrl + - environment + - linkedAccountsEnabled + - maintananceMode + - oidc + - proxyTemplateUrl + - proxyToolboxUrl + - version type: object - Url: + CreateApiKey: example: - url: url + name: My API Key + permissions: + - write:registries + - write:registries + expiresAt: 2025-06-09T12:00:00Z properties: - url: - description: URL response + name: + description: The name of the API key + example: My API Key + type: string + permissions: + description: The list of organization resource permissions explicitly assigned + to the API key + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array + expiresAt: + description: When the API key expires + example: 2025-06-09T12:00:00Z + format: date-time + nullable: true type: string required: - - url + - name + - permissions type: object - SshAccessDto: + ApiKeyResponse: example: - createdAt: 2025-01-01T11:00:00Z - sshCommand: ssh -p 2222 token@localhost - sandboxId: 123e4567-e89b-12d3-a456-426614174000 - id: 123e4567-e89b-12d3-a456-426614174000 - expiresAt: 2025-01-01T12:00:00Z - token: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz - updatedAt: 2025-01-01T11:00:00Z + name: My API Key + value: bb_sk_1234567890abcdef + createdAt: 2024-03-14T12:00:00Z + permissions: + - write:registries + - write:registries + expiresAt: 2025-06-09T12:00:00Z properties: - id: - description: Unique identifier for the SSH access - example: 123e4567-e89b-12d3-a456-426614174000 - type: string - sandboxId: - description: ID of the sandbox this SSH access is for - example: 123e4567-e89b-12d3-a456-426614174000 - type: string - token: - description: SSH access token - example: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz + name: + description: The name of the API key + example: My API Key type: string - expiresAt: - description: When the SSH access expires - example: 2025-01-01T12:00:00Z - format: date-time + value: + description: The API key value + example: bb_sk_1234567890abcdef type: string createdAt: - description: When the SSH access was created - example: 2025-01-01T11:00:00Z + description: When the API key was created + example: 2024-03-14T12:00:00Z format: date-time type: string - updatedAt: - description: When the SSH access was last updated - example: 2025-01-01T11:00:00Z + permissions: + description: The list of organization resource permissions assigned to the + API key + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array + expiresAt: + description: When the API key expires + example: 2025-06-09T12:00:00Z format: date-time - type: string - sshCommand: - description: SSH command to connect to the sandbox - example: ssh -p 2222 token@localhost + nullable: true type: string required: - createdAt - expiresAt - - id - - sandboxId - - sshCommand - - token - - updatedAt + - name + - permissions + - value type: object - SshAccessValidationDto: + ApiKeyList: example: - valid: true - sandboxId: 123e4567-e89b-12d3-a456-426614174000 + name: My API Key + value: bb_********************def + createdAt: 2024-03-14T12:00:00Z + permissions: + - write:registries + - write:registries + lastUsedAt: 2024-03-14T12:00:00Z + expiresAt: 2024-03-14T12:00:00Z + userId: "123" properties: - valid: - description: Whether the SSH access token is valid - example: true - type: boolean - sandboxId: - description: ID of the sandbox this SSH access is for - example: 123e4567-e89b-12d3-a456-426614174000 + name: + description: The name of the API key + example: My API Key type: string - required: - - sandboxId - - valid - type: object - ToolboxProxyUrl: - example: - url: https://proxy.app.boxlite.io/toolbox - properties: - url: - description: The toolbox proxy URL for the sandbox - example: https://proxy.app.boxlite.io/toolbox + value: + description: The masked API key value + example: bb_********************def type: string - required: - - url - type: object - CreateRunner: - example: - regionId: regionId - name: name - properties: - regionId: + createdAt: + description: When the API key was created + example: 2024-03-14T12:00:00Z + format: date-time + type: string + permissions: + description: The list of organization resource permissions assigned to the + API key + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array + lastUsedAt: + description: When the API key was last used + example: 2024-03-14T12:00:00Z + format: date-time + nullable: true + type: string + expiresAt: + description: When the API key expires + example: 2024-03-14T12:00:00Z + format: date-time + nullable: true type: string - name: + userId: + description: The user ID of the user who created the API key + example: "123" type: string required: + - createdAt + - expiresAt + - lastUsedAt - name - - regionId + - permissions + - userId + - value type: object - CreateRunnerResponse: + OrganizationRole: example: - apiKey: dtn_1234567890 - id: runner123 + id: id + name: name + description: description + permissions: + - write:registries + - write:registries + isGlobal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 properties: id: - description: The ID of the runner - example: runner123 + description: Role ID type: string - apiKey: - description: The API key for the runner - example: dtn_1234567890 + name: + description: Role name + type: string + description: + description: Role description + type: string + permissions: + description: Roles assigned to the user + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array + isGlobal: + description: Global role flag + type: boolean + createdAt: + description: Creation timestamp + format: date-time + type: string + updatedAt: + description: Last update timestamp + format: date-time type: string required: - - apiKey + - createdAt + - description - id + - isGlobal + - name + - permissions + - updatedAt type: object - SandboxClass: - description: The class of the runner - enum: - - small - - medium - - large - type: string - RunnerState: - description: The state of the runner - enum: - - initializing - - ready - - disabled - - decommissioned - - unresponsive - type: string - RunnerFull: + OrganizationInvitation: example: - currentStartedSandboxes: 5 - appVersion: v0.0.0-dev - memory: 16 - currentMemoryUsagePercentage: 68.2 - apiKey: dtn_1234567890 - availabilityScore: 85 - currentDiskUsagePercentage: 33.8 - currentCpuUsagePercentage: 45.6 - createdAt: 2023-10-01T12:00:00Z - apiVersion: "0" - apiUrl: https://api.runner1.example.com - regionType: shared - id: runner123 - state: initializing - unschedulable: false - class: small - currentAllocatedDiskGiB: 50000 - currentAllocatedMemoryGiB: 8000 - gpuType: gpuType - currentAllocatedCpu: 4000 - updatedAt: 2023-10-01T12:00:00Z - proxyUrl: https://proxy.runner1.example.com - cpu: 8 - gpu: 1 - currentSnapshotCount: 12 - version: "0" - disk: 100 - domain: runner1.example.com - name: runner1 - region: us - lastChecked: 2024-10-01T12:00:00Z + id: id + email: email + invitedBy: invitedBy + organizationId: organizationId + organizationName: organizationName + expiresAt: 2000-01-23T04:56:07.000+00:00 + status: pending + role: owner + assignedRoles: + - id: id + name: name + description: description + permissions: + - write:registries + - write:registries + isGlobal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 + - id: id + name: name + description: description + permissions: + - write:registries + - write:registries + isGlobal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 properties: id: - description: The ID of the runner - example: runner123 - type: string - domain: - description: The domain of the runner - example: runner1.example.com - type: string - apiUrl: - description: The API URL of the runner - example: https://api.runner1.example.com - type: string - proxyUrl: - description: The proxy URL of the runner - example: https://proxy.runner1.example.com - type: string - cpu: - description: The CPU capacity of the runner - example: 8 - type: number - memory: - description: The memory capacity of the runner in GiB - example: 16 - type: number - disk: - description: The disk capacity of the runner in GiB - example: 100 - type: number - gpu: - description: The GPU capacity of the runner - example: 1 - type: number - gpuType: - description: The type of GPU + description: Invitation ID type: string - class: - allOf: - - $ref: '#/components/schemas/SandboxClass' - description: The class of the runner - example: small - currentCpuUsagePercentage: - description: Current CPU usage percentage - example: 45.6 - type: number - currentMemoryUsagePercentage: - description: Current RAM usage percentage - example: 68.2 - type: number - currentDiskUsagePercentage: - description: Current disk usage percentage - example: 33.8 - type: number - currentAllocatedCpu: - description: Current allocated CPU - example: 4000 - type: number - currentAllocatedMemoryGiB: - description: Current allocated memory in GiB - example: 8000 - type: number - currentAllocatedDiskGiB: - description: Current allocated disk in GiB - example: 50000 - type: number - currentSnapshotCount: - description: Current snapshot count - example: 12 - type: number - currentStartedSandboxes: - description: Current number of started sandboxes - example: 5 - type: number - availabilityScore: - description: Runner availability score - example: 85 - type: number - region: - description: The region of the runner - example: us + email: + description: Email address of the invitee type: string - name: - description: The name of the runner - example: runner1 + invitedBy: + description: Email address of the inviter type: string - state: - allOf: - - $ref: '#/components/schemas/RunnerState' - description: The state of the runner - example: initializing - lastChecked: - description: The last time the runner was checked - example: 2024-10-01T12:00:00Z + organizationId: + description: Organization ID type: string - unschedulable: - description: Whether the runner is unschedulable - example: false - type: boolean - createdAt: - description: The creation timestamp of the runner - example: 2023-10-01T12:00:00Z + organizationName: + description: Organization name type: string - updatedAt: - description: The last update timestamp of the runner - example: 2023-10-01T12:00:00Z + expiresAt: + description: Expiration date of the invitation + format: date-time type: string - version: - deprecated: true - description: The version of the runner (deprecated in favor of apiVersion) - example: "0" + status: + description: Invitation status + enum: + - pending + - accepted + - declined + - cancelled type: string - apiVersion: - deprecated: true - description: The api version of the runner - example: "0" + role: + description: Member role + enum: + - owner + - member type: string - appVersion: - deprecated: true - description: The app version of the runner - example: v0.0.0-dev + assignedRoles: + description: Assigned roles + items: + $ref: "#/components/schemas/OrganizationRole" + type: array + createdAt: + description: Creation timestamp + format: date-time type: string - apiKey: - description: The API key for the runner - example: dtn_1234567890 + updatedAt: + description: Last update timestamp + format: date-time type: string - regionType: - allOf: - - $ref: '#/components/schemas/RegionType' - description: The region type of the runner - example: shared required: - - apiKey - - apiVersion - - class - - cpu + - assignedRoles - createdAt - - disk + - email + - expiresAt - id - - memory - - name - - region - - state - - unschedulable + - invitedBy + - organizationId + - organizationName + - role + - status - updatedAt - - version type: object - RunnerSnapshotDto: + CreateOrganization: example: - runnerDomain: runner.example.com - runnerId: 123e4567-e89b-12d3-a456-426614174000 - runnerSnapshotId: 123e4567-e89b-12d3-a456-426614174000 + name: My Organization + defaultRegionId: us properties: - runnerSnapshotId: - description: Runner snapshot ID - example: 123e4567-e89b-12d3-a456-426614174000 - type: string - runnerId: - description: Runner ID - example: 123e4567-e89b-12d3-a456-426614174000 + name: + description: The name of organization + example: My Organization type: string - runnerDomain: - description: Runner domain - example: runner.example.com + defaultRegionId: + description: The ID of the default region for the organization + example: us type: string required: - - runnerId - - runnerSnapshotId + - defaultRegionId + - name type: object - Runner: + Organization: example: - currentStartedSandboxes: 5 - appVersion: v0.0.0-dev - memory: 16 - currentMemoryUsagePercentage: 68.2 - availabilityScore: 85 - currentDiskUsagePercentage: 33.8 - currentCpuUsagePercentage: 45.6 - createdAt: 2023-10-01T12:00:00Z - apiVersion: "0" - apiUrl: https://api.runner1.example.com - id: runner123 - state: initializing - unschedulable: false - class: small - currentAllocatedDiskGiB: 50000 - currentAllocatedMemoryGiB: 8000 - gpuType: gpuType - currentAllocatedCpu: 4000 - updatedAt: 2023-10-01T12:00:00Z - proxyUrl: https://proxy.runner1.example.com - cpu: 8 - gpu: 1 - currentSnapshotCount: 12 - version: "0" - disk: 100 - domain: runner1.example.com - name: runner1 - region: us - lastChecked: 2024-10-01T12:00:00Z + id: id + name: name + createdBy: createdBy + isDefaultForAuthenticatedUser: true + personal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 + suspended: true + suspendedAt: 2000-01-23T04:56:07.000+00:00 + suspensionReason: suspensionReason + suspendedUntil: 2000-01-23T04:56:07.000+00:00 + suspensionCleanupGracePeriodHours: 0.8008281904610115 + maxCpuPerBox: 6.027456183070403 + maxMemoryPerBox: 1.4658129805029452 + maxDiskPerBox: 5.962133916683182 + templateDeactivationTimeoutMinutes: 5.637376656633329 + boxLimitedNetworkEgress: true + defaultRegionId: defaultRegionId + authenticatedRateLimit: 2.3021358869347655 + boxCreateRateLimit: 7.061401241503109 + boxLifecycleRateLimit: 9.301444243932576 + experimentalConfig: "{}" + authenticatedRateLimitTtlSeconds: 3.616076749251911 + boxCreateRateLimitTtlSeconds: 2.027123023002322 + boxLifecycleRateLimitTtlSeconds: 4.145608029883936 properties: id: - description: The ID of the runner - example: runner123 - type: string - domain: - description: The domain of the runner - example: runner1.example.com - type: string - apiUrl: - description: The API URL of the runner - example: https://api.runner1.example.com - type: string - proxyUrl: - description: The proxy URL of the runner - example: https://proxy.runner1.example.com - type: string - cpu: - description: The CPU capacity of the runner - example: 8 - type: number - memory: - description: The memory capacity of the runner in GiB - example: 16 - type: number - disk: - description: The disk capacity of the runner in GiB - example: 100 - type: number - gpu: - description: The GPU capacity of the runner - example: 1 - type: number - gpuType: - description: The type of GPU - type: string - class: - allOf: - - $ref: '#/components/schemas/SandboxClass' - description: The class of the runner - example: small - currentCpuUsagePercentage: - description: Current CPU usage percentage - example: 45.6 - type: number - currentMemoryUsagePercentage: - description: Current RAM usage percentage - example: 68.2 - type: number - currentDiskUsagePercentage: - description: Current disk usage percentage - example: 33.8 - type: number - currentAllocatedCpu: - description: Current allocated CPU - example: 4000 - type: number - currentAllocatedMemoryGiB: - description: Current allocated memory in GiB - example: 8000 - type: number - currentAllocatedDiskGiB: - description: Current allocated disk in GiB - example: 50000 - type: number - currentSnapshotCount: - description: Current snapshot count - example: 12 - type: number - currentStartedSandboxes: - description: Current number of started sandboxes - example: 5 - type: number - availabilityScore: - description: Runner availability score - example: 85 - type: number - region: - description: The region of the runner - example: us + description: Organization ID type: string name: - description: The name of the runner - example: runner1 + description: Organization name type: string - state: - allOf: - - $ref: '#/components/schemas/RunnerState' - description: The state of the runner - example: initializing - lastChecked: - description: The last time the runner was checked - example: 2024-10-01T12:00:00Z + createdBy: + description: User ID of the organization creator type: string - unschedulable: - description: Whether the runner is unschedulable - example: false + isDefaultForAuthenticatedUser: + description: Whether this organization is the authenticated user default + organization + type: boolean + personal: + deprecated: true + description: Deprecated alias for isDefaultForAuthenticatedUser. Kept for + backward compatibility with older REST clients. type: boolean createdAt: - description: The creation timestamp of the runner - example: 2023-10-01T12:00:00Z + description: Creation timestamp + format: date-time type: string updatedAt: - description: The last update timestamp of the runner - example: 2023-10-01T12:00:00Z + description: Last update timestamp + format: date-time type: string - version: - deprecated: true - description: The version of the runner (deprecated in favor of apiVersion) - example: "0" + suspended: + description: Suspended flag + type: boolean + suspendedAt: + description: Suspended at + format: date-time type: string - apiVersion: - deprecated: true - description: The api version of the runner - example: "0" + suspensionReason: + description: Suspended reason type: string - appVersion: - deprecated: true - description: The app version of the runner - example: v0.0.0-dev + suspendedUntil: + description: Suspended until + format: date-time type: string - required: - - apiVersion - - class - - cpu - - createdAt - - disk - - id - - memory - - name - - region - - state - - unschedulable - - updatedAt - - version - type: object - RunnerHealthMetrics: - properties: - currentCpuLoadAverage: - description: Current CPU load average - example: 0.98 - type: number - currentCpuUsagePercentage: - description: Current CPU usage percentage - example: 45.5 + suspensionCleanupGracePeriodHours: + description: Suspension cleanup grace period hours type: number - currentMemoryUsagePercentage: - description: Current memory usage percentage - example: 60.2 + maxCpuPerBox: + description: Max CPU per box type: number - currentDiskUsagePercentage: - description: Current disk usage percentage - example: 35.8 + maxMemoryPerBox: + description: Max memory per box type: number - currentAllocatedCpu: - description: Currently allocated CPU cores - example: 8 + maxDiskPerBox: + description: Max disk per box type: number - currentAllocatedMemoryGiB: - description: Currently allocated memory in GiB - example: 16 + templateDeactivationTimeoutMinutes: + default: 20160 + description: Time in minutes before an unused template is deactivated type: number - currentAllocatedDiskGiB: - description: Currently allocated disk in GiB - example: 100 + boxLimitedNetworkEgress: + description: Box default network block all + type: boolean + defaultRegionId: + description: Default region ID + type: string + authenticatedRateLimit: + description: Authenticated rate limit per minute + nullable: true type: number - currentSnapshotCount: - description: Number of snapshots currently stored - example: 5 + boxCreateRateLimit: + description: Box create rate limit per minute + nullable: true type: number - currentStartedSandboxes: - description: Number of started sandboxes - example: 10 + boxLifecycleRateLimit: + description: Box lifecycle rate limit per minute + nullable: true type: number - cpu: - description: Total CPU cores on the runner - example: 8 + experimentalConfig: + description: Experimental configuration + type: object + authenticatedRateLimitTtlSeconds: + description: Authenticated rate limit TTL in seconds + nullable: true type: number - memoryGiB: - description: Total RAM in GiB on the runner - example: 16 + boxCreateRateLimitTtlSeconds: + description: Box create rate limit TTL in seconds + nullable: true type: number - diskGiB: - description: Total disk space in GiB on the runner - example: 100 + boxLifecycleRateLimitTtlSeconds: + description: Box lifecycle rate limit TTL in seconds + nullable: true type: number required: - - cpu - - currentAllocatedCpu - - currentAllocatedDiskGiB - - currentAllocatedMemoryGiB - - currentCpuLoadAverage - - currentCpuUsagePercentage - - currentDiskUsagePercentage - - currentMemoryUsagePercentage - - currentSnapshotCount - - currentStartedSandboxes - - diskGiB - - memoryGiB + - authenticatedRateLimit + - authenticatedRateLimitTtlSeconds + - boxCreateRateLimit + - boxCreateRateLimitTtlSeconds + - boxLifecycleRateLimit + - boxLifecycleRateLimitTtlSeconds + - boxLimitedNetworkEgress + - createdAt + - createdBy + - experimentalConfig + - id + - isDefaultForAuthenticatedUser + - maxCpuPerBox + - maxDiskPerBox + - maxMemoryPerBox + - name + - personal + - suspended + - suspendedAt + - suspendedUntil + - suspensionCleanupGracePeriodHours + - suspensionReason + - templateDeactivationTimeoutMinutes + - updatedAt type: object - RunnerServiceHealth: + UpdateOrganizationName: example: - healthy: false - errorReason: Cannot connect to the runner - serviceName: runner + name: Default Organization properties: - serviceName: - description: Name of the service being checked - example: runner + name: + description: The public name of the organization + example: Default Organization type: string - healthy: - description: Whether the service is healthy - example: false - type: boolean - errorReason: - description: Error reason if the service is unhealthy - example: Cannot connect to the runner + required: + - name + type: object + UpdateOrganizationDefaultRegion: + example: + defaultRegionId: us + properties: + defaultRegionId: + description: The ID of the default region for the organization + example: us type: string required: - - healthy - - serviceName + - defaultRegionId type: object - RunnerHealthcheck: + OrganizationSuspension: example: - appVersion: v0.0.0-dev - apiUrl: http://api.boxlite.example.com:8080 - proxyUrl: http://proxy.boxlite.example.com:8080 - domain: runner-123.boxlite.example.com - serviceHealth: - - healthy: false - errorReason: Cannot connect to the runner - serviceName: runner - - healthy: false - errorReason: Cannot connect to the runner - serviceName: runner - metrics: "" + reason: reason + until: 2000-01-23T04:56:07.000+00:00 + suspensionCleanupGracePeriodHours: 0.08008281904610115 properties: - metrics: - allOf: - - $ref: '#/components/schemas/RunnerHealthMetrics' - description: Runner metrics - serviceHealth: - description: Health status of individual services on the runner - items: - $ref: '#/components/schemas/RunnerServiceHealth' - type: array - domain: - description: Runner domain - example: runner-123.boxlite.example.com + reason: + description: Suspension reason type: string - proxyUrl: - description: Runner proxy URL - example: http://proxy.boxlite.example.com:8080 + until: + description: Suspension until + format: date-time type: string - apiUrl: - description: Runner API URL - example: http://api.boxlite.example.com:8080 + suspensionCleanupGracePeriodHours: + description: Suspension cleanup grace period hours + minimum: 0 + type: number + required: + - reason + - until + type: object + OtelConfig: + example: + endpoint: endpoint + headers: + x-api-key: my-api-key + properties: + endpoint: + description: Endpoint type: string - appVersion: - description: Runner app version - example: v0.0.0-dev + headers: + additionalProperties: + type: string + description: Headers + example: + x-api-key: my-api-key + nullable: true + type: object + required: + - endpoint + type: object + OrganizationBoxDefaultLimitedNetworkEgress: + example: + boxDefaultLimitedNetworkEgress: true + properties: + boxDefaultLimitedNetworkEgress: + description: Box default limited network egress + type: boolean + required: + - boxDefaultLimitedNetworkEgress + type: object + CreateOrganizationRole: + example: + name: Maintainer + description: Can manage all resources + permissions: + - write:registries + - write:registries + properties: + name: + description: The name of the role + example: Maintainer + type: string + description: + description: The description of the role + example: Can manage all resources type: string + permissions: + description: The list of permissions assigned to the role + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array required: - - appVersion + - description + - name + - permissions type: object - ProjectDirResponse: + UpdateOrganizationRole: example: - dir: dir + name: Maintainer + description: Can manage all resources + permissions: + - write:registries + - write:registries properties: - dir: + name: + description: The name of the role + example: Maintainer + type: string + description: + description: The description of the role + example: Can manage all resources type: string + permissions: + description: The list of permissions assigned to the role + items: + enum: + - write:registries + - delete:registries + - write:templates + - delete:templates + - write:boxes + - delete:boxes + - read:volumes + - write:volumes + - delete:volumes + - write:regions + - delete:regions + - read:runners + - write:runners + - delete:runners + - read:audit_logs + type: string + type: array + required: + - description + - name + - permissions type: object - UserHomeDirResponse: + OrganizationUser: example: - dir: dir + userId: userId + organizationId: organizationId + name: name + email: email + role: owner + isDefaultForUser: true + assignedRoles: + - id: id + name: name + description: description + permissions: + - write:registries + - write:registries + isGlobal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 + - id: id + name: name + description: description + permissions: + - write:registries + - write:registries + isGlobal: true + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 + createdAt: 2000-01-23T04:56:07.000+00:00 + updatedAt: 2000-01-23T04:56:07.000+00:00 properties: - dir: + userId: + description: User ID type: string - type: object - WorkDirResponse: - example: - dir: dir - properties: - dir: + organizationId: + description: Organization ID type: string - type: object - FileInfo: - example: - mode: mode - owner: owner - size: 0.8008281904610115 - modTime: modTime - permissions: permissions - name: name - isDir: true - group: group - properties: name: + description: User name type: string - isDir: - type: boolean - size: - type: number - modTime: - type: string - mode: + email: + description: User email type: string - permissions: + role: + description: Member role + enum: + - owner + - member type: string - owner: + isDefaultForUser: + description: Whether this organization membership is the user default organization + type: boolean + assignedRoles: + description: Roles assigned to the user + items: + $ref: "#/components/schemas/OrganizationRole" + type: array + createdAt: + description: Creation timestamp + format: date-time type: string - group: + updatedAt: + description: Last update timestamp + format: date-time type: string required: - - group - - isDir - - modTime - - mode + - assignedRoles + - createdAt + - email + - isDefaultForUser - name - - owner - - permissions - - size + - organizationId + - role + - updatedAt + - userId type: object - DownloadFiles: + UpdateOrganizationMemberAccess: example: - paths: - - paths - - paths + role: member + assignedRoleIds: + - assignedRoleIds + - assignedRoleIds properties: - paths: - description: List of remote file paths to download + role: + default: member + description: Organization member role + enum: + - owner + - member + type: string + assignedRoleIds: + default: [] + description: Array of assigned role IDs items: type: string type: array required: - - paths + - assignedRoleIds + - role type: object - Match: + CreateOrganizationInvitation: example: - file: file - line: 0.8008281904610115 - content: content + email: mail@example.com + role: member + assignedRoleIds: + - assignedRoleIds + - assignedRoleIds + expiresAt: 2021-12-31T23:59:59Z properties: - file: + email: + description: Email address of the invitee + example: mail@example.com type: string - line: - type: number - content: + role: + default: member + description: Organization member role for the invitee + enum: + - owner + - member + type: string + assignedRoleIds: + default: [] + description: Array of assigned role IDs for the invitee + items: + type: string + type: array + expiresAt: + description: Expiration date of the invitation + example: 2021-12-31T23:59:59Z + format: date-time type: string required: - - content - - file - - line + - assignedRoleIds + - email + - role type: object - ReplaceRequest: + UpdateOrganizationInvitation: example: - newValue: newValue - pattern: pattern - files: - - files - - files + role: owner + assignedRoleIds: + - assignedRoleIds + - assignedRoleIds + expiresAt: 2021-12-31T23:59:59Z properties: - files: + role: + description: Organization member role + enum: + - owner + - member + type: string + assignedRoleIds: + description: Array of role IDs items: type: string type: array - pattern: - type: string - newValue: + expiresAt: + description: Expiration date of the invitation + example: 2021-12-31T23:59:59Z + format: date-time type: string required: - - files - - newValue - - pattern + - assignedRoleIds + - role type: object - ReplaceResult: + RegionType: + description: The type of the region + enum: + - shared + - dedicated + - custom + type: string + Region: example: - file: file - success: true - error: error + id: "123456789012" + name: us-east-1 + organizationId: 123e4567-e89b-12d3-a456-426614174000 + regionType: shared + createdAt: 2023-01-01T00:00:00.000Z + updatedAt: 2023-01-01T00:00:00.000Z + proxyUrl: https://proxy.example.com + sshGatewayUrl: http://ssh-gateway.example.com properties: - file: + id: + description: Region ID + example: "123456789012" type: string - success: - type: boolean - error: + name: + description: Region name + example: us-east-1 + type: string + organizationId: + description: Organization ID + example: 123e4567-e89b-12d3-a456-426614174000 + nullable: true + type: string + regionType: + allOf: + - $ref: "#/components/schemas/RegionType" + description: The type of the region + example: shared + createdAt: + description: Creation timestamp + example: 2023-01-01T00:00:00.000Z + type: string + updatedAt: + description: Last update timestamp + example: 2023-01-01T00:00:00.000Z + type: string + proxyUrl: + description: Proxy URL for the region + example: https://proxy.example.com + nullable: true + type: string + sshGatewayUrl: + description: SSH Gateway URL for the region + example: http://ssh-gateway.example.com + nullable: true type: string - type: object - SearchFilesResponse: - example: - files: - - files - - files - properties: - files: - items: - type: string - type: array required: - - files + - createdAt + - id + - name + - regionType + - updatedAt type: object - UploadFile: + CreateRegion: + example: + name: us-east-1 + proxyUrl: https://proxy.example.com + sshGatewayUrl: ssh://ssh-gateway.example.com properties: - file: - format: binary + name: + description: Region name + example: us-east-1 type: string - path: + proxyUrl: + description: Proxy URL for the region + example: https://proxy.example.com + nullable: true + type: string + sshGatewayUrl: + description: SSH Gateway URL for the region + example: ssh://ssh-gateway.example.com + nullable: true type: string required: - - file - - path + - name type: object - GitAddRequest: + CreateRegionResponse: example: - path: path - files: - - files - - files + id: region_12345 + proxyApiKey: proxy-api-key-xyz + sshGatewayApiKey: ssh-gateway-api-key-abc properties: - path: + id: + description: ID of the created region + example: region_12345 + type: string + proxyApiKey: + description: Proxy API key for the region + example: proxy-api-key-xyz + nullable: true + type: string + sshGatewayApiKey: + description: SSH Gateway API key for the region + example: ssh-gateway-api-key-abc + nullable: true type: string - files: - description: files to add (use . for all files) - items: - type: string - type: array required: - - files - - path + - id type: object - ListBranchResponse: + RegenerateApiKeyResponse: example: - branches: - - branches - - branches + apiKey: api-key-xyz123 properties: - branches: - items: - type: string - type: array + apiKey: + description: The newly generated API key + example: api-key-xyz123 + type: string required: - - branches + - apiKey type: object - GitBranchRequest: + UpdateRegion: example: - path: path - name: name + proxyUrl: https://proxy.example.com + sshGatewayUrl: ssh://ssh-gateway.example.com properties: - path: + proxyUrl: + description: Proxy URL for the region + example: https://proxy.example.com + nullable: true type: string - name: + sshGatewayUrl: + description: SSH Gateway URL for the region + example: ssh://ssh-gateway.example.com + nullable: true type: string - required: - - name - - path type: object - GitDeleteBranchRequest: + UserPublicKey: example: - path: path + key: key name: name properties: - path: + key: + description: Public key type: string name: + description: Key name type: string required: + - key - name - - path type: object - GitCloneRequest: + User: example: - path: path - password: password - branch: branch - commit_id: commit_id - url: url - username: username + id: id + name: name + email: email + publicKeys: + - key: key + name: name + - key: key + name: name + createdAt: 2000-01-23T04:56:07.000+00:00 properties: - url: - type: string - path: - type: string - username: + id: + description: User ID type: string - password: + name: + description: User name type: string - branch: + email: + description: User email type: string - commit_id: + publicKeys: + description: User public keys + items: + $ref: "#/components/schemas/UserPublicKey" + type: array + createdAt: + description: Creation timestamp + format: date-time type: string required: - - path - - url + - createdAt + - email + - id + - name + - publicKeys type: object - GitCommitRequest: + CreateUser: example: - path: path - author: author - message: message + id: id + name: name email: email - allow_empty: false + defaultOrganizationDefaultRegionId: defaultOrganizationDefaultRegionId + personalOrganizationDefaultRegionId: personalOrganizationDefaultRegionId + role: admin + emailVerified: true properties: - path: - type: string - message: + id: type: string - author: + name: type: string email: type: string - allow_empty: - default: false - description: Allow creating an empty commit when no changes are staged + defaultOrganizationDefaultRegionId: + type: string + personalOrganizationDefaultRegionId: + deprecated: true + description: Deprecated alias for defaultOrganizationDefaultRegionId. + type: string + role: + enum: + - admin + - user + type: string + emailVerified: type: boolean required: - - author - - email - - message - - path + - id + - name type: object - GitCommitResponse: + AccountProvider: example: - hash: hash + name: name + displayName: displayName properties: - hash: + name: + type: string + displayName: type: string required: - - hash + - displayName + - name type: object - GitCommitInfo: + CreateLinkedAccount: example: - author: author - message: message - hash: hash - email: email - timestamp: timestamp + provider: provider + userId: userId properties: - hash: - type: string - message: - type: string - author: - type: string - email: + provider: + description: The authentication provider of the secondary account type: string - timestamp: + userId: + description: The user ID of the secondary account type: string required: - - author - - email - - hash - - message - - timestamp + - provider + - userId type: object - GitRepoRequest: + BoxState: + description: The state of the box + enum: + - creating + - restoring + - destroyed + - destroying + - started + - stopped + - starting + - stopping + - error + - unknown + - archived + - archiving + - resizing + type: string + BoxDesiredState: + description: The desired state of the box + enum: + - destroyed + - started + - stopped + - resized + type: string + BoxVolume: example: - path: path - password: password - username: username + volumeId: volume123 + mountPath: /data + subpath: users/alice properties: - path: + volumeId: + description: The ID of the volume + example: volume123 type: string - username: + mountPath: + description: The mount path for the volume + example: /data type: string - password: + subpath: + description: "Optional subpath within the volume to mount. When specified,\ + \ only this S3 prefix will be accessible. When omitted, the entire volume\ + \ is mounted." + example: users/alice type: string required: - - path + - mountPath + - volumeId type: object - GitCheckoutRequest: + Box: example: - path: path - branch: branch + id: aB3cD4eF5gH6 + organizationId: organization123 + name: MyBox + user: boxlite + env: + NODE_ENV: production + labels: + boxlite.io/public: "true" + public: false + networkBlockAll: false + networkAllowList: "192.168.1.0/16,10.0.0.0/24" + target: local + image: boxlite/base + cpu: 2 + gpu: 0 + memory: 4 + disk: 10 + state: creating + desiredState: destroyed + errorReason: The box is not running + recoverable: true + autoStopInterval: 30 + autoDeleteInterval: 30 + volumes: + - volumeId: volume123 + mountPath: /data + subpath: users/alice + - volumeId: volume123 + mountPath: /data + subpath: users/alice + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + class: small + daemonVersion: 1.0.0 + runnerId: runner123 + toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox properties: - path: + id: + description: The public 12-character Box ID + example: aB3cD4eF5gH6 type: string - branch: + organizationId: + description: The organization ID of the box + example: organization123 type: string - required: - - branch - - path - type: object - FileStatus: - example: - extra: extra - name: name - staging: staging - worktree: worktree - properties: name: + description: The name of the box + example: MyBox type: string - staging: + user: + description: The user associated with the project + example: boxlite type: string - worktree: + env: + additionalProperties: + type: string + description: Environment variables for the box + example: + NODE_ENV: production + type: object + labels: + additionalProperties: + type: string + description: Labels for the box + example: + boxlite.io/public: "true" + type: object + public: + description: Whether the box http preview is public + example: false + type: boolean + networkBlockAll: + description: Whether to block all network access for the box + example: false + type: boolean + networkAllowList: + description: Comma-separated list of allowed CIDR network addresses for + the box + example: "192.168.1.0/16,10.0.0.0/24" type: string - extra: + target: + description: The target environment for the box + example: local type: string - required: - - extra - - name - - staging - - worktree - type: object - GitStatus: - example: - behind: 6.027456183070403 - fileStatus: - - extra: extra - name: name - staging: staging - worktree: worktree - - extra: extra - name: name - staging: staging - worktree: worktree - ahead: 0.8008281904610115 - branchPublished: true - currentBranch: currentBranch - properties: - currentBranch: + image: + description: The image used for the box + example: boxlite/base type: string - fileStatus: - items: - $ref: '#/components/schemas/FileStatus' - type: array - ahead: + cpu: + description: The CPU quota for the box + example: 2 + type: number + gpu: + description: The GPU quota for the box + example: 0 + type: number + memory: + description: The memory quota for the box + example: 4 type: number - behind: + disk: + description: The disk quota for the box + example: 10 type: number - branchPublished: + state: + allOf: + - $ref: "#/components/schemas/BoxState" + description: The state of the box + example: creating + desiredState: + allOf: + - $ref: "#/components/schemas/BoxDesiredState" + description: The desired state of the box + example: destroyed + errorReason: + description: The error reason of the box + example: The box is not running + type: string + recoverable: + description: Whether the box error is recoverable. + example: true type: boolean - required: - - currentBranch - - fileStatus - type: object - ExecuteRequest: - example: - cwd: cwd - command: command - timeout: 0.8008281904610115 - properties: - command: + autoStopInterval: + description: Auto-stop interval in minutes (0 means disabled) + example: 30 + type: number + autoDeleteInterval: + description: "Auto-delete interval in minutes (negative value means disabled,\ + \ 0 means delete immediately upon stopping)" + example: 30 + type: number + volumes: + description: Array of volumes attached to the box + items: + $ref: "#/components/schemas/BoxVolume" + type: array + createdAt: + description: The creation timestamp of the box + example: 2024-10-01T12:00:00Z type: string - cwd: - description: Current working directory + updatedAt: + description: The last update timestamp of the box + example: 2024-10-01T12:00:00Z + type: string + class: + deprecated: true + description: The class of the box + enum: + - small + - medium + - large + example: small type: string - timeout: - description: "Timeout in seconds, defaults to 10 seconds" - type: number - required: - - command - type: object - ExecuteResponse: - example: - result: Command output here - exitCode: 0 - properties: - exitCode: - description: Exit code - example: 0 - type: number - result: - description: Command output - example: Command output here + daemonVersion: + description: The version of the daemon running in the box + example: 1.0.0 type: string - required: - - exitCode - - result - type: object - Command: - example: - exitCode: 0 - id: cmd-123 - command: ls -la - properties: - id: - description: The ID of the command - example: cmd-123 + runnerId: + description: The runner ID of the box + example: runner123 type: string - command: - description: The command that was executed - example: ls -la + toolboxProxyUrl: + description: The toolbox proxy URL for the box + example: https://proxy.app.boxlite.io/toolbox type: string - exitCode: - description: The exit code of the command - example: 0 - type: number required: - - command + - cpu + - disk + - env + - gpu - id + - labels + - memory + - name + - networkBlockAll + - organizationId + - public + - target + - toolboxProxyUrl + - user type: object - Session: + PaginatedBoxes: example: - sessionId: session-123 - commands: - - exitCode: 0 - id: cmd-123 - command: ls -la - - exitCode: 0 - id: cmd-123 - command: ls -la + items: + - id: aB3cD4eF5gH6 + organizationId: organization123 + name: MyBox + user: boxlite + env: + NODE_ENV: production + labels: + boxlite.io/public: "true" + public: false + networkBlockAll: false + networkAllowList: "192.168.1.0/16,10.0.0.0/24" + target: local + image: boxlite/base + cpu: 2 + gpu: 0 + memory: 4 + disk: 10 + state: creating + desiredState: destroyed + errorReason: The box is not running + recoverable: true + autoStopInterval: 30 + autoDeleteInterval: 30 + volumes: + - volumeId: volume123 + mountPath: /data + subpath: users/alice + - volumeId: volume123 + mountPath: /data + subpath: users/alice + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + class: small + daemonVersion: 1.0.0 + runnerId: runner123 + toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox + - id: aB3cD4eF5gH6 + organizationId: organization123 + name: MyBox + user: boxlite + env: + NODE_ENV: production + labels: + boxlite.io/public: "true" + public: false + networkBlockAll: false + networkAllowList: "192.168.1.0/16,10.0.0.0/24" + target: local + image: boxlite/base + cpu: 2 + gpu: 0 + memory: 4 + disk: 10 + state: creating + desiredState: destroyed + errorReason: The box is not running + recoverable: true + autoStopInterval: 30 + autoDeleteInterval: 30 + volumes: + - volumeId: volume123 + mountPath: /data + subpath: users/alice + - volumeId: volume123 + mountPath: /data + subpath: users/alice + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + class: small + daemonVersion: 1.0.0 + runnerId: runner123 + toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox + total: 0.8008281904610115 + page: 6.027456183070403 + totalPages: 1.4658129805029452 properties: - sessionId: - description: The ID of the session - example: session-123 - type: string - commands: - description: The list of commands executed in this session + items: items: - $ref: '#/components/schemas/Command' - nullable: true + $ref: "#/components/schemas/Box" type: array + total: + type: number + page: + type: number + totalPages: + type: number required: - - commands - - sessionId + - items + - page + - total + - totalPages type: object - CreateSessionRequest: + ResizeBox: example: - sessionId: session-123 + cpu: 2 + memory: 4 + disk: 20 properties: - sessionId: - description: The ID of the session - example: session-123 - type: string - required: - - sessionId + cpu: + description: "CPU cores to allocate to the box (minimum: 1)" + example: 2 + minimum: 1 + type: integer + memory: + description: "Memory in GB to allocate to the box (minimum: 1)" + example: 4 + minimum: 1 + type: integer + disk: + description: Disk space in GB to allocate to the box (can only be increased) + example: 20 + minimum: 1 + type: integer type: object - SessionExecuteRequest: + BoxLabels: example: - async: false - command: ls -la - runAsync: false + labels: + environment: dev + team: backend properties: - command: - description: The command to execute - example: ls -la - type: string - runAsync: - description: Whether to execute the command asynchronously - example: false - type: boolean - async: - deprecated: true - description: "Deprecated: Use runAsync instead. Whether to execute the command\ - \ asynchronously" - example: false - type: boolean + labels: + additionalProperties: + type: string + description: Key-value pairs of labels + example: + environment: dev + team: backend + type: object required: - - command - type: object - SessionExecuteResponse: - example: - output: |- - total 20 - drwxr-xr-x 4 user group 128 Mar 15 10:30 . - cmdId: cmd-123 - exitCode: 0 - properties: - cmdId: - description: The ID of the executed command - example: cmd-123 - type: string - output: - description: The output of the executed command marked with stdout and stderr - prefixes - example: |- - total 20 - drwxr-xr-x 4 user group 128 Mar 15 10:30 . - type: string - exitCode: - description: The exit code of the executed command - example: 0 - type: number + - labels type: object - PtySessionInfo: + UpdateBoxStateDto: example: - cwd: /home/user - createdAt: 2024-01-15T10:30:45Z - lazyStart: false - envs: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - active: true - id: pty-session-12345 - rows: 24 - cols: 80 + state: started + errorReason: Failed to pull artifact image + recoverable: true properties: - id: - description: The unique identifier for the PTY session - example: pty-session-12345 - type: string - cwd: - description: "Starting directory for the PTY session, defaults to the sandbox's\ - \ working directory" - example: /home/user + state: + description: The new state for the box + enum: + - creating + - restoring + - destroyed + - destroying + - started + - stopped + - starting + - stopping + - error + - unknown + - archived + - archiving + - resizing + example: started type: string - envs: - description: Environment variables for the PTY session - example: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - type: object - cols: - description: Number of terminal columns - example: 80 - type: number - rows: - description: Number of terminal rows - example: 24 - type: number - createdAt: - description: When the PTY session was created - example: 2024-01-15T10:30:45Z + errorReason: + description: Optional error message when reporting an error state + example: Failed to pull artifact image type: string - active: - description: Whether the PTY session is currently active + recoverable: + description: Whether the box is recoverable example: true type: boolean - lazyStart: - default: false - description: Whether the PTY session uses lazy start (only start when first - client connects) - example: false - type: boolean - required: - - active - - cols - - createdAt - - cwd - - envs - - id - - lazyStart - - rows - type: object - PtyListResponse: - example: - sessions: - - cwd: /home/user - createdAt: 2024-01-15T10:30:45Z - lazyStart: false - envs: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - active: true - id: pty-session-12345 - rows: 24 - cols: 80 - - cwd: /home/user - createdAt: 2024-01-15T10:30:45Z - lazyStart: false - envs: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - active: true - id: pty-session-12345 - rows: 24 - cols: 80 - properties: - sessions: - description: List of active PTY sessions - items: - $ref: '#/components/schemas/PtySessionInfo' - type: array required: - - sessions + - state type: object - PtyCreateRequest: + PortPreviewUrl: example: - cwd: /home/user - lazyStart: false - envs: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - id: pty-session-12345 - rows: 24 - cols: 80 + boxId: "123456" + url: "https://{port}-{boxId}.{proxyDomain}" + token: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l properties: - id: - description: The unique identifier for the PTY session - example: pty-session-12345 + boxId: + description: ID of the box + example: "123456" type: string - cwd: - description: "Starting directory for the PTY session, defaults to the sandbox's\ - \ working directory" - example: /home/user + url: + description: Preview url + example: "https://{port}-{boxId}.{proxyDomain}" type: string - envs: - description: Environment variables for the PTY session - example: - TERM: xterm-256color - PS1: '\u@boxlite:\w$ ' - type: object - cols: - description: Number of terminal columns - example: 80 - type: number - rows: - description: Number of terminal rows - example: 24 - type: number - lazyStart: - default: false - description: Whether to start the PTY session lazily (only start when first - client connects) - example: false - type: boolean - required: - - id - type: object - PtyCreateResponse: - example: - sessionId: pty-session-12345 - properties: - sessionId: - description: The unique identifier for the created PTY session - example: pty-session-12345 + token: + description: Access token + example: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l type: string required: - - sessionId - type: object - PtyResizeRequest: - example: - rows: 24 - cols: 80 - properties: - cols: - description: Number of terminal columns - example: 80 - type: number - rows: - description: Number of terminal rows - example: 24 - type: number - required: - - cols - - rows - type: object - Position: - example: - character: 6.027456183070403 - line: 0.8008281904610115 - properties: - line: - type: number - character: - type: number - required: - - character - - line + - boxId + - token + - url type: object - CompletionContext: + SignedPortPreviewUrl: example: - triggerCharacter: triggerCharacter - triggerKind: 1.4658129805029452 + boxId: "123456" + port: 3000 + token: jl6wb9z5o3eii + url: "https://{port}-{token}.{proxyDomain}" properties: - triggerKind: - type: number - triggerCharacter: + boxId: + description: ID of the box + example: "123456" + type: string + port: + description: Port number of the signed preview URL + example: 3000 + type: integer + token: + description: Token of the signed preview URL + example: jl6wb9z5o3eii + type: string + url: + description: Signed preview url + example: "https://{port}-{token}.{proxyDomain}" type: string required: - - triggerKind + - boxId + - port + - token + - url type: object - LspCompletionParams: + SshAccessDto: example: - pathToProject: pathToProject - languageId: languageId - context: - triggerCharacter: triggerCharacter - triggerKind: 1.4658129805029452 - position: - character: 6.027456183070403 - line: 0.8008281904610115 - uri: uri + id: 123e4567-e89b-12d3-a456-426614174000 + boxId: 123e4567-e89b-12d3-a456-426614174000 + token: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz + expiresAt: 2025-01-01T12:00:00Z + createdAt: 2025-01-01T11:00:00Z + updatedAt: 2025-01-01T11:00:00Z + sshCommand: ssh -p 2222 token@localhost properties: - languageId: - description: Language identifier - type: string - pathToProject: - description: Path to the project + id: + description: Unique identifier for the SSH access + example: 123e4567-e89b-12d3-a456-426614174000 type: string - uri: - description: Document URI + boxId: + description: ID of the box this SSH access is for + example: 123e4567-e89b-12d3-a456-426614174000 type: string - position: - $ref: '#/components/schemas/Position' - context: - $ref: '#/components/schemas/CompletionContext' - required: - - languageId - - pathToProject - - position - - uri - type: object - CompletionItem: - example: - insertText: insertText - kind: 0.8008281904610115 - sortText: sortText - documentation: "{}" - label: label - detail: detail - filterText: filterText - properties: - label: + token: + description: SSH access token + example: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz type: string - kind: - type: number - detail: + expiresAt: + description: When the SSH access expires + example: 2025-01-01T12:00:00Z + format: date-time type: string - documentation: - type: object - sortText: + createdAt: + description: When the SSH access was created + example: 2025-01-01T11:00:00Z + format: date-time type: string - filterText: + updatedAt: + description: When the SSH access was last updated + example: 2025-01-01T11:00:00Z + format: date-time type: string - insertText: + sshCommand: + description: SSH command to connect to the box + example: ssh -p 2222 token@localhost type: string required: - - label + - boxId + - createdAt + - expiresAt + - id + - sshCommand + - token + - updatedAt type: object - CompletionList: + SshAccessValidationDto: example: - items: - - insertText: insertText - kind: 0.8008281904610115 - sortText: sortText - documentation: "{}" - label: label - detail: detail - filterText: filterText - - insertText: insertText - kind: 0.8008281904610115 - sortText: sortText - documentation: "{}" - label: label - detail: detail - filterText: filterText - isIncomplete: true + valid: true + boxId: 123e4567-e89b-12d3-a456-426614174000 properties: - isIncomplete: + valid: + description: Whether the SSH access token is valid + example: true type: boolean - items: - items: - $ref: '#/components/schemas/CompletionItem' - type: array - required: - - isIncomplete - - items - type: object - LspDocumentRequest: - example: - pathToProject: pathToProject - languageId: languageId - uri: uri - properties: - languageId: - description: Language identifier - type: string - pathToProject: - description: Path to the project - type: string - uri: - description: Document URI + boxId: + description: ID of the box this SSH access is for + example: 123e4567-e89b-12d3-a456-426614174000 type: string required: - - languageId - - pathToProject - - uri - type: object - Range: - example: - start: - character: 6.027456183070403 - line: 0.8008281904610115 - end: - character: 6.027456183070403 - line: 0.8008281904610115 - properties: - start: - $ref: '#/components/schemas/Position' - end: - $ref: '#/components/schemas/Position' - required: - - end - - start + - boxId + - valid type: object - LspLocation: + ToolboxProxyUrl: example: - range: - start: - character: 6.027456183070403 - line: 0.8008281904610115 - end: - character: 6.027456183070403 - line: 0.8008281904610115 - uri: uri + url: https://proxy.app.boxlite.io/toolbox properties: - range: - $ref: '#/components/schemas/Range' - uri: + url: + description: The toolbox proxy URL for the box + example: https://proxy.app.boxlite.io/toolbox type: string required: - - range - - uri + - url type: object - LspSymbol: + CreateRunner: example: - kind: 0.8008281904610115 + regionId: regionId name: name - location: - range: - start: - character: 6.027456183070403 - line: 0.8008281904610115 - end: - character: 6.027456183070403 - line: 0.8008281904610115 - uri: uri properties: - kind: - type: number - location: - $ref: '#/components/schemas/LspLocation' + regionId: + type: string name: type: string required: - - kind - - location - name + - regionId type: object - LspServerRequest: + CreateRunnerResponse: example: - pathToProject: pathToProject - languageId: languageId + id: runner123 + apiKey: blk_svc_1234567890 properties: - languageId: - description: Language identifier + id: + description: The ID of the runner + example: runner123 type: string - pathToProject: - description: Path to the project + apiKey: + description: The API key for the runner + example: blk_svc_1234567890 type: string required: - - languageId - - pathToProject + - apiKey + - id type: object - ComputerUseStartResponse: + BoxClass: + description: The class of the runner + enum: + - small + - medium + - large + type: string + RunnerState: + description: The state of the runner + enum: + - initializing + - ready + - disabled + - decommissioned + - unresponsive + type: string + RunnerFull: example: - message: Computer use processes started successfully - status: - xvfb: - running: true - priority: 100 - autoRestart: true - pid: 12345 - xfce4: - running: true - priority: 200 - autoRestart: true - pid: 12346 - x11vnc: - running: true - priority: 300 - autoRestart: true - pid: 12347 - novnc: - running: true - priority: 400 - autoRestart: true - pid: 12348 + id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev + apiKey: blk_svc_1234567890 + regionType: shared properties: - message: - description: A message indicating the result of starting computer use processes - example: Computer use processes started successfully + id: + description: The ID of the runner + example: runner123 type: string - status: - description: Status information about all VNC desktop processes after starting - example: - xvfb: - running: true - priority: 100 - autoRestart: true - pid: 12345 - xfce4: - running: true - priority: 200 - autoRestart: true - pid: 12346 - x11vnc: - running: true - priority: 300 - autoRestart: true - pid: 12347 - novnc: - running: true - priority: 400 - autoRestart: true - pid: 12348 - type: object - required: - - message - - status - type: object - ComputerUseStopResponse: - example: - message: Computer use processes stopped successfully - status: - xvfb: - running: false - priority: 100 - autoRestart: true - xfce4: - running: false - priority: 200 - autoRestart: true - x11vnc: - running: false - priority: 300 - autoRestart: true - novnc: - running: false - priority: 400 - autoRestart: true - properties: - message: - description: A message indicating the result of stopping computer use processes - example: Computer use processes stopped successfully + domain: + description: The domain of the runner + example: runner1.example.com type: string - status: - description: Status information about all VNC desktop processes after stopping - example: - xvfb: - running: false - priority: 100 - autoRestart: true - xfce4: - running: false - priority: 200 - autoRestart: true - x11vnc: - running: false - priority: 300 - autoRestart: true - novnc: - running: false - priority: 400 - autoRestart: true - type: object - required: - - message - - status - type: object - ComputerUseStatusResponse: - example: - status: active - properties: - status: - description: "Status of computer use services (active, partial, inactive,\ - \ error)" - enum: - - active - - partial - - inactive - - error - example: active + apiUrl: + description: The API URL of the runner + example: https://api.runner1.example.com type: string - required: - - status - type: object - ProcessStatusResponse: - example: - running: true - processName: xfce4 - properties: - processName: - description: The name of the VNC process being checked - example: xfce4 + proxyUrl: + description: The proxy URL of the runner + example: https://proxy.runner1.example.com + type: string + cpu: + description: The CPU capacity of the runner + example: 8 + type: number + memory: + description: The memory capacity of the runner in GiB + example: 16 + type: number + disk: + description: The disk capacity of the runner in GiB + example: 100 + type: number + gpu: + description: The GPU capacity of the runner + example: 1 + type: number + gpuType: + description: The type of GPU + type: string + class: + allOf: + - $ref: "#/components/schemas/BoxClass" + description: The class of the runner + example: small + currentCpuUsagePercentage: + description: Current CPU usage percentage + example: 45.6 + type: number + currentMemoryUsagePercentage: + description: Current RAM usage percentage + example: 68.2 + type: number + currentDiskUsagePercentage: + description: Current disk usage percentage + example: 33.8 + type: number + currentAllocatedCpu: + description: Current allocated CPU + example: 4000 + type: number + currentAllocatedMemoryGiB: + description: Current allocated memory in GiB + example: 8000 + type: number + currentAllocatedDiskGiB: + description: Current allocated disk in GiB + example: 50000 + type: number + currentStartedBoxes: + description: Current number of started boxes + example: 5 + type: number + availabilityScore: + description: Runner availability score + example: 85 + type: number + region: + description: The region of the runner + example: us + type: string + name: + description: The name of the runner + example: runner1 + type: string + state: + allOf: + - $ref: "#/components/schemas/RunnerState" + description: The state of the runner + example: initializing + lastChecked: + description: The last time the runner was checked + example: 2024-10-01T12:00:00Z + type: string + unschedulable: + description: Whether the runner is unschedulable + example: false + type: boolean + createdAt: + description: The creation timestamp of the runner + example: 2023-10-01T12:00:00Z + type: string + updatedAt: + description: The last update timestamp of the runner + example: 2023-10-01T12:00:00Z + type: string + version: + deprecated: true + description: The version of the runner (deprecated in favor of apiVersion) + example: "0" type: string - running: - description: Whether the specified VNC process is currently running - example: true - type: boolean - required: - - processName - - running - type: object - ProcessRestartResponse: - example: - processName: xfce4 - message: Process xfce4 restarted successfully - properties: - message: - description: A message indicating the result of restarting the process - example: Process xfce4 restarted successfully + apiVersion: + deprecated: true + description: The api version of the runner + example: "0" + type: string + appVersion: + deprecated: true + description: The app version of the runner + example: v0.0.0-dev type: string - processName: - description: The name of the VNC process that was restarted - example: xfce4 + apiKey: + description: The API key for the runner + example: blk_svc_1234567890 type: string + regionType: + allOf: + - $ref: "#/components/schemas/RegionType" + description: The region type of the runner + example: shared required: - - message - - processName + - apiKey + - apiVersion + - class + - cpu + - createdAt + - disk + - id + - memory + - name + - region + - state + - unschedulable + - updatedAt + - version type: object - ProcessLogsResponse: + Runner: example: - processName: novnc - logs: "2024-01-15 10:30:45 [INFO] NoVNC server started on port 6080" + id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev properties: - processName: - description: The name of the VNC process whose logs were retrieved - example: novnc + id: + description: The ID of the runner + example: runner123 type: string - logs: - description: The log output from the specified VNC process - example: "2024-01-15 10:30:45 [INFO] NoVNC server started on port 6080" + domain: + description: The domain of the runner + example: runner1.example.com type: string - required: - - logs - - processName - type: object - ProcessErrorsResponse: - example: - processName: x11vnc - errors: "2024-01-15 10:30:45 [ERROR] Failed to bind to port 5901" - properties: - processName: - description: The name of the VNC process whose error logs were retrieved - example: x11vnc + apiUrl: + description: The API URL of the runner + example: https://api.runner1.example.com type: string - errors: - description: The error log output from the specified VNC process - example: "2024-01-15 10:30:45 [ERROR] Failed to bind to port 5901" + proxyUrl: + description: The proxy URL of the runner + example: https://proxy.runner1.example.com type: string - required: - - errors - - processName - type: object - MousePosition: - example: - x: 100 - "y": 200 - properties: - x: - description: The X coordinate of the mouse cursor position + cpu: + description: The CPU capacity of the runner + example: 8 + type: number + memory: + description: The memory capacity of the runner in GiB + example: 16 + type: number + disk: + description: The disk capacity of the runner in GiB example: 100 type: number - "y": - description: The Y coordinate of the mouse cursor position - example: 200 + gpu: + description: The GPU capacity of the runner + example: 1 type: number - required: - - x - - "y" - type: object - MouseMoveRequest: - example: - x: 150 - "y": 250 - properties: - x: - description: The target X coordinate to move the mouse cursor to - example: 150 + gpuType: + description: The type of GPU + type: string + class: + allOf: + - $ref: "#/components/schemas/BoxClass" + description: The class of the runner + example: small + currentCpuUsagePercentage: + description: Current CPU usage percentage + example: 45.6 type: number - "y": - description: The target Y coordinate to move the mouse cursor to - example: 250 + currentMemoryUsagePercentage: + description: Current RAM usage percentage + example: 68.2 type: number - required: - - x - - "y" - type: object - MouseMoveResponse: - example: - x: 150 - "y": 250 - properties: - x: - description: The actual X coordinate where the mouse cursor ended up - example: 150 + currentDiskUsagePercentage: + description: Current disk usage percentage + example: 33.8 type: number - "y": - description: The actual Y coordinate where the mouse cursor ended up - example: 250 + currentAllocatedCpu: + description: Current allocated CPU + example: 4000 type: number - required: - - x - - "y" - type: object - MouseClickRequest: - example: - button: left - double: false - x: 100 - "y": 200 - properties: - x: - description: The X coordinate where to perform the mouse click - example: 100 + currentAllocatedMemoryGiB: + description: Current allocated memory in GiB + example: 8000 + type: number + currentAllocatedDiskGiB: + description: Current allocated disk in GiB + example: 50000 + type: number + currentStartedBoxes: + description: Current number of started boxes + example: 5 type: number - "y": - description: The Y coordinate where to perform the mouse click - example: 200 + availabilityScore: + description: Runner availability score + example: 85 type: number - button: - description: "The mouse button to click (left, right, middle). Defaults\ - \ to left" - example: left + region: + description: The region of the runner + example: us + type: string + name: + description: The name of the runner + example: runner1 + type: string + state: + allOf: + - $ref: "#/components/schemas/RunnerState" + description: The state of the runner + example: initializing + lastChecked: + description: The last time the runner was checked + example: 2024-10-01T12:00:00Z type: string - double: - description: Whether to perform a double-click instead of a single click + unschedulable: + description: Whether the runner is unschedulable example: false type: boolean + createdAt: + description: The creation timestamp of the runner + example: 2023-10-01T12:00:00Z + type: string + updatedAt: + description: The last update timestamp of the runner + example: 2023-10-01T12:00:00Z + type: string + version: + deprecated: true + description: The version of the runner (deprecated in favor of apiVersion) + example: "0" + type: string + apiVersion: + deprecated: true + description: The api version of the runner + example: "0" + type: string + appVersion: + deprecated: true + description: The app version of the runner + example: v0.0.0-dev + type: string required: - - x - - "y" + - apiVersion + - class + - cpu + - createdAt + - disk + - id + - memory + - name + - region + - state + - unschedulable + - updatedAt + - version type: object - MouseClickResponse: - example: - x: 100 - "y": 200 + RunnerHealthMetrics: properties: - x: - description: The actual X coordinate where the click occurred - example: 100 + currentCpuLoadAverage: + description: Current CPU load average + example: 0.98 type: number - "y": - description: The actual Y coordinate where the click occurred - example: 200 + currentCpuUsagePercentage: + description: Current CPU usage percentage + example: 45.5 type: number - required: - - x - - "y" - type: object - MouseDragRequest: - example: - button: left - endY: 400 - endX: 300 - startY: 200 - startX: 100 - properties: - startX: - description: The starting X coordinate for the drag operation - example: 100 + currentMemoryUsagePercentage: + description: Current memory usage percentage + example: 60.2 type: number - startY: - description: The starting Y coordinate for the drag operation - example: 200 + currentDiskUsagePercentage: + description: Current disk usage percentage + example: 35.8 type: number - endX: - description: The ending X coordinate for the drag operation - example: 300 + currentAllocatedCpu: + description: Currently allocated CPU cores + example: 8 type: number - endY: - description: The ending Y coordinate for the drag operation - example: 400 + currentAllocatedMemoryGiB: + description: Currently allocated memory in GiB + example: 16 type: number - button: - description: "The mouse button to use for dragging (left, right, middle).\ - \ Defaults to left" - example: left - type: string - required: - - endX - - endY - - startX - - startY - type: object - MouseDragResponse: - example: - x: 300 - "y": 400 - properties: - x: - description: The actual X coordinate where the drag ended - example: 300 + currentAllocatedDiskGiB: + description: Currently allocated disk in GiB + example: 100 type: number - "y": - description: The actual Y coordinate where the drag ended - example: 400 + currentStartedBoxes: + description: Number of started boxes + example: 10 type: number - required: - - x - - "y" - type: object - MouseScrollRequest: - example: - amount: 3 - x: 100 - "y": 200 - direction: down - properties: - x: - description: The X coordinate where to perform the scroll operation - example: 100 + cpu: + description: Total CPU cores on the runner + example: 8 type: number - "y": - description: The Y coordinate where to perform the scroll operation - example: 200 + memoryGiB: + description: Total RAM in GiB on the runner + example: 16 type: number - direction: - description: "The scroll direction (up, down)" - example: down - type: string - amount: - description: The number of scroll units to scroll. Defaults to 1 - example: 3 + diskGiB: + description: Total disk space in GiB on the runner + example: 100 type: number required: - - direction - - x - - "y" + - cpu + - currentAllocatedCpu + - currentAllocatedDiskGiB + - currentAllocatedMemoryGiB + - currentCpuLoadAverage + - currentCpuUsagePercentage + - currentDiskUsagePercentage + - currentMemoryUsagePercentage + - currentStartedBoxes + - diskGiB + - memoryGiB type: object - MouseScrollResponse: + RunnerServiceHealth: example: - success: true + serviceName: runner + healthy: false + errorReason: Cannot connect to the runner properties: - success: - description: Whether the mouse scroll operation was successful - example: true + serviceName: + description: Name of the service being checked + example: runner + type: string + healthy: + description: Whether the service is healthy + example: false type: boolean - required: - - success - type: object - KeyboardTypeRequest: - example: - delay: 100 - text: "Hello, World!" - properties: - text: - description: The text to type using the keyboard - example: "Hello, World!" + errorReason: + description: Error reason if the service is unhealthy + example: Cannot connect to the runner type: string - delay: - description: Delay in milliseconds between keystrokes. Defaults to 0 - example: 100 - type: number required: - - text + - healthy + - serviceName type: object - KeyboardPressRequest: + RunnerHealthcheck: example: - modifiers: - - ctrl - - shift - key: enter + metrics: "" + serviceHealth: + - serviceName: runner + healthy: false + errorReason: Cannot connect to the runner + - serviceName: runner + healthy: false + errorReason: Cannot connect to the runner + domain: runner-123.boxlite.example.com + proxyUrl: http://proxy.boxlite.example.com:8080 + apiUrl: http://api.boxlite.example.com:8080 + appVersion: v0.0.0-dev properties: - key: - description: "The key to press (e.g., a, b, c, enter, space, etc.)" - example: enter - type: string - modifiers: - description: "Array of modifier keys to press along with the main key (ctrl,\ - \ alt, shift, cmd)" - example: - - ctrl - - shift + metrics: + allOf: + - $ref: "#/components/schemas/RunnerHealthMetrics" + description: Runner metrics + serviceHealth: + description: Health status of individual services on the runner items: - type: string + $ref: "#/components/schemas/RunnerServiceHealth" type: array + domain: + description: Runner domain + example: runner-123.boxlite.example.com + type: string + proxyUrl: + description: Runner proxy URL + example: http://proxy.boxlite.example.com:8080 + type: string + apiUrl: + description: Runner API URL + example: http://api.boxlite.example.com:8080 + type: string + appVersion: + description: Runner app version + example: v0.0.0-dev + type: string required: - - key + - appVersion type: object - KeyboardHotkeyRequest: + VolumeState: + description: Volume state + enum: + - creating + - ready + - pending_create + - pending_delete + - deleting + - deleted + - error + type: string + VolumeDto: example: - keys: ctrl+c + id: vol-12345678 + name: my-volume + organizationId: 123e4567-e89b-12d3-a456-426614174000 + state: ready + createdAt: 2023-01-01T00:00:00.000Z + updatedAt: 2023-01-01T00:00:00.000Z + lastUsedAt: 2023-01-01T00:00:00.000Z + errorReason: Error processing volume properties: - keys: - description: "The hotkey combination to press (e.g., \"ctrl+c\", \"cmd+v\"\ - , \"alt+tab\")" - example: ctrl+c + id: + description: Volume ID + example: vol-12345678 + type: string + name: + description: Volume name + example: my-volume + type: string + organizationId: + description: Organization ID + example: 123e4567-e89b-12d3-a456-426614174000 + type: string + state: + allOf: + - $ref: "#/components/schemas/VolumeState" + description: Volume state + example: ready + createdAt: + description: Creation timestamp + example: 2023-01-01T00:00:00.000Z + type: string + updatedAt: + description: Last update timestamp + example: 2023-01-01T00:00:00.000Z + type: string + lastUsedAt: + description: Last used timestamp + example: 2023-01-01T00:00:00.000Z + nullable: true + type: string + errorReason: + description: The error reason of the volume + example: Error processing volume + nullable: true type: string required: - - keys + - createdAt + - errorReason + - id + - name + - organizationId + - state + - updatedAt type: object - ScreenshotResponse: + CreateVolume: example: - cursorPosition: - x: 500 - "y": 300 - screenshot: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== - sizeBytes: 24576 + name: name properties: - screenshot: - description: Base64 encoded screenshot image data - example: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== + name: type: string - cursorPosition: - description: The current cursor position when the screenshot was taken - example: - x: 500 - "y": 300 - type: object - sizeBytes: - description: The size of the screenshot data in bytes - example: 24576 - type: number required: - - screenshot + - name type: object - RegionScreenshotResponse: + JobStatus: + enum: + - PENDING + - IN_PROGRESS + - COMPLETED + - FAILED + type: string + JobType: + description: The type of the job + enum: + - CREATE_BOX + - START_BOX + - STOP_BOX + - DESTROY_BOX + - RESIZE_BOX + - CREATE_BACKUP + - PULL_ARTIFACT + - RECOVER_BOX + - INSPECT_ARTIFACT_IN_REGISTRY + - REMOVE_ARTIFACT + - UPDATE_BOX_NETWORK_SETTINGS + type: string + Job: example: - cursorPosition: - x: 500 - "y": 300 - screenshot: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== - sizeBytes: 24576 + id: job123 + type: CREATE_BOX + status: PENDING + resourceType: BOX + resourceId: box123 + payload: payload + traceContext: + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 + errorMessage: Failed to create box + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z properties: - screenshot: - description: Base64 encoded screenshot image data of the specified region - example: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== + id: + description: The ID of the job + example: job123 + type: string + type: + allOf: + - $ref: "#/components/schemas/JobType" + description: The type of the job + example: CREATE_BOX + status: + allOf: + - $ref: "#/components/schemas/JobStatus" + description: The status of the job + example: PENDING + resourceType: + description: The type of resource this job operates on + enum: + - BOX + - ARTIFACT + - BACKUP + example: BOX + type: string + resourceId: + description: "The ID of the resource this job operates on (boxId, etc.)" + example: box123 + type: string + payload: + description: Job-specific JSON-encoded payload data (operational metadata) type: string - cursorPosition: - description: The current cursor position when the region screenshot was - taken + traceContext: + additionalProperties: true + description: OpenTelemetry trace context for distributed tracing (W3C Trace + Context format) example: - x: 500 - "y": 300 + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 type: object - sizeBytes: - description: The size of the screenshot data in bytes - example: 24576 - type: number + errorMessage: + description: Error message if the job failed + example: Failed to create box + type: string + createdAt: + description: The creation timestamp of the job + example: 2024-10-01T12:00:00Z + type: string + updatedAt: + description: The last update timestamp of the job + example: 2024-10-01T12:00:00Z + type: string required: - - screenshot + - createdAt + - id + - resourceId + - resourceType + - status + - type type: object - CompressedScreenshotResponse: + PaginatedJobs: example: - cursorPosition: - x: 250 - "y": 150 - screenshot: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== - sizeBytes: 12288 + items: + - id: job123 + type: CREATE_BOX + status: PENDING + resourceType: BOX + resourceId: box123 + payload: payload + traceContext: + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 + errorMessage: Failed to create box + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + - id: job123 + type: CREATE_BOX + status: PENDING + resourceType: BOX + resourceId: box123 + payload: payload + traceContext: + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 + errorMessage: Failed to create box + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + total: 0.8008281904610115 + page: 6.027456183070403 + totalPages: 1.4658129805029452 properties: - screenshot: - description: Base64 encoded compressed screenshot image data - example: iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg== - type: string - cursorPosition: - description: The current cursor position when the compressed screenshot - was taken - example: - x: 250 - "y": 150 - type: object - sizeBytes: - description: The size of the compressed screenshot data in bytes - example: 12288 + items: + items: + $ref: "#/components/schemas/Job" + type: array + total: + type: number + page: + type: number + totalPages: type: number required: - - screenshot + - items + - page + - total + - totalPages type: object - DisplayInfoResponse: + PollJobsResponse: example: - displays: - - id: 0 - x: 0 - "y": 0 - width: 1920 - height: 1080 - is_active: true + jobs: + - id: job123 + type: CREATE_BOX + status: PENDING + resourceType: BOX + resourceId: box123 + payload: payload + traceContext: + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 + errorMessage: Failed to create box + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z + - id: job123 + type: CREATE_BOX + status: PENDING + resourceType: BOX + resourceId: box123 + payload: payload + traceContext: + traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 + errorMessage: Failed to create box + createdAt: 2024-10-01T12:00:00Z + updatedAt: 2024-10-01T12:00:00Z properties: - displays: - description: Array of display information for all connected displays - example: - - id: 0 - x: 0 - "y": 0 - width: 1920 - height: 1080 - is_active: true + jobs: + description: List of jobs items: - type: object + $ref: "#/components/schemas/Job" type: array required: - - displays + - jobs type: object - WindowsResponse: + UpdateJobStatus: example: - count: 5 - windows: - - id: 12345 - title: Terminal + status: IN_PROGRESS + errorMessage: Failed to create box + resultMetadata: resultMetadata properties: - windows: - description: Array of window information for all visible windows - example: - - id: 12345 - title: Terminal - items: - type: object - type: array - count: - description: The total number of windows found - example: 5 - type: number + status: + allOf: + - $ref: "#/components/schemas/JobStatus" + description: The new status of the job + example: IN_PROGRESS + errorMessage: + description: Error message if the job failed + example: Failed to create box + type: string + resultMetadata: + description: Result metadata for the job + type: string required: - - count - - windows + - status type: object - CreateSnapshot: + AdminCreateRunner: example: - general: true - disk: 3 - imageName: ubuntu:22.04 - memory: 1 - buildInfo: "" regionId: regionId - entrypoint: sleep infinity - name: ubuntu-4vcpu-8ram-100gb - cpu: 1 - gpu: 0 + name: name + apiKey: apiKey + apiVersion: "2" + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memoryGiB: 16 + diskGiB: 100 properties: + regionId: + type: string name: - description: The name of the snapshot - example: ubuntu-4vcpu-8ram-100gb type: string - imageName: - description: The image name of the snapshot - example: ubuntu:22.04 + apiKey: type: string - entrypoint: - description: The entrypoint command for the snapshot - example: sleep infinity - items: - type: string - type: array - general: - description: Whether the snapshot is general - type: boolean - cpu: - description: CPU cores allocated to the resulting sandbox - example: 1 - type: integer - gpu: - description: GPU units allocated to the resulting sandbox - example: 0 - type: integer - memory: - description: Memory allocated to the resulting sandbox in GB - example: 1 - type: integer - disk: - description: Disk space allocated to the sandbox in GB - example: 3 - type: integer - buildInfo: - allOf: - - $ref: '#/components/schemas/CreateBuildInfo' - description: Build information for the snapshot - regionId: - description: ID of the region where the snapshot will be available. Defaults - to organization default region if not specified. + apiVersion: + description: The api version of the runner to create + example: "2" + pattern: ^(0|2)$ + type: string + domain: + description: The domain of the runner + example: runner1.example.com + type: string + apiUrl: + description: The API URL of the runner + example: https://api.runner1.example.com + type: string + proxyUrl: + description: The proxy URL of the runner + example: https://proxy.runner1.example.com type: string + cpu: + description: The CPU capacity of the runner + example: 8 + type: number + memoryGiB: + description: The memory capacity of the runner in GiB + example: 16 + type: number + diskGiB: + description: The disk capacity of the runner in GiB + example: 100 + type: number required: + - apiKey + - apiVersion - name + - regionId type: object - SnapshotState: - enum: - - building - - pending - - pulling - - active - - inactive - - error - - build_failed - - removing - type: string - SnapshotDto: + AdminRunner: example: - imageName: imageName - buildInfo: "" - cpu: 6.027456183070403 - regionIds: - - regionIds - - regionIds - gpu: 1.4658129805029452 - organizationId: organizationId - general: true - createdAt: 2000-01-23T04:56:07.000+00:00 - disk: 5.637376656633329 - ref: boxlite-ai/sandbox:latest - size: 0.8008281904610115 - mem: 5.962133916683182 - lastUsedAt: 2000-01-23T04:56:07.000+00:00 - entrypoint: - - entrypoint - - entrypoint - errorReason: errorReason - name: name - id: id - state: "" - initialRunnerId: runner123 - updatedAt: 2000-01-23T04:56:07.000+00:00 + id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev + regionType: shared properties: id: + description: The ID of the runner + example: runner123 type: string - organizationId: + domain: + description: The domain of the runner + example: runner1.example.com type: string - general: - type: boolean - name: + apiUrl: + description: The API URL of the runner + example: https://api.runner1.example.com type: string - imageName: + proxyUrl: + description: The proxy URL of the runner + example: https://proxy.runner1.example.com type: string - state: - allOf: - - $ref: '#/components/schemas/SnapshotState' - size: - nullable: true - type: number - entrypoint: - items: - type: string - nullable: true - type: array cpu: + description: The CPU capacity of the runner + example: 8 + type: number + memory: + description: The memory capacity of the runner in GiB + example: 16 + type: number + disk: + description: The disk capacity of the runner in GiB + example: 100 type: number gpu: + description: The GPU capacity of the runner + example: 1 type: number - mem: + gpuType: + description: The type of GPU + type: string + class: + allOf: + - $ref: "#/components/schemas/BoxClass" + description: The class of the runner + example: small + currentCpuUsagePercentage: + description: Current CPU usage percentage + example: 45.6 type: number - disk: + currentMemoryUsagePercentage: + description: Current RAM usage percentage + example: 68.2 type: number - errorReason: - nullable: true + currentDiskUsagePercentage: + description: Current disk usage percentage + example: 33.8 + type: number + currentAllocatedCpu: + description: Current allocated CPU + example: 4000 + type: number + currentAllocatedMemoryGiB: + description: Current allocated memory in GiB + example: 8000 + type: number + currentAllocatedDiskGiB: + description: Current allocated disk in GiB + example: 50000 + type: number + currentStartedBoxes: + description: Current number of started boxes + example: 5 + type: number + availabilityScore: + description: Runner availability score + example: 85 + type: number + region: + description: The region of the runner + example: us + type: string + name: + description: The name of the runner + example: runner1 + type: string + state: + allOf: + - $ref: "#/components/schemas/RunnerState" + description: The state of the runner + example: initializing + lastChecked: + description: The last time the runner was checked + example: 2024-10-01T12:00:00Z type: string + unschedulable: + description: Whether the runner is unschedulable + example: false + type: boolean createdAt: - format: date-time + description: The creation timestamp of the runner + example: 2023-10-01T12:00:00Z type: string updatedAt: - format: date-time + description: The last update timestamp of the runner + example: 2023-10-01T12:00:00Z type: string - lastUsedAt: - format: date-time - nullable: true + version: + deprecated: true + description: The version of the runner (deprecated in favor of apiVersion) + example: "0" type: string - buildInfo: - allOf: - - $ref: '#/components/schemas/BuildInfo' - description: Build information for the snapshot - regionIds: - description: IDs of regions where the snapshot is available - items: - type: string - type: array - initialRunnerId: - description: The initial runner ID of the snapshot - example: runner123 + apiVersion: + deprecated: true + description: The api version of the runner + example: "0" type: string - ref: - description: The snapshot reference - example: boxlite-ai/sandbox:latest + appVersion: + deprecated: true + description: The app version of the runner + example: v0.0.0-dev type: string + regionType: + allOf: + - $ref: "#/components/schemas/RegionType" + description: The region type of the runner + example: shared required: + - apiVersion + - class - cpu - createdAt - disk - - entrypoint - - errorReason - - general - - gpu - id - - lastUsedAt - - mem + - memory - name - - size + - region - state + - unschedulable - updatedAt + - version type: object - PaginatedSnapshots: + AdminOverviewBoxes: example: - total: 2.3021358869347655 - totalPages: 9.301444243932576 - page: 7.061401241503109 - items: - - imageName: imageName - buildInfo: "" - cpu: 6.027456183070403 - regionIds: - - regionIds - - regionIds - gpu: 1.4658129805029452 - organizationId: organizationId - general: true - createdAt: 2000-01-23T04:56:07.000+00:00 - disk: 5.637376656633329 - ref: boxlite-ai/sandbox:latest - size: 0.8008281904610115 - mem: 5.962133916683182 - lastUsedAt: 2000-01-23T04:56:07.000+00:00 - entrypoint: - - entrypoint - - entrypoint - errorReason: errorReason - name: name - id: id - state: "" - initialRunnerId: runner123 - updatedAt: 2000-01-23T04:56:07.000+00:00 - - imageName: imageName - buildInfo: "" - cpu: 6.027456183070403 - regionIds: - - regionIds - - regionIds - gpu: 1.4658129805029452 - organizationId: organizationId - general: true - createdAt: 2000-01-23T04:56:07.000+00:00 - disk: 5.637376656633329 - ref: boxlite-ai/sandbox:latest - size: 0.8008281904610115 - mem: 5.962133916683182 - lastUsedAt: 2000-01-23T04:56:07.000+00:00 - entrypoint: - - entrypoint - - entrypoint - errorReason: errorReason - name: name - id: id - state: "" - initialRunnerId: runner123 - updatedAt: 2000-01-23T04:56:07.000+00:00 + total: 80 + byState: + started: 15 + error: 2 + build_failed: 1 + stopped: 62 properties: - items: - items: - $ref: '#/components/schemas/SnapshotDto' - type: array total: + description: Total number of boxes across all states + example: 80 + type: number + byState: + additionalProperties: + type: number + description: Box counts keyed by BoxState + example: + started: 15 + error: 2 + build_failed: 1 + stopped: 62 + type: object + required: + - byState + - total + type: object + AdminOverviewRunners: + example: + online: 3 + total: 5 + draining: 1 + properties: + online: + description: Number of online (ready) runners + example: 3 type: number - page: + total: + description: Total number of runners + example: 5 type: number - totalPages: + draining: + description: Number of draining runners + example: 1 type: number required: - - items - - page + - draining + - online - total - - totalPages type: object - SetSnapshotGeneralStatusDto: + AdminOverviewCluster: + example: + cpuUtil: 0.42 + oversell: 1.1 + properties: + cpuUtil: + description: Average CPU utilisation across all runners (0–1) + example: 0.42 + type: number + oversell: + description: Average CPU oversell ratio (allocated / capacity); 0 when no + capacity + example: 1.1 + type: number + required: + - cpuUtil + - oversell + type: object + AdminOverview: + example: + users: 120 + activeBoxes: 47 + boxes: + total: 80 + byState: + started: 15 + error: 2 + build_failed: 1 + stopped: 62 + runners: + online: 3 + total: 5 + draining: 1 + cluster: + cpuUtil: 0.42 + oversell: 1.1 + properties: + users: + description: Total number of users + example: 120 + type: number + activeBoxes: + description: Number of active (started) boxes + example: 47 + type: number + boxes: + $ref: "#/components/schemas/AdminOverviewBoxes" + runners: + $ref: "#/components/schemas/AdminOverviewRunners" + cluster: + $ref: "#/components/schemas/AdminOverviewCluster" + required: + - activeBoxes + - boxes + - cluster + - runners + - users + type: object + SystemRole: + enum: + - admin + - user + type: string + AdminUserItem: example: - general: true + id: usr_abc123 + email: alice@example.com + name: Alice Smith + role: user properties: - general: - description: Whether the snapshot is general - example: true - type: boolean + id: + description: User ID + example: usr_abc123 + type: string + email: + description: User email + example: alice@example.com + type: string + name: + description: Display name + example: Alice Smith + type: string + role: + allOf: + - $ref: "#/components/schemas/SystemRole" + example: user required: - - general + - email + - id + - name + - role type: object - SandboxInfo: + AdminBoxOwner: + example: + userId: usr_abc123 + name: Alice Smith + email: alice@example.com + orgName: Alice Personal + personal: true properties: - created: - description: The creation timestamp of the project - example: 2023-10-01T12:00:00Z + userId: + description: Creator/user ID behind this owner group when available + example: usr_abc123 type: string name: - default: "" - deprecated: true - description: "Deprecated: The name of the sandbox" - example: MySandbox + description: Display name for the owner group + example: Alice Smith type: string - providerMetadata: - description: Additional metadata provided by the provider - example: "{\"key\": \"value\"}" + email: + description: "Owner email for personal organizations, blank when unavailable" + example: alice@example.com + type: string + orgName: + description: Organization name backing this box + example: Alice Personal type: string + personal: + description: Whether this is a personal organization + example: true + type: boolean required: - - created + - email - name + - orgName + - personal type: object - Workspace: + AdminBoxItem: example: - memory: 4 - buildInfo: "" - toolboxProxyUrl: https://proxy.app.boxlite.io/toolbox - autoStopInterval: 30 - organizationId: organization123 - createdAt: 2024-10-01T12:00:00Z - networkBlockAll: false - autoDeleteInterval: 30 - snapshotState: None - public: false - errorReason: The sandbox is not running - runnerId: runner123 - id: sandbox123 - state: creating - class: small - updatedAt: 2024-10-01T12:00:00Z - info: "" - desiredState: destroyed - image: boxlite-ai/workspace:latest - networkAllowList: "192.168.1.0/16,10.0.0.0/24" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice + id: box_abc123 + organizationId: org_xyz + state: started + runnerId: runner-uuid cpu: 2 - recoverable: true - env: - NODE_ENV: production - snapshotCreatedAt: 2024-10-01T12:00:00Z - gpu: 0 - daemonVersion: 1.0.0 - backupCreatedAt: 2024-10-01T12:00:00Z - labels: - boxlite.io/public: "true" - target: local - disk: 10 - name: MySandbox - backupState: None - user: boxlite - autoArchiveInterval: 10080 - snapshot: boxlite-ai/sandbox:latest + memoryGiB: 4 + createdAt: 2024-01-01T00:00:00Z + owner: + userId: usr_abc123 + name: Alice Smith + email: alice@example.com + orgName: Alice Personal + personal: true properties: id: - description: The ID of the sandbox - example: sandbox123 + description: Box ID + example: box_abc123 type: string organizationId: - description: The organization ID of the sandbox - example: organization123 + description: Organization ID + example: org_xyz type: string - name: - description: The name of the sandbox - example: MySandbox + state: + allOf: + - $ref: "#/components/schemas/BoxState" + example: started + runnerId: + description: Runner ID the box is assigned to + example: runner-uuid type: string - snapshot: - description: The snapshot used for the sandbox - example: boxlite-ai/sandbox:latest + cpu: + description: Allocated CPU (vCPUs) + example: 2 + type: number + memoryGiB: + description: Allocated memory in GiB + example: 4 + type: number + createdAt: + description: Creation timestamp + example: 2024-01-01T00:00:00Z type: string - user: - description: The user associated with the project - example: boxlite + owner: + $ref: "#/components/schemas/AdminBoxOwner" + required: + - cpu + - createdAt + - id + - organizationId + - owner + - state + type: object + AdminRunnerItem: + example: + id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev + regionType: shared + draining: false + properties: + id: + description: The ID of the runner + example: runner123 type: string - env: - additionalProperties: - type: string - description: Environment variables for the sandbox - example: - NODE_ENV: production - type: object - labels: - additionalProperties: - type: string - description: Labels for the sandbox - example: - boxlite.io/public: "true" - type: object - public: - description: Whether the sandbox http preview is public - example: false - type: boolean - networkBlockAll: - description: Whether to block all network access for the sandbox - example: false - type: boolean - networkAllowList: - description: Comma-separated list of allowed CIDR network addresses for - the sandbox - example: "192.168.1.0/16,10.0.0.0/24" + domain: + description: The domain of the runner + example: runner1.example.com type: string - target: - description: The target environment for the sandbox - example: local + apiUrl: + description: The API URL of the runner + example: https://api.runner1.example.com + type: string + proxyUrl: + description: The proxy URL of the runner + example: https://proxy.runner1.example.com type: string cpu: - description: The CPU quota for the sandbox - example: 2 - type: number - gpu: - description: The GPU quota for the sandbox - example: 0 + description: The CPU capacity of the runner + example: 8 type: number memory: - description: The memory quota for the sandbox - example: 4 + description: The memory capacity of the runner in GiB + example: 16 type: number disk: - description: The disk quota for the sandbox - example: 10 + description: The disk capacity of the runner in GiB + example: 100 type: number - state: - allOf: - - $ref: '#/components/schemas/SandboxState' - description: The state of the sandbox - example: creating - desiredState: - allOf: - - $ref: '#/components/schemas/SandboxDesiredState' - description: The desired state of the sandbox - example: destroyed - errorReason: - description: The error reason of the sandbox - example: The sandbox is not running - type: string - recoverable: - description: Whether the sandbox error is recoverable. - example: true - type: boolean - backupState: - description: The state of the backup - enum: - - None - - Pending - - InProgress - - Completed - - Error - example: None - type: string - backupCreatedAt: - description: The creation timestamp of the last backup - example: 2024-10-01T12:00:00Z + gpu: + description: The GPU capacity of the runner + example: 1 + type: number + gpuType: + description: The type of GPU type: string - autoStopInterval: - description: Auto-stop interval in minutes (0 means disabled) - example: 30 + class: + allOf: + - $ref: "#/components/schemas/BoxClass" + description: The class of the runner + example: small + currentCpuUsagePercentage: + description: Current CPU usage percentage + example: 45.6 type: number - autoArchiveInterval: - description: Auto-archive interval in minutes - example: 10080 + currentMemoryUsagePercentage: + description: Current RAM usage percentage + example: 68.2 type: number - autoDeleteInterval: - description: "Auto-delete interval in minutes (negative value means disabled,\ - \ 0 means delete immediately upon stopping)" - example: 30 + currentDiskUsagePercentage: + description: Current disk usage percentage + example: 33.8 type: number - volumes: - description: Array of volumes attached to the sandbox - items: - $ref: '#/components/schemas/SandboxVolume' - type: array - buildInfo: + currentAllocatedCpu: + description: Current allocated CPU + example: 4000 + type: number + currentAllocatedMemoryGiB: + description: Current allocated memory in GiB + example: 8000 + type: number + currentAllocatedDiskGiB: + description: Current allocated disk in GiB + example: 50000 + type: number + currentStartedBoxes: + description: Current number of started boxes + example: 5 + type: number + availabilityScore: + description: Runner availability score + example: 85 + type: number + region: + description: The region of the runner + example: us + type: string + name: + description: The name of the runner + example: runner1 + type: string + state: allOf: - - $ref: '#/components/schemas/BuildInfo' - description: Build information for the sandbox - createdAt: - description: The creation timestamp of the sandbox + - $ref: "#/components/schemas/RunnerState" + description: The state of the runner + example: initializing + lastChecked: + description: The last time the runner was checked example: 2024-10-01T12:00:00Z type: string + unschedulable: + description: Whether the runner is unschedulable + example: false + type: boolean + createdAt: + description: The creation timestamp of the runner + example: 2023-10-01T12:00:00Z + type: string updatedAt: - description: The last update timestamp of the sandbox - example: 2024-10-01T12:00:00Z + description: The last update timestamp of the runner + example: 2023-10-01T12:00:00Z type: string - class: + version: deprecated: true - description: The class of the sandbox - enum: - - small - - medium - - large - example: small - type: string - daemonVersion: - description: The version of the daemon running in the sandbox - example: 1.0.0 - type: string - runnerId: - description: The runner ID of the sandbox - example: runner123 - type: string - toolboxProxyUrl: - description: The toolbox proxy URL for the sandbox - example: https://proxy.app.boxlite.io/toolbox + description: The version of the runner (deprecated in favor of apiVersion) + example: "0" type: string - image: - description: The image used for the workspace - example: boxlite-ai/workspace:latest + apiVersion: + deprecated: true + description: The api version of the runner + example: "0" type: string - snapshotState: - description: The state of the snapshot - enum: - - None - - Pending - - InProgress - - Completed - - Error - example: None - type: string - snapshotCreatedAt: - description: The creation timestamp of the last snapshot - example: 2024-10-01T12:00:00Z + appVersion: + deprecated: true + description: The app version of the runner + example: v0.0.0-dev type: string - info: + regionType: allOf: - - $ref: '#/components/schemas/SandboxInfo' - description: Additional information about the sandbox + - $ref: "#/components/schemas/RegionType" + description: The region type of the runner + example: shared + draining: + description: Whether the runner is currently draining + example: false + type: boolean required: + - apiVersion + - class - cpu + - createdAt - disk - - env - - gpu + - draining - id - - labels - memory - name - - networkBlockAll - - organizationId - - public - - target - - toolboxProxyUrl - - user + - region + - state + - unschedulable + - updatedAt + - version type: object - CreateWorkspace: + AdminMachineItem: example: - image: boxlite-ai/workspace:latest - memory: 1 - buildInfo: "" - volumes: - - mountPath: /data - volumeId: volume123 - subpath: users/alice - - mountPath: /data - volumeId: volume123 - subpath: users/alice - cpu: 2 - env: - NODE_ENV: production - gpu: 1 - autoStopInterval: 30 - labels: - boxlite.io/public: "true" - target: eu - disk: 3 - public: false - user: boxlite - class: small - autoArchiveInterval: 10080 + host: runner-uuid + region: us-east-1 + oversellCpu: 0.75 + cpuWaterline: 45.6 + memWaterline: 68.2 + boxes: 5 properties: - image: - description: The image used for the workspace - example: boxlite-ai/workspace:latest + host: + description: Runner / host ID + example: runner-uuid type: string - user: - description: The user associated with the project - example: boxlite + region: + description: Region ID + example: us-east-1 type: string - env: - additionalProperties: - type: string - description: Environment variables for the workspace - example: - NODE_ENV: production - type: object - labels: - additionalProperties: - type: string - description: Labels for the workspace - example: - boxlite.io/public: "true" - type: object - public: - description: Whether the workspace http preview is publicly accessible - example: false + oversellCpu: + description: CPU oversell ratio (allocatedCpu / totalCpu); 0 when capacity + is 0 + example: 0.75 + type: number + cpuWaterline: + description: CPU utilisation waterline (0–100) + example: 45.6 + type: number + memWaterline: + description: Memory utilisation waterline (0–100) + example: 68.2 + type: number + boxes: + description: Number of currently started boxes on this runner + example: 5 + type: number + required: + - boxes + - cpuWaterline + - host + - memWaterline + - oversellCpu + - region + type: object + AdminObservabilityBackendStatusDto: + example: + configured: true + state: missing + message: message + properties: + configured: + description: Whether ClickHouse/ClickStack query configuration is present type: boolean - class: - description: The workspace class type + state: enum: - - small - - medium - - large - example: small + - missing + - configured + - receiving + - stale + - error type: string - target: - description: The target (region) where the workspace will be created - enum: - - eu - - us - - asia - example: eu + message: type: string - cpu: - description: CPU cores allocated to the workspace - example: 2 - type: integer - gpu: - description: GPU units allocated to the workspace - example: 1 - type: integer - memory: - description: Memory allocated to the workspace in GB - example: 1 - type: integer - disk: - description: Disk space allocated to the workspace in GB - example: 3 - type: integer - autoStopInterval: - description: Auto-stop interval in minutes (0 means disabled) - example: 30 - type: integer - autoArchiveInterval: - description: Auto-archive interval in minutes (0 means the maximum interval - will be used) - example: 10080 - type: integer - volumes: - description: Array of volumes to attach to the workspace - items: - $ref: '#/components/schemas/SandboxVolume' - type: array - buildInfo: - allOf: - - $ref: '#/components/schemas/CreateBuildInfo' - description: Build information for the workspace + required: + - configured + - state type: object - WorkspacePortPreviewUrl: + AdminObservabilityLayerSignalsDto: example: - url: https://123456-mysandbox.runner.com - token: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l + logs: missing + traces: missing + metrics: missing properties: - url: - description: Preview url - example: https://123456-mysandbox.runner.com + logs: + enum: + - missing + - configured + - receiving + - stale + - error type: string - token: - description: Access token - example: ul67qtv-jl6wb9z5o3eii-ljqt9qed6l + traces: + enum: + - missing + - configured + - receiving + - stale + - error + type: string + metrics: + enum: + - missing + - configured + - receiving + - stale + - error type: string required: - - token - - url + - logs + - metrics + - traces type: object - VolumeState: - description: Volume state - enum: - - creating - - ready - - pending_create - - pending_delete - - deleting - - deleted - - error - type: string - VolumeDto: + AdminObservabilityLayerStatusDto: example: - organizationId: 123e4567-e89b-12d3-a456-426614174000 - createdAt: 2023-01-01T00:00:00.000Z - lastUsedAt: 2023-01-01T00:00:00.000Z - errorReason: Error processing volume - name: my-volume - id: vol-12345678 - state: ready - updatedAt: 2023-01-01T00:00:00.000Z + layer: api + state: missing + signals: + logs: missing + traces: missing + metrics: missing + lastSeen: lastSeen properties: - id: - description: Volume ID - example: vol-12345678 + layer: + enum: + - api + - runner + - ec2_host + - box type: string - name: - description: Volume name - example: my-volume + state: + enum: + - missing + - configured + - receiving + - stale + - error type: string - organizationId: - description: Organization ID - example: 123e4567-e89b-12d3-a456-426614174000 + signals: + $ref: "#/components/schemas/AdminObservabilityLayerSignalsDto" + lastSeen: type: string - state: - allOf: - - $ref: '#/components/schemas/VolumeState' - description: Volume state - example: ready - createdAt: - description: Creation timestamp - example: 2023-01-01T00:00:00.000Z + required: + - layer + - signals + - state + type: object + AdminObservabilityStatusDto: + example: + backend: + configured: true + state: missing + message: message + layers: + - layer: api + state: missing + signals: + logs: missing + traces: missing + metrics: missing + lastSeen: lastSeen + - layer: api + state: missing + signals: + logs: missing + traces: missing + metrics: missing + lastSeen: lastSeen + properties: + backend: + $ref: "#/components/schemas/AdminObservabilityBackendStatusDto" + layers: + items: + $ref: "#/components/schemas/AdminObservabilityLayerStatusDto" + type: array + required: + - backend + - layers + type: object + LogEntry: + example: + timestamp: timestamp + body: body + severityText: severityText + severityNumber: 0.8008281904610115 + serviceName: serviceName + resourceAttributes: + key: resourceAttributes + logAttributes: + key: logAttributes + traceId: traceId + spanId: spanId + properties: + timestamp: + description: Timestamp of the log entry type: string - updatedAt: - description: Last update timestamp - example: 2023-01-01T00:00:00.000Z + body: + description: Log message body type: string - lastUsedAt: - description: Last used timestamp - example: 2023-01-01T00:00:00.000Z - nullable: true + severityText: + description: "Severity level text (e.g., INFO, WARN, ERROR)" + type: string + severityNumber: + description: Severity level number + type: number + serviceName: + description: Service name that generated the log + type: string + resourceAttributes: + additionalProperties: + type: string + description: Resource attributes from OTEL + type: object + logAttributes: + additionalProperties: + type: string + description: Log-specific attributes + type: object + traceId: + description: Associated trace ID if available type: string - errorReason: - description: The error reason of the volume - example: Error processing volume - nullable: true + spanId: + description: Associated span ID if available type: string required: - - createdAt - - errorReason - - id - - name - - organizationId - - state - - updatedAt + - body + - logAttributes + - resourceAttributes + - serviceName + - severityText + - timestamp type: object - CreateVolume: + PaginatedLogs: example: - name: name + items: + - timestamp: timestamp + body: body + severityText: severityText + severityNumber: 0.8008281904610115 + serviceName: serviceName + resourceAttributes: + key: resourceAttributes + logAttributes: + key: logAttributes + traceId: traceId + spanId: spanId + - timestamp: timestamp + body: body + severityText: severityText + severityNumber: 0.8008281904610115 + serviceName: serviceName + resourceAttributes: + key: resourceAttributes + logAttributes: + key: logAttributes + traceId: traceId + spanId: spanId + total: 6.027456183070403 + page: 1.4658129805029452 + totalPages: 5.962133916683182 properties: - name: - type: string + items: + description: List of log entries + items: + $ref: "#/components/schemas/LogEntry" + type: array + total: + description: Total number of log entries matching the query + type: number + page: + description: Current page number + type: number + totalPages: + description: Total number of pages + type: number required: - - name + - items + - page + - total + - totalPages type: object - JobStatus: - enum: - - PENDING - - IN_PROGRESS - - COMPLETED - - FAILED - type: string - JobType: - description: The type of the job - enum: - - CREATE_SANDBOX - - START_SANDBOX - - STOP_SANDBOX - - DESTROY_SANDBOX - - RESIZE_SANDBOX - - CREATE_BACKUP - - BUILD_SNAPSHOT - - PULL_SNAPSHOT - - RECOVER_SANDBOX - - INSPECT_SNAPSHOT_IN_REGISTRY - - REMOVE_SNAPSHOT - - UPDATE_SANDBOX_NETWORK_SETTINGS - type: string - Job: + TraceSummary: example: - createdAt: 2024-10-01T12:00:00Z - resourceId: sandbox123 - payload: payload - errorMessage: Failed to create sandbox - id: job123 - type: CREATE_SANDBOX - traceContext: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - status: PENDING - resourceType: SANDBOX - updatedAt: 2024-10-01T12:00:00Z + traceId: traceId + rootSpanName: rootSpanName + startTime: startTime + endTime: endTime + durationMs: 0.8008281904610115 + spanCount: 6.027456183070403 + statusCode: statusCode properties: - id: - description: The ID of the job - example: job123 - type: string - type: - allOf: - - $ref: '#/components/schemas/JobType' - description: The type of the job - example: CREATE_SANDBOX - status: - allOf: - - $ref: '#/components/schemas/JobStatus' - description: The status of the job - example: PENDING - resourceType: - description: The type of resource this job operates on - enum: - - SANDBOX - - SNAPSHOT - - BACKUP - example: SANDBOX - type: string - resourceId: - description: "The ID of the resource this job operates on (sandboxId, snapshotRef,\ - \ etc.)" - example: sandbox123 + traceId: + description: Unique trace identifier type: string - payload: - description: Job-specific JSON-encoded payload data (operational metadata) + rootSpanName: + description: Name of the root span type: string - traceContext: - additionalProperties: true - description: OpenTelemetry trace context for distributed tracing (W3C Trace - Context format) - example: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - type: object - errorMessage: - description: Error message if the job failed - example: Failed to create sandbox + startTime: + description: Trace start time type: string - createdAt: - description: The creation timestamp of the job - example: 2024-10-01T12:00:00Z + endTime: + description: Trace end time type: string - updatedAt: - description: The last update timestamp of the job - example: 2024-10-01T12:00:00Z + durationMs: + description: Total duration in milliseconds + type: number + spanCount: + description: Number of spans in this trace + type: number + statusCode: + description: Status code of the trace type: string required: - - createdAt - - id - - resourceId - - resourceType - - status - - type + - durationMs + - endTime + - rootSpanName + - spanCount + - startTime + - traceId type: object - PaginatedJobs: + PaginatedTraces: example: - total: 0.8008281904610115 - totalPages: 1.4658129805029452 - page: 6.027456183070403 items: - - createdAt: 2024-10-01T12:00:00Z - resourceId: sandbox123 - payload: payload - errorMessage: Failed to create sandbox - id: job123 - type: CREATE_SANDBOX - traceContext: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - status: PENDING - resourceType: SANDBOX - updatedAt: 2024-10-01T12:00:00Z - - createdAt: 2024-10-01T12:00:00Z - resourceId: sandbox123 - payload: payload - errorMessage: Failed to create sandbox - id: job123 - type: CREATE_SANDBOX - traceContext: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - status: PENDING - resourceType: SANDBOX - updatedAt: 2024-10-01T12:00:00Z + - traceId: traceId + rootSpanName: rootSpanName + startTime: startTime + endTime: endTime + durationMs: 0.8008281904610115 + spanCount: 6.027456183070403 + statusCode: statusCode + - traceId: traceId + rootSpanName: rootSpanName + startTime: startTime + endTime: endTime + durationMs: 0.8008281904610115 + spanCount: 6.027456183070403 + statusCode: statusCode + total: 1.4658129805029452 + page: 5.962133916683182 + totalPages: 5.637376656633329 properties: items: + description: List of trace summaries items: - $ref: '#/components/schemas/Job' + $ref: "#/components/schemas/TraceSummary" type: array total: + description: Total number of traces matching the query type: number page: + description: Current page number type: number totalPages: + description: Total number of pages + type: number + required: + - items + - page + - total + - totalPages + type: object + TraceSpan: + example: + traceId: traceId + spanId: spanId + parentSpanId: parentSpanId + spanName: spanName + serviceName: serviceName + layer: layer + timestamp: timestamp + durationNs: 0.8008281904610115 + spanAttributes: + key: spanAttributes + statusCode: statusCode + statusMessage: statusMessage + properties: + traceId: + description: Trace identifier + type: string + spanId: + description: Span identifier + type: string + parentSpanId: + description: Parent span identifier + type: string + spanName: + description: Span name + type: string + serviceName: + description: "Emitting service name (e.g. boxlite-api, boxlite-runner, box-)" + type: string + layer: + description: "Resolved emitting layer: api | runner | ec2_host | box" + type: string + timestamp: + description: Span start timestamp + type: string + durationNs: + description: Span duration in nanoseconds + type: number + spanAttributes: + additionalProperties: + type: string + description: Span attributes + type: object + statusCode: + description: Status code of the span + type: string + statusMessage: + description: Status message + type: string + required: + - durationNs + - spanAttributes + - spanId + - spanName + - timestamp + - traceId + type: object + MetricDataPoint: + example: + timestamp: timestamp + value: 0.8008281904610115 + properties: + timestamp: + description: Timestamp of the data point + type: string + value: + description: Value at this timestamp type: number required: - - items - - page - - total - - totalPages + - timestamp + - value type: object - PollJobsResponse: + MetricSeries: example: - jobs: - - createdAt: 2024-10-01T12:00:00Z - resourceId: sandbox123 - payload: payload - errorMessage: Failed to create sandbox - id: job123 - type: CREATE_SANDBOX - traceContext: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - status: PENDING - resourceType: SANDBOX - updatedAt: 2024-10-01T12:00:00Z - - createdAt: 2024-10-01T12:00:00Z - resourceId: sandbox123 - payload: payload - errorMessage: Failed to create sandbox - id: job123 - type: CREATE_SANDBOX - traceContext: - traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 - status: PENDING - resourceType: SANDBOX - updatedAt: 2024-10-01T12:00:00Z + metricName: metricName + dataPoints: + - timestamp: timestamp + value: 0.8008281904610115 + - timestamp: timestamp + value: 0.8008281904610115 properties: - jobs: - description: List of jobs + metricName: + description: Name of the metric + type: string + dataPoints: + description: Data points for this metric items: - $ref: '#/components/schemas/Job' + $ref: "#/components/schemas/MetricDataPoint" type: array required: - - jobs + - dataPoints + - metricName type: object - UpdateJobStatus: + MetricsResponse: example: - resultMetadata: resultMetadata - errorMessage: Failed to create sandbox - status: IN_PROGRESS + series: + - metricName: metricName + dataPoints: + - timestamp: timestamp + value: 0.8008281904610115 + - timestamp: timestamp + value: 0.8008281904610115 + - metricName: metricName + dataPoints: + - timestamp: timestamp + value: 0.8008281904610115 + - timestamp: timestamp + value: 0.8008281904610115 properties: - status: - allOf: - - $ref: '#/components/schemas/JobStatus' - description: The new status of the job - example: IN_PROGRESS - errorMessage: - description: Error message if the job failed - example: Failed to create sandbox - type: string - resultMetadata: - description: Result metadata for the job - type: string + series: + description: List of metric series + items: + $ref: "#/components/schemas/MetricSeries" + type: array required: - - status + - series type: object - CreateDockerRegistry: + AdminObservabilityResourceSummary: example: - registryType: organization - password: password - isDefault: true - name: name - project: project - url: url - username: username + type: box + title: title + subtitle: subtitle + state: state + owner: owner + identifiers: + key: "" + timeRange: + key: "" properties: - name: - description: Registry name + type: + enum: + - box + - runner + - machine + - trace + - request + - operation + - execution + - job + - org + - user + - unknown type: string - url: - description: Registry URL + title: + type: string + subtitle: type: string - username: - description: Registry username + state: type: string - password: - description: Registry password + owner: type: string - project: - description: Registry project + identifiers: + additionalProperties: true + type: object + timeRange: + additionalProperties: true + type: object + required: + - title + - type + type: object + AdminObservabilityCorrelation: + example: + traceIds: + - traceIds + - traceIds + orgIds: + - orgIds + - orgIds + userIds: + - userIds + - userIds + boxIds: + - boxIds + - boxIds + runnerIds: + - runnerIds + - runnerIds + machineIds: + - machineIds + - machineIds + requestIds: + - requestIds + - requestIds + operationIds: + - operationIds + - operationIds + executionIds: + - executionIds + - executionIds + jobIds: + - jobIds + - jobIds + serviceNames: + - serviceNames + - serviceNames + properties: + traceIds: + items: + type: string + type: array + orgIds: + items: + type: string + type: array + userIds: + items: + type: string + type: array + boxIds: + items: + type: string + type: array + runnerIds: + items: + type: string + type: array + machineIds: + items: + type: string + type: array + requestIds: + items: + type: string + type: array + operationIds: + items: + type: string + type: array + executionIds: + items: + type: string + type: array + jobIds: + items: + type: string + type: array + serviceNames: + items: + type: string + type: array + required: + - boxIds + - executionIds + - jobIds + - machineIds + - operationIds + - orgIds + - requestIds + - runnerIds + - serviceNames + - traceIds + - userIds + type: object + AdminObservabilitySourceStatus: + example: + source: clickhouse + state: available + message: message + count: 0.8008281904610115 + properties: + source: + enum: + - clickhouse + - clickstack + - postgres + - audit + - cloudwatch + - s3 + - xlog type: string - registryType: - default: organization - description: Registry type + state: enum: - - internal - - organization - - transient - - backup + - available + - missing + - stale + - not_configured + - error type: string - isDefault: - description: Set as default registry - type: boolean + message: + type: string + count: + type: number required: - - name - - password - - registryType - - url - - username + - source + - state type: object - DockerRegistry: + AdminObservabilityAuditLog: example: - registryType: internal - createdAt: 2024-01-31T12:00:00Z - name: My Docker Hub - project: my-project - id: 123e4567-e89b-12d3-a456-426614174000 - url: https://registry.hub.docker.com - username: username - updatedAt: 2024-01-31T12:00:00Z + id: id + actorId: actorId + actorEmail: actorEmail + organizationId: organizationId + action: action + targetType: targetType + targetId: targetId + statusCode: 6.027456183070403 + errorMessage: errorMessage + source: source + metadata: + key: "" + createdAt: 2000-01-23T04:56:07.000+00:00 properties: id: - description: Registry ID - example: 123e4567-e89b-12d3-a456-426614174000 type: string - name: - description: Registry name - example: My Docker Hub + actorId: type: string - url: - description: Registry URL - example: https://registry.hub.docker.com + actorEmail: type: string - username: - description: Registry username - example: username + organizationId: type: string - project: - description: Registry project - example: my-project + action: type: string - registryType: - description: Registry type - enum: - - internal - - organization - - transient - - backup - example: internal + targetType: type: string - createdAt: - description: Creation timestamp - example: 2024-01-31T12:00:00Z - format: date-time + targetId: type: string - updatedAt: - description: Last update timestamp - example: 2024-01-31T12:00:00Z + statusCode: + type: number + errorMessage: + type: string + source: + type: string + metadata: + additionalProperties: true + type: object + createdAt: format: date-time type: string required: + - action + - actorEmail + - actorId - createdAt - id - - name - - project - - registryType - - updatedAt - - url - - username type: object - RegistryPushAccessDto: + AdminObservabilityXLog: example: - registryUrl: registry.example.com - registryId: 123e4567-e89b-12d3-a456-426614174000 - project: library - secret: eyJhbGciOiJIUzI1NiIs... - expiresAt: 2023-12-31T23:59:59Z - username: temp-user-123 + source: source + timestamp: timestamp + serviceName: serviceName + body: body + severityText: severityText + traceId: traceId + spanId: spanId + executionId: executionId + jobId: jobId + stream: stream + attributes: + key: "" properties: - username: - description: Temporary username for registry authentication - example: temp-user-123 + source: type: string - secret: - description: Temporary secret for registry authentication - example: eyJhbGciOiJIUzI1NiIs... + timestamp: type: string - registryUrl: - description: Registry URL - example: registry.example.com + serviceName: type: string - registryId: - description: Registry ID - example: 123e4567-e89b-12d3-a456-426614174000 + body: type: string - project: - description: Registry project ID - example: library + severityText: type: string - expiresAt: - description: Token expiration time in ISO format - example: 2023-12-31T23:59:59Z + traceId: type: string + spanId: + type: string + executionId: + type: string + jobId: + type: string + stream: + type: string + attributes: + additionalProperties: true + type: object required: - - expiresAt - - project - - registryId - - registryUrl - - secret - - username + - body + - serviceName + - source + - timestamp type: object - UpdateDockerRegistry: + AdminObservabilityS3Object: example: - password: password - name: name - project: project - url: url - username: username + bucket: bucket + key: key + size: 1.4658129805029452 + lastModified: 2000-01-23T04:56:07.000+00:00 + etag: etag + matchedBy: matchedBy properties: - name: - description: Registry name + bucket: type: string - url: - description: Registry URL + key: type: string - username: - description: Registry username + size: + type: number + lastModified: + format: date-time type: string - password: - description: Registry password + etag: type: string - project: - description: Registry project + matchedBy: type: string required: - - name - - url - - username + - bucket + - key type: object - AdminCreateRunner: + AdminObservabilityTimelineEvent: example: - diskGiB: 100 - apiVersion: "2" - apiKey: apiKey - apiUrl: https://api.runner1.example.com - regionId: regionId - proxyUrl: https://proxy.runner1.example.com - memoryGiB: 16 - domain: runner1.example.com - name: name - cpu: 8 + timestamp: timestamp + source: source + title: title + detail: detail + severity: severity + identifiers: + key: "" properties: - regionId: + timestamp: type: string - name: + source: + type: string + title: + type: string + detail: + type: string + severity: + type: string + identifiers: + additionalProperties: true + type: object + required: + - source + - timestamp + - title + type: object + AdminObservabilityOperation: + example: + id: id + label: label + state: enabled + method: method + path: path + reason: reason + targetId: targetId + properties: + id: + type: string + label: type: string - apiKey: + state: + enum: + - enabled + - disabled + - request_only type: string - apiVersion: - description: The api version of the runner to create - example: "2" - pattern: ^(0|2)$ + method: type: string - domain: - description: The domain of the runner - example: runner1.example.com + path: type: string - apiUrl: - description: The API URL of the runner - example: https://api.runner1.example.com + reason: type: string - proxyUrl: - description: The proxy URL of the runner - example: https://proxy.runner1.example.com + targetId: type: string - cpu: - description: The CPU capacity of the runner - example: 8 - type: number - memoryGiB: - description: The memory capacity of the runner in GiB - example: 16 - type: number - diskGiB: - description: The disk capacity of the runner in GiB - example: 100 - type: number required: - - apiKey - - apiVersion - - name - - regionId + - id + - label + - method + - path + - reason + - state type: object - WebhookAppPortalAccess: + AdminObservabilityCommands: example: - url: https://app.svix.com/app_1234567890 - token: appsk_... + api: api + aiAgentPrompt: aiAgentPrompt properties: - token: - description: The authentication token for the Svix consumer app portal - example: appsk_... + api: type: string - url: - description: The URL to the webhook app portal - example: https://app.svix.com/app_1234567890 + aiAgentPrompt: type: string required: - - token - - url + - aiAgentPrompt + - api type: object - WebhookEvent: - description: The type of event being sent - enum: - - sandbox.created - - sandbox.state.updated - - snapshot.created - - snapshot.state.updated - - snapshot.removed - - volume.created - - volume.state.updated - type: string - SendWebhookDto: + AdminObservabilityClickStackSourceSetup: example: - eventId: evt_1234567890abcdef - payload: - id: sandbox-123 - name: My Sandbox - eventType: sandbox.created + kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" properties: - eventType: - allOf: - - $ref: '#/components/schemas/WebhookEvent' - description: The type of event being sent - example: sandbox.created - payload: - description: The payload data to send - example: - id: sandbox-123 - name: My Sandbox - type: object - eventId: - description: Optional event ID for idempotency - example: evt_1234567890abcdef + kind: + enum: + - logs + - traces + - metrics type: string - required: - - eventType - - payload - type: object - WebhookInitializationStatus: - example: - organizationId: 123e4567-e89b-12d3-a456-426614174000 - createdAt: 2023-01-01T00:00:00.000Z - lastError: Failed to create Svix application - retryCount: 3 - svixApplicationId: app_1234567890 - updatedAt: 2023-01-01T00:00:00.000Z - properties: - organizationId: - description: Organization ID - example: 123e4567-e89b-12d3-a456-426614174000 + envVar: type: string - svixApplicationId: - description: The ID of the Svix application - example: app_1234567890 - nullable: true + name: type: string - lastError: - description: The error reason for the last initialization attempt - example: Failed to create Svix application - nullable: true + dataType: type: string - retryCount: - description: The number of times the initialization has been attempted - example: 3 - type: number - createdAt: - description: When the webhook initialization was created - example: 2023-01-01T00:00:00.000Z + database: type: string - updatedAt: - description: When the webhook initialization was last updated - example: 2023-01-01T00:00:00.000Z + table: + type: string + timestampColumn: + type: string + defaultSelect: type: string + fields: + additionalProperties: true + type: object + metricTables: + additionalProperties: true + type: object required: - - createdAt - - lastError - - organizationId - - retryCount - - svixApplicationId - - updatedAt + - dataType + - database + - envVar + - kind + - name + - timestampColumn type: object - StorageAccessDto: + AdminObservabilityClickStackLinks: example: - organizationId: 123e4567-e89b-12d3-a456-426614174000 - bucket: boxlite - accessKey: temp-user-123 - sessionToken: eyJhbGciOiJIUzI1NiIs... - secret: abchbGciOiJIUzI1NiIs... - storageUrl: storage.example.com + configured: true + message: message + missingSources: + - missingSources + - missingSources + sourceSetup: + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + logsUrl: logsUrl + dashboardUrl: dashboardUrl + tracesUrl: tracesUrl + metricsUrl: metricsUrl + query: query + queryContext: + key: "" properties: - accessKey: - description: Access key for storage authentication - example: temp-user-123 + configured: + type: boolean + message: type: string - secret: - description: Secret key for storage authentication - example: abchbGciOiJIUzI1NiIs... + missingSources: + items: + type: string + type: array + sourceSetup: + items: + $ref: "#/components/schemas/AdminObservabilityClickStackSourceSetup" + type: array + logsUrl: type: string - sessionToken: - description: Session token for storage authentication - example: eyJhbGciOiJIUzI1NiIs... + dashboardUrl: type: string - storageUrl: - description: Storage URL - example: storage.example.com + tracesUrl: type: string - organizationId: - description: Organization ID - example: 123e4567-e89b-12d3-a456-426614174000 + metricsUrl: type: string - bucket: - description: S3 bucket name - example: boxlite + query: type: string + queryContext: + additionalProperties: true + type: object + required: + - configured + type: object + AdminObservabilityExternalLinks: + example: + clickstack: + configured: true + message: message + missingSources: + - missingSources + - missingSources + sourceSetup: + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + logsUrl: logsUrl + dashboardUrl: dashboardUrl + tracesUrl: tracesUrl + metricsUrl: metricsUrl + query: query + queryContext: + key: "" + properties: + clickstack: + $ref: "#/components/schemas/AdminObservabilityClickStackLinks" + required: + - clickstack + type: object + AdminObservabilityInvestigateResponse: + example: + resource: + type: box + title: title + subtitle: subtitle + state: state + owner: owner + identifiers: + key: "" + timeRange: + key: "" + correlation: + traceIds: + - traceIds + - traceIds + orgIds: + - orgIds + - orgIds + userIds: + - userIds + - userIds + boxIds: + - boxIds + - boxIds + runnerIds: + - runnerIds + - runnerIds + machineIds: + - machineIds + - machineIds + requestIds: + - requestIds + - requestIds + operationIds: + - operationIds + - operationIds + executionIds: + - executionIds + - executionIds + jobIds: + - jobIds + - jobIds + serviceNames: + - serviceNames + - serviceNames + sources: + - source: clickhouse + state: available + message: message + count: 0.8008281904610115 + - source: clickhouse + state: available + message: message + count: 0.8008281904610115 + traceSpans: + - traceId: traceId + spanId: spanId + parentSpanId: parentSpanId + spanName: spanName + serviceName: serviceName + layer: layer + timestamp: timestamp + durationNs: 0.8008281904610115 + spanAttributes: + key: spanAttributes + statusCode: statusCode + statusMessage: statusMessage + - traceId: traceId + spanId: spanId + parentSpanId: parentSpanId + spanName: spanName + serviceName: serviceName + layer: layer + timestamp: timestamp + durationNs: 0.8008281904610115 + spanAttributes: + key: spanAttributes + statusCode: statusCode + statusMessage: statusMessage + logs: + - timestamp: timestamp + body: body + severityText: severityText + severityNumber: 0.8008281904610115 + serviceName: serviceName + resourceAttributes: + key: resourceAttributes + logAttributes: + key: logAttributes + traceId: traceId + spanId: spanId + - timestamp: timestamp + body: body + severityText: severityText + severityNumber: 0.8008281904610115 + serviceName: serviceName + resourceAttributes: + key: resourceAttributes + logAttributes: + key: logAttributes + traceId: traceId + spanId: spanId + metrics: + series: + - metricName: metricName + dataPoints: + - timestamp: timestamp + value: 0.8008281904610115 + - timestamp: timestamp + value: 0.8008281904610115 + - metricName: metricName + dataPoints: + - timestamp: timestamp + value: 0.8008281904610115 + - timestamp: timestamp + value: 0.8008281904610115 + boxes: + - id: box_abc123 + organizationId: org_xyz + state: started + runnerId: runner-uuid + cpu: 2 + memoryGiB: 4 + createdAt: 2024-01-01T00:00:00Z + owner: + userId: usr_abc123 + name: Alice Smith + email: alice@example.com + orgName: Alice Personal + personal: true + - id: box_abc123 + organizationId: org_xyz + state: started + runnerId: runner-uuid + cpu: 2 + memoryGiB: 4 + createdAt: 2024-01-01T00:00:00Z + owner: + userId: usr_abc123 + name: Alice Smith + email: alice@example.com + orgName: Alice Personal + personal: true + runners: + - id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev + regionType: shared + draining: false + - id: runner123 + domain: runner1.example.com + apiUrl: https://api.runner1.example.com + proxyUrl: https://proxy.runner1.example.com + cpu: 8 + memory: 16 + disk: 100 + gpu: 1 + gpuType: gpuType + class: small + currentCpuUsagePercentage: 45.6 + currentMemoryUsagePercentage: 68.2 + currentDiskUsagePercentage: 33.8 + currentAllocatedCpu: 4000 + currentAllocatedMemoryGiB: 8000 + currentAllocatedDiskGiB: 50000 + currentStartedBoxes: 5 + availabilityScore: 85 + region: us + name: runner1 + state: initializing + lastChecked: 2024-10-01T12:00:00Z + unschedulable: false + createdAt: 2023-10-01T12:00:00Z + updatedAt: 2023-10-01T12:00:00Z + version: "0" + apiVersion: "0" + appVersion: v0.0.0-dev + regionType: shared + draining: false + machines: + - host: runner-uuid + region: us-east-1 + oversellCpu: 0.75 + cpuWaterline: 45.6 + memWaterline: 68.2 + boxes: 5 + - host: runner-uuid + region: us-east-1 + oversellCpu: 0.75 + cpuWaterline: 45.6 + memWaterline: 68.2 + boxes: 5 + auditLogs: + - id: id + actorId: actorId + actorEmail: actorEmail + organizationId: organizationId + action: action + targetType: targetType + targetId: targetId + statusCode: 6.027456183070403 + errorMessage: errorMessage + source: source + metadata: + key: "" + createdAt: 2000-01-23T04:56:07.000+00:00 + - id: id + actorId: actorId + actorEmail: actorEmail + organizationId: organizationId + action: action + targetType: targetType + targetId: targetId + statusCode: 6.027456183070403 + errorMessage: errorMessage + source: source + metadata: + key: "" + createdAt: 2000-01-23T04:56:07.000+00:00 + xlogs: + - source: source + timestamp: timestamp + serviceName: serviceName + body: body + severityText: severityText + traceId: traceId + spanId: spanId + executionId: executionId + jobId: jobId + stream: stream + attributes: + key: "" + - source: source + timestamp: timestamp + serviceName: serviceName + body: body + severityText: severityText + traceId: traceId + spanId: spanId + executionId: executionId + jobId: jobId + stream: stream + attributes: + key: "" + s3Objects: + - bucket: bucket + key: key + size: 1.4658129805029452 + lastModified: 2000-01-23T04:56:07.000+00:00 + etag: etag + matchedBy: matchedBy + - bucket: bucket + key: key + size: 1.4658129805029452 + lastModified: 2000-01-23T04:56:07.000+00:00 + etag: etag + matchedBy: matchedBy + timeline: + - timestamp: timestamp + source: source + title: title + detail: detail + severity: severity + identifiers: + key: "" + - timestamp: timestamp + source: source + title: title + detail: detail + severity: severity + identifiers: + key: "" + operations: + - id: id + label: label + state: enabled + method: method + path: path + reason: reason + targetId: targetId + - id: id + label: label + state: enabled + method: method + path: path + reason: reason + targetId: targetId + commands: + api: api + aiAgentPrompt: aiAgentPrompt + externalLinks: + clickstack: + configured: true + message: message + missingSources: + - missingSources + - missingSources + sourceSetup: + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + - kind: logs + envVar: envVar + name: name + dataType: dataType + database: database + table: table + timestampColumn: timestampColumn + defaultSelect: defaultSelect + fields: + key: "" + metricTables: + key: "" + logsUrl: logsUrl + dashboardUrl: dashboardUrl + tracesUrl: tracesUrl + metricsUrl: metricsUrl + query: query + queryContext: + key: "" + properties: + resource: + $ref: "#/components/schemas/AdminObservabilityResourceSummary" + correlation: + $ref: "#/components/schemas/AdminObservabilityCorrelation" + sources: + items: + $ref: "#/components/schemas/AdminObservabilitySourceStatus" + type: array + traceSpans: + items: + $ref: "#/components/schemas/TraceSpan" + type: array + logs: + items: + $ref: "#/components/schemas/LogEntry" + type: array + metrics: + $ref: "#/components/schemas/MetricsResponse" + boxes: + items: + $ref: "#/components/schemas/AdminBoxItem" + type: array + runners: + items: + $ref: "#/components/schemas/AdminRunnerItem" + type: array + machines: + items: + $ref: "#/components/schemas/AdminMachineItem" + type: array + auditLogs: + items: + $ref: "#/components/schemas/AdminObservabilityAuditLog" + type: array + xlogs: + items: + $ref: "#/components/schemas/AdminObservabilityXLog" + type: array + s3Objects: + items: + $ref: "#/components/schemas/AdminObservabilityS3Object" + type: array + timeline: + items: + $ref: "#/components/schemas/AdminObservabilityTimelineEvent" + type: array + operations: + items: + $ref: "#/components/schemas/AdminObservabilityOperation" + type: array + commands: + $ref: "#/components/schemas/AdminObservabilityCommands" + externalLinks: + $ref: "#/components/schemas/AdminObservabilityExternalLinks" required: - - accessKey - - bucket - - organizationId - - secret - - sessionToken - - storageUrl + - auditLogs + - boxes + - commands + - correlation + - externalLinks + - logs + - machines + - metrics + - operations + - resource + - runners + - s3Objects + - sources + - timeline + - traceSpans + - xlogs type: object AuditLog: example: - metadata: - key: "" + id: id + actorId: actorId + actorEmail: actorEmail + organizationId: organizationId + action: action + targetType: targetType targetId: targetId + statusCode: 0.8008281904610115 errorMessage: errorMessage ipAddress: ipAddress - actorEmail: actorEmail - targetType: targetType userAgent: userAgent source: source - organizationId: organizationId + metadata: + key: "" createdAt: 2000-01-23T04:56:07.000+00:00 - actorId: actorId - action: action - id: id - statusCode: 0.8008281904610115 properties: id: type: string @@ -14401,358 +9708,199 @@ components: type: object PaginatedAuditLogs: example: - total: 6.027456183070403 - nextToken: nextToken - totalPages: 5.962133916683182 - page: 1.4658129805029452 items: - - metadata: - key: "" - targetId: targetId - errorMessage: errorMessage - ipAddress: ipAddress - actorEmail: actorEmail - targetType: targetType - userAgent: userAgent - source: source - organizationId: organizationId - createdAt: 2000-01-23T04:56:07.000+00:00 + - id: id actorId: actorId - action: action - id: id - statusCode: 0.8008281904610115 - - metadata: - key: "" - targetId: targetId - errorMessage: errorMessage - ipAddress: ipAddress actorEmail: actorEmail - targetType: targetType - userAgent: userAgent - source: source organizationId: organizationId - createdAt: 2000-01-23T04:56:07.000+00:00 - actorId: actorId action: action - id: id - statusCode: 0.8008281904610115 - properties: - items: - items: - $ref: '#/components/schemas/AuditLog' - type: array - total: - type: number - page: - type: number - totalPages: - type: number - nextToken: - description: Token for next page in cursor-based pagination - type: string - required: - - items - - page - - total - - totalPages - type: object - LogEntry: - example: - traceId: traceId - spanId: spanId - severityText: severityText - severityNumber: 0.8008281904610115 - logAttributes: - key: logAttributes - body: body - serviceName: serviceName - resourceAttributes: - key: resourceAttributes - timestamp: timestamp - properties: - timestamp: - description: Timestamp of the log entry - type: string - body: - description: Log message body - type: string - severityText: - description: "Severity level text (e.g., INFO, WARN, ERROR)" - type: string - severityNumber: - description: Severity level number - type: number - serviceName: - description: Service name that generated the log - type: string - resourceAttributes: - additionalProperties: - type: string - description: Resource attributes from OTEL - type: object - logAttributes: - additionalProperties: - type: string - description: Log-specific attributes - type: object - traceId: - description: Associated trace ID if available - type: string - spanId: - description: Associated span ID if available - type: string - required: - - body - - logAttributes - - resourceAttributes - - serviceName - - severityText - - timestamp - type: object - PaginatedLogs: - example: - total: 6.027456183070403 - totalPages: 5.962133916683182 - page: 1.4658129805029452 - items: - - traceId: traceId - spanId: spanId - severityText: severityText - severityNumber: 0.8008281904610115 - logAttributes: - key: logAttributes - body: body - serviceName: serviceName - resourceAttributes: - key: resourceAttributes - timestamp: timestamp - - traceId: traceId - spanId: spanId - severityText: severityText - severityNumber: 0.8008281904610115 - logAttributes: - key: logAttributes - body: body - serviceName: serviceName - resourceAttributes: - key: resourceAttributes - timestamp: timestamp - properties: - items: - description: List of log entries - items: - $ref: '#/components/schemas/LogEntry' - type: array - total: - description: Total number of log entries matching the query - type: number - page: - description: Current page number - type: number - totalPages: - description: Total number of pages - type: number - required: - - items - - page - - total - - totalPages - type: object - TraceSummary: - example: - traceId: traceId - spanCount: 6.027456183070403 - rootSpanName: rootSpanName - startTime: startTime - endTime: endTime - durationMs: 0.8008281904610115 - statusCode: statusCode - properties: - traceId: - description: Unique trace identifier - type: string - rootSpanName: - description: Name of the root span - type: string - startTime: - description: Trace start time - type: string - endTime: - description: Trace end time - type: string - durationMs: - description: Total duration in milliseconds - type: number - spanCount: - description: Number of spans in this trace - type: number - statusCode: - description: Status code of the trace - type: string - required: - - durationMs - - endTime - - rootSpanName - - spanCount - - startTime - - traceId - type: object - PaginatedTraces: - example: - total: 1.4658129805029452 - totalPages: 5.637376656633329 - page: 5.962133916683182 - items: - - traceId: traceId - spanCount: 6.027456183070403 - rootSpanName: rootSpanName - startTime: startTime - endTime: endTime - durationMs: 0.8008281904610115 - statusCode: statusCode - - traceId: traceId - spanCount: 6.027456183070403 - rootSpanName: rootSpanName - startTime: startTime - endTime: endTime - durationMs: 0.8008281904610115 - statusCode: statusCode + targetType: targetType + targetId: targetId + statusCode: 0.8008281904610115 + errorMessage: errorMessage + ipAddress: ipAddress + userAgent: userAgent + source: source + metadata: + key: "" + createdAt: 2000-01-23T04:56:07.000+00:00 + - id: id + actorId: actorId + actorEmail: actorEmail + organizationId: organizationId + action: action + targetType: targetType + targetId: targetId + statusCode: 0.8008281904610115 + errorMessage: errorMessage + ipAddress: ipAddress + userAgent: userAgent + source: source + metadata: + key: "" + createdAt: 2000-01-23T04:56:07.000+00:00 + total: 6.027456183070403 + page: 1.4658129805029452 + totalPages: 5.962133916683182 + nextToken: nextToken properties: items: - description: List of trace summaries items: - $ref: '#/components/schemas/TraceSummary' + $ref: "#/components/schemas/AuditLog" type: array total: - description: Total number of traces matching the query type: number page: - description: Current page number type: number totalPages: - description: Total number of pages type: number + nextToken: + description: Token for next page in cursor-based pagination + type: string required: - items - page - total - totalPages type: object - TraceSpan: + WebhookAppPortalAccess: example: - traceId: traceId - spanId: spanId - spanAttributes: - key: spanAttributes - parentSpanId: parentSpanId - durationNs: 0.8008281904610115 - statusMessage: statusMessage - spanName: spanName - timestamp: timestamp - statusCode: statusCode + token: appsk_... + url: https://app.svix.com/app_1234567890 properties: - traceId: - description: Trace identifier - type: string - spanId: - description: Span identifier - type: string - parentSpanId: - description: Parent span identifier - type: string - spanName: - description: Span name - type: string - timestamp: - description: Span start timestamp - type: string - durationNs: - description: Span duration in nanoseconds - type: number - spanAttributes: - additionalProperties: - type: string - description: Span attributes - type: object - statusCode: - description: Status code of the span + token: + description: The authentication token for the Svix consumer app portal + example: appsk_... type: string - statusMessage: - description: Status message + url: + description: The URL to the webhook app portal + example: https://app.svix.com/app_1234567890 type: string required: - - durationNs - - spanAttributes - - spanId - - spanName - - timestamp - - traceId + - token + - url type: object - MetricDataPoint: + WebhookEvent: + description: The type of event being sent + enum: + - box.created + - box.state.updated + - template.created + - template.state.updated + - template.removed + - volume.created + - volume.state.updated + type: string + SendWebhookDto: example: - value: 0.8008281904610115 - timestamp: timestamp + eventType: box.created + payload: + id: box-123 + name: My Box + eventId: evt_1234567890abcdef properties: - timestamp: - description: Timestamp of the data point + eventType: + allOf: + - $ref: "#/components/schemas/WebhookEvent" + description: The type of event being sent + example: box.created + payload: + description: The payload data to send + example: + id: box-123 + name: My Box + type: object + eventId: + description: Optional event ID for idempotency + example: evt_1234567890abcdef type: string - value: - description: Value at this timestamp - type: number required: - - timestamp - - value + - eventType + - payload type: object - MetricSeries: + WebhookInitializationStatus: example: - metricName: metricName - dataPoints: - - value: 0.8008281904610115 - timestamp: timestamp - - value: 0.8008281904610115 - timestamp: timestamp + organizationId: 123e4567-e89b-12d3-a456-426614174000 + svixApplicationId: app_1234567890 + lastError: Failed to create Svix application + retryCount: 3 + createdAt: 2023-01-01T00:00:00.000Z + updatedAt: 2023-01-01T00:00:00.000Z properties: - metricName: - description: Name of the metric + organizationId: + description: Organization ID + example: 123e4567-e89b-12d3-a456-426614174000 + type: string + svixApplicationId: + description: The ID of the Svix application + example: app_1234567890 + nullable: true + type: string + lastError: + description: The error reason for the last initialization attempt + example: Failed to create Svix application + nullable: true + type: string + retryCount: + description: The number of times the initialization has been attempted + example: 3 + type: number + createdAt: + description: When the webhook initialization was created + example: 2023-01-01T00:00:00.000Z + type: string + updatedAt: + description: When the webhook initialization was last updated + example: 2023-01-01T00:00:00.000Z type: string - dataPoints: - description: Data points for this metric - items: - $ref: '#/components/schemas/MetricDataPoint' - type: array required: - - dataPoints - - metricName + - createdAt + - lastError + - organizationId + - retryCount + - svixApplicationId + - updatedAt type: object - MetricsResponse: + StorageAccessDto: example: - series: - - metricName: metricName - dataPoints: - - value: 0.8008281904610115 - timestamp: timestamp - - value: 0.8008281904610115 - timestamp: timestamp - - metricName: metricName - dataPoints: - - value: 0.8008281904610115 - timestamp: timestamp - - value: 0.8008281904610115 - timestamp: timestamp - properties: - series: - description: List of metric series - items: - $ref: '#/components/schemas/MetricSeries' - type: array - required: - - series - type: object - uploadFile_deprecated_request: + accessKey: temp-user-123 + secret: abchbGciOiJIUzI1NiIs... + sessionToken: eyJhbGciOiJIUzI1NiIs... + storageUrl: storage.example.com + organizationId: 123e4567-e89b-12d3-a456-426614174000 + bucket: boxlite properties: - file: - format: binary + accessKey: + description: Access key for storage authentication + example: temp-user-123 + type: string + secret: + description: Secret key for storage authentication + example: abchbGciOiJIUzI1NiIs... + type: string + sessionToken: + description: Session token for storage authentication + example: eyJhbGciOiJIUzI1NiIs... + type: string + storageUrl: + description: Storage URL + example: storage.example.com + type: string + organizationId: + description: Organization ID + example: 123e4567-e89b-12d3-a456-426614174000 + type: string + bucket: + description: S3 bucket name + example: boxlite type: string + required: + - accessKey + - bucket + - organizationId + - secret + - sessionToken + - storageUrl type: object WebhookController_getStatus_200_response: example: @@ -14771,63 +9919,21 @@ components: type: object HealthController_check_200_response: example: - details: - database: - status: up - error: {} status: ok info: database: status: up - properties: - status: - example: ok - type: string - info: - additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' - example: - database: - status: up - nullable: true - type: object - error: - additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' - example: {} - nullable: true - type: object - details: - additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' - example: - database: - status: up - type: object - type: object - HealthController_check_503_response: - example: + error: {} details: database: status: up - redis: - status: down - message: Could not connect - error: - redis: - status: down - message: Could not connect - status: error - info: - database: - status: up properties: status: - example: error + example: ok type: string info: additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' + $ref: "#/components/schemas/HealthController_check_200_response_info_value" example: database: status: up @@ -14835,22 +9941,16 @@ components: type: object error: additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' - example: - redis: - status: down - message: Could not connect + $ref: "#/components/schemas/HealthController_check_200_response_info_value" + example: {} nullable: true type: object details: additionalProperties: - $ref: '#/components/schemas/HealthController_check_200_response_info_value' + $ref: "#/components/schemas/HealthController_check_200_response_info_value" example: database: status: up - redis: - status: down - message: Could not connect type: object type: object securitySchemes: diff --git a/apps/api-client-go/api_admin.go b/apps/api-client-go/api_admin.go index adbf03156..7471ca1b9 100644 --- a/apps/api-client-go/api_admin.go +++ b/apps/api-client-go/api_admin.go @@ -18,6 +18,8 @@ import ( "net/http" "net/url" "strings" + "time" + "reflect" ) @@ -47,6 +49,79 @@ type AdminAPI interface { // AdminDeleteRunnerExecute executes the request AdminDeleteRunnerExecute(r AdminAPIAdminDeleteRunnerRequest) (*http.Response, error) + /* + AdminGetObservabilityLogs Get admin-scoped logs + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityLogsRequest + */ + AdminGetObservabilityLogs(ctx context.Context) AdminAPIAdminGetObservabilityLogsRequest + + // AdminGetObservabilityLogsExecute executes the request + // @return PaginatedLogs + AdminGetObservabilityLogsExecute(r AdminAPIAdminGetObservabilityLogsRequest) (*PaginatedLogs, *http.Response, error) + + /* + AdminGetObservabilityMetrics Get admin-scoped metrics + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityMetricsRequest + */ + AdminGetObservabilityMetrics(ctx context.Context) AdminAPIAdminGetObservabilityMetricsRequest + + // AdminGetObservabilityMetricsExecute executes the request + // @return MetricsResponse + AdminGetObservabilityMetricsExecute(r AdminAPIAdminGetObservabilityMetricsRequest) (*MetricsResponse, *http.Response, error) + + /* + AdminGetObservabilityStatus Get admin observability backend and layer status + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityStatusRequest + */ + AdminGetObservabilityStatus(ctx context.Context) AdminAPIAdminGetObservabilityStatusRequest + + // AdminGetObservabilityStatusExecute executes the request + // @return AdminObservabilityStatusDto + AdminGetObservabilityStatusExecute(r AdminAPIAdminGetObservabilityStatusRequest) (*AdminObservabilityStatusDto, *http.Response, error) + + /* + AdminGetObservabilityTraceSpans Get admin-scoped trace spans + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param traceId + @return AdminAPIAdminGetObservabilityTraceSpansRequest + */ + AdminGetObservabilityTraceSpans(ctx context.Context, traceId string) AdminAPIAdminGetObservabilityTraceSpansRequest + + // AdminGetObservabilityTraceSpansExecute executes the request + // @return []TraceSpan + AdminGetObservabilityTraceSpansExecute(r AdminAPIAdminGetObservabilityTraceSpansRequest) ([]TraceSpan, *http.Response, error) + + /* + AdminGetObservabilityTraces Get admin-scoped traces + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityTracesRequest + */ + AdminGetObservabilityTraces(ctx context.Context) AdminAPIAdminGetObservabilityTracesRequest + + // AdminGetObservabilityTracesExecute executes the request + // @return PaginatedTraces + AdminGetObservabilityTracesExecute(r AdminAPIAdminGetObservabilityTracesRequest) (*PaginatedTraces, *http.Response, error) + + /* + AdminGetOverview Admin KPI summary + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetOverviewRequest + */ + AdminGetOverview(ctx context.Context) AdminAPIAdminGetOverviewRequest + + // AdminGetOverviewExecute executes the request + // @return AdminOverview + AdminGetOverviewExecute(r AdminAPIAdminGetOverviewRequest) (*AdminOverview, *http.Response, error) + /* AdminGetRunnerById Get runner by ID @@ -57,8 +132,44 @@ type AdminAPI interface { AdminGetRunnerById(ctx context.Context, id string) AdminAPIAdminGetRunnerByIdRequest // AdminGetRunnerByIdExecute executes the request - // @return RunnerFull - AdminGetRunnerByIdExecute(r AdminAPIAdminGetRunnerByIdRequest) (*RunnerFull, *http.Response, error) + // @return AdminRunner + AdminGetRunnerByIdExecute(r AdminAPIAdminGetRunnerByIdRequest) (*AdminRunner, *http.Response, error) + + /* + AdminInvestigateObservability Investigate related observability and platform state from trace or resource identifiers + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminInvestigateObservabilityRequest + */ + AdminInvestigateObservability(ctx context.Context) AdminAPIAdminInvestigateObservabilityRequest + + // AdminInvestigateObservabilityExecute executes the request + // @return AdminObservabilityInvestigateResponse + AdminInvestigateObservabilityExecute(r AdminAPIAdminInvestigateObservabilityRequest) (*AdminObservabilityInvestigateResponse, *http.Response, error) + + /* + AdminListBoxes List all boxes (cross-org) + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListBoxesRequest + */ + AdminListBoxes(ctx context.Context) AdminAPIAdminListBoxesRequest + + // AdminListBoxesExecute executes the request + // @return []AdminBoxItem + AdminListBoxesExecute(r AdminAPIAdminListBoxesRequest) ([]AdminBoxItem, *http.Response, error) + + /* + AdminListMachines Runner-as-machine resource view + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListMachinesRequest + */ + AdminListMachines(ctx context.Context) AdminAPIAdminListMachinesRequest + + // AdminListMachinesExecute executes the request + // @return []AdminMachineItem + AdminListMachinesExecute(r AdminAPIAdminListMachinesRequest) ([]AdminMachineItem, *http.Response, error) /* AdminListRunners List all runners @@ -69,21 +180,45 @@ type AdminAPI interface { AdminListRunners(ctx context.Context) AdminAPIAdminListRunnersRequest // AdminListRunnersExecute executes the request - // @return []RunnerFull - AdminListRunnersExecute(r AdminAPIAdminListRunnersRequest) ([]RunnerFull, *http.Response, error) + // @return []AdminRunner + AdminListRunnersExecute(r AdminAPIAdminListRunnersRequest) ([]AdminRunner, *http.Response, error) + + /* + AdminListRunnersOverview List all runners with full details + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListRunnersOverviewRequest + */ + AdminListRunnersOverview(ctx context.Context) AdminAPIAdminListRunnersOverviewRequest + + // AdminListRunnersOverviewExecute executes the request + // @return []AdminRunnerItem + AdminListRunnersOverviewExecute(r AdminAPIAdminListRunnersOverviewRequest) ([]AdminRunnerItem, *http.Response, error) + + /* + AdminListUsers List all users (cross-org) + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListUsersRequest + */ + AdminListUsers(ctx context.Context) AdminAPIAdminListUsersRequest + + // AdminListUsersExecute executes the request + // @return []AdminUserItem + AdminListUsersExecute(r AdminAPIAdminListUsersRequest) ([]AdminUserItem, *http.Response, error) /* - AdminRecoverSandbox Recover sandbox from error state as an admin + AdminRecoverBox Recover box from error state as an admin @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return AdminAPIAdminRecoverSandboxRequest + @param boxId ID of the box + @return AdminAPIAdminRecoverBoxRequest */ - AdminRecoverSandbox(ctx context.Context, sandboxId string) AdminAPIAdminRecoverSandboxRequest + AdminRecoverBox(ctx context.Context, boxId string) AdminAPIAdminRecoverBoxRequest - // AdminRecoverSandboxExecute executes the request - // @return Sandbox - AdminRecoverSandboxExecute(r AdminAPIAdminRecoverSandboxRequest) (*Sandbox, *http.Response, error) + // AdminRecoverBoxExecute executes the request + // @return Box + AdminRecoverBoxExecute(r AdminAPIAdminRecoverBoxRequest) (*Box, *http.Response, error) /* AdminUpdateRunnerScheduling Update runner scheduling status @@ -299,53 +434,245 @@ func (a *AdminAPIService) AdminDeleteRunnerExecute(r AdminAPIAdminDeleteRunnerRe return localVarHTTPResponse, nil } -type AdminAPIAdminGetRunnerByIdRequest struct { +type AdminAPIAdminGetObservabilityLogsRequest struct { ctx context.Context ApiService AdminAPI - id string + from *time.Time + to *time.Time + page *float32 + limit *float32 + layer *string + serviceName *string + orgId *string + userId *string + boxId *string + runnerId *string + machineId *string + traceId *string + requestId *string + operationId *string + executionId *string + jobId *string + severities *[]string + search *string } -func (r AdminAPIAdminGetRunnerByIdRequest) Execute() (*RunnerFull, *http.Response, error) { - return r.ApiService.AdminGetRunnerByIdExecute(r) +// Start of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityLogsRequest) From(from time.Time) AdminAPIAdminGetObservabilityLogsRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityLogsRequest) To(to time.Time) AdminAPIAdminGetObservabilityLogsRequest { + r.to = &to + return r +} + +// Page number (1-indexed) +func (r AdminAPIAdminGetObservabilityLogsRequest) Page(page float32) AdminAPIAdminGetObservabilityLogsRequest { + r.page = &page + return r +} + +// Number of items per page +func (r AdminAPIAdminGetObservabilityLogsRequest) Limit(limit float32) AdminAPIAdminGetObservabilityLogsRequest { + r.limit = &limit + return r +} + +// Telemetry producer layer +func (r AdminAPIAdminGetObservabilityLogsRequest) Layer(layer string) AdminAPIAdminGetObservabilityLogsRequest { + r.layer = &layer + return r +} + +// OpenTelemetry service.name filter +func (r AdminAPIAdminGetObservabilityLogsRequest) ServiceName(serviceName string) AdminAPIAdminGetObservabilityLogsRequest { + r.serviceName = &serviceName + return r +} + +// Organization ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) OrgId(orgId string) AdminAPIAdminGetObservabilityLogsRequest { + r.orgId = &orgId + return r +} + +// User ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) UserId(userId string) AdminAPIAdminGetObservabilityLogsRequest { + r.userId = &userId + return r +} + +// Box ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) BoxId(boxId string) AdminAPIAdminGetObservabilityLogsRequest { + r.boxId = &boxId + return r +} + +// Runner ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) RunnerId(runnerId string) AdminAPIAdminGetObservabilityLogsRequest { + r.runnerId = &runnerId + return r +} + +// Machine or host ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) MachineId(machineId string) AdminAPIAdminGetObservabilityLogsRequest { + r.machineId = &machineId + return r +} + +// Trace ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) TraceId(traceId string) AdminAPIAdminGetObservabilityLogsRequest { + r.traceId = &traceId + return r +} + +// Request ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) RequestId(requestId string) AdminAPIAdminGetObservabilityLogsRequest { + r.requestId = &requestId + return r +} + +// Operation ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) OperationId(operationId string) AdminAPIAdminGetObservabilityLogsRequest { + r.operationId = &operationId + return r +} + +// Execution ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) ExecutionId(executionId string) AdminAPIAdminGetObservabilityLogsRequest { + r.executionId = &executionId + return r +} + +// Job ID filter +func (r AdminAPIAdminGetObservabilityLogsRequest) JobId(jobId string) AdminAPIAdminGetObservabilityLogsRequest { + r.jobId = &jobId + return r +} + +// Filter by severity levels (DEBUG, INFO, WARN, ERROR) +func (r AdminAPIAdminGetObservabilityLogsRequest) Severities(severities []string) AdminAPIAdminGetObservabilityLogsRequest { + r.severities = &severities + return r +} + +// Search in log body +func (r AdminAPIAdminGetObservabilityLogsRequest) Search(search string) AdminAPIAdminGetObservabilityLogsRequest { + r.search = &search + return r +} + +func (r AdminAPIAdminGetObservabilityLogsRequest) Execute() (*PaginatedLogs, *http.Response, error) { + return r.ApiService.AdminGetObservabilityLogsExecute(r) } /* -AdminGetRunnerById Get runner by ID +AdminGetObservabilityLogs Get admin-scoped logs @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Runner ID - @return AdminAPIAdminGetRunnerByIdRequest + @return AdminAPIAdminGetObservabilityLogsRequest */ -func (a *AdminAPIService) AdminGetRunnerById(ctx context.Context, id string) AdminAPIAdminGetRunnerByIdRequest { - return AdminAPIAdminGetRunnerByIdRequest{ +func (a *AdminAPIService) AdminGetObservabilityLogs(ctx context.Context) AdminAPIAdminGetObservabilityLogsRequest { + return AdminAPIAdminGetObservabilityLogsRequest{ ApiService: a, ctx: ctx, - id: id, } } // Execute executes the request -// @return RunnerFull -func (a *AdminAPIService) AdminGetRunnerByIdExecute(r AdminAPIAdminGetRunnerByIdRequest) (*RunnerFull, *http.Response, error) { +// @return PaginatedLogs +func (a *AdminAPIService) AdminGetObservabilityLogsExecute(r AdminAPIAdminGetObservabilityLogsRequest) (*PaginatedLogs, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} formFiles []formFile - localVarReturnValue *RunnerFull + localVarReturnValue *PaginatedLogs ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetRunnerById") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetObservabilityLogs") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/admin/runners/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + localVarPath := localBasePath + "/admin/observability/logs" localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} + if r.from != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + } + if r.to != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + } + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.layer != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "layer", r.layer, "form", "") + } + if r.serviceName != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "serviceName", r.serviceName, "form", "") + } + if r.orgId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "orgId", r.orgId, "form", "") + } + if r.userId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "userId", r.userId, "form", "") + } + if r.boxId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "boxId", r.boxId, "form", "") + } + if r.runnerId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "runnerId", r.runnerId, "form", "") + } + if r.machineId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "machineId", r.machineId, "form", "") + } + if r.traceId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "traceId", r.traceId, "form", "") + } + if r.requestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "requestId", r.requestId, "form", "") + } + if r.operationId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "operationId", r.operationId, "form", "") + } + if r.executionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "executionId", r.executionId, "form", "") + } + if r.jobId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "jobId", r.jobId, "form", "") + } + if r.severities != nil { + t := *r.severities + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "severities", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "severities", t, "form", "multi") + } + } + if r.search != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "search", r.search, "form", "") + } // to determine the Content-Type header localVarHTTPContentTypes := []string{} @@ -400,59 +727,1814 @@ func (a *AdminAPIService) AdminGetRunnerByIdExecute(r AdminAPIAdminGetRunnerById return localVarReturnValue, localVarHTTPResponse, nil } -type AdminAPIAdminListRunnersRequest struct { +type AdminAPIAdminGetObservabilityMetricsRequest struct { ctx context.Context ApiService AdminAPI - regionId *string + from *time.Time + to *time.Time + page *float32 + limit *float32 + layer *string + serviceName *string + orgId *string + userId *string + boxId *string + runnerId *string + machineId *string + traceId *string + requestId *string + operationId *string + executionId *string + jobId *string + metricNames *[]string } -// Filter runners by region ID -func (r AdminAPIAdminListRunnersRequest) RegionId(regionId string) AdminAPIAdminListRunnersRequest { - r.regionId = ®ionId +// Start of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityMetricsRequest) From(from time.Time) AdminAPIAdminGetObservabilityMetricsRequest { + r.from = &from return r } -func (r AdminAPIAdminListRunnersRequest) Execute() ([]RunnerFull, *http.Response, error) { - return r.ApiService.AdminListRunnersExecute(r) +// End of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityMetricsRequest) To(to time.Time) AdminAPIAdminGetObservabilityMetricsRequest { + r.to = &to + return r +} + +// Page number (1-indexed) +func (r AdminAPIAdminGetObservabilityMetricsRequest) Page(page float32) AdminAPIAdminGetObservabilityMetricsRequest { + r.page = &page + return r +} + +// Number of items per page +func (r AdminAPIAdminGetObservabilityMetricsRequest) Limit(limit float32) AdminAPIAdminGetObservabilityMetricsRequest { + r.limit = &limit + return r +} + +// Telemetry producer layer +func (r AdminAPIAdminGetObservabilityMetricsRequest) Layer(layer string) AdminAPIAdminGetObservabilityMetricsRequest { + r.layer = &layer + return r +} + +// OpenTelemetry service.name filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) ServiceName(serviceName string) AdminAPIAdminGetObservabilityMetricsRequest { + r.serviceName = &serviceName + return r +} + +// Organization ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) OrgId(orgId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.orgId = &orgId + return r +} + +// User ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) UserId(userId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.userId = &userId + return r +} + +// Box ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) BoxId(boxId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.boxId = &boxId + return r +} + +// Runner ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) RunnerId(runnerId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.runnerId = &runnerId + return r +} + +// Machine or host ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) MachineId(machineId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.machineId = &machineId + return r +} + +// Trace ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) TraceId(traceId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.traceId = &traceId + return r +} + +// Request ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) RequestId(requestId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.requestId = &requestId + return r +} + +// Operation ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) OperationId(operationId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.operationId = &operationId + return r +} + +// Execution ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) ExecutionId(executionId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.executionId = &executionId + return r +} + +// Job ID filter +func (r AdminAPIAdminGetObservabilityMetricsRequest) JobId(jobId string) AdminAPIAdminGetObservabilityMetricsRequest { + r.jobId = &jobId + return r +} + +// Filter by metric names +func (r AdminAPIAdminGetObservabilityMetricsRequest) MetricNames(metricNames []string) AdminAPIAdminGetObservabilityMetricsRequest { + r.metricNames = &metricNames + return r +} + +func (r AdminAPIAdminGetObservabilityMetricsRequest) Execute() (*MetricsResponse, *http.Response, error) { + return r.ApiService.AdminGetObservabilityMetricsExecute(r) } /* -AdminListRunners List all runners +AdminGetObservabilityMetrics Get admin-scoped metrics @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return AdminAPIAdminListRunnersRequest + @return AdminAPIAdminGetObservabilityMetricsRequest */ -func (a *AdminAPIService) AdminListRunners(ctx context.Context) AdminAPIAdminListRunnersRequest { - return AdminAPIAdminListRunnersRequest{ +func (a *AdminAPIService) AdminGetObservabilityMetrics(ctx context.Context) AdminAPIAdminGetObservabilityMetricsRequest { + return AdminAPIAdminGetObservabilityMetricsRequest{ ApiService: a, ctx: ctx, } } // Execute executes the request -// @return []RunnerFull -func (a *AdminAPIService) AdminListRunnersExecute(r AdminAPIAdminListRunnersRequest) ([]RunnerFull, *http.Response, error) { +// @return MetricsResponse +func (a *AdminAPIService) AdminGetObservabilityMetricsExecute(r AdminAPIAdminGetObservabilityMetricsRequest) (*MetricsResponse, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} formFiles []formFile - localVarReturnValue []RunnerFull + localVarReturnValue *MetricsResponse ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListRunners") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetObservabilityMetrics") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/admin/runners" + localVarPath := localBasePath + "/admin/observability/metrics" localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} - if r.regionId != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "regionId", r.regionId, "form", "") + if r.from != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + } + if r.to != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + } + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.layer != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "layer", r.layer, "form", "") + } + if r.serviceName != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "serviceName", r.serviceName, "form", "") + } + if r.orgId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "orgId", r.orgId, "form", "") + } + if r.userId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "userId", r.userId, "form", "") + } + if r.boxId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "boxId", r.boxId, "form", "") + } + if r.runnerId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "runnerId", r.runnerId, "form", "") + } + if r.machineId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "machineId", r.machineId, "form", "") + } + if r.traceId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "traceId", r.traceId, "form", "") + } + if r.requestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "requestId", r.requestId, "form", "") + } + if r.operationId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "operationId", r.operationId, "form", "") + } + if r.executionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "executionId", r.executionId, "form", "") + } + if r.jobId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "jobId", r.jobId, "form", "") + } + if r.metricNames != nil { + t := *r.metricNames + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", t, "form", "multi") + } + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminGetObservabilityStatusRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminGetObservabilityStatusRequest) Execute() (*AdminObservabilityStatusDto, *http.Response, error) { + return r.ApiService.AdminGetObservabilityStatusExecute(r) +} + +/* +AdminGetObservabilityStatus Get admin observability backend and layer status + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityStatusRequest +*/ +func (a *AdminAPIService) AdminGetObservabilityStatus(ctx context.Context) AdminAPIAdminGetObservabilityStatusRequest { + return AdminAPIAdminGetObservabilityStatusRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return AdminObservabilityStatusDto +func (a *AdminAPIService) AdminGetObservabilityStatusExecute(r AdminAPIAdminGetObservabilityStatusRequest) (*AdminObservabilityStatusDto, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *AdminObservabilityStatusDto + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetObservabilityStatus") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/observability/status" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminGetObservabilityTraceSpansRequest struct { + ctx context.Context + ApiService AdminAPI + traceId string + from *time.Time + to *time.Time + page *float32 + limit *float32 + layer *string + serviceName *string + orgId *string + userId *string + boxId *string + runnerId *string + machineId *string + requestId *string + operationId *string + executionId *string + jobId *string +} + +// Start of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) From(from time.Time) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) To(to time.Time) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.to = &to + return r +} + +// Page number (1-indexed) +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) Page(page float32) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.page = &page + return r +} + +// Number of items per page +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) Limit(limit float32) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.limit = &limit + return r +} + +// Telemetry producer layer +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) Layer(layer string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.layer = &layer + return r +} + +// OpenTelemetry service.name filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) ServiceName(serviceName string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.serviceName = &serviceName + return r +} + +// Organization ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) OrgId(orgId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.orgId = &orgId + return r +} + +// User ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) UserId(userId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.userId = &userId + return r +} + +// Box ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) BoxId(boxId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.boxId = &boxId + return r +} + +// Runner ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) RunnerId(runnerId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.runnerId = &runnerId + return r +} + +// Machine or host ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) MachineId(machineId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.machineId = &machineId + return r +} + +// Request ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) RequestId(requestId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.requestId = &requestId + return r +} + +// Operation ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) OperationId(operationId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.operationId = &operationId + return r +} + +// Execution ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) ExecutionId(executionId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.executionId = &executionId + return r +} + +// Job ID filter +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) JobId(jobId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + r.jobId = &jobId + return r +} + +func (r AdminAPIAdminGetObservabilityTraceSpansRequest) Execute() ([]TraceSpan, *http.Response, error) { + return r.ApiService.AdminGetObservabilityTraceSpansExecute(r) +} + +/* +AdminGetObservabilityTraceSpans Get admin-scoped trace spans + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param traceId + @return AdminAPIAdminGetObservabilityTraceSpansRequest +*/ +func (a *AdminAPIService) AdminGetObservabilityTraceSpans(ctx context.Context, traceId string) AdminAPIAdminGetObservabilityTraceSpansRequest { + return AdminAPIAdminGetObservabilityTraceSpansRequest{ + ApiService: a, + ctx: ctx, + traceId: traceId, + } +} + +// Execute executes the request +// @return []TraceSpan +func (a *AdminAPIService) AdminGetObservabilityTraceSpansExecute(r AdminAPIAdminGetObservabilityTraceSpansRequest) ([]TraceSpan, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []TraceSpan + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetObservabilityTraceSpans") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/observability/traces/{traceId}" + localVarPath = strings.Replace(localVarPath, "{"+"traceId"+"}", url.PathEscape(parameterValueToString(r.traceId, "traceId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.from != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + } + if r.to != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + } + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.layer != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "layer", r.layer, "form", "") + } + if r.serviceName != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "serviceName", r.serviceName, "form", "") + } + if r.orgId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "orgId", r.orgId, "form", "") + } + if r.userId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "userId", r.userId, "form", "") + } + if r.boxId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "boxId", r.boxId, "form", "") + } + if r.runnerId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "runnerId", r.runnerId, "form", "") + } + if r.machineId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "machineId", r.machineId, "form", "") + } + if r.requestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "requestId", r.requestId, "form", "") + } + if r.operationId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "operationId", r.operationId, "form", "") + } + if r.executionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "executionId", r.executionId, "form", "") + } + if r.jobId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "jobId", r.jobId, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminGetObservabilityTracesRequest struct { + ctx context.Context + ApiService AdminAPI + from *time.Time + to *time.Time + page *float32 + limit *float32 + layer *string + serviceName *string + orgId *string + userId *string + boxId *string + runnerId *string + machineId *string + traceId *string + requestId *string + operationId *string + executionId *string + jobId *string +} + +// Start of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityTracesRequest) From(from time.Time) AdminAPIAdminGetObservabilityTracesRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r AdminAPIAdminGetObservabilityTracesRequest) To(to time.Time) AdminAPIAdminGetObservabilityTracesRequest { + r.to = &to + return r +} + +// Page number (1-indexed) +func (r AdminAPIAdminGetObservabilityTracesRequest) Page(page float32) AdminAPIAdminGetObservabilityTracesRequest { + r.page = &page + return r +} + +// Number of items per page +func (r AdminAPIAdminGetObservabilityTracesRequest) Limit(limit float32) AdminAPIAdminGetObservabilityTracesRequest { + r.limit = &limit + return r +} + +// Telemetry producer layer +func (r AdminAPIAdminGetObservabilityTracesRequest) Layer(layer string) AdminAPIAdminGetObservabilityTracesRequest { + r.layer = &layer + return r +} + +// OpenTelemetry service.name filter +func (r AdminAPIAdminGetObservabilityTracesRequest) ServiceName(serviceName string) AdminAPIAdminGetObservabilityTracesRequest { + r.serviceName = &serviceName + return r +} + +// Organization ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) OrgId(orgId string) AdminAPIAdminGetObservabilityTracesRequest { + r.orgId = &orgId + return r +} + +// User ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) UserId(userId string) AdminAPIAdminGetObservabilityTracesRequest { + r.userId = &userId + return r +} + +// Box ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) BoxId(boxId string) AdminAPIAdminGetObservabilityTracesRequest { + r.boxId = &boxId + return r +} + +// Runner ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) RunnerId(runnerId string) AdminAPIAdminGetObservabilityTracesRequest { + r.runnerId = &runnerId + return r +} + +// Machine or host ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) MachineId(machineId string) AdminAPIAdminGetObservabilityTracesRequest { + r.machineId = &machineId + return r +} + +// Trace ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) TraceId(traceId string) AdminAPIAdminGetObservabilityTracesRequest { + r.traceId = &traceId + return r +} + +// Request ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) RequestId(requestId string) AdminAPIAdminGetObservabilityTracesRequest { + r.requestId = &requestId + return r +} + +// Operation ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) OperationId(operationId string) AdminAPIAdminGetObservabilityTracesRequest { + r.operationId = &operationId + return r +} + +// Execution ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) ExecutionId(executionId string) AdminAPIAdminGetObservabilityTracesRequest { + r.executionId = &executionId + return r +} + +// Job ID filter +func (r AdminAPIAdminGetObservabilityTracesRequest) JobId(jobId string) AdminAPIAdminGetObservabilityTracesRequest { + r.jobId = &jobId + return r +} + +func (r AdminAPIAdminGetObservabilityTracesRequest) Execute() (*PaginatedTraces, *http.Response, error) { + return r.ApiService.AdminGetObservabilityTracesExecute(r) +} + +/* +AdminGetObservabilityTraces Get admin-scoped traces + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetObservabilityTracesRequest +*/ +func (a *AdminAPIService) AdminGetObservabilityTraces(ctx context.Context) AdminAPIAdminGetObservabilityTracesRequest { + return AdminAPIAdminGetObservabilityTracesRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return PaginatedTraces +func (a *AdminAPIService) AdminGetObservabilityTracesExecute(r AdminAPIAdminGetObservabilityTracesRequest) (*PaginatedTraces, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *PaginatedTraces + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetObservabilityTraces") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/observability/traces" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.from != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + } + if r.to != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + } + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.layer != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "layer", r.layer, "form", "") + } + if r.serviceName != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "serviceName", r.serviceName, "form", "") + } + if r.orgId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "orgId", r.orgId, "form", "") + } + if r.userId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "userId", r.userId, "form", "") + } + if r.boxId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "boxId", r.boxId, "form", "") + } + if r.runnerId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "runnerId", r.runnerId, "form", "") + } + if r.machineId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "machineId", r.machineId, "form", "") + } + if r.traceId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "traceId", r.traceId, "form", "") + } + if r.requestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "requestId", r.requestId, "form", "") + } + if r.operationId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "operationId", r.operationId, "form", "") + } + if r.executionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "executionId", r.executionId, "form", "") + } + if r.jobId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "jobId", r.jobId, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminGetOverviewRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminGetOverviewRequest) Execute() (*AdminOverview, *http.Response, error) { + return r.ApiService.AdminGetOverviewExecute(r) +} + +/* +AdminGetOverview Admin KPI summary + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminGetOverviewRequest +*/ +func (a *AdminAPIService) AdminGetOverview(ctx context.Context) AdminAPIAdminGetOverviewRequest { + return AdminAPIAdminGetOverviewRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return AdminOverview +func (a *AdminAPIService) AdminGetOverviewExecute(r AdminAPIAdminGetOverviewRequest) (*AdminOverview, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *AdminOverview + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetOverview") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/overview" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminGetRunnerByIdRequest struct { + ctx context.Context + ApiService AdminAPI + id string +} + +func (r AdminAPIAdminGetRunnerByIdRequest) Execute() (*AdminRunner, *http.Response, error) { + return r.ApiService.AdminGetRunnerByIdExecute(r) +} + +/* +AdminGetRunnerById Get runner by ID + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param id Runner ID + @return AdminAPIAdminGetRunnerByIdRequest +*/ +func (a *AdminAPIService) AdminGetRunnerById(ctx context.Context, id string) AdminAPIAdminGetRunnerByIdRequest { + return AdminAPIAdminGetRunnerByIdRequest{ + ApiService: a, + ctx: ctx, + id: id, + } +} + +// Execute executes the request +// @return AdminRunner +func (a *AdminAPIService) AdminGetRunnerByIdExecute(r AdminAPIAdminGetRunnerByIdRequest) (*AdminRunner, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *AdminRunner + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminGetRunnerById") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/runners/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminInvestigateObservabilityRequest struct { + ctx context.Context + ApiService AdminAPI + from *time.Time + to *time.Time + page *float32 + limit *float32 + layer *string + serviceName *string + orgId *string + userId *string + boxId *string + runnerId *string + machineId *string + traceId *string + requestId *string + operationId *string + executionId *string + jobId *string +} + +// Start of time range (ISO 8601) +func (r AdminAPIAdminInvestigateObservabilityRequest) From(from time.Time) AdminAPIAdminInvestigateObservabilityRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r AdminAPIAdminInvestigateObservabilityRequest) To(to time.Time) AdminAPIAdminInvestigateObservabilityRequest { + r.to = &to + return r +} + +// Page number (1-indexed) +func (r AdminAPIAdminInvestigateObservabilityRequest) Page(page float32) AdminAPIAdminInvestigateObservabilityRequest { + r.page = &page + return r +} + +// Number of items per page +func (r AdminAPIAdminInvestigateObservabilityRequest) Limit(limit float32) AdminAPIAdminInvestigateObservabilityRequest { + r.limit = &limit + return r +} + +// Telemetry producer layer +func (r AdminAPIAdminInvestigateObservabilityRequest) Layer(layer string) AdminAPIAdminInvestigateObservabilityRequest { + r.layer = &layer + return r +} + +// OpenTelemetry service.name filter +func (r AdminAPIAdminInvestigateObservabilityRequest) ServiceName(serviceName string) AdminAPIAdminInvestigateObservabilityRequest { + r.serviceName = &serviceName + return r +} + +// Organization ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) OrgId(orgId string) AdminAPIAdminInvestigateObservabilityRequest { + r.orgId = &orgId + return r +} + +// User ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) UserId(userId string) AdminAPIAdminInvestigateObservabilityRequest { + r.userId = &userId + return r +} + +// Box ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) BoxId(boxId string) AdminAPIAdminInvestigateObservabilityRequest { + r.boxId = &boxId + return r +} + +// Runner ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) RunnerId(runnerId string) AdminAPIAdminInvestigateObservabilityRequest { + r.runnerId = &runnerId + return r +} + +// Machine or host ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) MachineId(machineId string) AdminAPIAdminInvestigateObservabilityRequest { + r.machineId = &machineId + return r +} + +// Trace ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) TraceId(traceId string) AdminAPIAdminInvestigateObservabilityRequest { + r.traceId = &traceId + return r +} + +// Request ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) RequestId(requestId string) AdminAPIAdminInvestigateObservabilityRequest { + r.requestId = &requestId + return r +} + +// Operation ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) OperationId(operationId string) AdminAPIAdminInvestigateObservabilityRequest { + r.operationId = &operationId + return r +} + +// Execution ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) ExecutionId(executionId string) AdminAPIAdminInvestigateObservabilityRequest { + r.executionId = &executionId + return r +} + +// Job ID filter +func (r AdminAPIAdminInvestigateObservabilityRequest) JobId(jobId string) AdminAPIAdminInvestigateObservabilityRequest { + r.jobId = &jobId + return r +} + +func (r AdminAPIAdminInvestigateObservabilityRequest) Execute() (*AdminObservabilityInvestigateResponse, *http.Response, error) { + return r.ApiService.AdminInvestigateObservabilityExecute(r) +} + +/* +AdminInvestigateObservability Investigate related observability and platform state from trace or resource identifiers + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminInvestigateObservabilityRequest +*/ +func (a *AdminAPIService) AdminInvestigateObservability(ctx context.Context) AdminAPIAdminInvestigateObservabilityRequest { + return AdminAPIAdminInvestigateObservabilityRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return AdminObservabilityInvestigateResponse +func (a *AdminAPIService) AdminInvestigateObservabilityExecute(r AdminAPIAdminInvestigateObservabilityRequest) (*AdminObservabilityInvestigateResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *AdminObservabilityInvestigateResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminInvestigateObservability") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/observability/investigate" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.from != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + } + if r.to != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + } + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.layer != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "layer", r.layer, "form", "") + } + if r.serviceName != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "serviceName", r.serviceName, "form", "") + } + if r.orgId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "orgId", r.orgId, "form", "") + } + if r.userId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "userId", r.userId, "form", "") + } + if r.boxId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "boxId", r.boxId, "form", "") + } + if r.runnerId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "runnerId", r.runnerId, "form", "") + } + if r.machineId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "machineId", r.machineId, "form", "") + } + if r.traceId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "traceId", r.traceId, "form", "") + } + if r.requestId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "requestId", r.requestId, "form", "") + } + if r.operationId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "operationId", r.operationId, "form", "") + } + if r.executionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "executionId", r.executionId, "form", "") + } + if r.jobId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "jobId", r.jobId, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminListBoxesRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminListBoxesRequest) Execute() ([]AdminBoxItem, *http.Response, error) { + return r.ApiService.AdminListBoxesExecute(r) +} + +/* +AdminListBoxes List all boxes (cross-org) + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListBoxesRequest +*/ +func (a *AdminAPIService) AdminListBoxes(ctx context.Context) AdminAPIAdminListBoxesRequest { + return AdminAPIAdminListBoxesRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []AdminBoxItem +func (a *AdminAPIService) AdminListBoxesExecute(r AdminAPIAdminListBoxesRequest) ([]AdminBoxItem, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []AdminBoxItem + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListBoxes") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/overview/boxes" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminListMachinesRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminListMachinesRequest) Execute() ([]AdminMachineItem, *http.Response, error) { + return r.ApiService.AdminListMachinesExecute(r) +} + +/* +AdminListMachines Runner-as-machine resource view + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListMachinesRequest +*/ +func (a *AdminAPIService) AdminListMachines(ctx context.Context) AdminAPIAdminListMachinesRequest { + return AdminAPIAdminListMachinesRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []AdminMachineItem +func (a *AdminAPIService) AdminListMachinesExecute(r AdminAPIAdminListMachinesRequest) ([]AdminMachineItem, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []AdminMachineItem + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListMachines") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/overview/machines" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminListRunnersRequest struct { + ctx context.Context + ApiService AdminAPI + regionId *string +} + +// Filter runners by region ID +func (r AdminAPIAdminListRunnersRequest) RegionId(regionId string) AdminAPIAdminListRunnersRequest { + r.regionId = ®ionId + return r +} + +func (r AdminAPIAdminListRunnersRequest) Execute() ([]AdminRunner, *http.Response, error) { + return r.ApiService.AdminListRunnersExecute(r) +} + +/* +AdminListRunners List all runners + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListRunnersRequest +*/ +func (a *AdminAPIService) AdminListRunners(ctx context.Context) AdminAPIAdminListRunnersRequest { + return AdminAPIAdminListRunnersRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []AdminRunner +func (a *AdminAPIService) AdminListRunnersExecute(r AdminAPIAdminListRunnersRequest) ([]AdminRunner, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []AdminRunner + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListRunners") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/runners" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.regionId != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "regionId", r.regionId, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminListRunnersOverviewRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminListRunnersOverviewRequest) Execute() ([]AdminRunnerItem, *http.Response, error) { + return r.ApiService.AdminListRunnersOverviewExecute(r) +} + +/* +AdminListRunnersOverview List all runners with full details + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListRunnersOverviewRequest +*/ +func (a *AdminAPIService) AdminListRunnersOverview(ctx context.Context) AdminAPIAdminListRunnersOverviewRequest { + return AdminAPIAdminListRunnersOverviewRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []AdminRunnerItem +func (a *AdminAPIService) AdminListRunnersOverviewExecute(r AdminAPIAdminListRunnersOverviewRequest) ([]AdminRunnerItem, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []AdminRunnerItem + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListRunnersOverview") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/overview/runners" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type AdminAPIAdminListUsersRequest struct { + ctx context.Context + ApiService AdminAPI +} + +func (r AdminAPIAdminListUsersRequest) Execute() ([]AdminUserItem, *http.Response, error) { + return r.ApiService.AdminListUsersExecute(r) +} + +/* +AdminListUsers List all users (cross-org) + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AdminAPIAdminListUsersRequest +*/ +func (a *AdminAPIService) AdminListUsers(ctx context.Context) AdminAPIAdminListUsersRequest { + return AdminAPIAdminListUsersRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []AdminUserItem +func (a *AdminAPIService) AdminListUsersExecute(r AdminAPIAdminListUsersRequest) ([]AdminUserItem, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []AdminUserItem + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminListUsers") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/admin/overview/users" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + // to determine the Content-Type header localVarHTTPContentTypes := []string{} @@ -507,48 +2589,48 @@ func (a *AdminAPIService) AdminListRunnersExecute(r AdminAPIAdminListRunnersRequ return localVarReturnValue, localVarHTTPResponse, nil } -type AdminAPIAdminRecoverSandboxRequest struct { +type AdminAPIAdminRecoverBoxRequest struct { ctx context.Context ApiService AdminAPI - sandboxId string + boxId string } -func (r AdminAPIAdminRecoverSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.AdminRecoverSandboxExecute(r) +func (r AdminAPIAdminRecoverBoxRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.AdminRecoverBoxExecute(r) } /* -AdminRecoverSandbox Recover sandbox from error state as an admin +AdminRecoverBox Recover box from error state as an admin @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return AdminAPIAdminRecoverSandboxRequest + @param boxId ID of the box + @return AdminAPIAdminRecoverBoxRequest */ -func (a *AdminAPIService) AdminRecoverSandbox(ctx context.Context, sandboxId string) AdminAPIAdminRecoverSandboxRequest { - return AdminAPIAdminRecoverSandboxRequest{ +func (a *AdminAPIService) AdminRecoverBox(ctx context.Context, boxId string) AdminAPIAdminRecoverBoxRequest { + return AdminAPIAdminRecoverBoxRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + boxId: boxId, } } // Execute executes the request -// @return Sandbox -func (a *AdminAPIService) AdminRecoverSandboxExecute(r AdminAPIAdminRecoverSandboxRequest) (*Sandbox, *http.Response, error) { +// @return Box +func (a *AdminAPIService) AdminRecoverBoxExecute(r AdminAPIAdminRecoverBoxRequest) (*Box, *http.Response, error) { var ( localVarHTTPMethod = http.MethodPost localVarPostBody interface{} formFiles []formFile - localVarReturnValue *Sandbox + localVarReturnValue *Box ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminRecoverSandbox") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AdminAPIService.AdminRecoverBox") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/admin/sandbox/{sandboxId}/recover" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/admin/box/{boxId}/recover" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} diff --git a/apps/api-client-go/api_audit.go b/apps/api-client-go/api_audit.go index 6c402b232..dfe0acfbf 100644 --- a/apps/api-client-go/api_audit.go +++ b/apps/api-client-go/api_audit.go @@ -135,12 +135,14 @@ func (a *AuditAPIService) GetAllAuditLogsExecute(r AuditAPIGetAllAuditLogsReques parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") } else { var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") r.page = &defaultValue } if r.limit != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") } else { var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") r.limit = &defaultValue } if r.from != nil { @@ -292,12 +294,14 @@ func (a *AuditAPIService) GetOrganizationAuditLogsExecute(r AuditAPIGetOrganizat parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") } else { var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") r.page = &defaultValue } if r.limit != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") } else { var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") r.limit = &defaultValue } if r.from != nil { diff --git a/apps/api-client-go/api_auth.go b/apps/api-client-go/api_auth.go new file mode 100644 index 000000000..147c92c26 --- /dev/null +++ b/apps/api-client-go/api_auth.go @@ -0,0 +1,155 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" +) + + +type AuthAPI interface { + + /* + LogoutControllerEndSession OIDC RP-initiated logout endpoint + + Implements OpenID Connect RP-Initiated Logout 1.0 for IdPs (e.g. Dex) that do not natively advertise end_session_endpoint. Validates the post-logout redirect target, then 302-redirects the browser back to the SPA. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AuthAPILogoutControllerEndSessionRequest + */ + LogoutControllerEndSession(ctx context.Context) AuthAPILogoutControllerEndSessionRequest + + // LogoutControllerEndSessionExecute executes the request + LogoutControllerEndSessionExecute(r AuthAPILogoutControllerEndSessionRequest) (*http.Response, error) +} + +// AuthAPIService AuthAPI service +type AuthAPIService service + +type AuthAPILogoutControllerEndSessionRequest struct { + ctx context.Context + ApiService AuthAPI + postLogoutRedirectUri *string + idTokenHint *string + state *string +} + +func (r AuthAPILogoutControllerEndSessionRequest) PostLogoutRedirectUri(postLogoutRedirectUri string) AuthAPILogoutControllerEndSessionRequest { + r.postLogoutRedirectUri = &postLogoutRedirectUri + return r +} + +func (r AuthAPILogoutControllerEndSessionRequest) IdTokenHint(idTokenHint string) AuthAPILogoutControllerEndSessionRequest { + r.idTokenHint = &idTokenHint + return r +} + +func (r AuthAPILogoutControllerEndSessionRequest) State(state string) AuthAPILogoutControllerEndSessionRequest { + r.state = &state + return r +} + +func (r AuthAPILogoutControllerEndSessionRequest) Execute() (*http.Response, error) { + return r.ApiService.LogoutControllerEndSessionExecute(r) +} + +/* +LogoutControllerEndSession OIDC RP-initiated logout endpoint + +Implements OpenID Connect RP-Initiated Logout 1.0 for IdPs (e.g. Dex) that do not natively advertise end_session_endpoint. Validates the post-logout redirect target, then 302-redirects the browser back to the SPA. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return AuthAPILogoutControllerEndSessionRequest +*/ +func (a *AuthAPIService) LogoutControllerEndSession(ctx context.Context) AuthAPILogoutControllerEndSessionRequest { + return AuthAPILogoutControllerEndSessionRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +func (a *AuthAPIService) LogoutControllerEndSessionExecute(r AuthAPILogoutControllerEndSessionRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "AuthAPIService.LogoutControllerEndSession") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/auth/end-session" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.postLogoutRedirectUri != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "post_logout_redirect_uri", r.postLogoutRedirectUri, "form", "") + } + if r.idTokenHint != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "id_token_hint", r.idTokenHint, "form", "") + } + if r.state != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "state", r.state, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} diff --git a/apps/api-client-go/api_box.go b/apps/api-client-go/api_box.go new file mode 100644 index 000000000..8dae8b5f6 --- /dev/null +++ b/apps/api-client-go/api_box.go @@ -0,0 +1,3400 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" + "strings" + "time" + "reflect" +) + + +type BoxAPI interface { + + /* + CreateSshAccess Create SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPICreateSshAccessRequest + */ + CreateSshAccess(ctx context.Context, boxIdOrName string) BoxAPICreateSshAccessRequest + + // CreateSshAccessExecute executes the request + // @return SshAccessDto + CreateSshAccessExecute(r BoxAPICreateSshAccessRequest) (*SshAccessDto, *http.Response, error) + + /* + ExpireSignedPortPreviewUrl Expire signed preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to expire signed preview URL for + @param token Token to expire signed preview URL for + @return BoxAPIExpireSignedPortPreviewUrlRequest + */ + ExpireSignedPortPreviewUrl(ctx context.Context, boxIdOrName string, port int32, token string) BoxAPIExpireSignedPortPreviewUrlRequest + + // ExpireSignedPortPreviewUrlExecute executes the request + ExpireSignedPortPreviewUrlExecute(r BoxAPIExpireSignedPortPreviewUrlRequest) (*http.Response, error) + + /* + GetBox Get box details + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIGetBoxRequest + */ + GetBox(ctx context.Context, boxIdOrName string) BoxAPIGetBoxRequest + + // GetBoxExecute executes the request + // @return Box + GetBoxExecute(r BoxAPIGetBoxRequest) (*Box, *http.Response, error) + + /* + GetBoxLogs Get box logs + + Retrieve OTEL logs for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxLogsRequest + */ + GetBoxLogs(ctx context.Context, boxId string) BoxAPIGetBoxLogsRequest + + // GetBoxLogsExecute executes the request + // @return PaginatedLogs + GetBoxLogsExecute(r BoxAPIGetBoxLogsRequest) (*PaginatedLogs, *http.Response, error) + + /* + GetBoxMetrics Get box metrics + + Retrieve OTEL metrics for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxMetricsRequest + */ + GetBoxMetrics(ctx context.Context, boxId string) BoxAPIGetBoxMetricsRequest + + // GetBoxMetricsExecute executes the request + // @return MetricsResponse + GetBoxMetricsExecute(r BoxAPIGetBoxMetricsRequest) (*MetricsResponse, *http.Response, error) + + /* + GetBoxTraceSpans Get trace spans + + Retrieve all spans for a specific trace + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @param traceId ID of the trace + @return BoxAPIGetBoxTraceSpansRequest + */ + GetBoxTraceSpans(ctx context.Context, boxId string, traceId string) BoxAPIGetBoxTraceSpansRequest + + // GetBoxTraceSpansExecute executes the request + // @return []TraceSpan + GetBoxTraceSpansExecute(r BoxAPIGetBoxTraceSpansRequest) ([]TraceSpan, *http.Response, error) + + /* + GetBoxTraces Get box traces + + Retrieve OTEL traces for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxTracesRequest + */ + GetBoxTraces(ctx context.Context, boxId string) BoxAPIGetBoxTracesRequest + + // GetBoxTracesExecute executes the request + // @return PaginatedTraces + GetBoxTracesExecute(r BoxAPIGetBoxTracesRequest) (*PaginatedTraces, *http.Response, error) + + /* + GetBoxesForRunner Get boxes for the authenticated runner + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIGetBoxesForRunnerRequest + */ + GetBoxesForRunner(ctx context.Context) BoxAPIGetBoxesForRunnerRequest + + // GetBoxesForRunnerExecute executes the request + // @return []Box + GetBoxesForRunnerExecute(r BoxAPIGetBoxesForRunnerRequest) ([]Box, *http.Response, error) + + /* + GetPortPreviewUrl Get preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to get preview URL for + @return BoxAPIGetPortPreviewUrlRequest + */ + GetPortPreviewUrl(ctx context.Context, boxIdOrName string, port float32) BoxAPIGetPortPreviewUrlRequest + + // GetPortPreviewUrlExecute executes the request + // @return PortPreviewUrl + GetPortPreviewUrlExecute(r BoxAPIGetPortPreviewUrlRequest) (*PortPreviewUrl, *http.Response, error) + + /* + GetSignedPortPreviewUrl Get signed preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to get signed preview URL for + @return BoxAPIGetSignedPortPreviewUrlRequest + */ + GetSignedPortPreviewUrl(ctx context.Context, boxIdOrName string, port int32) BoxAPIGetSignedPortPreviewUrlRequest + + // GetSignedPortPreviewUrlExecute executes the request + // @return SignedPortPreviewUrl + GetSignedPortPreviewUrlExecute(r BoxAPIGetSignedPortPreviewUrlRequest) (*SignedPortPreviewUrl, *http.Response, error) + + /* + GetToolboxProxyUrl Get toolbox proxy URL for a box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetToolboxProxyUrlRequest + */ + GetToolboxProxyUrl(ctx context.Context, boxId string) BoxAPIGetToolboxProxyUrlRequest + + // GetToolboxProxyUrlExecute executes the request + // @return ToolboxProxyUrl + GetToolboxProxyUrlExecute(r BoxAPIGetToolboxProxyUrlRequest) (*ToolboxProxyUrl, *http.Response, error) + + /* + ListBoxes List all boxes + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIListBoxesRequest + */ + ListBoxes(ctx context.Context) BoxAPIListBoxesRequest + + // ListBoxesExecute executes the request + // @return []Box + ListBoxesExecute(r BoxAPIListBoxesRequest) ([]Box, *http.Response, error) + + /* + ListBoxesPaginated List all boxes paginated + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIListBoxesPaginatedRequest + */ + ListBoxesPaginated(ctx context.Context) BoxAPIListBoxesPaginatedRequest + + // ListBoxesPaginatedExecute executes the request + // @return PaginatedBoxes + ListBoxesPaginatedExecute(r BoxAPIListBoxesPaginatedRequest) (*PaginatedBoxes, *http.Response, error) + + /* + RecoverBox Recover box from error state + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIRecoverBoxRequest + */ + RecoverBox(ctx context.Context, boxIdOrName string) BoxAPIRecoverBoxRequest + + // RecoverBoxExecute executes the request + // @return Box + RecoverBoxExecute(r BoxAPIRecoverBoxRequest) (*Box, *http.Response, error) + + /* + ReplaceLabels Replace box labels + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIReplaceLabelsRequest + */ + ReplaceLabels(ctx context.Context, boxIdOrName string) BoxAPIReplaceLabelsRequest + + // ReplaceLabelsExecute executes the request + // @return BoxLabels + ReplaceLabelsExecute(r BoxAPIReplaceLabelsRequest) (*BoxLabels, *http.Response, error) + + /* + ResizeBox Resize box resources + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIResizeBoxRequest + */ + ResizeBox(ctx context.Context, boxIdOrName string) BoxAPIResizeBoxRequest + + // ResizeBoxExecute executes the request + // @return Box + ResizeBoxExecute(r BoxAPIResizeBoxRequest) (*Box, *http.Response, error) + + /* + RevokeSshAccess Revoke SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIRevokeSshAccessRequest + */ + RevokeSshAccess(ctx context.Context, boxIdOrName string) BoxAPIRevokeSshAccessRequest + + // RevokeSshAccessExecute executes the request + // @return Box + RevokeSshAccessExecute(r BoxAPIRevokeSshAccessRequest) (*Box, *http.Response, error) + + /* + SetAutoDeleteInterval Set box auto-delete interval + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param interval Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) + @return BoxAPISetAutoDeleteIntervalRequest + */ + SetAutoDeleteInterval(ctx context.Context, boxIdOrName string, interval float32) BoxAPISetAutoDeleteIntervalRequest + + // SetAutoDeleteIntervalExecute executes the request + // @return Box + SetAutoDeleteIntervalExecute(r BoxAPISetAutoDeleteIntervalRequest) (*Box, *http.Response, error) + + /* + SetAutostopInterval Set box auto-stop interval + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param interval Auto-stop interval in minutes (0 to disable) + @return BoxAPISetAutostopIntervalRequest + */ + SetAutostopInterval(ctx context.Context, boxIdOrName string, interval float32) BoxAPISetAutostopIntervalRequest + + // SetAutostopIntervalExecute executes the request + // @return Box + SetAutostopIntervalExecute(r BoxAPISetAutostopIntervalRequest) (*Box, *http.Response, error) + + /* + UpdateBoxState Update box state + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIUpdateBoxStateRequest + */ + UpdateBoxState(ctx context.Context, boxId string) BoxAPIUpdateBoxStateRequest + + // UpdateBoxStateExecute executes the request + UpdateBoxStateExecute(r BoxAPIUpdateBoxStateRequest) (*http.Response, error) + + /* + UpdateLastActivity Update box last activity + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIUpdateLastActivityRequest + */ + UpdateLastActivity(ctx context.Context, boxId string) BoxAPIUpdateLastActivityRequest + + // UpdateLastActivityExecute executes the request + UpdateLastActivityExecute(r BoxAPIUpdateLastActivityRequest) (*http.Response, error) + + /* + UpdatePublicStatus Update public status + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param isPublic Public status to set + @return BoxAPIUpdatePublicStatusRequest + */ + UpdatePublicStatus(ctx context.Context, boxIdOrName string, isPublic bool) BoxAPIUpdatePublicStatusRequest + + // UpdatePublicStatusExecute executes the request + // @return Box + UpdatePublicStatusExecute(r BoxAPIUpdatePublicStatusRequest) (*Box, *http.Response, error) + + /* + ValidateSshAccess Validate SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIValidateSshAccessRequest + */ + ValidateSshAccess(ctx context.Context) BoxAPIValidateSshAccessRequest + + // ValidateSshAccessExecute executes the request + // @return SshAccessValidationDto + ValidateSshAccessExecute(r BoxAPIValidateSshAccessRequest) (*SshAccessValidationDto, *http.Response, error) +} + +// BoxAPIService BoxAPI service +type BoxAPIService service + +type BoxAPICreateSshAccessRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + xBoxLiteOrganizationID *string + expiresInMinutes *float32 +} + +// Use with JWT to specify the organization ID +func (r BoxAPICreateSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPICreateSshAccessRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Expiration time in minutes (default: 60) +func (r BoxAPICreateSshAccessRequest) ExpiresInMinutes(expiresInMinutes float32) BoxAPICreateSshAccessRequest { + r.expiresInMinutes = &expiresInMinutes + return r +} + +func (r BoxAPICreateSshAccessRequest) Execute() (*SshAccessDto, *http.Response, error) { + return r.ApiService.CreateSshAccessExecute(r) +} + +/* +CreateSshAccess Create SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPICreateSshAccessRequest +*/ +func (a *BoxAPIService) CreateSshAccess(ctx context.Context, boxIdOrName string) BoxAPICreateSshAccessRequest { + return BoxAPICreateSshAccessRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return SshAccessDto +func (a *BoxAPIService) CreateSshAccessExecute(r BoxAPICreateSshAccessRequest) (*SshAccessDto, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *SshAccessDto + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.CreateSshAccess") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/ssh-access" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.expiresInMinutes != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "expiresInMinutes", r.expiresInMinutes, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIExpireSignedPortPreviewUrlRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + port int32 + token string + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIExpireSignedPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIExpireSignedPortPreviewUrlRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIExpireSignedPortPreviewUrlRequest) Execute() (*http.Response, error) { + return r.ApiService.ExpireSignedPortPreviewUrlExecute(r) +} + +/* +ExpireSignedPortPreviewUrl Expire signed preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to expire signed preview URL for + @param token Token to expire signed preview URL for + @return BoxAPIExpireSignedPortPreviewUrlRequest +*/ +func (a *BoxAPIService) ExpireSignedPortPreviewUrl(ctx context.Context, boxIdOrName string, port int32, token string) BoxAPIExpireSignedPortPreviewUrlRequest { + return BoxAPIExpireSignedPortPreviewUrlRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + port: port, + token: token, + } +} + +// Execute executes the request +func (a *BoxAPIService) ExpireSignedPortPreviewUrlExecute(r BoxAPIExpireSignedPortPreviewUrlRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ExpireSignedPortPreviewUrl") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/ports/{port}/signed-preview-url/{token}/expire" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"token"+"}", url.PathEscape(parameterValueToString(r.token, "token")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type BoxAPIGetBoxRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + xBoxLiteOrganizationID *string + verbose *bool +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Include verbose output +func (r BoxAPIGetBoxRequest) Verbose(verbose bool) BoxAPIGetBoxRequest { + r.verbose = &verbose + return r +} + +func (r BoxAPIGetBoxRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.GetBoxExecute(r) +} + +/* +GetBox Get box details + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIGetBoxRequest +*/ +func (a *BoxAPIService) GetBox(ctx context.Context, boxIdOrName string) BoxAPIGetBoxRequest { + return BoxAPIGetBoxRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) GetBoxExecute(r BoxAPIGetBoxRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBox") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.verbose != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetBoxLogsRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + from *time.Time + to *time.Time + xBoxLiteOrganizationID *string + page *float32 + limit *float32 + severities *[]string + search *string +} + +// Start of time range (ISO 8601) +func (r BoxAPIGetBoxLogsRequest) From(from time.Time) BoxAPIGetBoxLogsRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r BoxAPIGetBoxLogsRequest) To(to time.Time) BoxAPIGetBoxLogsRequest { + r.to = &to + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxLogsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxLogsRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Page number (1-indexed) +func (r BoxAPIGetBoxLogsRequest) Page(page float32) BoxAPIGetBoxLogsRequest { + r.page = &page + return r +} + +// Number of items per page +func (r BoxAPIGetBoxLogsRequest) Limit(limit float32) BoxAPIGetBoxLogsRequest { + r.limit = &limit + return r +} + +// Filter by severity levels (DEBUG, INFO, WARN, ERROR) +func (r BoxAPIGetBoxLogsRequest) Severities(severities []string) BoxAPIGetBoxLogsRequest { + r.severities = &severities + return r +} + +// Search in log body +func (r BoxAPIGetBoxLogsRequest) Search(search string) BoxAPIGetBoxLogsRequest { + r.search = &search + return r +} + +func (r BoxAPIGetBoxLogsRequest) Execute() (*PaginatedLogs, *http.Response, error) { + return r.ApiService.GetBoxLogsExecute(r) +} + +/* +GetBoxLogs Get box logs + +Retrieve OTEL logs for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxLogsRequest +*/ +func (a *BoxAPIService) GetBoxLogs(ctx context.Context, boxId string) BoxAPIGetBoxLogsRequest { + return BoxAPIGetBoxLogsRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +// @return PaginatedLogs +func (a *BoxAPIService) GetBoxLogsExecute(r BoxAPIGetBoxLogsRequest) (*PaginatedLogs, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *PaginatedLogs + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBoxLogs") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/telemetry/logs" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.from == nil { + return localVarReturnValue, nil, reportError("from is required and must be specified") + } + if r.to == nil { + return localVarReturnValue, nil, reportError("to is required and must be specified") + } + + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.severities != nil { + t := *r.severities + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "severities", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "severities", t, "form", "multi") + } + } + if r.search != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "search", r.search, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetBoxMetricsRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + from *time.Time + to *time.Time + xBoxLiteOrganizationID *string + metricNames *[]string +} + +// Start of time range (ISO 8601) +func (r BoxAPIGetBoxMetricsRequest) From(from time.Time) BoxAPIGetBoxMetricsRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r BoxAPIGetBoxMetricsRequest) To(to time.Time) BoxAPIGetBoxMetricsRequest { + r.to = &to + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxMetricsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxMetricsRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Filter by metric names +func (r BoxAPIGetBoxMetricsRequest) MetricNames(metricNames []string) BoxAPIGetBoxMetricsRequest { + r.metricNames = &metricNames + return r +} + +func (r BoxAPIGetBoxMetricsRequest) Execute() (*MetricsResponse, *http.Response, error) { + return r.ApiService.GetBoxMetricsExecute(r) +} + +/* +GetBoxMetrics Get box metrics + +Retrieve OTEL metrics for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxMetricsRequest +*/ +func (a *BoxAPIService) GetBoxMetrics(ctx context.Context, boxId string) BoxAPIGetBoxMetricsRequest { + return BoxAPIGetBoxMetricsRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +// @return MetricsResponse +func (a *BoxAPIService) GetBoxMetricsExecute(r BoxAPIGetBoxMetricsRequest) (*MetricsResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *MetricsResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBoxMetrics") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/telemetry/metrics" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.from == nil { + return localVarReturnValue, nil, reportError("from is required and must be specified") + } + if r.to == nil { + return localVarReturnValue, nil, reportError("to is required and must be specified") + } + + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + if r.metricNames != nil { + t := *r.metricNames + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", t, "form", "multi") + } + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetBoxTraceSpansRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + traceId string + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxTraceSpansRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxTraceSpansRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIGetBoxTraceSpansRequest) Execute() ([]TraceSpan, *http.Response, error) { + return r.ApiService.GetBoxTraceSpansExecute(r) +} + +/* +GetBoxTraceSpans Get trace spans + +Retrieve all spans for a specific trace + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @param traceId ID of the trace + @return BoxAPIGetBoxTraceSpansRequest +*/ +func (a *BoxAPIService) GetBoxTraceSpans(ctx context.Context, boxId string, traceId string) BoxAPIGetBoxTraceSpansRequest { + return BoxAPIGetBoxTraceSpansRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + traceId: traceId, + } +} + +// Execute executes the request +// @return []TraceSpan +func (a *BoxAPIService) GetBoxTraceSpansExecute(r BoxAPIGetBoxTraceSpansRequest) ([]TraceSpan, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []TraceSpan + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBoxTraceSpans") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/telemetry/traces/{traceId}" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"traceId"+"}", url.PathEscape(parameterValueToString(r.traceId, "traceId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetBoxTracesRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + from *time.Time + to *time.Time + xBoxLiteOrganizationID *string + page *float32 + limit *float32 +} + +// Start of time range (ISO 8601) +func (r BoxAPIGetBoxTracesRequest) From(from time.Time) BoxAPIGetBoxTracesRequest { + r.from = &from + return r +} + +// End of time range (ISO 8601) +func (r BoxAPIGetBoxTracesRequest) To(to time.Time) BoxAPIGetBoxTracesRequest { + r.to = &to + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxTracesRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxTracesRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Page number (1-indexed) +func (r BoxAPIGetBoxTracesRequest) Page(page float32) BoxAPIGetBoxTracesRequest { + r.page = &page + return r +} + +// Number of items per page +func (r BoxAPIGetBoxTracesRequest) Limit(limit float32) BoxAPIGetBoxTracesRequest { + r.limit = &limit + return r +} + +func (r BoxAPIGetBoxTracesRequest) Execute() (*PaginatedTraces, *http.Response, error) { + return r.ApiService.GetBoxTracesExecute(r) +} + +/* +GetBoxTraces Get box traces + +Retrieve OTEL traces for a box within a time range + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetBoxTracesRequest +*/ +func (a *BoxAPIService) GetBoxTraces(ctx context.Context, boxId string) BoxAPIGetBoxTracesRequest { + return BoxAPIGetBoxTracesRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +// @return PaginatedTraces +func (a *BoxAPIService) GetBoxTracesExecute(r BoxAPIGetBoxTracesRequest) (*PaginatedTraces, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *PaginatedTraces + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBoxTraces") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/telemetry/traces" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.from == nil { + return localVarReturnValue, nil, reportError("from is required and must be specified") + } + if r.to == nil { + return localVarReturnValue, nil, reportError("to is required and must be specified") + } + + parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") + parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetBoxesForRunnerRequest struct { + ctx context.Context + ApiService BoxAPI + xBoxLiteOrganizationID *string + states *string + skipReconcilingBoxes *bool +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetBoxesForRunnerRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetBoxesForRunnerRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Comma-separated list of box states to filter by +func (r BoxAPIGetBoxesForRunnerRequest) States(states string) BoxAPIGetBoxesForRunnerRequest { + r.states = &states + return r +} + +// Skip boxes where state differs from desired state +func (r BoxAPIGetBoxesForRunnerRequest) SkipReconcilingBoxes(skipReconcilingBoxes bool) BoxAPIGetBoxesForRunnerRequest { + r.skipReconcilingBoxes = &skipReconcilingBoxes + return r +} + +func (r BoxAPIGetBoxesForRunnerRequest) Execute() ([]Box, *http.Response, error) { + return r.ApiService.GetBoxesForRunnerExecute(r) +} + +/* +GetBoxesForRunner Get boxes for the authenticated runner + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIGetBoxesForRunnerRequest +*/ +func (a *BoxAPIService) GetBoxesForRunner(ctx context.Context) BoxAPIGetBoxesForRunnerRequest { + return BoxAPIGetBoxesForRunnerRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []Box +func (a *BoxAPIService) GetBoxesForRunnerExecute(r BoxAPIGetBoxesForRunnerRequest) ([]Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetBoxesForRunner") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/for-runner" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.states != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "states", r.states, "form", "") + } + if r.skipReconcilingBoxes != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "skipReconcilingBoxes", r.skipReconcilingBoxes, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetPortPreviewUrlRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + port float32 + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetPortPreviewUrlRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIGetPortPreviewUrlRequest) Execute() (*PortPreviewUrl, *http.Response, error) { + return r.ApiService.GetPortPreviewUrlExecute(r) +} + +/* +GetPortPreviewUrl Get preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to get preview URL for + @return BoxAPIGetPortPreviewUrlRequest +*/ +func (a *BoxAPIService) GetPortPreviewUrl(ctx context.Context, boxIdOrName string, port float32) BoxAPIGetPortPreviewUrlRequest { + return BoxAPIGetPortPreviewUrlRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + port: port, + } +} + +// Execute executes the request +// @return PortPreviewUrl +func (a *BoxAPIService) GetPortPreviewUrlExecute(r BoxAPIGetPortPreviewUrlRequest) (*PortPreviewUrl, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *PortPreviewUrl + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetPortPreviewUrl") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/ports/{port}/preview-url" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetSignedPortPreviewUrlRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + port int32 + xBoxLiteOrganizationID *string + expiresInSeconds *int32 +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetSignedPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetSignedPortPreviewUrlRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Expiration time in seconds (default: 60 seconds) +func (r BoxAPIGetSignedPortPreviewUrlRequest) ExpiresInSeconds(expiresInSeconds int32) BoxAPIGetSignedPortPreviewUrlRequest { + r.expiresInSeconds = &expiresInSeconds + return r +} + +func (r BoxAPIGetSignedPortPreviewUrlRequest) Execute() (*SignedPortPreviewUrl, *http.Response, error) { + return r.ApiService.GetSignedPortPreviewUrlExecute(r) +} + +/* +GetSignedPortPreviewUrl Get signed preview URL for a box port + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param port Port number to get signed preview URL for + @return BoxAPIGetSignedPortPreviewUrlRequest +*/ +func (a *BoxAPIService) GetSignedPortPreviewUrl(ctx context.Context, boxIdOrName string, port int32) BoxAPIGetSignedPortPreviewUrlRequest { + return BoxAPIGetSignedPortPreviewUrlRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + port: port, + } +} + +// Execute executes the request +// @return SignedPortPreviewUrl +func (a *BoxAPIService) GetSignedPortPreviewUrlExecute(r BoxAPIGetSignedPortPreviewUrlRequest) (*SignedPortPreviewUrl, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *SignedPortPreviewUrl + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetSignedPortPreviewUrl") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/ports/{port}/signed-preview-url" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.expiresInSeconds != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "expiresInSeconds", r.expiresInSeconds, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIGetToolboxProxyUrlRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIGetToolboxProxyUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIGetToolboxProxyUrlRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIGetToolboxProxyUrlRequest) Execute() (*ToolboxProxyUrl, *http.Response, error) { + return r.ApiService.GetToolboxProxyUrlExecute(r) +} + +/* +GetToolboxProxyUrl Get toolbox proxy URL for a box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIGetToolboxProxyUrlRequest +*/ +func (a *BoxAPIService) GetToolboxProxyUrl(ctx context.Context, boxId string) BoxAPIGetToolboxProxyUrlRequest { + return BoxAPIGetToolboxProxyUrlRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +// @return ToolboxProxyUrl +func (a *BoxAPIService) GetToolboxProxyUrlExecute(r BoxAPIGetToolboxProxyUrlRequest) (*ToolboxProxyUrl, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *ToolboxProxyUrl + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.GetToolboxProxyUrl") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/toolbox-proxy-url" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIListBoxesRequest struct { + ctx context.Context + ApiService BoxAPI + xBoxLiteOrganizationID *string + verbose *bool + labels *string + includeErroredDeleted *bool +} + +// Use with JWT to specify the organization ID +func (r BoxAPIListBoxesRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIListBoxesRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Include verbose output +func (r BoxAPIListBoxesRequest) Verbose(verbose bool) BoxAPIListBoxesRequest { + r.verbose = &verbose + return r +} + +// JSON encoded labels to filter by +func (r BoxAPIListBoxesRequest) Labels(labels string) BoxAPIListBoxesRequest { + r.labels = &labels + return r +} + +// Include errored and deleted boxes +func (r BoxAPIListBoxesRequest) IncludeErroredDeleted(includeErroredDeleted bool) BoxAPIListBoxesRequest { + r.includeErroredDeleted = &includeErroredDeleted + return r +} + +func (r BoxAPIListBoxesRequest) Execute() ([]Box, *http.Response, error) { + return r.ApiService.ListBoxesExecute(r) +} + +/* +ListBoxes List all boxes + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIListBoxesRequest +*/ +func (a *BoxAPIService) ListBoxes(ctx context.Context) BoxAPIListBoxesRequest { + return BoxAPIListBoxesRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return []Box +func (a *BoxAPIService) ListBoxesExecute(r BoxAPIListBoxesRequest) ([]Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue []Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ListBoxes") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.verbose != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") + } + if r.labels != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "labels", r.labels, "form", "") + } + if r.includeErroredDeleted != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "includeErroredDeleted", r.includeErroredDeleted, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIListBoxesPaginatedRequest struct { + ctx context.Context + ApiService BoxAPI + xBoxLiteOrganizationID *string + page *float32 + limit *float32 + id *string + name *string + labels *string + includeErroredDeleted *bool + states *[]string + regions *[]string + minCpu *float32 + maxCpu *float32 + minMemoryGiB *float32 + maxMemoryGiB *float32 + minDiskGiB *float32 + maxDiskGiB *float32 + lastEventAfter *time.Time + lastEventBefore *time.Time + sort *string + order *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIListBoxesPaginatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIListBoxesPaginatedRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// Page number of the results +func (r BoxAPIListBoxesPaginatedRequest) Page(page float32) BoxAPIListBoxesPaginatedRequest { + r.page = &page + return r +} + +// Number of results per page +func (r BoxAPIListBoxesPaginatedRequest) Limit(limit float32) BoxAPIListBoxesPaginatedRequest { + r.limit = &limit + return r +} + +// Filter by partial Box ID or name match +func (r BoxAPIListBoxesPaginatedRequest) Id(id string) BoxAPIListBoxesPaginatedRequest { + r.id = &id + return r +} + +// Filter by partial name match +func (r BoxAPIListBoxesPaginatedRequest) Name(name string) BoxAPIListBoxesPaginatedRequest { + r.name = &name + return r +} + +// JSON encoded labels to filter by +func (r BoxAPIListBoxesPaginatedRequest) Labels(labels string) BoxAPIListBoxesPaginatedRequest { + r.labels = &labels + return r +} + +// Include results with errored state and deleted desired state +func (r BoxAPIListBoxesPaginatedRequest) IncludeErroredDeleted(includeErroredDeleted bool) BoxAPIListBoxesPaginatedRequest { + r.includeErroredDeleted = &includeErroredDeleted + return r +} + +// List of states to filter by +func (r BoxAPIListBoxesPaginatedRequest) States(states []string) BoxAPIListBoxesPaginatedRequest { + r.states = &states + return r +} + +// List of regions to filter by +func (r BoxAPIListBoxesPaginatedRequest) Regions(regions []string) BoxAPIListBoxesPaginatedRequest { + r.regions = ®ions + return r +} + +// Minimum CPU +func (r BoxAPIListBoxesPaginatedRequest) MinCpu(minCpu float32) BoxAPIListBoxesPaginatedRequest { + r.minCpu = &minCpu + return r +} + +// Maximum CPU +func (r BoxAPIListBoxesPaginatedRequest) MaxCpu(maxCpu float32) BoxAPIListBoxesPaginatedRequest { + r.maxCpu = &maxCpu + return r +} + +// Minimum memory in GiB +func (r BoxAPIListBoxesPaginatedRequest) MinMemoryGiB(minMemoryGiB float32) BoxAPIListBoxesPaginatedRequest { + r.minMemoryGiB = &minMemoryGiB + return r +} + +// Maximum memory in GiB +func (r BoxAPIListBoxesPaginatedRequest) MaxMemoryGiB(maxMemoryGiB float32) BoxAPIListBoxesPaginatedRequest { + r.maxMemoryGiB = &maxMemoryGiB + return r +} + +// Minimum disk space in GiB +func (r BoxAPIListBoxesPaginatedRequest) MinDiskGiB(minDiskGiB float32) BoxAPIListBoxesPaginatedRequest { + r.minDiskGiB = &minDiskGiB + return r +} + +// Maximum disk space in GiB +func (r BoxAPIListBoxesPaginatedRequest) MaxDiskGiB(maxDiskGiB float32) BoxAPIListBoxesPaginatedRequest { + r.maxDiskGiB = &maxDiskGiB + return r +} + +// Include items with last event after this timestamp +func (r BoxAPIListBoxesPaginatedRequest) LastEventAfter(lastEventAfter time.Time) BoxAPIListBoxesPaginatedRequest { + r.lastEventAfter = &lastEventAfter + return r +} + +// Include items with last event before this timestamp +func (r BoxAPIListBoxesPaginatedRequest) LastEventBefore(lastEventBefore time.Time) BoxAPIListBoxesPaginatedRequest { + r.lastEventBefore = &lastEventBefore + return r +} + +// Field to sort by +func (r BoxAPIListBoxesPaginatedRequest) Sort(sort string) BoxAPIListBoxesPaginatedRequest { + r.sort = &sort + return r +} + +// Direction to sort by +func (r BoxAPIListBoxesPaginatedRequest) Order(order string) BoxAPIListBoxesPaginatedRequest { + r.order = &order + return r +} + +func (r BoxAPIListBoxesPaginatedRequest) Execute() (*PaginatedBoxes, *http.Response, error) { + return r.ApiService.ListBoxesPaginatedExecute(r) +} + +/* +ListBoxesPaginated List all boxes paginated + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIListBoxesPaginatedRequest +*/ +func (a *BoxAPIService) ListBoxesPaginated(ctx context.Context) BoxAPIListBoxesPaginatedRequest { + return BoxAPIListBoxesPaginatedRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return PaginatedBoxes +func (a *BoxAPIService) ListBoxesPaginatedExecute(r BoxAPIListBoxesPaginatedRequest) (*PaginatedBoxes, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *PaginatedBoxes + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ListBoxesPaginated") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/paginated" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.page != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") + } else { + var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") + r.page = &defaultValue + } + if r.limit != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") + } else { + var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") + r.limit = &defaultValue + } + if r.id != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "id", r.id, "form", "") + } + if r.name != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "name", r.name, "form", "") + } + if r.labels != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "labels", r.labels, "form", "") + } + if r.includeErroredDeleted != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "includeErroredDeleted", r.includeErroredDeleted, "form", "") + } else { + var defaultValue bool = false + parameterAddToHeaderOrQuery(localVarQueryParams, "includeErroredDeleted", defaultValue, "form", "") + r.includeErroredDeleted = &defaultValue + } + if r.states != nil { + t := *r.states + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "states", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "states", t, "form", "multi") + } + } + if r.regions != nil { + t := *r.regions + if reflect.TypeOf(t).Kind() == reflect.Slice { + s := reflect.ValueOf(t) + for i := 0; i < s.Len(); i++ { + parameterAddToHeaderOrQuery(localVarQueryParams, "regions", s.Index(i).Interface(), "form", "multi") + } + } else { + parameterAddToHeaderOrQuery(localVarQueryParams, "regions", t, "form", "multi") + } + } + if r.minCpu != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "minCpu", r.minCpu, "form", "") + } + if r.maxCpu != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "maxCpu", r.maxCpu, "form", "") + } + if r.minMemoryGiB != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "minMemoryGiB", r.minMemoryGiB, "form", "") + } + if r.maxMemoryGiB != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "maxMemoryGiB", r.maxMemoryGiB, "form", "") + } + if r.minDiskGiB != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "minDiskGiB", r.minDiskGiB, "form", "") + } + if r.maxDiskGiB != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "maxDiskGiB", r.maxDiskGiB, "form", "") + } + if r.lastEventAfter != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "lastEventAfter", r.lastEventAfter, "form", "") + } + if r.lastEventBefore != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "lastEventBefore", r.lastEventBefore, "form", "") + } + if r.sort != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "sort", r.sort, "form", "") + } else { + var defaultValue string = "createdAt" + parameterAddToHeaderOrQuery(localVarQueryParams, "sort", defaultValue, "form", "") + r.sort = &defaultValue + } + if r.order != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "order", r.order, "form", "") + } else { + var defaultValue string = "desc" + parameterAddToHeaderOrQuery(localVarQueryParams, "order", defaultValue, "form", "") + r.order = &defaultValue + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIRecoverBoxRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIRecoverBoxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIRecoverBoxRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIRecoverBoxRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.RecoverBoxExecute(r) +} + +/* +RecoverBox Recover box from error state + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIRecoverBoxRequest +*/ +func (a *BoxAPIService) RecoverBox(ctx context.Context, boxIdOrName string) BoxAPIRecoverBoxRequest { + return BoxAPIRecoverBoxRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) RecoverBoxExecute(r BoxAPIRecoverBoxRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.RecoverBox") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/recover" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIReplaceLabelsRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + boxLabels *BoxLabels + xBoxLiteOrganizationID *string +} + +func (r BoxAPIReplaceLabelsRequest) BoxLabels(boxLabels BoxLabels) BoxAPIReplaceLabelsRequest { + r.boxLabels = &boxLabels + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIReplaceLabelsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIReplaceLabelsRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIReplaceLabelsRequest) Execute() (*BoxLabels, *http.Response, error) { + return r.ApiService.ReplaceLabelsExecute(r) +} + +/* +ReplaceLabels Replace box labels + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIReplaceLabelsRequest +*/ +func (a *BoxAPIService) ReplaceLabels(ctx context.Context, boxIdOrName string) BoxAPIReplaceLabelsRequest { + return BoxAPIReplaceLabelsRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return BoxLabels +func (a *BoxAPIService) ReplaceLabelsExecute(r BoxAPIReplaceLabelsRequest) (*BoxLabels, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPut + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *BoxLabels + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ReplaceLabels") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/labels" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.boxLabels == nil { + return localVarReturnValue, nil, reportError("boxLabels is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + // body params + localVarPostBody = r.boxLabels + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIResizeBoxRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + resizeBox *ResizeBox + xBoxLiteOrganizationID *string +} + +func (r BoxAPIResizeBoxRequest) ResizeBox(resizeBox ResizeBox) BoxAPIResizeBoxRequest { + r.resizeBox = &resizeBox + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIResizeBoxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIResizeBoxRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIResizeBoxRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.ResizeBoxExecute(r) +} + +/* +ResizeBox Resize box resources + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIResizeBoxRequest +*/ +func (a *BoxAPIService) ResizeBox(ctx context.Context, boxIdOrName string) BoxAPIResizeBoxRequest { + return BoxAPIResizeBoxRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) ResizeBoxExecute(r BoxAPIResizeBoxRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ResizeBox") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/resize" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.resizeBox == nil { + return localVarReturnValue, nil, reportError("resizeBox is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + // body params + localVarPostBody = r.resizeBox + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIRevokeSshAccessRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + xBoxLiteOrganizationID *string + token *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIRevokeSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIRevokeSshAccessRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +// SSH access token to revoke. If not provided, all SSH access for the box will be revoked. +func (r BoxAPIRevokeSshAccessRequest) Token(token string) BoxAPIRevokeSshAccessRequest { + r.token = &token + return r +} + +func (r BoxAPIRevokeSshAccessRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.RevokeSshAccessExecute(r) +} + +/* +RevokeSshAccess Revoke SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @return BoxAPIRevokeSshAccessRequest +*/ +func (a *BoxAPIService) RevokeSshAccess(ctx context.Context, boxIdOrName string) BoxAPIRevokeSshAccessRequest { + return BoxAPIRevokeSshAccessRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) RevokeSshAccessExecute(r BoxAPIRevokeSshAccessRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodDelete + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.RevokeSshAccess") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/ssh-access" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + if r.token != nil { + parameterAddToHeaderOrQuery(localVarQueryParams, "token", r.token, "form", "") + } + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPISetAutoDeleteIntervalRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + interval float32 + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPISetAutoDeleteIntervalRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPISetAutoDeleteIntervalRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPISetAutoDeleteIntervalRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.SetAutoDeleteIntervalExecute(r) +} + +/* +SetAutoDeleteInterval Set box auto-delete interval + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param interval Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) + @return BoxAPISetAutoDeleteIntervalRequest +*/ +func (a *BoxAPIService) SetAutoDeleteInterval(ctx context.Context, boxIdOrName string, interval float32) BoxAPISetAutoDeleteIntervalRequest { + return BoxAPISetAutoDeleteIntervalRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + interval: interval, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) SetAutoDeleteIntervalExecute(r BoxAPISetAutoDeleteIntervalRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.SetAutoDeleteInterval") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/autodelete/{interval}" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPISetAutostopIntervalRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + interval float32 + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPISetAutostopIntervalRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPISetAutostopIntervalRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPISetAutostopIntervalRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.SetAutostopIntervalExecute(r) +} + +/* +SetAutostopInterval Set box auto-stop interval + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param interval Auto-stop interval in minutes (0 to disable) + @return BoxAPISetAutostopIntervalRequest +*/ +func (a *BoxAPIService) SetAutostopInterval(ctx context.Context, boxIdOrName string, interval float32) BoxAPISetAutostopIntervalRequest { + return BoxAPISetAutostopIntervalRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + interval: interval, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) SetAutostopIntervalExecute(r BoxAPISetAutostopIntervalRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.SetAutostopInterval") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/autostop/{interval}" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIUpdateBoxStateRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + updateBoxStateDto *UpdateBoxStateDto + xBoxLiteOrganizationID *string +} + +func (r BoxAPIUpdateBoxStateRequest) UpdateBoxStateDto(updateBoxStateDto UpdateBoxStateDto) BoxAPIUpdateBoxStateRequest { + r.updateBoxStateDto = &updateBoxStateDto + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIUpdateBoxStateRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIUpdateBoxStateRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIUpdateBoxStateRequest) Execute() (*http.Response, error) { + return r.ApiService.UpdateBoxStateExecute(r) +} + +/* +UpdateBoxState Update box state + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIUpdateBoxStateRequest +*/ +func (a *BoxAPIService) UpdateBoxState(ctx context.Context, boxId string) BoxAPIUpdateBoxStateRequest { + return BoxAPIUpdateBoxStateRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +func (a *BoxAPIService) UpdateBoxStateExecute(r BoxAPIUpdateBoxStateRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPut + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.UpdateBoxState") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/state" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.updateBoxStateDto == nil { + return nil, reportError("updateBoxStateDto is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + // body params + localVarPostBody = r.updateBoxStateDto + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type BoxAPIUpdateLastActivityRequest struct { + ctx context.Context + ApiService BoxAPI + boxId string + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIUpdateLastActivityRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIUpdateLastActivityRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIUpdateLastActivityRequest) Execute() (*http.Response, error) { + return r.ApiService.UpdateLastActivityExecute(r) +} + +/* +UpdateLastActivity Update box last activity + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId ID of the box + @return BoxAPIUpdateLastActivityRequest +*/ +func (a *BoxAPIService) UpdateLastActivity(ctx context.Context, boxId string) BoxAPIUpdateLastActivityRequest { + return BoxAPIUpdateLastActivityRequest{ + ApiService: a, + ctx: ctx, + boxId: boxId, + } +} + +// Execute executes the request +func (a *BoxAPIService) UpdateLastActivityExecute(r BoxAPIUpdateLastActivityRequest) (*http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.UpdateLastActivity") + if err != nil { + return nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxId}/last-activity" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarHTTPResponse, newErr + } + + return localVarHTTPResponse, nil +} + +type BoxAPIUpdatePublicStatusRequest struct { + ctx context.Context + ApiService BoxAPI + boxIdOrName string + isPublic bool + xBoxLiteOrganizationID *string +} + +// Use with JWT to specify the organization ID +func (r BoxAPIUpdatePublicStatusRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIUpdatePublicStatusRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIUpdatePublicStatusRequest) Execute() (*Box, *http.Response, error) { + return r.ApiService.UpdatePublicStatusExecute(r) +} + +/* +UpdatePublicStatus Update public status + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxIdOrName ID or name of the box + @param isPublic Public status to set + @return BoxAPIUpdatePublicStatusRequest +*/ +func (a *BoxAPIService) UpdatePublicStatus(ctx context.Context, boxIdOrName string, isPublic bool) BoxAPIUpdatePublicStatusRequest { + return BoxAPIUpdatePublicStatusRequest{ + ApiService: a, + ctx: ctx, + boxIdOrName: boxIdOrName, + isPublic: isPublic, + } +} + +// Execute executes the request +// @return Box +func (a *BoxAPIService) UpdatePublicStatusExecute(r BoxAPIUpdatePublicStatusRequest) (*Box, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *Box + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.UpdatePublicStatus") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/{boxIdOrName}/public/{isPublic}" + localVarPath = strings.Replace(localVarPath, "{"+"boxIdOrName"+"}", url.PathEscape(parameterValueToString(r.boxIdOrName, "boxIdOrName")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"isPublic"+"}", url.PathEscape(parameterValueToString(r.isPublic, "isPublic")), -1) + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type BoxAPIValidateSshAccessRequest struct { + ctx context.Context + ApiService BoxAPI + token *string + xBoxLiteOrganizationID *string +} + +// SSH access token to validate +func (r BoxAPIValidateSshAccessRequest) Token(token string) BoxAPIValidateSshAccessRequest { + r.token = &token + return r +} + +// Use with JWT to specify the organization ID +func (r BoxAPIValidateSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) BoxAPIValidateSshAccessRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r BoxAPIValidateSshAccessRequest) Execute() (*SshAccessValidationDto, *http.Response, error) { + return r.ApiService.ValidateSshAccessExecute(r) +} + +/* +ValidateSshAccess Validate SSH access for box + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return BoxAPIValidateSshAccessRequest +*/ +func (a *BoxAPIService) ValidateSshAccess(ctx context.Context) BoxAPIValidateSshAccessRequest { + return BoxAPIValidateSshAccessRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return SshAccessValidationDto +func (a *BoxAPIService) ValidateSshAccessExecute(r BoxAPIValidateSshAccessRequest) (*SshAccessValidationDto, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *SshAccessValidationDto + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "BoxAPIService.ValidateSshAccess") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/box/ssh-access/validate" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.token == nil { + return localVarReturnValue, nil, reportError("token is required and must be specified") + } + + parameterAddToHeaderOrQuery(localVarQueryParams, "token", r.token, "form", "") + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"application/json"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/apps/api-client-go/api_docker_registry.go b/apps/api-client-go/api_docker_registry.go deleted file mode 100644 index 2cb242125..000000000 --- a/apps/api-client-go/api_docker_registry.go +++ /dev/null @@ -1,901 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "bytes" - "context" - "io" - "net/http" - "net/url" - "strings" -) - - -type DockerRegistryAPI interface { - - /* - CreateRegistry Create registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPICreateRegistryRequest - */ - CreateRegistry(ctx context.Context) DockerRegistryAPICreateRegistryRequest - - // CreateRegistryExecute executes the request - // @return DockerRegistry - CreateRegistryExecute(r DockerRegistryAPICreateRegistryRequest) (*DockerRegistry, *http.Response, error) - - /* - DeleteRegistry Delete registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIDeleteRegistryRequest - */ - DeleteRegistry(ctx context.Context, id string) DockerRegistryAPIDeleteRegistryRequest - - // DeleteRegistryExecute executes the request - DeleteRegistryExecute(r DockerRegistryAPIDeleteRegistryRequest) (*http.Response, error) - - /* - GetRegistry Get registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIGetRegistryRequest - */ - GetRegistry(ctx context.Context, id string) DockerRegistryAPIGetRegistryRequest - - // GetRegistryExecute executes the request - // @return DockerRegistry - GetRegistryExecute(r DockerRegistryAPIGetRegistryRequest) (*DockerRegistry, *http.Response, error) - - /* - GetTransientPushAccess Get temporary registry access for pushing snapshots - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPIGetTransientPushAccessRequest - */ - GetTransientPushAccess(ctx context.Context) DockerRegistryAPIGetTransientPushAccessRequest - - // GetTransientPushAccessExecute executes the request - // @return RegistryPushAccessDto - GetTransientPushAccessExecute(r DockerRegistryAPIGetTransientPushAccessRequest) (*RegistryPushAccessDto, *http.Response, error) - - /* - ListRegistries List registries - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPIListRegistriesRequest - */ - ListRegistries(ctx context.Context) DockerRegistryAPIListRegistriesRequest - - // ListRegistriesExecute executes the request - // @return []DockerRegistry - ListRegistriesExecute(r DockerRegistryAPIListRegistriesRequest) ([]DockerRegistry, *http.Response, error) - - /* - SetDefaultRegistry Set default registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPISetDefaultRegistryRequest - */ - SetDefaultRegistry(ctx context.Context, id string) DockerRegistryAPISetDefaultRegistryRequest - - // SetDefaultRegistryExecute executes the request - // @return DockerRegistry - SetDefaultRegistryExecute(r DockerRegistryAPISetDefaultRegistryRequest) (*DockerRegistry, *http.Response, error) - - /* - UpdateRegistry Update registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIUpdateRegistryRequest - */ - UpdateRegistry(ctx context.Context, id string) DockerRegistryAPIUpdateRegistryRequest - - // UpdateRegistryExecute executes the request - // @return DockerRegistry - UpdateRegistryExecute(r DockerRegistryAPIUpdateRegistryRequest) (*DockerRegistry, *http.Response, error) -} - -// DockerRegistryAPIService DockerRegistryAPI service -type DockerRegistryAPIService service - -type DockerRegistryAPICreateRegistryRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - createDockerRegistry *CreateDockerRegistry - xBoxLiteOrganizationID *string -} - -func (r DockerRegistryAPICreateRegistryRequest) CreateDockerRegistry(createDockerRegistry CreateDockerRegistry) DockerRegistryAPICreateRegistryRequest { - r.createDockerRegistry = &createDockerRegistry - return r -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPICreateRegistryRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPICreateRegistryRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPICreateRegistryRequest) Execute() (*DockerRegistry, *http.Response, error) { - return r.ApiService.CreateRegistryExecute(r) -} - -/* -CreateRegistry Create registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPICreateRegistryRequest -*/ -func (a *DockerRegistryAPIService) CreateRegistry(ctx context.Context) DockerRegistryAPICreateRegistryRequest { - return DockerRegistryAPICreateRegistryRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return DockerRegistry -func (a *DockerRegistryAPIService) CreateRegistryExecute(r DockerRegistryAPICreateRegistryRequest) (*DockerRegistry, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *DockerRegistry - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.CreateRegistry") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.createDockerRegistry == nil { - return localVarReturnValue, nil, reportError("createDockerRegistry is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.createDockerRegistry - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type DockerRegistryAPIDeleteRegistryRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPIDeleteRegistryRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPIDeleteRegistryRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPIDeleteRegistryRequest) Execute() (*http.Response, error) { - return r.ApiService.DeleteRegistryExecute(r) -} - -/* -DeleteRegistry Delete registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIDeleteRegistryRequest -*/ -func (a *DockerRegistryAPIService) DeleteRegistry(ctx context.Context, id string) DockerRegistryAPIDeleteRegistryRequest { - return DockerRegistryAPIDeleteRegistryRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -func (a *DockerRegistryAPIService) DeleteRegistryExecute(r DockerRegistryAPIDeleteRegistryRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.DeleteRegistry") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type DockerRegistryAPIGetRegistryRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPIGetRegistryRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPIGetRegistryRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPIGetRegistryRequest) Execute() (*DockerRegistry, *http.Response, error) { - return r.ApiService.GetRegistryExecute(r) -} - -/* -GetRegistry Get registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIGetRegistryRequest -*/ -func (a *DockerRegistryAPIService) GetRegistry(ctx context.Context, id string) DockerRegistryAPIGetRegistryRequest { - return DockerRegistryAPIGetRegistryRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return DockerRegistry -func (a *DockerRegistryAPIService) GetRegistryExecute(r DockerRegistryAPIGetRegistryRequest) (*DockerRegistry, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *DockerRegistry - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.GetRegistry") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type DockerRegistryAPIGetTransientPushAccessRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - xBoxLiteOrganizationID *string - regionId *string -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPIGetTransientPushAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPIGetTransientPushAccessRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// ID of the region where the snapshot will be available (defaults to organization default region) -func (r DockerRegistryAPIGetTransientPushAccessRequest) RegionId(regionId string) DockerRegistryAPIGetTransientPushAccessRequest { - r.regionId = ®ionId - return r -} - -func (r DockerRegistryAPIGetTransientPushAccessRequest) Execute() (*RegistryPushAccessDto, *http.Response, error) { - return r.ApiService.GetTransientPushAccessExecute(r) -} - -/* -GetTransientPushAccess Get temporary registry access for pushing snapshots - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPIGetTransientPushAccessRequest -*/ -func (a *DockerRegistryAPIService) GetTransientPushAccess(ctx context.Context) DockerRegistryAPIGetTransientPushAccessRequest { - return DockerRegistryAPIGetTransientPushAccessRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return RegistryPushAccessDto -func (a *DockerRegistryAPIService) GetTransientPushAccessExecute(r DockerRegistryAPIGetTransientPushAccessRequest) (*RegistryPushAccessDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *RegistryPushAccessDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.GetTransientPushAccess") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry/registry-push-access" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.regionId != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "regionId", r.regionId, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type DockerRegistryAPIListRegistriesRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPIListRegistriesRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPIListRegistriesRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPIListRegistriesRequest) Execute() ([]DockerRegistry, *http.Response, error) { - return r.ApiService.ListRegistriesExecute(r) -} - -/* -ListRegistries List registries - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return DockerRegistryAPIListRegistriesRequest -*/ -func (a *DockerRegistryAPIService) ListRegistries(ctx context.Context) DockerRegistryAPIListRegistriesRequest { - return DockerRegistryAPIListRegistriesRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return []DockerRegistry -func (a *DockerRegistryAPIService) ListRegistriesExecute(r DockerRegistryAPIListRegistriesRequest) ([]DockerRegistry, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []DockerRegistry - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.ListRegistries") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type DockerRegistryAPISetDefaultRegistryRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPISetDefaultRegistryRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPISetDefaultRegistryRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPISetDefaultRegistryRequest) Execute() (*DockerRegistry, *http.Response, error) { - return r.ApiService.SetDefaultRegistryExecute(r) -} - -/* -SetDefaultRegistry Set default registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPISetDefaultRegistryRequest -*/ -func (a *DockerRegistryAPIService) SetDefaultRegistry(ctx context.Context, id string) DockerRegistryAPISetDefaultRegistryRequest { - return DockerRegistryAPISetDefaultRegistryRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return DockerRegistry -func (a *DockerRegistryAPIService) SetDefaultRegistryExecute(r DockerRegistryAPISetDefaultRegistryRequest) (*DockerRegistry, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *DockerRegistry - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.SetDefaultRegistry") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry/{id}/set-default" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type DockerRegistryAPIUpdateRegistryRequest struct { - ctx context.Context - ApiService DockerRegistryAPI - id string - updateDockerRegistry *UpdateDockerRegistry - xBoxLiteOrganizationID *string -} - -func (r DockerRegistryAPIUpdateRegistryRequest) UpdateDockerRegistry(updateDockerRegistry UpdateDockerRegistry) DockerRegistryAPIUpdateRegistryRequest { - r.updateDockerRegistry = &updateDockerRegistry - return r -} - -// Use with JWT to specify the organization ID -func (r DockerRegistryAPIUpdateRegistryRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) DockerRegistryAPIUpdateRegistryRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r DockerRegistryAPIUpdateRegistryRequest) Execute() (*DockerRegistry, *http.Response, error) { - return r.ApiService.UpdateRegistryExecute(r) -} - -/* -UpdateRegistry Update registry - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id ID of the docker registry - @return DockerRegistryAPIUpdateRegistryRequest -*/ -func (a *DockerRegistryAPIService) UpdateRegistry(ctx context.Context, id string) DockerRegistryAPIUpdateRegistryRequest { - return DockerRegistryAPIUpdateRegistryRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return DockerRegistry -func (a *DockerRegistryAPIService) UpdateRegistryExecute(r DockerRegistryAPIUpdateRegistryRequest) (*DockerRegistry, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPatch - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *DockerRegistry - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "DockerRegistryAPIService.UpdateRegistry") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/docker-registry/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.updateDockerRegistry == nil { - return localVarReturnValue, nil, reportError("updateDockerRegistry is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.updateDockerRegistry - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/api_health.go b/apps/api-client-go/api_health.go index ebb778ad4..30b5600f2 100644 --- a/apps/api-client-go/api_health.go +++ b/apps/api-client-go/api_health.go @@ -132,7 +132,7 @@ func (a *HealthAPIService) HealthControllerCheckExecute(r HealthAPIHealthControl error: localVarHTTPResponse.Status, } if localVarHTTPResponse.StatusCode == 503 { - var v HealthControllerCheck503Response + var v HealthControllerCheck200Response err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) if err != nil { newErr.error = err.Error() diff --git a/apps/api-client-go/api_jobs.go b/apps/api-client-go/api_jobs.go index 1f9f65c64..6d52d1ec2 100644 --- a/apps/api-client-go/api_jobs.go +++ b/apps/api-client-go/api_jobs.go @@ -259,12 +259,14 @@ func (a *JobsAPIService) ListJobsExecute(r JobsAPIListJobsRequest) (*PaginatedJo parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") } else { var defaultValue float32 = 1 + parameterAddToHeaderOrQuery(localVarQueryParams, "page", defaultValue, "form", "") r.page = &defaultValue } if r.limit != nil { parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") } else { var defaultValue float32 = 100 + parameterAddToHeaderOrQuery(localVarQueryParams, "limit", defaultValue, "form", "") r.limit = &defaultValue } if r.status != nil { diff --git a/apps/api-client-go/api_organizations.go b/apps/api-client-go/api_organizations.go index c396e3207..1ca20c136 100644 --- a/apps/api-client-go/api_organizations.go +++ b/apps/api-client-go/api_organizations.go @@ -175,17 +175,17 @@ type OrganizationsAPI interface { GetOrganizationExecute(r OrganizationsAPIGetOrganizationRequest) (*Organization, *http.Response, error) /* - GetOrganizationBySandboxId Get organization by sandbox ID + GetOrganizationByBoxId Get organization by box ID @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId Sandbox ID - @return OrganizationsAPIGetOrganizationBySandboxIdRequest + @param boxId Box ID + @return OrganizationsAPIGetOrganizationByBoxIdRequest */ - GetOrganizationBySandboxId(ctx context.Context, sandboxId string) OrganizationsAPIGetOrganizationBySandboxIdRequest + GetOrganizationByBoxId(ctx context.Context, boxId string) OrganizationsAPIGetOrganizationByBoxIdRequest - // GetOrganizationBySandboxIdExecute executes the request + // GetOrganizationByBoxIdExecute executes the request // @return Organization - GetOrganizationBySandboxIdExecute(r OrganizationsAPIGetOrganizationBySandboxIdRequest) (*Organization, *http.Response, error) + GetOrganizationByBoxIdExecute(r OrganizationsAPIGetOrganizationByBoxIdRequest) (*Organization, *http.Response, error) /* GetOrganizationInvitationsCountForAuthenticatedUser Get count of organization invitations for authenticated user @@ -200,30 +200,17 @@ type OrganizationsAPI interface { GetOrganizationInvitationsCountForAuthenticatedUserExecute(r OrganizationsAPIGetOrganizationInvitationsCountForAuthenticatedUserRequest) (float32, *http.Response, error) /* - GetOrganizationOtelConfigBySandboxAuthToken Get organization OTEL config by sandbox auth token + GetOrganizationOtelConfigByBoxAuthToken Get organization OTEL config by box auth token @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param authToken Sandbox Auth Token - @return OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest + @param authToken Box Auth Token + @return OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest */ - GetOrganizationOtelConfigBySandboxAuthToken(ctx context.Context, authToken string) OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest + GetOrganizationOtelConfigByBoxAuthToken(ctx context.Context, authToken string) OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest - // GetOrganizationOtelConfigBySandboxAuthTokenExecute executes the request + // GetOrganizationOtelConfigByBoxAuthTokenExecute executes the request // @return OtelConfig - GetOrganizationOtelConfigBySandboxAuthTokenExecute(r OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest) (*OtelConfig, *http.Response, error) - - /* - GetOrganizationUsageOverview Get organization current usage overview - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPIGetOrganizationUsageOverviewRequest - */ - GetOrganizationUsageOverview(ctx context.Context, organizationId string) OrganizationsAPIGetOrganizationUsageOverviewRequest - - // GetOrganizationUsageOverviewExecute executes the request - // @return OrganizationUsageOverview - GetOrganizationUsageOverviewExecute(r OrganizationsAPIGetOrganizationUsageOverviewRequest) (*OrganizationUsageOverview, *http.Response, error) + GetOrganizationOtelConfigByBoxAuthTokenExecute(r OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest) (*OtelConfig, *http.Response, error) /* GetRegionById Get region by ID @@ -238,19 +225,6 @@ type OrganizationsAPI interface { // @return Region GetRegionByIdExecute(r OrganizationsAPIGetRegionByIdRequest) (*Region, *http.Response, error) - /* - GetRegionQuotaBySandboxId Get region quota by sandbox ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId Sandbox ID - @return OrganizationsAPIGetRegionQuotaBySandboxIdRequest - */ - GetRegionQuotaBySandboxId(ctx context.Context, sandboxId string) OrganizationsAPIGetRegionQuotaBySandboxIdRequest - - // GetRegionQuotaBySandboxIdExecute executes the request - // @return RegionQuota - GetRegionQuotaBySandboxIdExecute(r OrganizationsAPIGetRegionQuotaBySandboxIdRequest) (*RegionQuota, *http.Response, error) - /* LeaveOrganization Leave organization @@ -351,19 +325,6 @@ type OrganizationsAPI interface { // @return RegenerateApiKeyResponse RegenerateProxyApiKeyExecute(r OrganizationsAPIRegenerateProxyApiKeyRequest) (*RegenerateApiKeyResponse, *http.Response, error) - /* - RegenerateSnapshotManagerCredentials Regenerate snapshot manager credentials for a region - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Region ID - @return OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest - */ - RegenerateSnapshotManagerCredentials(ctx context.Context, id string) OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest - - // RegenerateSnapshotManagerCredentialsExecute executes the request - // @return SnapshotManagerCredentials - RegenerateSnapshotManagerCredentialsExecute(r OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest) (*SnapshotManagerCredentials, *http.Response, error) - /* RegenerateSshGatewayApiKey Regenerate SSH gateway API key for a region @@ -427,6 +388,18 @@ type OrganizationsAPI interface { // @return OrganizationUser UpdateAccessForOrganizationMemberExecute(r OrganizationsAPIUpdateAccessForOrganizationMemberRequest) (*OrganizationUser, *http.Response, error) + /* + UpdateBoxDefaultLimitedNetworkEgress Update box default limited network egress + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param organizationId Organization ID + @return OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest + */ + UpdateBoxDefaultLimitedNetworkEgress(ctx context.Context, organizationId string) OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest + + // UpdateBoxDefaultLimitedNetworkEgressExecute executes the request + UpdateBoxDefaultLimitedNetworkEgressExecute(r OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest) (*http.Response, error) + /* UpdateExperimentalConfig Update experimental configuration @@ -454,29 +427,17 @@ type OrganizationsAPI interface { UpdateOrganizationInvitationExecute(r OrganizationsAPIUpdateOrganizationInvitationRequest) (*OrganizationInvitation, *http.Response, error) /* - UpdateOrganizationQuota Update organization quota - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPIUpdateOrganizationQuotaRequest - */ - UpdateOrganizationQuota(ctx context.Context, organizationId string) OrganizationsAPIUpdateOrganizationQuotaRequest - - // UpdateOrganizationQuotaExecute executes the request - UpdateOrganizationQuotaExecute(r OrganizationsAPIUpdateOrganizationQuotaRequest) (*http.Response, error) - - /* - UpdateOrganizationRegionQuota Update organization region quota + UpdateOrganizationName Update organization name @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param organizationId Organization ID - @param regionId ID of the region where the updated quota will be applied - @return OrganizationsAPIUpdateOrganizationRegionQuotaRequest + @return OrganizationsAPIUpdateOrganizationNameRequest */ - UpdateOrganizationRegionQuota(ctx context.Context, organizationId string, regionId string) OrganizationsAPIUpdateOrganizationRegionQuotaRequest + UpdateOrganizationName(ctx context.Context, organizationId string) OrganizationsAPIUpdateOrganizationNameRequest - // UpdateOrganizationRegionQuotaExecute executes the request - UpdateOrganizationRegionQuotaExecute(r OrganizationsAPIUpdateOrganizationRegionQuotaRequest) (*http.Response, error) + // UpdateOrganizationNameExecute executes the request + // @return Organization + UpdateOrganizationNameExecute(r OrganizationsAPIUpdateOrganizationNameRequest) (*Organization, *http.Response, error) /* UpdateOrganizationRole Update organization role @@ -503,18 +464,6 @@ type OrganizationsAPI interface { // UpdateRegionExecute executes the request UpdateRegionExecute(r OrganizationsAPIUpdateRegionRequest) (*http.Response, error) - - /* - UpdateSandboxDefaultLimitedNetworkEgress Update sandbox default limited network egress - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest - */ - UpdateSandboxDefaultLimitedNetworkEgress(ctx context.Context, organizationId string) OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest - - // UpdateSandboxDefaultLimitedNetworkEgressExecute executes the request - UpdateSandboxDefaultLimitedNetworkEgressExecute(r OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest) (*http.Response, error) } // OrganizationsAPIService OrganizationsAPI service @@ -1734,34 +1683,34 @@ func (a *OrganizationsAPIService) GetOrganizationExecute(r OrganizationsAPIGetOr return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIGetOrganizationBySandboxIdRequest struct { +type OrganizationsAPIGetOrganizationByBoxIdRequest struct { ctx context.Context ApiService OrganizationsAPI - sandboxId string + boxId string } -func (r OrganizationsAPIGetOrganizationBySandboxIdRequest) Execute() (*Organization, *http.Response, error) { - return r.ApiService.GetOrganizationBySandboxIdExecute(r) +func (r OrganizationsAPIGetOrganizationByBoxIdRequest) Execute() (*Organization, *http.Response, error) { + return r.ApiService.GetOrganizationByBoxIdExecute(r) } /* -GetOrganizationBySandboxId Get organization by sandbox ID +GetOrganizationByBoxId Get organization by box ID @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId Sandbox ID - @return OrganizationsAPIGetOrganizationBySandboxIdRequest + @param boxId Box ID + @return OrganizationsAPIGetOrganizationByBoxIdRequest */ -func (a *OrganizationsAPIService) GetOrganizationBySandboxId(ctx context.Context, sandboxId string) OrganizationsAPIGetOrganizationBySandboxIdRequest { - return OrganizationsAPIGetOrganizationBySandboxIdRequest{ +func (a *OrganizationsAPIService) GetOrganizationByBoxId(ctx context.Context, boxId string) OrganizationsAPIGetOrganizationByBoxIdRequest { + return OrganizationsAPIGetOrganizationByBoxIdRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + boxId: boxId, } } // Execute executes the request // @return Organization -func (a *OrganizationsAPIService) GetOrganizationBySandboxIdExecute(r OrganizationsAPIGetOrganizationBySandboxIdRequest) (*Organization, *http.Response, error) { +func (a *OrganizationsAPIService) GetOrganizationByBoxIdExecute(r OrganizationsAPIGetOrganizationByBoxIdRequest) (*Organization, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} @@ -1769,13 +1718,13 @@ func (a *OrganizationsAPIService) GetOrganizationBySandboxIdExecute(r Organizati localVarReturnValue *Organization ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetOrganizationBySandboxId") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetOrganizationByBoxId") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/by-sandbox-id/{sandboxId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/organizations/by-box-id/{boxId}" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} @@ -1932,25 +1881,25 @@ func (a *OrganizationsAPIService) GetOrganizationInvitationsCountForAuthenticate return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest struct { +type OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest struct { ctx context.Context ApiService OrganizationsAPI authToken string } -func (r OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest) Execute() (*OtelConfig, *http.Response, error) { - return r.ApiService.GetOrganizationOtelConfigBySandboxAuthTokenExecute(r) +func (r OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest) Execute() (*OtelConfig, *http.Response, error) { + return r.ApiService.GetOrganizationOtelConfigByBoxAuthTokenExecute(r) } /* -GetOrganizationOtelConfigBySandboxAuthToken Get organization OTEL config by sandbox auth token +GetOrganizationOtelConfigByBoxAuthToken Get organization OTEL config by box auth token @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param authToken Sandbox Auth Token - @return OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest + @param authToken Box Auth Token + @return OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest */ -func (a *OrganizationsAPIService) GetOrganizationOtelConfigBySandboxAuthToken(ctx context.Context, authToken string) OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest { - return OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest{ +func (a *OrganizationsAPIService) GetOrganizationOtelConfigByBoxAuthToken(ctx context.Context, authToken string) OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest { + return OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest{ ApiService: a, ctx: ctx, authToken: authToken, @@ -1959,7 +1908,7 @@ func (a *OrganizationsAPIService) GetOrganizationOtelConfigBySandboxAuthToken(ct // Execute executes the request // @return OtelConfig -func (a *OrganizationsAPIService) GetOrganizationOtelConfigBySandboxAuthTokenExecute(r OrganizationsAPIGetOrganizationOtelConfigBySandboxAuthTokenRequest) (*OtelConfig, *http.Response, error) { +func (a *OrganizationsAPIService) GetOrganizationOtelConfigByBoxAuthTokenExecute(r OrganizationsAPIGetOrganizationOtelConfigByBoxAuthTokenRequest) (*OtelConfig, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} @@ -1967,12 +1916,12 @@ func (a *OrganizationsAPIService) GetOrganizationOtelConfigBySandboxAuthTokenExe localVarReturnValue *OtelConfig ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetOrganizationOtelConfigBySandboxAuthToken") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetOrganizationOtelConfigByBoxAuthToken") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/otel-config/by-sandbox-auth-token/{authToken}" + localVarPath := localBasePath + "/organizations/otel-config/by-box-auth-token/{authToken}" localVarPath = strings.Replace(localVarPath, "{"+"authToken"+"}", url.PathEscape(parameterValueToString(r.authToken, "authToken")), -1) localVarHeaderParams := make(map[string]string) @@ -2033,107 +1982,6 @@ func (a *OrganizationsAPIService) GetOrganizationOtelConfigBySandboxAuthTokenExe return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIGetOrganizationUsageOverviewRequest struct { - ctx context.Context - ApiService OrganizationsAPI - organizationId string -} - -func (r OrganizationsAPIGetOrganizationUsageOverviewRequest) Execute() (*OrganizationUsageOverview, *http.Response, error) { - return r.ApiService.GetOrganizationUsageOverviewExecute(r) -} - -/* -GetOrganizationUsageOverview Get organization current usage overview - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPIGetOrganizationUsageOverviewRequest -*/ -func (a *OrganizationsAPIService) GetOrganizationUsageOverview(ctx context.Context, organizationId string) OrganizationsAPIGetOrganizationUsageOverviewRequest { - return OrganizationsAPIGetOrganizationUsageOverviewRequest{ - ApiService: a, - ctx: ctx, - organizationId: organizationId, - } -} - -// Execute executes the request -// @return OrganizationUsageOverview -func (a *OrganizationsAPIService) GetOrganizationUsageOverviewExecute(r OrganizationsAPIGetOrganizationUsageOverviewRequest) (*OrganizationUsageOverview, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *OrganizationUsageOverview - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetOrganizationUsageOverview") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/organizations/{organizationId}/usage" - localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - type OrganizationsAPIGetRegionByIdRequest struct { ctx context.Context ApiService OrganizationsAPI @@ -2245,107 +2093,6 @@ func (a *OrganizationsAPIService) GetRegionByIdExecute(r OrganizationsAPIGetRegi return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIGetRegionQuotaBySandboxIdRequest struct { - ctx context.Context - ApiService OrganizationsAPI - sandboxId string -} - -func (r OrganizationsAPIGetRegionQuotaBySandboxIdRequest) Execute() (*RegionQuota, *http.Response, error) { - return r.ApiService.GetRegionQuotaBySandboxIdExecute(r) -} - -/* -GetRegionQuotaBySandboxId Get region quota by sandbox ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId Sandbox ID - @return OrganizationsAPIGetRegionQuotaBySandboxIdRequest -*/ -func (a *OrganizationsAPIService) GetRegionQuotaBySandboxId(ctx context.Context, sandboxId string) OrganizationsAPIGetRegionQuotaBySandboxIdRequest { - return OrganizationsAPIGetRegionQuotaBySandboxIdRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return RegionQuota -func (a *OrganizationsAPIService) GetRegionQuotaBySandboxIdExecute(r OrganizationsAPIGetRegionQuotaBySandboxIdRequest) (*RegionQuota, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *RegionQuota - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.GetRegionQuotaBySandboxId") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/organizations/region-quota/by-sandbox-id/{sandboxId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - type OrganizationsAPILeaveOrganizationRequest struct { ctx context.Context ApiService OrganizationsAPI @@ -3151,7 +2898,7 @@ func (a *OrganizationsAPIService) RegenerateProxyApiKeyExecute(r OrganizationsAP return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest struct { +type OrganizationsAPIRegenerateSshGatewayApiKeyRequest struct { ctx context.Context ApiService OrganizationsAPI id string @@ -3159,24 +2906,24 @@ type OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest struct { } // Use with JWT to specify the organization ID -func (r OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest { +func (r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) OrganizationsAPIRegenerateSshGatewayApiKeyRequest { r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID return r } -func (r OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest) Execute() (*SnapshotManagerCredentials, *http.Response, error) { - return r.ApiService.RegenerateSnapshotManagerCredentialsExecute(r) +func (r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) Execute() (*RegenerateApiKeyResponse, *http.Response, error) { + return r.ApiService.RegenerateSshGatewayApiKeyExecute(r) } /* -RegenerateSnapshotManagerCredentials Regenerate snapshot manager credentials for a region +RegenerateSshGatewayApiKey Regenerate SSH gateway API key for a region @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param id Region ID - @return OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest + @return OrganizationsAPIRegenerateSshGatewayApiKeyRequest */ -func (a *OrganizationsAPIService) RegenerateSnapshotManagerCredentials(ctx context.Context, id string) OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest { - return OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest{ +func (a *OrganizationsAPIService) RegenerateSshGatewayApiKey(ctx context.Context, id string) OrganizationsAPIRegenerateSshGatewayApiKeyRequest { + return OrganizationsAPIRegenerateSshGatewayApiKeyRequest{ ApiService: a, ctx: ctx, id: id, @@ -3184,21 +2931,21 @@ func (a *OrganizationsAPIService) RegenerateSnapshotManagerCredentials(ctx conte } // Execute executes the request -// @return SnapshotManagerCredentials -func (a *OrganizationsAPIService) RegenerateSnapshotManagerCredentialsExecute(r OrganizationsAPIRegenerateSnapshotManagerCredentialsRequest) (*SnapshotManagerCredentials, *http.Response, error) { +// @return RegenerateApiKeyResponse +func (a *OrganizationsAPIService) RegenerateSshGatewayApiKeyExecute(r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) (*RegenerateApiKeyResponse, *http.Response, error) { var ( localVarHTTPMethod = http.MethodPost localVarPostBody interface{} formFiles []formFile - localVarReturnValue *SnapshotManagerCredentials + localVarReturnValue *RegenerateApiKeyResponse ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.RegenerateSnapshotManagerCredentials") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.RegenerateSshGatewayApiKey") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/regions/{id}/regenerate-snapshot-manager-credentials" + localVarPath := localBasePath + "/regions/{id}/regenerate-ssh-gateway-api-key" localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) localVarHeaderParams := make(map[string]string) @@ -3262,145 +3009,34 @@ func (a *OrganizationsAPIService) RegenerateSnapshotManagerCredentialsExecute(r return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIRegenerateSshGatewayApiKeyRequest struct { +type OrganizationsAPISetOrganizationDefaultRegionRequest struct { ctx context.Context ApiService OrganizationsAPI - id string - xBoxLiteOrganizationID *string + organizationId string + updateOrganizationDefaultRegion *UpdateOrganizationDefaultRegion } -// Use with JWT to specify the organization ID -func (r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) OrganizationsAPIRegenerateSshGatewayApiKeyRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID +func (r OrganizationsAPISetOrganizationDefaultRegionRequest) UpdateOrganizationDefaultRegion(updateOrganizationDefaultRegion UpdateOrganizationDefaultRegion) OrganizationsAPISetOrganizationDefaultRegionRequest { + r.updateOrganizationDefaultRegion = &updateOrganizationDefaultRegion return r } -func (r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) Execute() (*RegenerateApiKeyResponse, *http.Response, error) { - return r.ApiService.RegenerateSshGatewayApiKeyExecute(r) +func (r OrganizationsAPISetOrganizationDefaultRegionRequest) Execute() (*http.Response, error) { + return r.ApiService.SetOrganizationDefaultRegionExecute(r) } /* -RegenerateSshGatewayApiKey Regenerate SSH gateway API key for a region +SetOrganizationDefaultRegion Set default region for organization @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Region ID - @return OrganizationsAPIRegenerateSshGatewayApiKeyRequest + @param organizationId Organization ID + @return OrganizationsAPISetOrganizationDefaultRegionRequest */ -func (a *OrganizationsAPIService) RegenerateSshGatewayApiKey(ctx context.Context, id string) OrganizationsAPIRegenerateSshGatewayApiKeyRequest { - return OrganizationsAPIRegenerateSshGatewayApiKeyRequest{ +func (a *OrganizationsAPIService) SetOrganizationDefaultRegion(ctx context.Context, organizationId string) OrganizationsAPISetOrganizationDefaultRegionRequest { + return OrganizationsAPISetOrganizationDefaultRegionRequest{ ApiService: a, ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return RegenerateApiKeyResponse -func (a *OrganizationsAPIService) RegenerateSshGatewayApiKeyExecute(r OrganizationsAPIRegenerateSshGatewayApiKeyRequest) (*RegenerateApiKeyResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *RegenerateApiKeyResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.RegenerateSshGatewayApiKey") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/regions/{id}/regenerate-ssh-gateway-api-key" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type OrganizationsAPISetOrganizationDefaultRegionRequest struct { - ctx context.Context - ApiService OrganizationsAPI - organizationId string - updateOrganizationDefaultRegion *UpdateOrganizationDefaultRegion -} - -func (r OrganizationsAPISetOrganizationDefaultRegionRequest) UpdateOrganizationDefaultRegion(updateOrganizationDefaultRegion UpdateOrganizationDefaultRegion) OrganizationsAPISetOrganizationDefaultRegionRequest { - r.updateOrganizationDefaultRegion = &updateOrganizationDefaultRegion - return r -} - -func (r OrganizationsAPISetOrganizationDefaultRegionRequest) Execute() (*http.Response, error) { - return r.ApiService.SetOrganizationDefaultRegionExecute(r) -} - -/* -SetOrganizationDefaultRegion Set default region for organization - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPISetOrganizationDefaultRegionRequest -*/ -func (a *OrganizationsAPIService) SetOrganizationDefaultRegion(ctx context.Context, organizationId string) OrganizationsAPISetOrganizationDefaultRegionRequest { - return OrganizationsAPISetOrganizationDefaultRegionRequest{ - ApiService: a, - ctx: ctx, - organizationId: organizationId, + organizationId: organizationId, } } @@ -3778,32 +3414,31 @@ func (a *OrganizationsAPIService) UpdateAccessForOrganizationMemberExecute(r Org return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIUpdateExperimentalConfigRequest struct { +type OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest struct { ctx context.Context ApiService OrganizationsAPI organizationId string - requestBody *map[string]interface{} + organizationBoxDefaultLimitedNetworkEgress *OrganizationBoxDefaultLimitedNetworkEgress } -// Experimental configuration as a JSON object. Set to null to clear the configuration. -func (r OrganizationsAPIUpdateExperimentalConfigRequest) RequestBody(requestBody map[string]interface{}) OrganizationsAPIUpdateExperimentalConfigRequest { - r.requestBody = &requestBody +func (r OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest) OrganizationBoxDefaultLimitedNetworkEgress(organizationBoxDefaultLimitedNetworkEgress OrganizationBoxDefaultLimitedNetworkEgress) OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest { + r.organizationBoxDefaultLimitedNetworkEgress = &organizationBoxDefaultLimitedNetworkEgress return r } -func (r OrganizationsAPIUpdateExperimentalConfigRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateExperimentalConfigExecute(r) +func (r OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest) Execute() (*http.Response, error) { + return r.ApiService.UpdateBoxDefaultLimitedNetworkEgressExecute(r) } /* -UpdateExperimentalConfig Update experimental configuration +UpdateBoxDefaultLimitedNetworkEgress Update box default limited network egress @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param organizationId Organization ID - @return OrganizationsAPIUpdateExperimentalConfigRequest + @return OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest */ -func (a *OrganizationsAPIService) UpdateExperimentalConfig(ctx context.Context, organizationId string) OrganizationsAPIUpdateExperimentalConfigRequest { - return OrganizationsAPIUpdateExperimentalConfigRequest{ +func (a *OrganizationsAPIService) UpdateBoxDefaultLimitedNetworkEgress(ctx context.Context, organizationId string) OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest { + return OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest{ ApiService: a, ctx: ctx, organizationId: organizationId, @@ -3811,24 +3446,27 @@ func (a *OrganizationsAPIService) UpdateExperimentalConfig(ctx context.Context, } // Execute executes the request -func (a *OrganizationsAPIService) UpdateExperimentalConfigExecute(r OrganizationsAPIUpdateExperimentalConfigRequest) (*http.Response, error) { +func (a *OrganizationsAPIService) UpdateBoxDefaultLimitedNetworkEgressExecute(r OrganizationsAPIUpdateBoxDefaultLimitedNetworkEgressRequest) (*http.Response, error) { var ( - localVarHTTPMethod = http.MethodPut + localVarHTTPMethod = http.MethodPost localVarPostBody interface{} formFiles []formFile ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateExperimentalConfig") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateBoxDefaultLimitedNetworkEgress") if err != nil { return nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/{organizationId}/experimental-config" + localVarPath := localBasePath + "/organizations/{organizationId}/box-default-limited-network-egress" localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} + if r.organizationBoxDefaultLimitedNetworkEgress == nil { + return nil, reportError("organizationBoxDefaultLimitedNetworkEgress is required and must be specified") + } // to determine the Content-Type header localVarHTTPContentTypes := []string{"application/json"} @@ -3848,7 +3486,7 @@ func (a *OrganizationsAPIService) UpdateExperimentalConfigExecute(r Organization localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } // body params - localVarPostBody = r.requestBody + localVarPostBody = r.organizationBoxDefaultLimitedNetworkEgress req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { return nil, err @@ -3877,65 +3515,57 @@ func (a *OrganizationsAPIService) UpdateExperimentalConfigExecute(r Organization return localVarHTTPResponse, nil } -type OrganizationsAPIUpdateOrganizationInvitationRequest struct { +type OrganizationsAPIUpdateExperimentalConfigRequest struct { ctx context.Context ApiService OrganizationsAPI organizationId string - invitationId string - updateOrganizationInvitation *UpdateOrganizationInvitation + requestBody *map[string]interface{} } -func (r OrganizationsAPIUpdateOrganizationInvitationRequest) UpdateOrganizationInvitation(updateOrganizationInvitation UpdateOrganizationInvitation) OrganizationsAPIUpdateOrganizationInvitationRequest { - r.updateOrganizationInvitation = &updateOrganizationInvitation +// Experimental configuration as a JSON object. Set to null to clear the configuration. +func (r OrganizationsAPIUpdateExperimentalConfigRequest) RequestBody(requestBody map[string]interface{}) OrganizationsAPIUpdateExperimentalConfigRequest { + r.requestBody = &requestBody return r } -func (r OrganizationsAPIUpdateOrganizationInvitationRequest) Execute() (*OrganizationInvitation, *http.Response, error) { - return r.ApiService.UpdateOrganizationInvitationExecute(r) +func (r OrganizationsAPIUpdateExperimentalConfigRequest) Execute() (*http.Response, error) { + return r.ApiService.UpdateExperimentalConfigExecute(r) } /* -UpdateOrganizationInvitation Update organization invitation +UpdateExperimentalConfig Update experimental configuration @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param organizationId Organization ID - @param invitationId Invitation ID - @return OrganizationsAPIUpdateOrganizationInvitationRequest + @return OrganizationsAPIUpdateExperimentalConfigRequest */ -func (a *OrganizationsAPIService) UpdateOrganizationInvitation(ctx context.Context, organizationId string, invitationId string) OrganizationsAPIUpdateOrganizationInvitationRequest { - return OrganizationsAPIUpdateOrganizationInvitationRequest{ +func (a *OrganizationsAPIService) UpdateExperimentalConfig(ctx context.Context, organizationId string) OrganizationsAPIUpdateExperimentalConfigRequest { + return OrganizationsAPIUpdateExperimentalConfigRequest{ ApiService: a, ctx: ctx, organizationId: organizationId, - invitationId: invitationId, } } // Execute executes the request -// @return OrganizationInvitation -func (a *OrganizationsAPIService) UpdateOrganizationInvitationExecute(r OrganizationsAPIUpdateOrganizationInvitationRequest) (*OrganizationInvitation, *http.Response, error) { +func (a *OrganizationsAPIService) UpdateExperimentalConfigExecute(r OrganizationsAPIUpdateExperimentalConfigRequest) (*http.Response, error) { var ( localVarHTTPMethod = http.MethodPut localVarPostBody interface{} formFiles []formFile - localVarReturnValue *OrganizationInvitation ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateOrganizationInvitation") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateExperimentalConfig") if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + return nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/{organizationId}/invitations/{invitationId}" + localVarPath := localBasePath + "/organizations/{organizationId}/experimental-config" localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"invitationId"+"}", url.PathEscape(parameterValueToString(r.invitationId, "invitationId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} - if r.updateOrganizationInvitation == nil { - return localVarReturnValue, nil, reportError("updateOrganizationInvitation is required and must be specified") - } // to determine the Content-Type header localVarHTTPContentTypes := []string{"application/json"} @@ -3947,7 +3577,7 @@ func (a *OrganizationsAPIService) UpdateOrganizationInvitationExecute(r Organiza } // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} + localVarHTTPHeaderAccepts := []string{} // set Accept header localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) @@ -3955,22 +3585,22 @@ func (a *OrganizationsAPIService) UpdateOrganizationInvitationExecute(r Organiza localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } // body params - localVarPostBody = r.updateOrganizationInvitation + localVarPostBody = r.requestBody req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { - return localVarReturnValue, nil, err + return nil, err } localVarHTTPResponse, err := a.client.callAPI(req) if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err + return localVarHTTPResponse, err } localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) if err != nil { - return localVarReturnValue, localVarHTTPResponse, err + return localVarHTTPResponse, err } if localVarHTTPResponse.StatusCode >= 300 { @@ -3978,73 +3608,70 @@ func (a *OrganizationsAPIService) UpdateOrganizationInvitationExecute(r Organiza body: localVarBody, error: localVarHTTPResponse.Status, } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr + return localVarHTTPResponse, newErr } - return localVarReturnValue, localVarHTTPResponse, nil + return localVarHTTPResponse, nil } -type OrganizationsAPIUpdateOrganizationQuotaRequest struct { +type OrganizationsAPIUpdateOrganizationInvitationRequest struct { ctx context.Context ApiService OrganizationsAPI organizationId string - updateOrganizationQuota *UpdateOrganizationQuota + invitationId string + updateOrganizationInvitation *UpdateOrganizationInvitation } -func (r OrganizationsAPIUpdateOrganizationQuotaRequest) UpdateOrganizationQuota(updateOrganizationQuota UpdateOrganizationQuota) OrganizationsAPIUpdateOrganizationQuotaRequest { - r.updateOrganizationQuota = &updateOrganizationQuota +func (r OrganizationsAPIUpdateOrganizationInvitationRequest) UpdateOrganizationInvitation(updateOrganizationInvitation UpdateOrganizationInvitation) OrganizationsAPIUpdateOrganizationInvitationRequest { + r.updateOrganizationInvitation = &updateOrganizationInvitation return r } -func (r OrganizationsAPIUpdateOrganizationQuotaRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateOrganizationQuotaExecute(r) +func (r OrganizationsAPIUpdateOrganizationInvitationRequest) Execute() (*OrganizationInvitation, *http.Response, error) { + return r.ApiService.UpdateOrganizationInvitationExecute(r) } /* -UpdateOrganizationQuota Update organization quota +UpdateOrganizationInvitation Update organization invitation @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param organizationId Organization ID - @return OrganizationsAPIUpdateOrganizationQuotaRequest + @param invitationId Invitation ID + @return OrganizationsAPIUpdateOrganizationInvitationRequest */ -func (a *OrganizationsAPIService) UpdateOrganizationQuota(ctx context.Context, organizationId string) OrganizationsAPIUpdateOrganizationQuotaRequest { - return OrganizationsAPIUpdateOrganizationQuotaRequest{ +func (a *OrganizationsAPIService) UpdateOrganizationInvitation(ctx context.Context, organizationId string, invitationId string) OrganizationsAPIUpdateOrganizationInvitationRequest { + return OrganizationsAPIUpdateOrganizationInvitationRequest{ ApiService: a, ctx: ctx, organizationId: organizationId, + invitationId: invitationId, } } // Execute executes the request -func (a *OrganizationsAPIService) UpdateOrganizationQuotaExecute(r OrganizationsAPIUpdateOrganizationQuotaRequest) (*http.Response, error) { +// @return OrganizationInvitation +func (a *OrganizationsAPIService) UpdateOrganizationInvitationExecute(r OrganizationsAPIUpdateOrganizationInvitationRequest) (*OrganizationInvitation, *http.Response, error) { var ( - localVarHTTPMethod = http.MethodPatch + localVarHTTPMethod = http.MethodPut localVarPostBody interface{} formFiles []formFile + localVarReturnValue *OrganizationInvitation ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateOrganizationQuota") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateOrganizationInvitation") if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/{organizationId}/quota" + localVarPath := localBasePath + "/organizations/{organizationId}/invitations/{invitationId}" localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) + localVarPath = strings.Replace(localVarPath, "{"+"invitationId"+"}", url.PathEscape(parameterValueToString(r.invitationId, "invitationId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} - if r.updateOrganizationQuota == nil { - return nil, reportError("updateOrganizationQuota is required and must be specified") + if r.updateOrganizationInvitation == nil { + return localVarReturnValue, nil, reportError("updateOrganizationInvitation is required and must be specified") } // to determine the Content-Type header @@ -4057,7 +3684,7 @@ func (a *OrganizationsAPIService) UpdateOrganizationQuotaExecute(r Organizations } // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} + localVarHTTPHeaderAccepts := []string{"application/json"} // set Accept header localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) @@ -4065,22 +3692,22 @@ func (a *OrganizationsAPIService) UpdateOrganizationQuotaExecute(r Organizations localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } // body params - localVarPostBody = r.updateOrganizationQuota + localVarPostBody = r.updateOrganizationInvitation req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { - return nil, err + return localVarReturnValue, nil, err } localVarHTTPResponse, err := a.client.callAPI(req) if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) if err != nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } if localVarHTTPResponse.StatusCode >= 300 { @@ -4088,68 +3715,75 @@ func (a *OrganizationsAPIService) UpdateOrganizationQuotaExecute(r Organizations body: localVarBody, error: localVarHTTPResponse.Status, } - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } - return localVarHTTPResponse, nil + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil } -type OrganizationsAPIUpdateOrganizationRegionQuotaRequest struct { +type OrganizationsAPIUpdateOrganizationNameRequest struct { ctx context.Context ApiService OrganizationsAPI organizationId string - regionId string - updateOrganizationRegionQuota *UpdateOrganizationRegionQuota + updateOrganizationName *UpdateOrganizationName } -func (r OrganizationsAPIUpdateOrganizationRegionQuotaRequest) UpdateOrganizationRegionQuota(updateOrganizationRegionQuota UpdateOrganizationRegionQuota) OrganizationsAPIUpdateOrganizationRegionQuotaRequest { - r.updateOrganizationRegionQuota = &updateOrganizationRegionQuota +func (r OrganizationsAPIUpdateOrganizationNameRequest) UpdateOrganizationName(updateOrganizationName UpdateOrganizationName) OrganizationsAPIUpdateOrganizationNameRequest { + r.updateOrganizationName = &updateOrganizationName return r } -func (r OrganizationsAPIUpdateOrganizationRegionQuotaRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateOrganizationRegionQuotaExecute(r) +func (r OrganizationsAPIUpdateOrganizationNameRequest) Execute() (*Organization, *http.Response, error) { + return r.ApiService.UpdateOrganizationNameExecute(r) } /* -UpdateOrganizationRegionQuota Update organization region quota +UpdateOrganizationName Update organization name @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param organizationId Organization ID - @param regionId ID of the region where the updated quota will be applied - @return OrganizationsAPIUpdateOrganizationRegionQuotaRequest + @return OrganizationsAPIUpdateOrganizationNameRequest */ -func (a *OrganizationsAPIService) UpdateOrganizationRegionQuota(ctx context.Context, organizationId string, regionId string) OrganizationsAPIUpdateOrganizationRegionQuotaRequest { - return OrganizationsAPIUpdateOrganizationRegionQuotaRequest{ +func (a *OrganizationsAPIService) UpdateOrganizationName(ctx context.Context, organizationId string) OrganizationsAPIUpdateOrganizationNameRequest { + return OrganizationsAPIUpdateOrganizationNameRequest{ ApiService: a, ctx: ctx, organizationId: organizationId, - regionId: regionId, } } // Execute executes the request -func (a *OrganizationsAPIService) UpdateOrganizationRegionQuotaExecute(r OrganizationsAPIUpdateOrganizationRegionQuotaRequest) (*http.Response, error) { +// @return Organization +func (a *OrganizationsAPIService) UpdateOrganizationNameExecute(r OrganizationsAPIUpdateOrganizationNameRequest) (*Organization, *http.Response, error) { var ( localVarHTTPMethod = http.MethodPatch localVarPostBody interface{} formFiles []formFile + localVarReturnValue *Organization ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateOrganizationRegionQuota") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateOrganizationName") if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/organizations/{organizationId}/quota/{regionId}" + localVarPath := localBasePath + "/organizations/{organizationId}/name" localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"regionId"+"}", url.PathEscape(parameterValueToString(r.regionId, "regionId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} localVarFormParams := url.Values{} - if r.updateOrganizationRegionQuota == nil { - return nil, reportError("updateOrganizationRegionQuota is required and must be specified") + if r.updateOrganizationName == nil { + return localVarReturnValue, nil, reportError("updateOrganizationName is required and must be specified") } // to determine the Content-Type header @@ -4162,7 +3796,7 @@ func (a *OrganizationsAPIService) UpdateOrganizationRegionQuotaExecute(r Organiz } // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} + localVarHTTPHeaderAccepts := []string{"application/json"} // set Accept header localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) @@ -4170,22 +3804,22 @@ func (a *OrganizationsAPIService) UpdateOrganizationRegionQuotaExecute(r Organiz localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } // body params - localVarPostBody = r.updateOrganizationRegionQuota + localVarPostBody = r.updateOrganizationName req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { - return nil, err + return localVarReturnValue, nil, err } localVarHTTPResponse, err := a.client.callAPI(req) if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) localVarHTTPResponse.Body.Close() localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) if err != nil { - return localVarHTTPResponse, err + return localVarReturnValue, localVarHTTPResponse, err } if localVarHTTPResponse.StatusCode >= 300 { @@ -4193,10 +3827,19 @@ func (a *OrganizationsAPIService) UpdateOrganizationRegionQuotaExecute(r Organiz body: localVarBody, error: localVarHTTPResponse.Status, } - return localVarHTTPResponse, newErr + return localVarReturnValue, localVarHTTPResponse, newErr } - return localVarHTTPResponse, nil + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil } type OrganizationsAPIUpdateOrganizationRoleRequest struct { @@ -4425,104 +4068,3 @@ func (a *OrganizationsAPIService) UpdateRegionExecute(r OrganizationsAPIUpdateRe return localVarHTTPResponse, nil } - -type OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest struct { - ctx context.Context - ApiService OrganizationsAPI - organizationId string - organizationSandboxDefaultLimitedNetworkEgress *OrganizationSandboxDefaultLimitedNetworkEgress -} - -func (r OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest) OrganizationSandboxDefaultLimitedNetworkEgress(organizationSandboxDefaultLimitedNetworkEgress OrganizationSandboxDefaultLimitedNetworkEgress) OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest { - r.organizationSandboxDefaultLimitedNetworkEgress = &organizationSandboxDefaultLimitedNetworkEgress - return r -} - -func (r OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateSandboxDefaultLimitedNetworkEgressExecute(r) -} - -/* -UpdateSandboxDefaultLimitedNetworkEgress Update sandbox default limited network egress - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param organizationId Organization ID - @return OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest -*/ -func (a *OrganizationsAPIService) UpdateSandboxDefaultLimitedNetworkEgress(ctx context.Context, organizationId string) OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest { - return OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest{ - ApiService: a, - ctx: ctx, - organizationId: organizationId, - } -} - -// Execute executes the request -func (a *OrganizationsAPIService) UpdateSandboxDefaultLimitedNetworkEgressExecute(r OrganizationsAPIUpdateSandboxDefaultLimitedNetworkEgressRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "OrganizationsAPIService.UpdateSandboxDefaultLimitedNetworkEgress") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/organizations/{organizationId}/sandbox-default-limited-network-egress" - localVarPath = strings.Replace(localVarPath, "{"+"organizationId"+"}", url.PathEscape(parameterValueToString(r.organizationId, "organizationId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.organizationSandboxDefaultLimitedNetworkEgress == nil { - return nil, reportError("organizationSandboxDefaultLimitedNetworkEgress is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - // body params - localVarPostBody = r.organizationSandboxDefaultLimitedNetworkEgress - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/api_preview.go b/apps/api-client-go/api_preview.go index 777790b5d..83f265fc4 100644 --- a/apps/api-client-go/api_preview.go +++ b/apps/api-client-go/api_preview.go @@ -24,54 +24,54 @@ import ( type PreviewAPI interface { /* - GetSandboxIdFromSignedPreviewUrlToken Get sandbox ID from signed preview URL token + GetBoxIdFromSignedPreviewUrlToken Get box ID from signed preview URL token @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param signedPreviewToken Signed preview URL token - @param port Port number to get sandbox ID from signed preview URL token - @return PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest + @param port Port number to get box ID from signed preview URL token + @return PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest */ - GetSandboxIdFromSignedPreviewUrlToken(ctx context.Context, signedPreviewToken string, port float32) PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest + GetBoxIdFromSignedPreviewUrlToken(ctx context.Context, signedPreviewToken string, port float32) PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest - // GetSandboxIdFromSignedPreviewUrlTokenExecute executes the request + // GetBoxIdFromSignedPreviewUrlTokenExecute executes the request // @return string - GetSandboxIdFromSignedPreviewUrlTokenExecute(r PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest) (string, *http.Response, error) + GetBoxIdFromSignedPreviewUrlTokenExecute(r PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest) (string, *http.Response, error) /* - HasSandboxAccess Check if user has access to the sandbox + HasBoxAccess Check if user has access to the box @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return PreviewAPIHasSandboxAccessRequest + @param boxId + @return PreviewAPIHasBoxAccessRequest */ - HasSandboxAccess(ctx context.Context, sandboxId string) PreviewAPIHasSandboxAccessRequest + HasBoxAccess(ctx context.Context, boxId string) PreviewAPIHasBoxAccessRequest - // HasSandboxAccessExecute executes the request + // HasBoxAccessExecute executes the request // @return bool - HasSandboxAccessExecute(r PreviewAPIHasSandboxAccessRequest) (bool, *http.Response, error) + HasBoxAccessExecute(r PreviewAPIHasBoxAccessRequest) (bool, *http.Response, error) /* - IsSandboxPublic Check if sandbox is public + IsBoxPublic Check if box is public @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return PreviewAPIIsSandboxPublicRequest + @param boxId ID of the box + @return PreviewAPIIsBoxPublicRequest */ - IsSandboxPublic(ctx context.Context, sandboxId string) PreviewAPIIsSandboxPublicRequest + IsBoxPublic(ctx context.Context, boxId string) PreviewAPIIsBoxPublicRequest - // IsSandboxPublicExecute executes the request + // IsBoxPublicExecute executes the request // @return bool - IsSandboxPublicExecute(r PreviewAPIIsSandboxPublicRequest) (bool, *http.Response, error) + IsBoxPublicExecute(r PreviewAPIIsBoxPublicRequest) (bool, *http.Response, error) /* - IsValidAuthToken Check if sandbox auth token is valid + IsValidAuthToken Check if box auth token is valid @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @param authToken Auth token of the sandbox + @param boxId ID of the box + @param authToken Auth token of the box @return PreviewAPIIsValidAuthTokenRequest */ - IsValidAuthToken(ctx context.Context, sandboxId string, authToken string) PreviewAPIIsValidAuthTokenRequest + IsValidAuthToken(ctx context.Context, boxId string, authToken string) PreviewAPIIsValidAuthTokenRequest // IsValidAuthTokenExecute executes the request // @return bool @@ -81,27 +81,27 @@ type PreviewAPI interface { // PreviewAPIService PreviewAPI service type PreviewAPIService service -type PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest struct { +type PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest struct { ctx context.Context ApiService PreviewAPI signedPreviewToken string port float32 } -func (r PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest) Execute() (string, *http.Response, error) { - return r.ApiService.GetSandboxIdFromSignedPreviewUrlTokenExecute(r) +func (r PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest) Execute() (string, *http.Response, error) { + return r.ApiService.GetBoxIdFromSignedPreviewUrlTokenExecute(r) } /* -GetSandboxIdFromSignedPreviewUrlToken Get sandbox ID from signed preview URL token +GetBoxIdFromSignedPreviewUrlToken Get box ID from signed preview URL token @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). @param signedPreviewToken Signed preview URL token - @param port Port number to get sandbox ID from signed preview URL token - @return PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest + @param port Port number to get box ID from signed preview URL token + @return PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest */ -func (a *PreviewAPIService) GetSandboxIdFromSignedPreviewUrlToken(ctx context.Context, signedPreviewToken string, port float32) PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest { - return PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest{ +func (a *PreviewAPIService) GetBoxIdFromSignedPreviewUrlToken(ctx context.Context, signedPreviewToken string, port float32) PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest { + return PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest{ ApiService: a, ctx: ctx, signedPreviewToken: signedPreviewToken, @@ -111,7 +111,7 @@ func (a *PreviewAPIService) GetSandboxIdFromSignedPreviewUrlToken(ctx context.Co // Execute executes the request // @return string -func (a *PreviewAPIService) GetSandboxIdFromSignedPreviewUrlTokenExecute(r PreviewAPIGetSandboxIdFromSignedPreviewUrlTokenRequest) (string, *http.Response, error) { +func (a *PreviewAPIService) GetBoxIdFromSignedPreviewUrlTokenExecute(r PreviewAPIGetBoxIdFromSignedPreviewUrlTokenRequest) (string, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} @@ -119,12 +119,12 @@ func (a *PreviewAPIService) GetSandboxIdFromSignedPreviewUrlTokenExecute(r Previ localVarReturnValue string ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.GetSandboxIdFromSignedPreviewUrlToken") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.GetBoxIdFromSignedPreviewUrlToken") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/preview/{signedPreviewToken}/{port}/sandbox-id" + localVarPath := localBasePath + "/preview/{signedPreviewToken}/{port}/box-id" localVarPath = strings.Replace(localVarPath, "{"+"signedPreviewToken"+"}", url.PathEscape(parameterValueToString(r.signedPreviewToken, "signedPreviewToken")), -1) localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) @@ -186,34 +186,34 @@ func (a *PreviewAPIService) GetSandboxIdFromSignedPreviewUrlTokenExecute(r Previ return localVarReturnValue, localVarHTTPResponse, nil } -type PreviewAPIHasSandboxAccessRequest struct { +type PreviewAPIHasBoxAccessRequest struct { ctx context.Context ApiService PreviewAPI - sandboxId string + boxId string } -func (r PreviewAPIHasSandboxAccessRequest) Execute() (bool, *http.Response, error) { - return r.ApiService.HasSandboxAccessExecute(r) +func (r PreviewAPIHasBoxAccessRequest) Execute() (bool, *http.Response, error) { + return r.ApiService.HasBoxAccessExecute(r) } /* -HasSandboxAccess Check if user has access to the sandbox +HasBoxAccess Check if user has access to the box @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return PreviewAPIHasSandboxAccessRequest + @param boxId + @return PreviewAPIHasBoxAccessRequest */ -func (a *PreviewAPIService) HasSandboxAccess(ctx context.Context, sandboxId string) PreviewAPIHasSandboxAccessRequest { - return PreviewAPIHasSandboxAccessRequest{ +func (a *PreviewAPIService) HasBoxAccess(ctx context.Context, boxId string) PreviewAPIHasBoxAccessRequest { + return PreviewAPIHasBoxAccessRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + boxId: boxId, } } // Execute executes the request // @return bool -func (a *PreviewAPIService) HasSandboxAccessExecute(r PreviewAPIHasSandboxAccessRequest) (bool, *http.Response, error) { +func (a *PreviewAPIService) HasBoxAccessExecute(r PreviewAPIHasBoxAccessRequest) (bool, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} @@ -221,13 +221,13 @@ func (a *PreviewAPIService) HasSandboxAccessExecute(r PreviewAPIHasSandboxAccess localVarReturnValue bool ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.HasSandboxAccess") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.HasBoxAccess") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/preview/{sandboxId}/access" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/preview/{boxId}/access" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} @@ -287,34 +287,34 @@ func (a *PreviewAPIService) HasSandboxAccessExecute(r PreviewAPIHasSandboxAccess return localVarReturnValue, localVarHTTPResponse, nil } -type PreviewAPIIsSandboxPublicRequest struct { +type PreviewAPIIsBoxPublicRequest struct { ctx context.Context ApiService PreviewAPI - sandboxId string + boxId string } -func (r PreviewAPIIsSandboxPublicRequest) Execute() (bool, *http.Response, error) { - return r.ApiService.IsSandboxPublicExecute(r) +func (r PreviewAPIIsBoxPublicRequest) Execute() (bool, *http.Response, error) { + return r.ApiService.IsBoxPublicExecute(r) } /* -IsSandboxPublic Check if sandbox is public +IsBoxPublic Check if box is public @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return PreviewAPIIsSandboxPublicRequest + @param boxId ID of the box + @return PreviewAPIIsBoxPublicRequest */ -func (a *PreviewAPIService) IsSandboxPublic(ctx context.Context, sandboxId string) PreviewAPIIsSandboxPublicRequest { - return PreviewAPIIsSandboxPublicRequest{ +func (a *PreviewAPIService) IsBoxPublic(ctx context.Context, boxId string) PreviewAPIIsBoxPublicRequest { + return PreviewAPIIsBoxPublicRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + boxId: boxId, } } // Execute executes the request // @return bool -func (a *PreviewAPIService) IsSandboxPublicExecute(r PreviewAPIIsSandboxPublicRequest) (bool, *http.Response, error) { +func (a *PreviewAPIService) IsBoxPublicExecute(r PreviewAPIIsBoxPublicRequest) (bool, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} @@ -322,13 +322,13 @@ func (a *PreviewAPIService) IsSandboxPublicExecute(r PreviewAPIIsSandboxPublicRe localVarReturnValue bool ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.IsSandboxPublic") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "PreviewAPIService.IsBoxPublic") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/preview/{sandboxId}/public" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/preview/{boxId}/public" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} @@ -391,7 +391,7 @@ func (a *PreviewAPIService) IsSandboxPublicExecute(r PreviewAPIIsSandboxPublicRe type PreviewAPIIsValidAuthTokenRequest struct { ctx context.Context ApiService PreviewAPI - sandboxId string + boxId string authToken string } @@ -400,18 +400,18 @@ func (r PreviewAPIIsValidAuthTokenRequest) Execute() (bool, *http.Response, erro } /* -IsValidAuthToken Check if sandbox auth token is valid +IsValidAuthToken Check if box auth token is valid @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @param authToken Auth token of the sandbox + @param boxId ID of the box + @param authToken Auth token of the box @return PreviewAPIIsValidAuthTokenRequest */ -func (a *PreviewAPIService) IsValidAuthToken(ctx context.Context, sandboxId string, authToken string) PreviewAPIIsValidAuthTokenRequest { +func (a *PreviewAPIService) IsValidAuthToken(ctx context.Context, boxId string, authToken string) PreviewAPIIsValidAuthTokenRequest { return PreviewAPIIsValidAuthTokenRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + boxId: boxId, authToken: authToken, } } @@ -431,8 +431,8 @@ func (a *PreviewAPIService) IsValidAuthTokenExecute(r PreviewAPIIsValidAuthToken return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/preview/{sandboxId}/validate/{authToken}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/preview/{boxId}/validate/{authToken}" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarPath = strings.Replace(localVarPath, "{"+"authToken"+"}", url.PathEscape(parameterValueToString(r.authToken, "authToken")), -1) localVarHeaderParams := make(map[string]string) diff --git a/apps/api-client-go/api_runners.go b/apps/api-client-go/api_runners.go index b3bb75860..fcbbbc884 100644 --- a/apps/api-client-go/api_runners.go +++ b/apps/api-client-go/api_runners.go @@ -59,6 +59,19 @@ type RunnersAPI interface { // @return RunnerFull GetInfoForAuthenticatedRunnerExecute(r RunnersAPIGetInfoForAuthenticatedRunnerRequest) (*RunnerFull, *http.Response, error) + /* + GetRunnerByBoxId Get runner by box ID + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @param boxId + @return RunnersAPIGetRunnerByBoxIdRequest + */ + GetRunnerByBoxId(ctx context.Context, boxId string) RunnersAPIGetRunnerByBoxIdRequest + + // GetRunnerByBoxIdExecute executes the request + // @return RunnerFull + GetRunnerByBoxIdExecute(r RunnersAPIGetRunnerByBoxIdRequest) (*RunnerFull, *http.Response, error) + /* GetRunnerById Get runner by ID @@ -72,19 +85,6 @@ type RunnersAPI interface { // @return Runner GetRunnerByIdExecute(r RunnersAPIGetRunnerByIdRequest) (*Runner, *http.Response, error) - /* - GetRunnerBySandboxId Get runner by sandbox ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return RunnersAPIGetRunnerBySandboxIdRequest - */ - GetRunnerBySandboxId(ctx context.Context, sandboxId string) RunnersAPIGetRunnerBySandboxIdRequest - - // GetRunnerBySandboxIdExecute executes the request - // @return RunnerFull - GetRunnerBySandboxIdExecute(r RunnersAPIGetRunnerBySandboxIdRequest) (*RunnerFull, *http.Response, error) - /* GetRunnerFullById Get runner by ID @@ -98,18 +98,6 @@ type RunnersAPI interface { // @return RunnerFull GetRunnerFullByIdExecute(r RunnersAPIGetRunnerFullByIdRequest) (*RunnerFull, *http.Response, error) - /* - GetRunnersBySnapshotRef Get runners by snapshot ref - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return RunnersAPIGetRunnersBySnapshotRefRequest - */ - GetRunnersBySnapshotRef(ctx context.Context) RunnersAPIGetRunnersBySnapshotRefRequest - - // GetRunnersBySnapshotRefExecute executes the request - // @return []RunnerSnapshotDto - GetRunnersBySnapshotRefExecute(r RunnersAPIGetRunnersBySnapshotRefRequest) ([]RunnerSnapshotDto, *http.Response, error) - /* ListRunners List all runners @@ -480,55 +468,48 @@ func (a *RunnersAPIService) GetInfoForAuthenticatedRunnerExecute(r RunnersAPIGet return localVarReturnValue, localVarHTTPResponse, nil } -type RunnersAPIGetRunnerByIdRequest struct { +type RunnersAPIGetRunnerByBoxIdRequest struct { ctx context.Context ApiService RunnersAPI - id string - xBoxLiteOrganizationID *string + boxId string } -// Use with JWT to specify the organization ID -func (r RunnersAPIGetRunnerByIdRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) RunnersAPIGetRunnerByIdRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r RunnersAPIGetRunnerByIdRequest) Execute() (*Runner, *http.Response, error) { - return r.ApiService.GetRunnerByIdExecute(r) +func (r RunnersAPIGetRunnerByBoxIdRequest) Execute() (*RunnerFull, *http.Response, error) { + return r.ApiService.GetRunnerByBoxIdExecute(r) } /* -GetRunnerById Get runner by ID +GetRunnerByBoxId Get runner by box ID @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Runner ID - @return RunnersAPIGetRunnerByIdRequest + @param boxId + @return RunnersAPIGetRunnerByBoxIdRequest */ -func (a *RunnersAPIService) GetRunnerById(ctx context.Context, id string) RunnersAPIGetRunnerByIdRequest { - return RunnersAPIGetRunnerByIdRequest{ +func (a *RunnersAPIService) GetRunnerByBoxId(ctx context.Context, boxId string) RunnersAPIGetRunnerByBoxIdRequest { + return RunnersAPIGetRunnerByBoxIdRequest{ ApiService: a, ctx: ctx, - id: id, + boxId: boxId, } } // Execute executes the request -// @return Runner -func (a *RunnersAPIService) GetRunnerByIdExecute(r RunnersAPIGetRunnerByIdRequest) (*Runner, *http.Response, error) { +// @return RunnerFull +func (a *RunnersAPIService) GetRunnerByBoxIdExecute(r RunnersAPIGetRunnerByBoxIdRequest) (*RunnerFull, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} formFiles []formFile - localVarReturnValue *Runner + localVarReturnValue *RunnerFull ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "RunnersAPIService.GetRunnerById") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "RunnersAPIService.GetRunnerByBoxId") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/runners/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) + localVarPath := localBasePath + "/runners/by-box/{boxId}" + localVarPath = strings.Replace(localVarPath, "{"+"boxId"+"}", url.PathEscape(parameterValueToString(r.boxId, "boxId")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} @@ -551,9 +532,6 @@ func (a *RunnersAPIService) GetRunnerByIdExecute(r RunnersAPIGetRunnerByIdReques if localVarHTTPHeaderAccept != "" { localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { return localVarReturnValue, nil, err @@ -591,48 +569,55 @@ func (a *RunnersAPIService) GetRunnerByIdExecute(r RunnersAPIGetRunnerByIdReques return localVarReturnValue, localVarHTTPResponse, nil } -type RunnersAPIGetRunnerBySandboxIdRequest struct { +type RunnersAPIGetRunnerByIdRequest struct { ctx context.Context ApiService RunnersAPI - sandboxId string + id string + xBoxLiteOrganizationID *string } -func (r RunnersAPIGetRunnerBySandboxIdRequest) Execute() (*RunnerFull, *http.Response, error) { - return r.ApiService.GetRunnerBySandboxIdExecute(r) +// Use with JWT to specify the organization ID +func (r RunnersAPIGetRunnerByIdRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) RunnersAPIGetRunnerByIdRequest { + r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID + return r +} + +func (r RunnersAPIGetRunnerByIdRequest) Execute() (*Runner, *http.Response, error) { + return r.ApiService.GetRunnerByIdExecute(r) } /* -GetRunnerBySandboxId Get runner by sandbox ID +GetRunnerById Get runner by ID @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return RunnersAPIGetRunnerBySandboxIdRequest + @param id Runner ID + @return RunnersAPIGetRunnerByIdRequest */ -func (a *RunnersAPIService) GetRunnerBySandboxId(ctx context.Context, sandboxId string) RunnersAPIGetRunnerBySandboxIdRequest { - return RunnersAPIGetRunnerBySandboxIdRequest{ +func (a *RunnersAPIService) GetRunnerById(ctx context.Context, id string) RunnersAPIGetRunnerByIdRequest { + return RunnersAPIGetRunnerByIdRequest{ ApiService: a, ctx: ctx, - sandboxId: sandboxId, + id: id, } } // Execute executes the request -// @return RunnerFull -func (a *RunnersAPIService) GetRunnerBySandboxIdExecute(r RunnersAPIGetRunnerBySandboxIdRequest) (*RunnerFull, *http.Response, error) { +// @return Runner +func (a *RunnersAPIService) GetRunnerByIdExecute(r RunnersAPIGetRunnerByIdRequest) (*Runner, *http.Response, error) { var ( localVarHTTPMethod = http.MethodGet localVarPostBody interface{} formFiles []formFile - localVarReturnValue *RunnerFull + localVarReturnValue *Runner ) - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "RunnersAPIService.GetRunnerBySandboxId") + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "RunnersAPIService.GetRunnerById") if err != nil { return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} } - localVarPath := localBasePath + "/runners/by-sandbox/{sandboxId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) + localVarPath := localBasePath + "/runners/{id}" + localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) localVarHeaderParams := make(map[string]string) localVarQueryParams := url.Values{} @@ -655,6 +640,9 @@ func (a *RunnersAPIService) GetRunnerBySandboxIdExecute(r RunnersAPIGetRunnerByS if localVarHTTPHeaderAccept != "" { localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept } + if r.xBoxLiteOrganizationID != nil { + parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") + } req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) if err != nil { return localVarReturnValue, nil, err @@ -793,114 +781,6 @@ func (a *RunnersAPIService) GetRunnerFullByIdExecute(r RunnersAPIGetRunnerFullBy return localVarReturnValue, localVarHTTPResponse, nil } -type RunnersAPIGetRunnersBySnapshotRefRequest struct { - ctx context.Context - ApiService RunnersAPI - ref *string -} - -// Snapshot ref -func (r RunnersAPIGetRunnersBySnapshotRefRequest) Ref(ref string) RunnersAPIGetRunnersBySnapshotRefRequest { - r.ref = &ref - return r -} - -func (r RunnersAPIGetRunnersBySnapshotRefRequest) Execute() ([]RunnerSnapshotDto, *http.Response, error) { - return r.ApiService.GetRunnersBySnapshotRefExecute(r) -} - -/* -GetRunnersBySnapshotRef Get runners by snapshot ref - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return RunnersAPIGetRunnersBySnapshotRefRequest -*/ -func (a *RunnersAPIService) GetRunnersBySnapshotRef(ctx context.Context) RunnersAPIGetRunnersBySnapshotRefRequest { - return RunnersAPIGetRunnersBySnapshotRefRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return []RunnerSnapshotDto -func (a *RunnersAPIService) GetRunnersBySnapshotRefExecute(r RunnersAPIGetRunnersBySnapshotRefRequest) ([]RunnerSnapshotDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []RunnerSnapshotDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "RunnersAPIService.GetRunnersBySnapshotRef") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/runners/by-snapshot-ref" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.ref == nil { - return localVarReturnValue, nil, reportError("ref is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "ref", r.ref, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - type RunnersAPIListRunnersRequest struct { ctx context.Context ApiService RunnersAPI diff --git a/apps/api-client-go/api_sandbox.go b/apps/api-client-go/api_sandbox.go deleted file mode 100644 index 8f93cab46..000000000 --- a/apps/api-client-go/api_sandbox.go +++ /dev/null @@ -1,4554 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "bytes" - "context" - "io" - "net/http" - "net/url" - "strings" - "time" - "reflect" -) - - -type SandboxAPI interface { - - /* - ArchiveSandbox Archive sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName - @return SandboxAPIArchiveSandboxRequest - */ - ArchiveSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIArchiveSandboxRequest - - // ArchiveSandboxExecute executes the request - // @return Sandbox - ArchiveSandboxExecute(r SandboxAPIArchiveSandboxRequest) (*Sandbox, *http.Response, error) - - /* - CreateBackup Create sandbox backup - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPICreateBackupRequest - */ - CreateBackup(ctx context.Context, sandboxIdOrName string) SandboxAPICreateBackupRequest - - // CreateBackupExecute executes the request - // @return Sandbox - CreateBackupExecute(r SandboxAPICreateBackupRequest) (*Sandbox, *http.Response, error) - - /* - CreateSandbox Create a new sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPICreateSandboxRequest - */ - CreateSandbox(ctx context.Context) SandboxAPICreateSandboxRequest - - // CreateSandboxExecute executes the request - // @return Sandbox - CreateSandboxExecute(r SandboxAPICreateSandboxRequest) (*Sandbox, *http.Response, error) - - /* - CreateSshAccess Create SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPICreateSshAccessRequest - */ - CreateSshAccess(ctx context.Context, sandboxIdOrName string) SandboxAPICreateSshAccessRequest - - // CreateSshAccessExecute executes the request - // @return SshAccessDto - CreateSshAccessExecute(r SandboxAPICreateSshAccessRequest) (*SshAccessDto, *http.Response, error) - - /* - DeleteSandbox Delete sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIDeleteSandboxRequest - */ - DeleteSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIDeleteSandboxRequest - - // DeleteSandboxExecute executes the request - // @return Sandbox - DeleteSandboxExecute(r SandboxAPIDeleteSandboxRequest) (*Sandbox, *http.Response, error) - - /* - ExpireSignedPortPreviewUrl Expire signed preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to expire signed preview URL for - @param token Token to expire signed preview URL for - @return SandboxAPIExpireSignedPortPreviewUrlRequest - */ - ExpireSignedPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port int32, token string) SandboxAPIExpireSignedPortPreviewUrlRequest - - // ExpireSignedPortPreviewUrlExecute executes the request - ExpireSignedPortPreviewUrlExecute(r SandboxAPIExpireSignedPortPreviewUrlRequest) (*http.Response, error) - - /* - GetBuildLogs Get build logs - - This endpoint is deprecated. Use `getBuildLogsUrl` instead. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetBuildLogsRequest - - Deprecated - */ - GetBuildLogs(ctx context.Context, sandboxIdOrName string) SandboxAPIGetBuildLogsRequest - - // GetBuildLogsExecute executes the request - // Deprecated - GetBuildLogsExecute(r SandboxAPIGetBuildLogsRequest) (*http.Response, error) - - /* - GetBuildLogsUrl Get build logs URL - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetBuildLogsUrlRequest - */ - GetBuildLogsUrl(ctx context.Context, sandboxIdOrName string) SandboxAPIGetBuildLogsUrlRequest - - // GetBuildLogsUrlExecute executes the request - // @return Url - GetBuildLogsUrlExecute(r SandboxAPIGetBuildLogsUrlRequest) (*Url, *http.Response, error) - - /* - GetPortPreviewUrl Get preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to get preview URL for - @return SandboxAPIGetPortPreviewUrlRequest - */ - GetPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port float32) SandboxAPIGetPortPreviewUrlRequest - - // GetPortPreviewUrlExecute executes the request - // @return PortPreviewUrl - GetPortPreviewUrlExecute(r SandboxAPIGetPortPreviewUrlRequest) (*PortPreviewUrl, *http.Response, error) - - /* - GetSandbox Get sandbox details - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetSandboxRequest - */ - GetSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIGetSandboxRequest - - // GetSandboxExecute executes the request - // @return Sandbox - GetSandboxExecute(r SandboxAPIGetSandboxRequest) (*Sandbox, *http.Response, error) - - /* - GetSandboxLogs Get sandbox logs - - Retrieve OTEL logs for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxLogsRequest - */ - GetSandboxLogs(ctx context.Context, sandboxId string) SandboxAPIGetSandboxLogsRequest - - // GetSandboxLogsExecute executes the request - // @return PaginatedLogs - GetSandboxLogsExecute(r SandboxAPIGetSandboxLogsRequest) (*PaginatedLogs, *http.Response, error) - - /* - GetSandboxMetrics Get sandbox metrics - - Retrieve OTEL metrics for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxMetricsRequest - */ - GetSandboxMetrics(ctx context.Context, sandboxId string) SandboxAPIGetSandboxMetricsRequest - - // GetSandboxMetricsExecute executes the request - // @return MetricsResponse - GetSandboxMetricsExecute(r SandboxAPIGetSandboxMetricsRequest) (*MetricsResponse, *http.Response, error) - - /* - GetSandboxTraceSpans Get trace spans - - Retrieve all spans for a specific trace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @param traceId ID of the trace - @return SandboxAPIGetSandboxTraceSpansRequest - */ - GetSandboxTraceSpans(ctx context.Context, sandboxId string, traceId string) SandboxAPIGetSandboxTraceSpansRequest - - // GetSandboxTraceSpansExecute executes the request - // @return []TraceSpan - GetSandboxTraceSpansExecute(r SandboxAPIGetSandboxTraceSpansRequest) ([]TraceSpan, *http.Response, error) - - /* - GetSandboxTraces Get sandbox traces - - Retrieve OTEL traces for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxTracesRequest - */ - GetSandboxTraces(ctx context.Context, sandboxId string) SandboxAPIGetSandboxTracesRequest - - // GetSandboxTracesExecute executes the request - // @return PaginatedTraces - GetSandboxTracesExecute(r SandboxAPIGetSandboxTracesRequest) (*PaginatedTraces, *http.Response, error) - - /* - GetSandboxesForRunner Get sandboxes for the authenticated runner - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIGetSandboxesForRunnerRequest - */ - GetSandboxesForRunner(ctx context.Context) SandboxAPIGetSandboxesForRunnerRequest - - // GetSandboxesForRunnerExecute executes the request - // @return []Sandbox - GetSandboxesForRunnerExecute(r SandboxAPIGetSandboxesForRunnerRequest) ([]Sandbox, *http.Response, error) - - /* - GetSignedPortPreviewUrl Get signed preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to get signed preview URL for - @return SandboxAPIGetSignedPortPreviewUrlRequest - */ - GetSignedPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port int32) SandboxAPIGetSignedPortPreviewUrlRequest - - // GetSignedPortPreviewUrlExecute executes the request - // @return SignedPortPreviewUrl - GetSignedPortPreviewUrlExecute(r SandboxAPIGetSignedPortPreviewUrlRequest) (*SignedPortPreviewUrl, *http.Response, error) - - /* - GetToolboxProxyUrl Get toolbox proxy URL for a sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetToolboxProxyUrlRequest - */ - GetToolboxProxyUrl(ctx context.Context, sandboxId string) SandboxAPIGetToolboxProxyUrlRequest - - // GetToolboxProxyUrlExecute executes the request - // @return ToolboxProxyUrl - GetToolboxProxyUrlExecute(r SandboxAPIGetToolboxProxyUrlRequest) (*ToolboxProxyUrl, *http.Response, error) - - /* - ListSandboxes List all sandboxes - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIListSandboxesRequest - */ - ListSandboxes(ctx context.Context) SandboxAPIListSandboxesRequest - - // ListSandboxesExecute executes the request - // @return []Sandbox - ListSandboxesExecute(r SandboxAPIListSandboxesRequest) ([]Sandbox, *http.Response, error) - - /* - ListSandboxesPaginated List all sandboxes paginated - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIListSandboxesPaginatedRequest - */ - ListSandboxesPaginated(ctx context.Context) SandboxAPIListSandboxesPaginatedRequest - - // ListSandboxesPaginatedExecute executes the request - // @return PaginatedSandboxes - ListSandboxesPaginatedExecute(r SandboxAPIListSandboxesPaginatedRequest) (*PaginatedSandboxes, *http.Response, error) - - /* - RecoverSandbox Recover sandbox from error state - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIRecoverSandboxRequest - */ - RecoverSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIRecoverSandboxRequest - - // RecoverSandboxExecute executes the request - // @return Sandbox - RecoverSandboxExecute(r SandboxAPIRecoverSandboxRequest) (*Sandbox, *http.Response, error) - - /* - ReplaceLabels Replace sandbox labels - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIReplaceLabelsRequest - */ - ReplaceLabels(ctx context.Context, sandboxIdOrName string) SandboxAPIReplaceLabelsRequest - - // ReplaceLabelsExecute executes the request - // @return SandboxLabels - ReplaceLabelsExecute(r SandboxAPIReplaceLabelsRequest) (*SandboxLabels, *http.Response, error) - - /* - ResizeSandbox Resize sandbox resources - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIResizeSandboxRequest - */ - ResizeSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIResizeSandboxRequest - - // ResizeSandboxExecute executes the request - // @return Sandbox - ResizeSandboxExecute(r SandboxAPIResizeSandboxRequest) (*Sandbox, *http.Response, error) - - /* - RevokeSshAccess Revoke SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIRevokeSshAccessRequest - */ - RevokeSshAccess(ctx context.Context, sandboxIdOrName string) SandboxAPIRevokeSshAccessRequest - - // RevokeSshAccessExecute executes the request - // @return Sandbox - RevokeSshAccessExecute(r SandboxAPIRevokeSshAccessRequest) (*Sandbox, *http.Response, error) - - /* - SetAutoArchiveInterval Set sandbox auto-archive interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-archive interval in minutes (0 means the maximum interval will be used) - @return SandboxAPISetAutoArchiveIntervalRequest - */ - SetAutoArchiveInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutoArchiveIntervalRequest - - // SetAutoArchiveIntervalExecute executes the request - // @return Sandbox - SetAutoArchiveIntervalExecute(r SandboxAPISetAutoArchiveIntervalRequest) (*Sandbox, *http.Response, error) - - /* - SetAutoDeleteInterval Set sandbox auto-delete interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - @return SandboxAPISetAutoDeleteIntervalRequest - */ - SetAutoDeleteInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutoDeleteIntervalRequest - - // SetAutoDeleteIntervalExecute executes the request - // @return Sandbox - SetAutoDeleteIntervalExecute(r SandboxAPISetAutoDeleteIntervalRequest) (*Sandbox, *http.Response, error) - - /* - SetAutostopInterval Set sandbox auto-stop interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-stop interval in minutes (0 to disable) - @return SandboxAPISetAutostopIntervalRequest - */ - SetAutostopInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutostopIntervalRequest - - // SetAutostopIntervalExecute executes the request - // @return Sandbox - SetAutostopIntervalExecute(r SandboxAPISetAutostopIntervalRequest) (*Sandbox, *http.Response, error) - - /* - StartSandbox Start sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIStartSandboxRequest - */ - StartSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIStartSandboxRequest - - // StartSandboxExecute executes the request - // @return Sandbox - StartSandboxExecute(r SandboxAPIStartSandboxRequest) (*Sandbox, *http.Response, error) - - /* - StopSandbox Stop sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIStopSandboxRequest - */ - StopSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIStopSandboxRequest - - // StopSandboxExecute executes the request - // @return Sandbox - StopSandboxExecute(r SandboxAPIStopSandboxRequest) (*Sandbox, *http.Response, error) - - /* - UpdateLastActivity Update sandbox last activity - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIUpdateLastActivityRequest - */ - UpdateLastActivity(ctx context.Context, sandboxId string) SandboxAPIUpdateLastActivityRequest - - // UpdateLastActivityExecute executes the request - UpdateLastActivityExecute(r SandboxAPIUpdateLastActivityRequest) (*http.Response, error) - - /* - UpdatePublicStatus Update public status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param isPublic Public status to set - @return SandboxAPIUpdatePublicStatusRequest - */ - UpdatePublicStatus(ctx context.Context, sandboxIdOrName string, isPublic bool) SandboxAPIUpdatePublicStatusRequest - - // UpdatePublicStatusExecute executes the request - // @return Sandbox - UpdatePublicStatusExecute(r SandboxAPIUpdatePublicStatusRequest) (*Sandbox, *http.Response, error) - - /* - UpdateSandboxState Update sandbox state - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIUpdateSandboxStateRequest - */ - UpdateSandboxState(ctx context.Context, sandboxId string) SandboxAPIUpdateSandboxStateRequest - - // UpdateSandboxStateExecute executes the request - UpdateSandboxStateExecute(r SandboxAPIUpdateSandboxStateRequest) (*http.Response, error) - - /* - ValidateSshAccess Validate SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIValidateSshAccessRequest - */ - ValidateSshAccess(ctx context.Context) SandboxAPIValidateSshAccessRequest - - // ValidateSshAccessExecute executes the request - // @return SshAccessValidationDto - ValidateSshAccessExecute(r SandboxAPIValidateSshAccessRequest) (*SshAccessValidationDto, *http.Response, error) -} - -// SandboxAPIService SandboxAPI service -type SandboxAPIService service - -type SandboxAPIArchiveSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIArchiveSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIArchiveSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIArchiveSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.ArchiveSandboxExecute(r) -} - -/* -ArchiveSandbox Archive sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName - @return SandboxAPIArchiveSandboxRequest -*/ -func (a *SandboxAPIService) ArchiveSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIArchiveSandboxRequest { - return SandboxAPIArchiveSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) ArchiveSandboxExecute(r SandboxAPIArchiveSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ArchiveSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/archive" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPICreateBackupRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPICreateBackupRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPICreateBackupRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPICreateBackupRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.CreateBackupExecute(r) -} - -/* -CreateBackup Create sandbox backup - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPICreateBackupRequest -*/ -func (a *SandboxAPIService) CreateBackup(ctx context.Context, sandboxIdOrName string) SandboxAPICreateBackupRequest { - return SandboxAPICreateBackupRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) CreateBackupExecute(r SandboxAPICreateBackupRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.CreateBackup") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/backup" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPICreateSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - createSandbox *CreateSandbox - xBoxLiteOrganizationID *string -} - -func (r SandboxAPICreateSandboxRequest) CreateSandbox(createSandbox CreateSandbox) SandboxAPICreateSandboxRequest { - r.createSandbox = &createSandbox - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPICreateSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPICreateSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPICreateSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.CreateSandboxExecute(r) -} - -/* -CreateSandbox Create a new sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPICreateSandboxRequest -*/ -func (a *SandboxAPIService) CreateSandbox(ctx context.Context) SandboxAPICreateSandboxRequest { - return SandboxAPICreateSandboxRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) CreateSandboxExecute(r SandboxAPICreateSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.CreateSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.createSandbox == nil { - return localVarReturnValue, nil, reportError("createSandbox is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.createSandbox - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPICreateSshAccessRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string - expiresInMinutes *float32 -} - -// Use with JWT to specify the organization ID -func (r SandboxAPICreateSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPICreateSshAccessRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Expiration time in minutes (default: 60) -func (r SandboxAPICreateSshAccessRequest) ExpiresInMinutes(expiresInMinutes float32) SandboxAPICreateSshAccessRequest { - r.expiresInMinutes = &expiresInMinutes - return r -} - -func (r SandboxAPICreateSshAccessRequest) Execute() (*SshAccessDto, *http.Response, error) { - return r.ApiService.CreateSshAccessExecute(r) -} - -/* -CreateSshAccess Create SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPICreateSshAccessRequest -*/ -func (a *SandboxAPIService) CreateSshAccess(ctx context.Context, sandboxIdOrName string) SandboxAPICreateSshAccessRequest { - return SandboxAPICreateSshAccessRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return SshAccessDto -func (a *SandboxAPIService) CreateSshAccessExecute(r SandboxAPICreateSshAccessRequest) (*SshAccessDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SshAccessDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.CreateSshAccess") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/ssh-access" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.expiresInMinutes != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "expiresInMinutes", r.expiresInMinutes, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIDeleteSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIDeleteSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIDeleteSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIDeleteSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.DeleteSandboxExecute(r) -} - -/* -DeleteSandbox Delete sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIDeleteSandboxRequest -*/ -func (a *SandboxAPIService) DeleteSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIDeleteSandboxRequest { - return SandboxAPIDeleteSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) DeleteSandboxExecute(r SandboxAPIDeleteSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.DeleteSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIExpireSignedPortPreviewUrlRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - port int32 - token string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIExpireSignedPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIExpireSignedPortPreviewUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIExpireSignedPortPreviewUrlRequest) Execute() (*http.Response, error) { - return r.ApiService.ExpireSignedPortPreviewUrlExecute(r) -} - -/* -ExpireSignedPortPreviewUrl Expire signed preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to expire signed preview URL for - @param token Token to expire signed preview URL for - @return SandboxAPIExpireSignedPortPreviewUrlRequest -*/ -func (a *SandboxAPIService) ExpireSignedPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port int32, token string) SandboxAPIExpireSignedPortPreviewUrlRequest { - return SandboxAPIExpireSignedPortPreviewUrlRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - port: port, - token: token, - } -} - -// Execute executes the request -func (a *SandboxAPIService) ExpireSignedPortPreviewUrlExecute(r SandboxAPIExpireSignedPortPreviewUrlRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ExpireSignedPortPreviewUrl") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/ports/{port}/signed-preview-url/{token}/expire" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"token"+"}", url.PathEscape(parameterValueToString(r.token, "token")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SandboxAPIGetBuildLogsRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string - follow *bool -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetBuildLogsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetBuildLogsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Whether to follow the logs stream -func (r SandboxAPIGetBuildLogsRequest) Follow(follow bool) SandboxAPIGetBuildLogsRequest { - r.follow = &follow - return r -} - -func (r SandboxAPIGetBuildLogsRequest) Execute() (*http.Response, error) { - return r.ApiService.GetBuildLogsExecute(r) -} - -/* -GetBuildLogs Get build logs - -This endpoint is deprecated. Use `getBuildLogsUrl` instead. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetBuildLogsRequest - -Deprecated -*/ -func (a *SandboxAPIService) GetBuildLogs(ctx context.Context, sandboxIdOrName string) SandboxAPIGetBuildLogsRequest { - return SandboxAPIGetBuildLogsRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// Deprecated -func (a *SandboxAPIService) GetBuildLogsExecute(r SandboxAPIGetBuildLogsRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetBuildLogs") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/build-logs" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.follow != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "follow", r.follow, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SandboxAPIGetBuildLogsUrlRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetBuildLogsUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetBuildLogsUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIGetBuildLogsUrlRequest) Execute() (*Url, *http.Response, error) { - return r.ApiService.GetBuildLogsUrlExecute(r) -} - -/* -GetBuildLogsUrl Get build logs URL - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetBuildLogsUrlRequest -*/ -func (a *SandboxAPIService) GetBuildLogsUrl(ctx context.Context, sandboxIdOrName string) SandboxAPIGetBuildLogsUrlRequest { - return SandboxAPIGetBuildLogsUrlRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Url -func (a *SandboxAPIService) GetBuildLogsUrlExecute(r SandboxAPIGetBuildLogsUrlRequest) (*Url, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Url - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetBuildLogsUrl") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/build-logs-url" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetPortPreviewUrlRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - port float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetPortPreviewUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIGetPortPreviewUrlRequest) Execute() (*PortPreviewUrl, *http.Response, error) { - return r.ApiService.GetPortPreviewUrlExecute(r) -} - -/* -GetPortPreviewUrl Get preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to get preview URL for - @return SandboxAPIGetPortPreviewUrlRequest -*/ -func (a *SandboxAPIService) GetPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port float32) SandboxAPIGetPortPreviewUrlRequest { - return SandboxAPIGetPortPreviewUrlRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - port: port, - } -} - -// Execute executes the request -// @return PortPreviewUrl -func (a *SandboxAPIService) GetPortPreviewUrlExecute(r SandboxAPIGetPortPreviewUrlRequest) (*PortPreviewUrl, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PortPreviewUrl - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetPortPreviewUrl") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/ports/{port}/preview-url" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string - verbose *bool -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Include verbose output -func (r SandboxAPIGetSandboxRequest) Verbose(verbose bool) SandboxAPIGetSandboxRequest { - r.verbose = &verbose - return r -} - -func (r SandboxAPIGetSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.GetSandboxExecute(r) -} - -/* -GetSandbox Get sandbox details - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIGetSandboxRequest -*/ -func (a *SandboxAPIService) GetSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIGetSandboxRequest { - return SandboxAPIGetSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) GetSandboxExecute(r SandboxAPIGetSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.verbose != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxLogsRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - from *time.Time - to *time.Time - xBoxLiteOrganizationID *string - page *float32 - limit *float32 - severities *[]string - search *string -} - -// Start of time range (ISO 8601) -func (r SandboxAPIGetSandboxLogsRequest) From(from time.Time) SandboxAPIGetSandboxLogsRequest { - r.from = &from - return r -} - -// End of time range (ISO 8601) -func (r SandboxAPIGetSandboxLogsRequest) To(to time.Time) SandboxAPIGetSandboxLogsRequest { - r.to = &to - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxLogsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxLogsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Page number (1-indexed) -func (r SandboxAPIGetSandboxLogsRequest) Page(page float32) SandboxAPIGetSandboxLogsRequest { - r.page = &page - return r -} - -// Number of items per page -func (r SandboxAPIGetSandboxLogsRequest) Limit(limit float32) SandboxAPIGetSandboxLogsRequest { - r.limit = &limit - return r -} - -// Filter by severity levels (DEBUG, INFO, WARN, ERROR) -func (r SandboxAPIGetSandboxLogsRequest) Severities(severities []string) SandboxAPIGetSandboxLogsRequest { - r.severities = &severities - return r -} - -// Search in log body -func (r SandboxAPIGetSandboxLogsRequest) Search(search string) SandboxAPIGetSandboxLogsRequest { - r.search = &search - return r -} - -func (r SandboxAPIGetSandboxLogsRequest) Execute() (*PaginatedLogs, *http.Response, error) { - return r.ApiService.GetSandboxLogsExecute(r) -} - -/* -GetSandboxLogs Get sandbox logs - -Retrieve OTEL logs for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxLogsRequest -*/ -func (a *SandboxAPIService) GetSandboxLogs(ctx context.Context, sandboxId string) SandboxAPIGetSandboxLogsRequest { - return SandboxAPIGetSandboxLogsRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return PaginatedLogs -func (a *SandboxAPIService) GetSandboxLogsExecute(r SandboxAPIGetSandboxLogsRequest) (*PaginatedLogs, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PaginatedLogs - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandboxLogs") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/telemetry/logs" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.from == nil { - return localVarReturnValue, nil, reportError("from is required and must be specified") - } - if r.to == nil { - return localVarReturnValue, nil, reportError("to is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") - if r.page != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") - } else { - var defaultValue float32 = 1 - r.page = &defaultValue - } - if r.limit != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") - } else { - var defaultValue float32 = 100 - r.limit = &defaultValue - } - if r.severities != nil { - t := *r.severities - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - parameterAddToHeaderOrQuery(localVarQueryParams, "severities", s.Index(i).Interface(), "form", "multi") - } - } else { - parameterAddToHeaderOrQuery(localVarQueryParams, "severities", t, "form", "multi") - } - } - if r.search != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "search", r.search, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxMetricsRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - from *time.Time - to *time.Time - xBoxLiteOrganizationID *string - metricNames *[]string -} - -// Start of time range (ISO 8601) -func (r SandboxAPIGetSandboxMetricsRequest) From(from time.Time) SandboxAPIGetSandboxMetricsRequest { - r.from = &from - return r -} - -// End of time range (ISO 8601) -func (r SandboxAPIGetSandboxMetricsRequest) To(to time.Time) SandboxAPIGetSandboxMetricsRequest { - r.to = &to - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxMetricsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxMetricsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Filter by metric names -func (r SandboxAPIGetSandboxMetricsRequest) MetricNames(metricNames []string) SandboxAPIGetSandboxMetricsRequest { - r.metricNames = &metricNames - return r -} - -func (r SandboxAPIGetSandboxMetricsRequest) Execute() (*MetricsResponse, *http.Response, error) { - return r.ApiService.GetSandboxMetricsExecute(r) -} - -/* -GetSandboxMetrics Get sandbox metrics - -Retrieve OTEL metrics for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxMetricsRequest -*/ -func (a *SandboxAPIService) GetSandboxMetrics(ctx context.Context, sandboxId string) SandboxAPIGetSandboxMetricsRequest { - return SandboxAPIGetSandboxMetricsRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MetricsResponse -func (a *SandboxAPIService) GetSandboxMetricsExecute(r SandboxAPIGetSandboxMetricsRequest) (*MetricsResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MetricsResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandboxMetrics") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/telemetry/metrics" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.from == nil { - return localVarReturnValue, nil, reportError("from is required and must be specified") - } - if r.to == nil { - return localVarReturnValue, nil, reportError("to is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") - if r.metricNames != nil { - t := *r.metricNames - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", s.Index(i).Interface(), "form", "multi") - } - } else { - parameterAddToHeaderOrQuery(localVarQueryParams, "metricNames", t, "form", "multi") - } - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxTraceSpansRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - traceId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxTraceSpansRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxTraceSpansRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIGetSandboxTraceSpansRequest) Execute() ([]TraceSpan, *http.Response, error) { - return r.ApiService.GetSandboxTraceSpansExecute(r) -} - -/* -GetSandboxTraceSpans Get trace spans - -Retrieve all spans for a specific trace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @param traceId ID of the trace - @return SandboxAPIGetSandboxTraceSpansRequest -*/ -func (a *SandboxAPIService) GetSandboxTraceSpans(ctx context.Context, sandboxId string, traceId string) SandboxAPIGetSandboxTraceSpansRequest { - return SandboxAPIGetSandboxTraceSpansRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - traceId: traceId, - } -} - -// Execute executes the request -// @return []TraceSpan -func (a *SandboxAPIService) GetSandboxTraceSpansExecute(r SandboxAPIGetSandboxTraceSpansRequest) ([]TraceSpan, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []TraceSpan - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandboxTraceSpans") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/telemetry/traces/{traceId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"traceId"+"}", url.PathEscape(parameterValueToString(r.traceId, "traceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxTracesRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - from *time.Time - to *time.Time - xBoxLiteOrganizationID *string - page *float32 - limit *float32 -} - -// Start of time range (ISO 8601) -func (r SandboxAPIGetSandboxTracesRequest) From(from time.Time) SandboxAPIGetSandboxTracesRequest { - r.from = &from - return r -} - -// End of time range (ISO 8601) -func (r SandboxAPIGetSandboxTracesRequest) To(to time.Time) SandboxAPIGetSandboxTracesRequest { - r.to = &to - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxTracesRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxTracesRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Page number (1-indexed) -func (r SandboxAPIGetSandboxTracesRequest) Page(page float32) SandboxAPIGetSandboxTracesRequest { - r.page = &page - return r -} - -// Number of items per page -func (r SandboxAPIGetSandboxTracesRequest) Limit(limit float32) SandboxAPIGetSandboxTracesRequest { - r.limit = &limit - return r -} - -func (r SandboxAPIGetSandboxTracesRequest) Execute() (*PaginatedTraces, *http.Response, error) { - return r.ApiService.GetSandboxTracesExecute(r) -} - -/* -GetSandboxTraces Get sandbox traces - -Retrieve OTEL traces for a sandbox within a time range - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetSandboxTracesRequest -*/ -func (a *SandboxAPIService) GetSandboxTraces(ctx context.Context, sandboxId string) SandboxAPIGetSandboxTracesRequest { - return SandboxAPIGetSandboxTracesRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return PaginatedTraces -func (a *SandboxAPIService) GetSandboxTracesExecute(r SandboxAPIGetSandboxTracesRequest) (*PaginatedTraces, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PaginatedTraces - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandboxTraces") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/telemetry/traces" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.from == nil { - return localVarReturnValue, nil, reportError("from is required and must be specified") - } - if r.to == nil { - return localVarReturnValue, nil, reportError("to is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "from", r.from, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "to", r.to, "form", "") - if r.page != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") - } else { - var defaultValue float32 = 1 - r.page = &defaultValue - } - if r.limit != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") - } else { - var defaultValue float32 = 100 - r.limit = &defaultValue - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSandboxesForRunnerRequest struct { - ctx context.Context - ApiService SandboxAPI - xBoxLiteOrganizationID *string - states *string - skipReconcilingSandboxes *bool -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSandboxesForRunnerRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSandboxesForRunnerRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Comma-separated list of sandbox states to filter by -func (r SandboxAPIGetSandboxesForRunnerRequest) States(states string) SandboxAPIGetSandboxesForRunnerRequest { - r.states = &states - return r -} - -// Skip sandboxes where state differs from desired state -func (r SandboxAPIGetSandboxesForRunnerRequest) SkipReconcilingSandboxes(skipReconcilingSandboxes bool) SandboxAPIGetSandboxesForRunnerRequest { - r.skipReconcilingSandboxes = &skipReconcilingSandboxes - return r -} - -func (r SandboxAPIGetSandboxesForRunnerRequest) Execute() ([]Sandbox, *http.Response, error) { - return r.ApiService.GetSandboxesForRunnerExecute(r) -} - -/* -GetSandboxesForRunner Get sandboxes for the authenticated runner - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIGetSandboxesForRunnerRequest -*/ -func (a *SandboxAPIService) GetSandboxesForRunner(ctx context.Context) SandboxAPIGetSandboxesForRunnerRequest { - return SandboxAPIGetSandboxesForRunnerRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return []Sandbox -func (a *SandboxAPIService) GetSandboxesForRunnerExecute(r SandboxAPIGetSandboxesForRunnerRequest) ([]Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSandboxesForRunner") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/for-runner" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.states != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "states", r.states, "form", "") - } - if r.skipReconcilingSandboxes != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "skipReconcilingSandboxes", r.skipReconcilingSandboxes, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetSignedPortPreviewUrlRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - port int32 - xBoxLiteOrganizationID *string - expiresInSeconds *int32 -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetSignedPortPreviewUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetSignedPortPreviewUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Expiration time in seconds (default: 60 seconds) -func (r SandboxAPIGetSignedPortPreviewUrlRequest) ExpiresInSeconds(expiresInSeconds int32) SandboxAPIGetSignedPortPreviewUrlRequest { - r.expiresInSeconds = &expiresInSeconds - return r -} - -func (r SandboxAPIGetSignedPortPreviewUrlRequest) Execute() (*SignedPortPreviewUrl, *http.Response, error) { - return r.ApiService.GetSignedPortPreviewUrlExecute(r) -} - -/* -GetSignedPortPreviewUrl Get signed preview URL for a sandbox port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param port Port number to get signed preview URL for - @return SandboxAPIGetSignedPortPreviewUrlRequest -*/ -func (a *SandboxAPIService) GetSignedPortPreviewUrl(ctx context.Context, sandboxIdOrName string, port int32) SandboxAPIGetSignedPortPreviewUrlRequest { - return SandboxAPIGetSignedPortPreviewUrlRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - port: port, - } -} - -// Execute executes the request -// @return SignedPortPreviewUrl -func (a *SandboxAPIService) GetSignedPortPreviewUrlExecute(r SandboxAPIGetSignedPortPreviewUrlRequest) (*SignedPortPreviewUrl, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SignedPortPreviewUrl - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetSignedPortPreviewUrl") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/ports/{port}/signed-preview-url" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.expiresInSeconds != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "expiresInSeconds", r.expiresInSeconds, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIGetToolboxProxyUrlRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIGetToolboxProxyUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIGetToolboxProxyUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIGetToolboxProxyUrlRequest) Execute() (*ToolboxProxyUrl, *http.Response, error) { - return r.ApiService.GetToolboxProxyUrlExecute(r) -} - -/* -GetToolboxProxyUrl Get toolbox proxy URL for a sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIGetToolboxProxyUrlRequest -*/ -func (a *SandboxAPIService) GetToolboxProxyUrl(ctx context.Context, sandboxId string) SandboxAPIGetToolboxProxyUrlRequest { - return SandboxAPIGetToolboxProxyUrlRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ToolboxProxyUrl -func (a *SandboxAPIService) GetToolboxProxyUrlExecute(r SandboxAPIGetToolboxProxyUrlRequest) (*ToolboxProxyUrl, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ToolboxProxyUrl - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.GetToolboxProxyUrl") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/toolbox-proxy-url" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIListSandboxesRequest struct { - ctx context.Context - ApiService SandboxAPI - xBoxLiteOrganizationID *string - verbose *bool - labels *string - includeErroredDeleted *bool -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIListSandboxesRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIListSandboxesRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Include verbose output -func (r SandboxAPIListSandboxesRequest) Verbose(verbose bool) SandboxAPIListSandboxesRequest { - r.verbose = &verbose - return r -} - -// JSON encoded labels to filter by -func (r SandboxAPIListSandboxesRequest) Labels(labels string) SandboxAPIListSandboxesRequest { - r.labels = &labels - return r -} - -// Include errored and deleted sandboxes -func (r SandboxAPIListSandboxesRequest) IncludeErroredDeleted(includeErroredDeleted bool) SandboxAPIListSandboxesRequest { - r.includeErroredDeleted = &includeErroredDeleted - return r -} - -func (r SandboxAPIListSandboxesRequest) Execute() ([]Sandbox, *http.Response, error) { - return r.ApiService.ListSandboxesExecute(r) -} - -/* -ListSandboxes List all sandboxes - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIListSandboxesRequest -*/ -func (a *SandboxAPIService) ListSandboxes(ctx context.Context) SandboxAPIListSandboxesRequest { - return SandboxAPIListSandboxesRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return []Sandbox -func (a *SandboxAPIService) ListSandboxesExecute(r SandboxAPIListSandboxesRequest) ([]Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ListSandboxes") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.verbose != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") - } - if r.labels != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "labels", r.labels, "form", "") - } - if r.includeErroredDeleted != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "includeErroredDeleted", r.includeErroredDeleted, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIListSandboxesPaginatedRequest struct { - ctx context.Context - ApiService SandboxAPI - xBoxLiteOrganizationID *string - page *float32 - limit *float32 - id *string - name *string - labels *string - includeErroredDeleted *bool - states *[]string - snapshots *[]string - regions *[]string - minCpu *float32 - maxCpu *float32 - minMemoryGiB *float32 - maxMemoryGiB *float32 - minDiskGiB *float32 - maxDiskGiB *float32 - lastEventAfter *time.Time - lastEventBefore *time.Time - sort *string - order *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIListSandboxesPaginatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIListSandboxesPaginatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Page number of the results -func (r SandboxAPIListSandboxesPaginatedRequest) Page(page float32) SandboxAPIListSandboxesPaginatedRequest { - r.page = &page - return r -} - -// Number of results per page -func (r SandboxAPIListSandboxesPaginatedRequest) Limit(limit float32) SandboxAPIListSandboxesPaginatedRequest { - r.limit = &limit - return r -} - -// Filter by partial ID match -func (r SandboxAPIListSandboxesPaginatedRequest) Id(id string) SandboxAPIListSandboxesPaginatedRequest { - r.id = &id - return r -} - -// Filter by partial name match -func (r SandboxAPIListSandboxesPaginatedRequest) Name(name string) SandboxAPIListSandboxesPaginatedRequest { - r.name = &name - return r -} - -// JSON encoded labels to filter by -func (r SandboxAPIListSandboxesPaginatedRequest) Labels(labels string) SandboxAPIListSandboxesPaginatedRequest { - r.labels = &labels - return r -} - -// Include results with errored state and deleted desired state -func (r SandboxAPIListSandboxesPaginatedRequest) IncludeErroredDeleted(includeErroredDeleted bool) SandboxAPIListSandboxesPaginatedRequest { - r.includeErroredDeleted = &includeErroredDeleted - return r -} - -// List of states to filter by -func (r SandboxAPIListSandboxesPaginatedRequest) States(states []string) SandboxAPIListSandboxesPaginatedRequest { - r.states = &states - return r -} - -// List of snapshot names to filter by -func (r SandboxAPIListSandboxesPaginatedRequest) Snapshots(snapshots []string) SandboxAPIListSandboxesPaginatedRequest { - r.snapshots = &snapshots - return r -} - -// List of regions to filter by -func (r SandboxAPIListSandboxesPaginatedRequest) Regions(regions []string) SandboxAPIListSandboxesPaginatedRequest { - r.regions = ®ions - return r -} - -// Minimum CPU -func (r SandboxAPIListSandboxesPaginatedRequest) MinCpu(minCpu float32) SandboxAPIListSandboxesPaginatedRequest { - r.minCpu = &minCpu - return r -} - -// Maximum CPU -func (r SandboxAPIListSandboxesPaginatedRequest) MaxCpu(maxCpu float32) SandboxAPIListSandboxesPaginatedRequest { - r.maxCpu = &maxCpu - return r -} - -// Minimum memory in GiB -func (r SandboxAPIListSandboxesPaginatedRequest) MinMemoryGiB(minMemoryGiB float32) SandboxAPIListSandboxesPaginatedRequest { - r.minMemoryGiB = &minMemoryGiB - return r -} - -// Maximum memory in GiB -func (r SandboxAPIListSandboxesPaginatedRequest) MaxMemoryGiB(maxMemoryGiB float32) SandboxAPIListSandboxesPaginatedRequest { - r.maxMemoryGiB = &maxMemoryGiB - return r -} - -// Minimum disk space in GiB -func (r SandboxAPIListSandboxesPaginatedRequest) MinDiskGiB(minDiskGiB float32) SandboxAPIListSandboxesPaginatedRequest { - r.minDiskGiB = &minDiskGiB - return r -} - -// Maximum disk space in GiB -func (r SandboxAPIListSandboxesPaginatedRequest) MaxDiskGiB(maxDiskGiB float32) SandboxAPIListSandboxesPaginatedRequest { - r.maxDiskGiB = &maxDiskGiB - return r -} - -// Include items with last event after this timestamp -func (r SandboxAPIListSandboxesPaginatedRequest) LastEventAfter(lastEventAfter time.Time) SandboxAPIListSandboxesPaginatedRequest { - r.lastEventAfter = &lastEventAfter - return r -} - -// Include items with last event before this timestamp -func (r SandboxAPIListSandboxesPaginatedRequest) LastEventBefore(lastEventBefore time.Time) SandboxAPIListSandboxesPaginatedRequest { - r.lastEventBefore = &lastEventBefore - return r -} - -// Field to sort by -func (r SandboxAPIListSandboxesPaginatedRequest) Sort(sort string) SandboxAPIListSandboxesPaginatedRequest { - r.sort = &sort - return r -} - -// Direction to sort by -func (r SandboxAPIListSandboxesPaginatedRequest) Order(order string) SandboxAPIListSandboxesPaginatedRequest { - r.order = &order - return r -} - -func (r SandboxAPIListSandboxesPaginatedRequest) Execute() (*PaginatedSandboxes, *http.Response, error) { - return r.ApiService.ListSandboxesPaginatedExecute(r) -} - -/* -ListSandboxesPaginated List all sandboxes paginated - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIListSandboxesPaginatedRequest -*/ -func (a *SandboxAPIService) ListSandboxesPaginated(ctx context.Context) SandboxAPIListSandboxesPaginatedRequest { - return SandboxAPIListSandboxesPaginatedRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return PaginatedSandboxes -func (a *SandboxAPIService) ListSandboxesPaginatedExecute(r SandboxAPIListSandboxesPaginatedRequest) (*PaginatedSandboxes, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PaginatedSandboxes - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ListSandboxesPaginated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/paginated" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.page != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") - } else { - var defaultValue float32 = 1 - r.page = &defaultValue - } - if r.limit != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") - } else { - var defaultValue float32 = 100 - r.limit = &defaultValue - } - if r.id != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "id", r.id, "form", "") - } - if r.name != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "name", r.name, "form", "") - } - if r.labels != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "labels", r.labels, "form", "") - } - if r.includeErroredDeleted != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "includeErroredDeleted", r.includeErroredDeleted, "form", "") - } else { - var defaultValue bool = false - r.includeErroredDeleted = &defaultValue - } - if r.states != nil { - t := *r.states - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - parameterAddToHeaderOrQuery(localVarQueryParams, "states", s.Index(i).Interface(), "form", "multi") - } - } else { - parameterAddToHeaderOrQuery(localVarQueryParams, "states", t, "form", "multi") - } - } - if r.snapshots != nil { - t := *r.snapshots - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - parameterAddToHeaderOrQuery(localVarQueryParams, "snapshots", s.Index(i).Interface(), "form", "multi") - } - } else { - parameterAddToHeaderOrQuery(localVarQueryParams, "snapshots", t, "form", "multi") - } - } - if r.regions != nil { - t := *r.regions - if reflect.TypeOf(t).Kind() == reflect.Slice { - s := reflect.ValueOf(t) - for i := 0; i < s.Len(); i++ { - parameterAddToHeaderOrQuery(localVarQueryParams, "regions", s.Index(i).Interface(), "form", "multi") - } - } else { - parameterAddToHeaderOrQuery(localVarQueryParams, "regions", t, "form", "multi") - } - } - if r.minCpu != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "minCpu", r.minCpu, "form", "") - } - if r.maxCpu != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "maxCpu", r.maxCpu, "form", "") - } - if r.minMemoryGiB != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "minMemoryGiB", r.minMemoryGiB, "form", "") - } - if r.maxMemoryGiB != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "maxMemoryGiB", r.maxMemoryGiB, "form", "") - } - if r.minDiskGiB != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "minDiskGiB", r.minDiskGiB, "form", "") - } - if r.maxDiskGiB != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "maxDiskGiB", r.maxDiskGiB, "form", "") - } - if r.lastEventAfter != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "lastEventAfter", r.lastEventAfter, "form", "") - } - if r.lastEventBefore != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "lastEventBefore", r.lastEventBefore, "form", "") - } - if r.sort != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "sort", r.sort, "form", "") - } else { - var defaultValue string = "createdAt" - r.sort = &defaultValue - } - if r.order != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "order", r.order, "form", "") - } else { - var defaultValue string = "desc" - r.order = &defaultValue - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIRecoverSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIRecoverSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIRecoverSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIRecoverSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.RecoverSandboxExecute(r) -} - -/* -RecoverSandbox Recover sandbox from error state - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIRecoverSandboxRequest -*/ -func (a *SandboxAPIService) RecoverSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIRecoverSandboxRequest { - return SandboxAPIRecoverSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) RecoverSandboxExecute(r SandboxAPIRecoverSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.RecoverSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/recover" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIReplaceLabelsRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - sandboxLabels *SandboxLabels - xBoxLiteOrganizationID *string -} - -func (r SandboxAPIReplaceLabelsRequest) SandboxLabels(sandboxLabels SandboxLabels) SandboxAPIReplaceLabelsRequest { - r.sandboxLabels = &sandboxLabels - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIReplaceLabelsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIReplaceLabelsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIReplaceLabelsRequest) Execute() (*SandboxLabels, *http.Response, error) { - return r.ApiService.ReplaceLabelsExecute(r) -} - -/* -ReplaceLabels Replace sandbox labels - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIReplaceLabelsRequest -*/ -func (a *SandboxAPIService) ReplaceLabels(ctx context.Context, sandboxIdOrName string) SandboxAPIReplaceLabelsRequest { - return SandboxAPIReplaceLabelsRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return SandboxLabels -func (a *SandboxAPIService) ReplaceLabelsExecute(r SandboxAPIReplaceLabelsRequest) (*SandboxLabels, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPut - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SandboxLabels - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ReplaceLabels") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/labels" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.sandboxLabels == nil { - return localVarReturnValue, nil, reportError("sandboxLabels is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.sandboxLabels - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIResizeSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - resizeSandbox *ResizeSandbox - xBoxLiteOrganizationID *string -} - -func (r SandboxAPIResizeSandboxRequest) ResizeSandbox(resizeSandbox ResizeSandbox) SandboxAPIResizeSandboxRequest { - r.resizeSandbox = &resizeSandbox - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIResizeSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIResizeSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIResizeSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.ResizeSandboxExecute(r) -} - -/* -ResizeSandbox Resize sandbox resources - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIResizeSandboxRequest -*/ -func (a *SandboxAPIService) ResizeSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIResizeSandboxRequest { - return SandboxAPIResizeSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) ResizeSandboxExecute(r SandboxAPIResizeSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ResizeSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/resize" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.resizeSandbox == nil { - return localVarReturnValue, nil, reportError("resizeSandbox is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.resizeSandbox - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIRevokeSshAccessRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string - token *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIRevokeSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIRevokeSshAccessRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// SSH access token to revoke. If not provided, all SSH access for the sandbox will be revoked. -func (r SandboxAPIRevokeSshAccessRequest) Token(token string) SandboxAPIRevokeSshAccessRequest { - r.token = &token - return r -} - -func (r SandboxAPIRevokeSshAccessRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.RevokeSshAccessExecute(r) -} - -/* -RevokeSshAccess Revoke SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIRevokeSshAccessRequest -*/ -func (a *SandboxAPIService) RevokeSshAccess(ctx context.Context, sandboxIdOrName string) SandboxAPIRevokeSshAccessRequest { - return SandboxAPIRevokeSshAccessRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) RevokeSshAccessExecute(r SandboxAPIRevokeSshAccessRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.RevokeSshAccess") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/ssh-access" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.token != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "token", r.token, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPISetAutoArchiveIntervalRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - interval float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPISetAutoArchiveIntervalRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPISetAutoArchiveIntervalRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPISetAutoArchiveIntervalRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.SetAutoArchiveIntervalExecute(r) -} - -/* -SetAutoArchiveInterval Set sandbox auto-archive interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-archive interval in minutes (0 means the maximum interval will be used) - @return SandboxAPISetAutoArchiveIntervalRequest -*/ -func (a *SandboxAPIService) SetAutoArchiveInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutoArchiveIntervalRequest { - return SandboxAPISetAutoArchiveIntervalRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - interval: interval, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) SetAutoArchiveIntervalExecute(r SandboxAPISetAutoArchiveIntervalRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.SetAutoArchiveInterval") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/autoarchive/{interval}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPISetAutoDeleteIntervalRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - interval float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPISetAutoDeleteIntervalRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPISetAutoDeleteIntervalRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPISetAutoDeleteIntervalRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.SetAutoDeleteIntervalExecute(r) -} - -/* -SetAutoDeleteInterval Set sandbox auto-delete interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - @return SandboxAPISetAutoDeleteIntervalRequest -*/ -func (a *SandboxAPIService) SetAutoDeleteInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutoDeleteIntervalRequest { - return SandboxAPISetAutoDeleteIntervalRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - interval: interval, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) SetAutoDeleteIntervalExecute(r SandboxAPISetAutoDeleteIntervalRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.SetAutoDeleteInterval") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/autodelete/{interval}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPISetAutostopIntervalRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - interval float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPISetAutostopIntervalRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPISetAutostopIntervalRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPISetAutostopIntervalRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.SetAutostopIntervalExecute(r) -} - -/* -SetAutostopInterval Set sandbox auto-stop interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param interval Auto-stop interval in minutes (0 to disable) - @return SandboxAPISetAutostopIntervalRequest -*/ -func (a *SandboxAPIService) SetAutostopInterval(ctx context.Context, sandboxIdOrName string, interval float32) SandboxAPISetAutostopIntervalRequest { - return SandboxAPISetAutostopIntervalRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - interval: interval, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) SetAutostopIntervalExecute(r SandboxAPISetAutostopIntervalRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.SetAutostopInterval") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/autostop/{interval}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIStartSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIStartSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIStartSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIStartSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.StartSandboxExecute(r) -} - -/* -StartSandbox Start sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIStartSandboxRequest -*/ -func (a *SandboxAPIService) StartSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIStartSandboxRequest { - return SandboxAPIStartSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) StartSandboxExecute(r SandboxAPIStartSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.StartSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/start" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIStopSandboxRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - xBoxLiteOrganizationID *string - force *bool -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIStopSandboxRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIStopSandboxRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Force stop the sandbox using SIGKILL instead of SIGTERM -func (r SandboxAPIStopSandboxRequest) Force(force bool) SandboxAPIStopSandboxRequest { - r.force = &force - return r -} - -func (r SandboxAPIStopSandboxRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.StopSandboxExecute(r) -} - -/* -StopSandbox Stop sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @return SandboxAPIStopSandboxRequest -*/ -func (a *SandboxAPIService) StopSandbox(ctx context.Context, sandboxIdOrName string) SandboxAPIStopSandboxRequest { - return SandboxAPIStopSandboxRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) StopSandboxExecute(r SandboxAPIStopSandboxRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.StopSandbox") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/stop" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.force != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "force", r.force, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIUpdateLastActivityRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIUpdateLastActivityRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIUpdateLastActivityRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIUpdateLastActivityRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateLastActivityExecute(r) -} - -/* -UpdateLastActivity Update sandbox last activity - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIUpdateLastActivityRequest -*/ -func (a *SandboxAPIService) UpdateLastActivity(ctx context.Context, sandboxId string) SandboxAPIUpdateLastActivityRequest { - return SandboxAPIUpdateLastActivityRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -func (a *SandboxAPIService) UpdateLastActivityExecute(r SandboxAPIUpdateLastActivityRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.UpdateLastActivity") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/last-activity" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SandboxAPIUpdatePublicStatusRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxIdOrName string - isPublic bool - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIUpdatePublicStatusRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIUpdatePublicStatusRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIUpdatePublicStatusRequest) Execute() (*Sandbox, *http.Response, error) { - return r.ApiService.UpdatePublicStatusExecute(r) -} - -/* -UpdatePublicStatus Update public status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxIdOrName ID or name of the sandbox - @param isPublic Public status to set - @return SandboxAPIUpdatePublicStatusRequest -*/ -func (a *SandboxAPIService) UpdatePublicStatus(ctx context.Context, sandboxIdOrName string, isPublic bool) SandboxAPIUpdatePublicStatusRequest { - return SandboxAPIUpdatePublicStatusRequest{ - ApiService: a, - ctx: ctx, - sandboxIdOrName: sandboxIdOrName, - isPublic: isPublic, - } -} - -// Execute executes the request -// @return Sandbox -func (a *SandboxAPIService) UpdatePublicStatusExecute(r SandboxAPIUpdatePublicStatusRequest) (*Sandbox, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Sandbox - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.UpdatePublicStatus") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxIdOrName}/public/{isPublic}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxIdOrName"+"}", url.PathEscape(parameterValueToString(r.sandboxIdOrName, "sandboxIdOrName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"isPublic"+"}", url.PathEscape(parameterValueToString(r.isPublic, "isPublic")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SandboxAPIUpdateSandboxStateRequest struct { - ctx context.Context - ApiService SandboxAPI - sandboxId string - updateSandboxStateDto *UpdateSandboxStateDto - xBoxLiteOrganizationID *string -} - -func (r SandboxAPIUpdateSandboxStateRequest) UpdateSandboxStateDto(updateSandboxStateDto UpdateSandboxStateDto) SandboxAPIUpdateSandboxStateRequest { - r.updateSandboxStateDto = &updateSandboxStateDto - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIUpdateSandboxStateRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIUpdateSandboxStateRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIUpdateSandboxStateRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdateSandboxStateExecute(r) -} - -/* -UpdateSandboxState Update sandbox state - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId ID of the sandbox - @return SandboxAPIUpdateSandboxStateRequest -*/ -func (a *SandboxAPIService) UpdateSandboxState(ctx context.Context, sandboxId string) SandboxAPIUpdateSandboxStateRequest { - return SandboxAPIUpdateSandboxStateRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -func (a *SandboxAPIService) UpdateSandboxStateExecute(r SandboxAPIUpdateSandboxStateRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPut - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.UpdateSandboxState") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/{sandboxId}/state" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.updateSandboxStateDto == nil { - return nil, reportError("updateSandboxStateDto is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.updateSandboxStateDto - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SandboxAPIValidateSshAccessRequest struct { - ctx context.Context - ApiService SandboxAPI - token *string - xBoxLiteOrganizationID *string -} - -// SSH access token to validate -func (r SandboxAPIValidateSshAccessRequest) Token(token string) SandboxAPIValidateSshAccessRequest { - r.token = &token - return r -} - -// Use with JWT to specify the organization ID -func (r SandboxAPIValidateSshAccessRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SandboxAPIValidateSshAccessRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SandboxAPIValidateSshAccessRequest) Execute() (*SshAccessValidationDto, *http.Response, error) { - return r.ApiService.ValidateSshAccessExecute(r) -} - -/* -ValidateSshAccess Validate SSH access for sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SandboxAPIValidateSshAccessRequest -*/ -func (a *SandboxAPIService) ValidateSshAccess(ctx context.Context) SandboxAPIValidateSshAccessRequest { - return SandboxAPIValidateSshAccessRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return SshAccessValidationDto -func (a *SandboxAPIService) ValidateSshAccessExecute(r SandboxAPIValidateSshAccessRequest) (*SshAccessValidationDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SshAccessValidationDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SandboxAPIService.ValidateSshAccess") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/sandbox/ssh-access/validate" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.token == nil { - return localVarReturnValue, nil, reportError("token is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "token", r.token, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/api_snapshots.go b/apps/api-client-go/api_snapshots.go deleted file mode 100644 index 79a04f08e..000000000 --- a/apps/api-client-go/api_snapshots.go +++ /dev/null @@ -1,1332 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "bytes" - "context" - "io" - "net/http" - "net/url" - "strings" -) - - -type SnapshotsAPI interface { - - /* - ActivateSnapshot Activate a snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIActivateSnapshotRequest - */ - ActivateSnapshot(ctx context.Context, id string) SnapshotsAPIActivateSnapshotRequest - - // ActivateSnapshotExecute executes the request - // @return SnapshotDto - ActivateSnapshotExecute(r SnapshotsAPIActivateSnapshotRequest) (*SnapshotDto, *http.Response, error) - - /* - CanCleanupImage Check if an image can be cleaned up - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPICanCleanupImageRequest - */ - CanCleanupImage(ctx context.Context) SnapshotsAPICanCleanupImageRequest - - // CanCleanupImageExecute executes the request - // @return bool - CanCleanupImageExecute(r SnapshotsAPICanCleanupImageRequest) (bool, *http.Response, error) - - /* - CreateSnapshot Create a new snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPICreateSnapshotRequest - */ - CreateSnapshot(ctx context.Context) SnapshotsAPICreateSnapshotRequest - - // CreateSnapshotExecute executes the request - // @return SnapshotDto - CreateSnapshotExecute(r SnapshotsAPICreateSnapshotRequest) (*SnapshotDto, *http.Response, error) - - /* - DeactivateSnapshot Deactivate a snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIDeactivateSnapshotRequest - */ - DeactivateSnapshot(ctx context.Context, id string) SnapshotsAPIDeactivateSnapshotRequest - - // DeactivateSnapshotExecute executes the request - DeactivateSnapshotExecute(r SnapshotsAPIDeactivateSnapshotRequest) (*http.Response, error) - - /* - GetAllSnapshots List all snapshots - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPIGetAllSnapshotsRequest - */ - GetAllSnapshots(ctx context.Context) SnapshotsAPIGetAllSnapshotsRequest - - // GetAllSnapshotsExecute executes the request - // @return PaginatedSnapshots - GetAllSnapshotsExecute(r SnapshotsAPIGetAllSnapshotsRequest) (*PaginatedSnapshots, *http.Response, error) - - /* - GetSnapshot Get snapshot by ID or name - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID or name - @return SnapshotsAPIGetSnapshotRequest - */ - GetSnapshot(ctx context.Context, id string) SnapshotsAPIGetSnapshotRequest - - // GetSnapshotExecute executes the request - // @return SnapshotDto - GetSnapshotExecute(r SnapshotsAPIGetSnapshotRequest) (*SnapshotDto, *http.Response, error) - - /* - GetSnapshotBuildLogs Get snapshot build logs - - This endpoint is deprecated. Use `getSnapshotBuildLogsUrl` instead. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIGetSnapshotBuildLogsRequest - - Deprecated - */ - GetSnapshotBuildLogs(ctx context.Context, id string) SnapshotsAPIGetSnapshotBuildLogsRequest - - // GetSnapshotBuildLogsExecute executes the request - // Deprecated - GetSnapshotBuildLogsExecute(r SnapshotsAPIGetSnapshotBuildLogsRequest) (*http.Response, error) - - /* - GetSnapshotBuildLogsUrl Get snapshot build logs URL - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIGetSnapshotBuildLogsUrlRequest - */ - GetSnapshotBuildLogsUrl(ctx context.Context, id string) SnapshotsAPIGetSnapshotBuildLogsUrlRequest - - // GetSnapshotBuildLogsUrlExecute executes the request - // @return Url - GetSnapshotBuildLogsUrlExecute(r SnapshotsAPIGetSnapshotBuildLogsUrlRequest) (*Url, *http.Response, error) - - /* - RemoveSnapshot Delete snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIRemoveSnapshotRequest - */ - RemoveSnapshot(ctx context.Context, id string) SnapshotsAPIRemoveSnapshotRequest - - // RemoveSnapshotExecute executes the request - RemoveSnapshotExecute(r SnapshotsAPIRemoveSnapshotRequest) (*http.Response, error) - - /* - SetSnapshotGeneralStatus Set snapshot general status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPISetSnapshotGeneralStatusRequest - */ - SetSnapshotGeneralStatus(ctx context.Context, id string) SnapshotsAPISetSnapshotGeneralStatusRequest - - // SetSnapshotGeneralStatusExecute executes the request - // @return SnapshotDto - SetSnapshotGeneralStatusExecute(r SnapshotsAPISetSnapshotGeneralStatusRequest) (*SnapshotDto, *http.Response, error) -} - -// SnapshotsAPIService SnapshotsAPI service -type SnapshotsAPIService service - -type SnapshotsAPIActivateSnapshotRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIActivateSnapshotRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIActivateSnapshotRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPIActivateSnapshotRequest) Execute() (*SnapshotDto, *http.Response, error) { - return r.ApiService.ActivateSnapshotExecute(r) -} - -/* -ActivateSnapshot Activate a snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIActivateSnapshotRequest -*/ -func (a *SnapshotsAPIService) ActivateSnapshot(ctx context.Context, id string) SnapshotsAPIActivateSnapshotRequest { - return SnapshotsAPIActivateSnapshotRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return SnapshotDto -func (a *SnapshotsAPIService) ActivateSnapshotExecute(r SnapshotsAPIActivateSnapshotRequest) (*SnapshotDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SnapshotDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.ActivateSnapshot") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}/activate" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPICanCleanupImageRequest struct { - ctx context.Context - ApiService SnapshotsAPI - imageName *string - xBoxLiteOrganizationID *string -} - -// Image name with tag to check -func (r SnapshotsAPICanCleanupImageRequest) ImageName(imageName string) SnapshotsAPICanCleanupImageRequest { - r.imageName = &imageName - return r -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPICanCleanupImageRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPICanCleanupImageRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPICanCleanupImageRequest) Execute() (bool, *http.Response, error) { - return r.ApiService.CanCleanupImageExecute(r) -} - -/* -CanCleanupImage Check if an image can be cleaned up - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPICanCleanupImageRequest -*/ -func (a *SnapshotsAPIService) CanCleanupImage(ctx context.Context) SnapshotsAPICanCleanupImageRequest { - return SnapshotsAPICanCleanupImageRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return bool -func (a *SnapshotsAPIService) CanCleanupImageExecute(r SnapshotsAPICanCleanupImageRequest) (bool, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue bool - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.CanCleanupImage") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/can-cleanup-image" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.imageName == nil { - return localVarReturnValue, nil, reportError("imageName is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "imageName", r.imageName, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPICreateSnapshotRequest struct { - ctx context.Context - ApiService SnapshotsAPI - createSnapshot *CreateSnapshot - xBoxLiteOrganizationID *string -} - -func (r SnapshotsAPICreateSnapshotRequest) CreateSnapshot(createSnapshot CreateSnapshot) SnapshotsAPICreateSnapshotRequest { - r.createSnapshot = &createSnapshot - return r -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPICreateSnapshotRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPICreateSnapshotRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPICreateSnapshotRequest) Execute() (*SnapshotDto, *http.Response, error) { - return r.ApiService.CreateSnapshotExecute(r) -} - -/* -CreateSnapshot Create a new snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPICreateSnapshotRequest -*/ -func (a *SnapshotsAPIService) CreateSnapshot(ctx context.Context) SnapshotsAPICreateSnapshotRequest { - return SnapshotsAPICreateSnapshotRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return SnapshotDto -func (a *SnapshotsAPIService) CreateSnapshotExecute(r SnapshotsAPICreateSnapshotRequest) (*SnapshotDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SnapshotDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.CreateSnapshot") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.createSnapshot == nil { - return localVarReturnValue, nil, reportError("createSnapshot is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.createSnapshot - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPIDeactivateSnapshotRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIDeactivateSnapshotRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIDeactivateSnapshotRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPIDeactivateSnapshotRequest) Execute() (*http.Response, error) { - return r.ApiService.DeactivateSnapshotExecute(r) -} - -/* -DeactivateSnapshot Deactivate a snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIDeactivateSnapshotRequest -*/ -func (a *SnapshotsAPIService) DeactivateSnapshot(ctx context.Context, id string) SnapshotsAPIDeactivateSnapshotRequest { - return SnapshotsAPIDeactivateSnapshotRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -func (a *SnapshotsAPIService) DeactivateSnapshotExecute(r SnapshotsAPIDeactivateSnapshotRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.DeactivateSnapshot") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}/deactivate" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SnapshotsAPIGetAllSnapshotsRequest struct { - ctx context.Context - ApiService SnapshotsAPI - xBoxLiteOrganizationID *string - page *float32 - limit *float32 - name *string - sort *string - order *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIGetAllSnapshotsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIGetAllSnapshotsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Page number of the results -func (r SnapshotsAPIGetAllSnapshotsRequest) Page(page float32) SnapshotsAPIGetAllSnapshotsRequest { - r.page = &page - return r -} - -// Number of results per page -func (r SnapshotsAPIGetAllSnapshotsRequest) Limit(limit float32) SnapshotsAPIGetAllSnapshotsRequest { - r.limit = &limit - return r -} - -// Filter by partial name match -func (r SnapshotsAPIGetAllSnapshotsRequest) Name(name string) SnapshotsAPIGetAllSnapshotsRequest { - r.name = &name - return r -} - -// Field to sort by -func (r SnapshotsAPIGetAllSnapshotsRequest) Sort(sort string) SnapshotsAPIGetAllSnapshotsRequest { - r.sort = &sort - return r -} - -// Direction to sort by -func (r SnapshotsAPIGetAllSnapshotsRequest) Order(order string) SnapshotsAPIGetAllSnapshotsRequest { - r.order = &order - return r -} - -func (r SnapshotsAPIGetAllSnapshotsRequest) Execute() (*PaginatedSnapshots, *http.Response, error) { - return r.ApiService.GetAllSnapshotsExecute(r) -} - -/* -GetAllSnapshots List all snapshots - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return SnapshotsAPIGetAllSnapshotsRequest -*/ -func (a *SnapshotsAPIService) GetAllSnapshots(ctx context.Context) SnapshotsAPIGetAllSnapshotsRequest { - return SnapshotsAPIGetAllSnapshotsRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return PaginatedSnapshots -func (a *SnapshotsAPIService) GetAllSnapshotsExecute(r SnapshotsAPIGetAllSnapshotsRequest) (*PaginatedSnapshots, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PaginatedSnapshots - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.GetAllSnapshots") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.page != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "page", r.page, "form", "") - } else { - var defaultValue float32 = 1 - r.page = &defaultValue - } - if r.limit != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "limit", r.limit, "form", "") - } else { - var defaultValue float32 = 100 - r.limit = &defaultValue - } - if r.name != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "name", r.name, "form", "") - } - if r.sort != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "sort", r.sort, "form", "") - } else { - var defaultValue string = "lastUsedAt" - r.sort = &defaultValue - } - if r.order != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "order", r.order, "form", "") - } else { - var defaultValue string = "desc" - r.order = &defaultValue - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPIGetSnapshotRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIGetSnapshotRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIGetSnapshotRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPIGetSnapshotRequest) Execute() (*SnapshotDto, *http.Response, error) { - return r.ApiService.GetSnapshotExecute(r) -} - -/* -GetSnapshot Get snapshot by ID or name - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID or name - @return SnapshotsAPIGetSnapshotRequest -*/ -func (a *SnapshotsAPIService) GetSnapshot(ctx context.Context, id string) SnapshotsAPIGetSnapshotRequest { - return SnapshotsAPIGetSnapshotRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return SnapshotDto -func (a *SnapshotsAPIService) GetSnapshotExecute(r SnapshotsAPIGetSnapshotRequest) (*SnapshotDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SnapshotDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.GetSnapshot") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPIGetSnapshotBuildLogsRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string - follow *bool -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIGetSnapshotBuildLogsRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIGetSnapshotBuildLogsRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Whether to follow the logs stream -func (r SnapshotsAPIGetSnapshotBuildLogsRequest) Follow(follow bool) SnapshotsAPIGetSnapshotBuildLogsRequest { - r.follow = &follow - return r -} - -func (r SnapshotsAPIGetSnapshotBuildLogsRequest) Execute() (*http.Response, error) { - return r.ApiService.GetSnapshotBuildLogsExecute(r) -} - -/* -GetSnapshotBuildLogs Get snapshot build logs - -This endpoint is deprecated. Use `getSnapshotBuildLogsUrl` instead. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIGetSnapshotBuildLogsRequest - -Deprecated -*/ -func (a *SnapshotsAPIService) GetSnapshotBuildLogs(ctx context.Context, id string) SnapshotsAPIGetSnapshotBuildLogsRequest { - return SnapshotsAPIGetSnapshotBuildLogsRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// Deprecated -func (a *SnapshotsAPIService) GetSnapshotBuildLogsExecute(r SnapshotsAPIGetSnapshotBuildLogsRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.GetSnapshotBuildLogs") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}/build-logs" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.follow != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "follow", r.follow, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SnapshotsAPIGetSnapshotBuildLogsUrlRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIGetSnapshotBuildLogsUrlRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIGetSnapshotBuildLogsUrlRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPIGetSnapshotBuildLogsUrlRequest) Execute() (*Url, *http.Response, error) { - return r.ApiService.GetSnapshotBuildLogsUrlExecute(r) -} - -/* -GetSnapshotBuildLogsUrl Get snapshot build logs URL - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIGetSnapshotBuildLogsUrlRequest -*/ -func (a *SnapshotsAPIService) GetSnapshotBuildLogsUrl(ctx context.Context, id string) SnapshotsAPIGetSnapshotBuildLogsUrlRequest { - return SnapshotsAPIGetSnapshotBuildLogsUrlRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return Url -func (a *SnapshotsAPIService) GetSnapshotBuildLogsUrlExecute(r SnapshotsAPIGetSnapshotBuildLogsUrlRequest) (*Url, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Url - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.GetSnapshotBuildLogsUrl") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}/build-logs-url" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type SnapshotsAPIRemoveSnapshotRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPIRemoveSnapshotRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPIRemoveSnapshotRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPIRemoveSnapshotRequest) Execute() (*http.Response, error) { - return r.ApiService.RemoveSnapshotExecute(r) -} - -/* -RemoveSnapshot Delete snapshot - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPIRemoveSnapshotRequest -*/ -func (a *SnapshotsAPIService) RemoveSnapshot(ctx context.Context, id string) SnapshotsAPIRemoveSnapshotRequest { - return SnapshotsAPIRemoveSnapshotRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -func (a *SnapshotsAPIService) RemoveSnapshotExecute(r SnapshotsAPIRemoveSnapshotRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.RemoveSnapshot") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type SnapshotsAPISetSnapshotGeneralStatusRequest struct { - ctx context.Context - ApiService SnapshotsAPI - id string - setSnapshotGeneralStatusDto *SetSnapshotGeneralStatusDto - xBoxLiteOrganizationID *string -} - -func (r SnapshotsAPISetSnapshotGeneralStatusRequest) SetSnapshotGeneralStatusDto(setSnapshotGeneralStatusDto SetSnapshotGeneralStatusDto) SnapshotsAPISetSnapshotGeneralStatusRequest { - r.setSnapshotGeneralStatusDto = &setSnapshotGeneralStatusDto - return r -} - -// Use with JWT to specify the organization ID -func (r SnapshotsAPISetSnapshotGeneralStatusRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) SnapshotsAPISetSnapshotGeneralStatusRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r SnapshotsAPISetSnapshotGeneralStatusRequest) Execute() (*SnapshotDto, *http.Response, error) { - return r.ApiService.SetSnapshotGeneralStatusExecute(r) -} - -/* -SetSnapshotGeneralStatus Set snapshot general status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param id Snapshot ID - @return SnapshotsAPISetSnapshotGeneralStatusRequest -*/ -func (a *SnapshotsAPIService) SetSnapshotGeneralStatus(ctx context.Context, id string) SnapshotsAPISetSnapshotGeneralStatusRequest { - return SnapshotsAPISetSnapshotGeneralStatusRequest{ - ApiService: a, - ctx: ctx, - id: id, - } -} - -// Execute executes the request -// @return SnapshotDto -func (a *SnapshotsAPIService) SetSnapshotGeneralStatusExecute(r SnapshotsAPISetSnapshotGeneralStatusRequest) (*SnapshotDto, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPatch - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SnapshotDto - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "SnapshotsAPIService.SetSnapshotGeneralStatus") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/snapshots/{id}/general" - localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", url.PathEscape(parameterValueToString(r.id, "id")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.setSnapshotGeneralStatusDto == nil { - return localVarReturnValue, nil, reportError("setSnapshotGeneralStatusDto is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.setSnapshotGeneralStatusDto - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/api_toolbox.go b/apps/api-client-go/api_toolbox.go deleted file mode 100644 index 03601ad49..000000000 --- a/apps/api-client-go/api_toolbox.go +++ /dev/null @@ -1,9682 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "bytes" - "context" - "io" - "net/http" - "net/url" - "strings" - "os" -) - - -type ToolboxAPI interface { - - /* - ClickMouseDeprecated [DEPRECATED] Click mouse - - Click mouse at specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIClickMouseDeprecatedRequest - - Deprecated - */ - ClickMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIClickMouseDeprecatedRequest - - // ClickMouseDeprecatedExecute executes the request - // @return MouseClickResponse - // Deprecated - ClickMouseDeprecatedExecute(r ToolboxAPIClickMouseDeprecatedRequest) (*MouseClickResponse, *http.Response, error) - - /* - CreateFolderDeprecated [DEPRECATED] Create folder - - Create folder inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreateFolderDeprecatedRequest - - Deprecated - */ - CreateFolderDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreateFolderDeprecatedRequest - - // CreateFolderDeprecatedExecute executes the request - // Deprecated - CreateFolderDeprecatedExecute(r ToolboxAPICreateFolderDeprecatedRequest) (*http.Response, error) - - /* - CreatePTYSessionDeprecated [DEPRECATED] Create PTY session - - Create a new PTY session in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreatePTYSessionDeprecatedRequest - - Deprecated - */ - CreatePTYSessionDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreatePTYSessionDeprecatedRequest - - // CreatePTYSessionDeprecatedExecute executes the request - // @return PtyCreateResponse - // Deprecated - CreatePTYSessionDeprecatedExecute(r ToolboxAPICreatePTYSessionDeprecatedRequest) (*PtyCreateResponse, *http.Response, error) - - /* - CreateSessionDeprecated [DEPRECATED] Create session - - Create a new session in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreateSessionDeprecatedRequest - - Deprecated - */ - CreateSessionDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreateSessionDeprecatedRequest - - // CreateSessionDeprecatedExecute executes the request - // Deprecated - CreateSessionDeprecatedExecute(r ToolboxAPICreateSessionDeprecatedRequest) (*http.Response, error) - - /* - DeleteFileDeprecated [DEPRECATED] Delete file - - Delete file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDeleteFileDeprecatedRequest - - Deprecated - */ - DeleteFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDeleteFileDeprecatedRequest - - // DeleteFileDeprecatedExecute executes the request - // Deprecated - DeleteFileDeprecatedExecute(r ToolboxAPIDeleteFileDeprecatedRequest) (*http.Response, error) - - /* - DeletePTYSessionDeprecated [DEPRECATED] Delete PTY session - - Delete a PTY session and terminate the associated process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIDeletePTYSessionDeprecatedRequest - - Deprecated - */ - DeletePTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIDeletePTYSessionDeprecatedRequest - - // DeletePTYSessionDeprecatedExecute executes the request - // Deprecated - DeletePTYSessionDeprecatedExecute(r ToolboxAPIDeletePTYSessionDeprecatedRequest) (*http.Response, error) - - /* - DeleteSessionDeprecated [DEPRECATED] Delete session - - Delete a specific session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIDeleteSessionDeprecatedRequest - - Deprecated - */ - DeleteSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIDeleteSessionDeprecatedRequest - - // DeleteSessionDeprecatedExecute executes the request - // Deprecated - DeleteSessionDeprecatedExecute(r ToolboxAPIDeleteSessionDeprecatedRequest) (*http.Response, error) - - /* - DownloadFileDeprecated [DEPRECATED] Download file - - Download file from sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDownloadFileDeprecatedRequest - - Deprecated - */ - DownloadFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDownloadFileDeprecatedRequest - - // DownloadFileDeprecatedExecute executes the request - // @return *os.File - // Deprecated - DownloadFileDeprecatedExecute(r ToolboxAPIDownloadFileDeprecatedRequest) (*os.File, *http.Response, error) - - /* - DownloadFilesDeprecated [DEPRECATED] Download multiple files - - Streams back a multipart/form-data bundle of the requested paths - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDownloadFilesDeprecatedRequest - - Deprecated - */ - DownloadFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDownloadFilesDeprecatedRequest - - // DownloadFilesDeprecatedExecute executes the request - // @return *os.File - // Deprecated - DownloadFilesDeprecatedExecute(r ToolboxAPIDownloadFilesDeprecatedRequest) (*os.File, *http.Response, error) - - /* - DragMouseDeprecated [DEPRECATED] Drag mouse - - Drag mouse from start to end coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDragMouseDeprecatedRequest - - Deprecated - */ - DragMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDragMouseDeprecatedRequest - - // DragMouseDeprecatedExecute executes the request - // @return MouseDragResponse - // Deprecated - DragMouseDeprecatedExecute(r ToolboxAPIDragMouseDeprecatedRequest) (*MouseDragResponse, *http.Response, error) - - /* - ExecuteCommandDeprecated [DEPRECATED] Execute command - - Execute command synchronously inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIExecuteCommandDeprecatedRequest - - Deprecated - */ - ExecuteCommandDeprecated(ctx context.Context, sandboxId string) ToolboxAPIExecuteCommandDeprecatedRequest - - // ExecuteCommandDeprecatedExecute executes the request - // @return ExecuteResponse - // Deprecated - ExecuteCommandDeprecatedExecute(r ToolboxAPIExecuteCommandDeprecatedRequest) (*ExecuteResponse, *http.Response, error) - - /* - ExecuteSessionCommandDeprecated [DEPRECATED] Execute command in session - - Execute a command in a specific session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIExecuteSessionCommandDeprecatedRequest - - Deprecated - */ - ExecuteSessionCommandDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIExecuteSessionCommandDeprecatedRequest - - // ExecuteSessionCommandDeprecatedExecute executes the request - // @return SessionExecuteResponse - // Deprecated - ExecuteSessionCommandDeprecatedExecute(r ToolboxAPIExecuteSessionCommandDeprecatedRequest) (*SessionExecuteResponse, *http.Response, error) - - /* - FindInFilesDeprecated [DEPRECATED] Search for text/pattern in files - - Search for text/pattern inside sandbox files - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIFindInFilesDeprecatedRequest - - Deprecated - */ - FindInFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIFindInFilesDeprecatedRequest - - // FindInFilesDeprecatedExecute executes the request - // @return []Match - // Deprecated - FindInFilesDeprecatedExecute(r ToolboxAPIFindInFilesDeprecatedRequest) ([]Match, *http.Response, error) - - /* - GetComputerUseStatusDeprecated [DEPRECATED] Get computer use status - - Get status of all VNC desktop processes - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetComputerUseStatusDeprecatedRequest - - Deprecated - */ - GetComputerUseStatusDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetComputerUseStatusDeprecatedRequest - - // GetComputerUseStatusDeprecatedExecute executes the request - // @return ComputerUseStatusResponse - // Deprecated - GetComputerUseStatusDeprecatedExecute(r ToolboxAPIGetComputerUseStatusDeprecatedRequest) (*ComputerUseStatusResponse, *http.Response, error) - - /* - GetDisplayInfoDeprecated [DEPRECATED] Get display info - - Get information about displays - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetDisplayInfoDeprecatedRequest - - Deprecated - */ - GetDisplayInfoDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetDisplayInfoDeprecatedRequest - - // GetDisplayInfoDeprecatedExecute executes the request - // @return DisplayInfoResponse - // Deprecated - GetDisplayInfoDeprecatedExecute(r ToolboxAPIGetDisplayInfoDeprecatedRequest) (*DisplayInfoResponse, *http.Response, error) - - /* - GetFileInfoDeprecated [DEPRECATED] Get file info - - Get file info inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetFileInfoDeprecatedRequest - - Deprecated - */ - GetFileInfoDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetFileInfoDeprecatedRequest - - // GetFileInfoDeprecatedExecute executes the request - // @return FileInfo - // Deprecated - GetFileInfoDeprecatedExecute(r ToolboxAPIGetFileInfoDeprecatedRequest) (*FileInfo, *http.Response, error) - - /* - GetMousePositionDeprecated [DEPRECATED] Get mouse position - - Get current mouse cursor position - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetMousePositionDeprecatedRequest - - Deprecated - */ - GetMousePositionDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetMousePositionDeprecatedRequest - - // GetMousePositionDeprecatedExecute executes the request - // @return MousePosition - // Deprecated - GetMousePositionDeprecatedExecute(r ToolboxAPIGetMousePositionDeprecatedRequest) (*MousePosition, *http.Response, error) - - /* - GetPTYSessionDeprecated [DEPRECATED] Get PTY session - - Get PTY session information by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIGetPTYSessionDeprecatedRequest - - Deprecated - */ - GetPTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIGetPTYSessionDeprecatedRequest - - // GetPTYSessionDeprecatedExecute executes the request - // @return PtySessionInfo - // Deprecated - GetPTYSessionDeprecatedExecute(r ToolboxAPIGetPTYSessionDeprecatedRequest) (*PtySessionInfo, *http.Response, error) - - /* - GetProcessErrorsDeprecated [DEPRECATED] Get process errors - - Get error logs for a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessErrorsDeprecatedRequest - - Deprecated - */ - GetProcessErrorsDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessErrorsDeprecatedRequest - - // GetProcessErrorsDeprecatedExecute executes the request - // @return ProcessErrorsResponse - // Deprecated - GetProcessErrorsDeprecatedExecute(r ToolboxAPIGetProcessErrorsDeprecatedRequest) (*ProcessErrorsResponse, *http.Response, error) - - /* - GetProcessLogsDeprecated [DEPRECATED] Get process logs - - Get logs for a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessLogsDeprecatedRequest - - Deprecated - */ - GetProcessLogsDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessLogsDeprecatedRequest - - // GetProcessLogsDeprecatedExecute executes the request - // @return ProcessLogsResponse - // Deprecated - GetProcessLogsDeprecatedExecute(r ToolboxAPIGetProcessLogsDeprecatedRequest) (*ProcessLogsResponse, *http.Response, error) - - /* - GetProcessStatusDeprecated [DEPRECATED] Get process status - - Get status of a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessStatusDeprecatedRequest - - Deprecated - */ - GetProcessStatusDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessStatusDeprecatedRequest - - // GetProcessStatusDeprecatedExecute executes the request - // @return ProcessStatusResponse - // Deprecated - GetProcessStatusDeprecatedExecute(r ToolboxAPIGetProcessStatusDeprecatedRequest) (*ProcessStatusResponse, *http.Response, error) - - /* - GetProjectDirDeprecated [DEPRECATED] Get sandbox project dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetProjectDirDeprecatedRequest - - Deprecated - */ - GetProjectDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetProjectDirDeprecatedRequest - - // GetProjectDirDeprecatedExecute executes the request - // @return ProjectDirResponse - // Deprecated - GetProjectDirDeprecatedExecute(r ToolboxAPIGetProjectDirDeprecatedRequest) (*ProjectDirResponse, *http.Response, error) - - /* - GetSessionCommandDeprecated [DEPRECATED] Get session command - - Get session command by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @param commandId - @return ToolboxAPIGetSessionCommandDeprecatedRequest - - Deprecated - */ - GetSessionCommandDeprecated(ctx context.Context, sandboxId string, sessionId string, commandId string) ToolboxAPIGetSessionCommandDeprecatedRequest - - // GetSessionCommandDeprecatedExecute executes the request - // @return Command - // Deprecated - GetSessionCommandDeprecatedExecute(r ToolboxAPIGetSessionCommandDeprecatedRequest) (*Command, *http.Response, error) - - /* - GetSessionCommandLogsDeprecated [DEPRECATED] Get command logs - - Get logs for a specific command in a session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @param commandId - @return ToolboxAPIGetSessionCommandLogsDeprecatedRequest - - Deprecated - */ - GetSessionCommandLogsDeprecated(ctx context.Context, sandboxId string, sessionId string, commandId string) ToolboxAPIGetSessionCommandLogsDeprecatedRequest - - // GetSessionCommandLogsDeprecatedExecute executes the request - // @return string - // Deprecated - GetSessionCommandLogsDeprecatedExecute(r ToolboxAPIGetSessionCommandLogsDeprecatedRequest) (string, *http.Response, error) - - /* - GetSessionDeprecated [DEPRECATED] Get session - - Get session by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIGetSessionDeprecatedRequest - - Deprecated - */ - GetSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIGetSessionDeprecatedRequest - - // GetSessionDeprecatedExecute executes the request - // @return Session - // Deprecated - GetSessionDeprecatedExecute(r ToolboxAPIGetSessionDeprecatedRequest) (*Session, *http.Response, error) - - /* - GetUserHomeDirDeprecated [DEPRECATED] Get sandbox user home dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetUserHomeDirDeprecatedRequest - - Deprecated - */ - GetUserHomeDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetUserHomeDirDeprecatedRequest - - // GetUserHomeDirDeprecatedExecute executes the request - // @return UserHomeDirResponse - // Deprecated - GetUserHomeDirDeprecatedExecute(r ToolboxAPIGetUserHomeDirDeprecatedRequest) (*UserHomeDirResponse, *http.Response, error) - - /* - GetWindowsDeprecated [DEPRECATED] Get windows - - Get list of open windows - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetWindowsDeprecatedRequest - - Deprecated - */ - GetWindowsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetWindowsDeprecatedRequest - - // GetWindowsDeprecatedExecute executes the request - // @return WindowsResponse - // Deprecated - GetWindowsDeprecatedExecute(r ToolboxAPIGetWindowsDeprecatedRequest) (*WindowsResponse, *http.Response, error) - - /* - GetWorkDirDeprecated [DEPRECATED] Get sandbox work-dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetWorkDirDeprecatedRequest - - Deprecated - */ - GetWorkDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetWorkDirDeprecatedRequest - - // GetWorkDirDeprecatedExecute executes the request - // @return WorkDirResponse - // Deprecated - GetWorkDirDeprecatedExecute(r ToolboxAPIGetWorkDirDeprecatedRequest) (*WorkDirResponse, *http.Response, error) - - /* - GitAddFilesDeprecated [DEPRECATED] Add files - - Add files to git commit - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitAddFilesDeprecatedRequest - - Deprecated - */ - GitAddFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitAddFilesDeprecatedRequest - - // GitAddFilesDeprecatedExecute executes the request - // Deprecated - GitAddFilesDeprecatedExecute(r ToolboxAPIGitAddFilesDeprecatedRequest) (*http.Response, error) - - /* - GitCheckoutBranchDeprecated [DEPRECATED] Checkout branch - - Checkout branch or commit in git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCheckoutBranchDeprecatedRequest - - Deprecated - */ - GitCheckoutBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCheckoutBranchDeprecatedRequest - - // GitCheckoutBranchDeprecatedExecute executes the request - // Deprecated - GitCheckoutBranchDeprecatedExecute(r ToolboxAPIGitCheckoutBranchDeprecatedRequest) (*http.Response, error) - - /* - GitCloneRepositoryDeprecated [DEPRECATED] Clone repository - - Clone git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCloneRepositoryDeprecatedRequest - - Deprecated - */ - GitCloneRepositoryDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCloneRepositoryDeprecatedRequest - - // GitCloneRepositoryDeprecatedExecute executes the request - // Deprecated - GitCloneRepositoryDeprecatedExecute(r ToolboxAPIGitCloneRepositoryDeprecatedRequest) (*http.Response, error) - - /* - GitCommitChangesDeprecated [DEPRECATED] Commit changes - - Commit changes to git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCommitChangesDeprecatedRequest - - Deprecated - */ - GitCommitChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCommitChangesDeprecatedRequest - - // GitCommitChangesDeprecatedExecute executes the request - // @return GitCommitResponse - // Deprecated - GitCommitChangesDeprecatedExecute(r ToolboxAPIGitCommitChangesDeprecatedRequest) (*GitCommitResponse, *http.Response, error) - - /* - GitCreateBranchDeprecated [DEPRECATED] Create branch - - Create branch on git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCreateBranchDeprecatedRequest - - Deprecated - */ - GitCreateBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCreateBranchDeprecatedRequest - - // GitCreateBranchDeprecatedExecute executes the request - // Deprecated - GitCreateBranchDeprecatedExecute(r ToolboxAPIGitCreateBranchDeprecatedRequest) (*http.Response, error) - - /* - GitDeleteBranchDeprecated [DEPRECATED] Delete branch - - Delete branch on git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitDeleteBranchDeprecatedRequest - - Deprecated - */ - GitDeleteBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitDeleteBranchDeprecatedRequest - - // GitDeleteBranchDeprecatedExecute executes the request - // Deprecated - GitDeleteBranchDeprecatedExecute(r ToolboxAPIGitDeleteBranchDeprecatedRequest) (*http.Response, error) - - /* - GitGetHistoryDeprecated [DEPRECATED] Get commit history - - Get commit history from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitGetHistoryDeprecatedRequest - - Deprecated - */ - GitGetHistoryDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitGetHistoryDeprecatedRequest - - // GitGetHistoryDeprecatedExecute executes the request - // @return []GitCommitInfo - // Deprecated - GitGetHistoryDeprecatedExecute(r ToolboxAPIGitGetHistoryDeprecatedRequest) ([]GitCommitInfo, *http.Response, error) - - /* - GitGetStatusDeprecated [DEPRECATED] Get git status - - Get status from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitGetStatusDeprecatedRequest - - Deprecated - */ - GitGetStatusDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitGetStatusDeprecatedRequest - - // GitGetStatusDeprecatedExecute executes the request - // @return GitStatus - // Deprecated - GitGetStatusDeprecatedExecute(r ToolboxAPIGitGetStatusDeprecatedRequest) (*GitStatus, *http.Response, error) - - /* - GitListBranchesDeprecated [DEPRECATED] Get branch list - - Get branch list from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitListBranchesDeprecatedRequest - - Deprecated - */ - GitListBranchesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitListBranchesDeprecatedRequest - - // GitListBranchesDeprecatedExecute executes the request - // @return ListBranchResponse - // Deprecated - GitListBranchesDeprecatedExecute(r ToolboxAPIGitListBranchesDeprecatedRequest) (*ListBranchResponse, *http.Response, error) - - /* - GitPullChangesDeprecated [DEPRECATED] Pull changes - - Pull changes from remote - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitPullChangesDeprecatedRequest - - Deprecated - */ - GitPullChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitPullChangesDeprecatedRequest - - // GitPullChangesDeprecatedExecute executes the request - // Deprecated - GitPullChangesDeprecatedExecute(r ToolboxAPIGitPullChangesDeprecatedRequest) (*http.Response, error) - - /* - GitPushChangesDeprecated [DEPRECATED] Push changes - - Push changes to remote - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitPushChangesDeprecatedRequest - - Deprecated - */ - GitPushChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitPushChangesDeprecatedRequest - - // GitPushChangesDeprecatedExecute executes the request - // Deprecated - GitPushChangesDeprecatedExecute(r ToolboxAPIGitPushChangesDeprecatedRequest) (*http.Response, error) - - /* - ListFilesDeprecated [DEPRECATED] List files - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListFilesDeprecatedRequest - - Deprecated - */ - ListFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListFilesDeprecatedRequest - - // ListFilesDeprecatedExecute executes the request - // @return []FileInfo - // Deprecated - ListFilesDeprecatedExecute(r ToolboxAPIListFilesDeprecatedRequest) ([]FileInfo, *http.Response, error) - - /* - ListPTYSessionsDeprecated [DEPRECATED] List PTY sessions - - List all active PTY sessions in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListPTYSessionsDeprecatedRequest - - Deprecated - */ - ListPTYSessionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListPTYSessionsDeprecatedRequest - - // ListPTYSessionsDeprecatedExecute executes the request - // @return PtyListResponse - // Deprecated - ListPTYSessionsDeprecatedExecute(r ToolboxAPIListPTYSessionsDeprecatedRequest) (*PtyListResponse, *http.Response, error) - - /* - ListSessionsDeprecated [DEPRECATED] List sessions - - List all active sessions in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListSessionsDeprecatedRequest - - Deprecated - */ - ListSessionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListSessionsDeprecatedRequest - - // ListSessionsDeprecatedExecute executes the request - // @return []Session - // Deprecated - ListSessionsDeprecatedExecute(r ToolboxAPIListSessionsDeprecatedRequest) ([]Session, *http.Response, error) - - /* - LspCompletionsDeprecated [DEPRECATED] Get Lsp Completions - - The Completion request is sent from the client to the server to compute completion items at a given cursor position. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspCompletionsDeprecatedRequest - - Deprecated - */ - LspCompletionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspCompletionsDeprecatedRequest - - // LspCompletionsDeprecatedExecute executes the request - // @return CompletionList - // Deprecated - LspCompletionsDeprecatedExecute(r ToolboxAPILspCompletionsDeprecatedRequest) (*CompletionList, *http.Response, error) - - /* - LspDidCloseDeprecated [DEPRECATED] Call Lsp DidClose - - The document close notification is sent from the client to the server when the document got closed in the client. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDidCloseDeprecatedRequest - - Deprecated - */ - LspDidCloseDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDidCloseDeprecatedRequest - - // LspDidCloseDeprecatedExecute executes the request - // Deprecated - LspDidCloseDeprecatedExecute(r ToolboxAPILspDidCloseDeprecatedRequest) (*http.Response, error) - - /* - LspDidOpenDeprecated [DEPRECATED] Call Lsp DidOpen - - The document open notification is sent from the client to the server to signal newly opened text documents. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDidOpenDeprecatedRequest - - Deprecated - */ - LspDidOpenDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDidOpenDeprecatedRequest - - // LspDidOpenDeprecatedExecute executes the request - // Deprecated - LspDidOpenDeprecatedExecute(r ToolboxAPILspDidOpenDeprecatedRequest) (*http.Response, error) - - /* - LspDocumentSymbolsDeprecated [DEPRECATED] Call Lsp DocumentSymbols - - The document symbol request is sent from the client to the server. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDocumentSymbolsDeprecatedRequest - - Deprecated - */ - LspDocumentSymbolsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDocumentSymbolsDeprecatedRequest - - // LspDocumentSymbolsDeprecatedExecute executes the request - // @return []LspSymbol - // Deprecated - LspDocumentSymbolsDeprecatedExecute(r ToolboxAPILspDocumentSymbolsDeprecatedRequest) ([]LspSymbol, *http.Response, error) - - /* - LspStartDeprecated [DEPRECATED] Start Lsp server - - Start Lsp server process inside sandbox project - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspStartDeprecatedRequest - - Deprecated - */ - LspStartDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspStartDeprecatedRequest - - // LspStartDeprecatedExecute executes the request - // Deprecated - LspStartDeprecatedExecute(r ToolboxAPILspStartDeprecatedRequest) (*http.Response, error) - - /* - LspStopDeprecated [DEPRECATED] Stop Lsp server - - Stop Lsp server process inside sandbox project - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspStopDeprecatedRequest - - Deprecated - */ - LspStopDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspStopDeprecatedRequest - - // LspStopDeprecatedExecute executes the request - // Deprecated - LspStopDeprecatedExecute(r ToolboxAPILspStopDeprecatedRequest) (*http.Response, error) - - /* - LspWorkspaceSymbolsDeprecated [DEPRECATED] Call Lsp WorkspaceSymbols - - The workspace symbol request is sent from the client to the server to list project-wide symbols matching the query string. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspWorkspaceSymbolsDeprecatedRequest - - Deprecated - */ - LspWorkspaceSymbolsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest - - // LspWorkspaceSymbolsDeprecatedExecute executes the request - // @return []LspSymbol - // Deprecated - LspWorkspaceSymbolsDeprecatedExecute(r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) ([]LspSymbol, *http.Response, error) - - /* - MoveFileDeprecated [DEPRECATED] Move file - - Move file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIMoveFileDeprecatedRequest - - Deprecated - */ - MoveFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIMoveFileDeprecatedRequest - - // MoveFileDeprecatedExecute executes the request - // Deprecated - MoveFileDeprecatedExecute(r ToolboxAPIMoveFileDeprecatedRequest) (*http.Response, error) - - /* - MoveMouseDeprecated [DEPRECATED] Move mouse - - Move mouse cursor to specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIMoveMouseDeprecatedRequest - - Deprecated - */ - MoveMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIMoveMouseDeprecatedRequest - - // MoveMouseDeprecatedExecute executes the request - // @return MouseMoveResponse - // Deprecated - MoveMouseDeprecatedExecute(r ToolboxAPIMoveMouseDeprecatedRequest) (*MouseMoveResponse, *http.Response, error) - - /* - PressHotkeyDeprecated [DEPRECATED] Press hotkey - - Press a hotkey combination - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIPressHotkeyDeprecatedRequest - - Deprecated - */ - PressHotkeyDeprecated(ctx context.Context, sandboxId string) ToolboxAPIPressHotkeyDeprecatedRequest - - // PressHotkeyDeprecatedExecute executes the request - // Deprecated - PressHotkeyDeprecatedExecute(r ToolboxAPIPressHotkeyDeprecatedRequest) (*http.Response, error) - - /* - PressKeyDeprecated [DEPRECATED] Press key - - Press a key with optional modifiers - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIPressKeyDeprecatedRequest - - Deprecated - */ - PressKeyDeprecated(ctx context.Context, sandboxId string) ToolboxAPIPressKeyDeprecatedRequest - - // PressKeyDeprecatedExecute executes the request - // Deprecated - PressKeyDeprecatedExecute(r ToolboxAPIPressKeyDeprecatedRequest) (*http.Response, error) - - /* - ReplaceInFilesDeprecated [DEPRECATED] Replace in files - - Replace text/pattern in multiple files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIReplaceInFilesDeprecatedRequest - - Deprecated - */ - ReplaceInFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIReplaceInFilesDeprecatedRequest - - // ReplaceInFilesDeprecatedExecute executes the request - // @return []ReplaceResult - // Deprecated - ReplaceInFilesDeprecatedExecute(r ToolboxAPIReplaceInFilesDeprecatedRequest) ([]ReplaceResult, *http.Response, error) - - /* - ResizePTYSessionDeprecated [DEPRECATED] Resize PTY session - - Resize a PTY session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIResizePTYSessionDeprecatedRequest - - Deprecated - */ - ResizePTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIResizePTYSessionDeprecatedRequest - - // ResizePTYSessionDeprecatedExecute executes the request - // @return PtySessionInfo - // Deprecated - ResizePTYSessionDeprecatedExecute(r ToolboxAPIResizePTYSessionDeprecatedRequest) (*PtySessionInfo, *http.Response, error) - - /* - RestartProcessDeprecated [DEPRECATED] Restart process - - Restart a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIRestartProcessDeprecatedRequest - - Deprecated - */ - RestartProcessDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIRestartProcessDeprecatedRequest - - // RestartProcessDeprecatedExecute executes the request - // @return ProcessRestartResponse - // Deprecated - RestartProcessDeprecatedExecute(r ToolboxAPIRestartProcessDeprecatedRequest) (*ProcessRestartResponse, *http.Response, error) - - /* - ScrollMouseDeprecated [DEPRECATED] Scroll mouse - - Scroll mouse at specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIScrollMouseDeprecatedRequest - - Deprecated - */ - ScrollMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIScrollMouseDeprecatedRequest - - // ScrollMouseDeprecatedExecute executes the request - // @return MouseScrollResponse - // Deprecated - ScrollMouseDeprecatedExecute(r ToolboxAPIScrollMouseDeprecatedRequest) (*MouseScrollResponse, *http.Response, error) - - /* - SearchFilesDeprecated [DEPRECATED] Search files - - Search for files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPISearchFilesDeprecatedRequest - - Deprecated - */ - SearchFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPISearchFilesDeprecatedRequest - - // SearchFilesDeprecatedExecute executes the request - // @return SearchFilesResponse - // Deprecated - SearchFilesDeprecatedExecute(r ToolboxAPISearchFilesDeprecatedRequest) (*SearchFilesResponse, *http.Response, error) - - /* - SetFilePermissionsDeprecated [DEPRECATED] Set file permissions - - Set file owner/group/permissions inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPISetFilePermissionsDeprecatedRequest - - Deprecated - */ - SetFilePermissionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPISetFilePermissionsDeprecatedRequest - - // SetFilePermissionsDeprecatedExecute executes the request - // Deprecated - SetFilePermissionsDeprecatedExecute(r ToolboxAPISetFilePermissionsDeprecatedRequest) (*http.Response, error) - - /* - StartComputerUseDeprecated [DEPRECATED] Start computer use processes - - Start all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc) - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIStartComputerUseDeprecatedRequest - - Deprecated - */ - StartComputerUseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIStartComputerUseDeprecatedRequest - - // StartComputerUseDeprecatedExecute executes the request - // @return ComputerUseStartResponse - // Deprecated - StartComputerUseDeprecatedExecute(r ToolboxAPIStartComputerUseDeprecatedRequest) (*ComputerUseStartResponse, *http.Response, error) - - /* - StopComputerUseDeprecated [DEPRECATED] Stop computer use processes - - Stop all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc) - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIStopComputerUseDeprecatedRequest - - Deprecated - */ - StopComputerUseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIStopComputerUseDeprecatedRequest - - // StopComputerUseDeprecatedExecute executes the request - // @return ComputerUseStopResponse - // Deprecated - StopComputerUseDeprecatedExecute(r ToolboxAPIStopComputerUseDeprecatedRequest) (*ComputerUseStopResponse, *http.Response, error) - - /* - TakeCompressedRegionScreenshotDeprecated [DEPRECATED] Take compressed region screenshot - - Take a compressed screenshot of a specific region - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest - - Deprecated - */ - TakeCompressedRegionScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest - - // TakeCompressedRegionScreenshotDeprecatedExecute executes the request - // @return CompressedScreenshotResponse - // Deprecated - TakeCompressedRegionScreenshotDeprecatedExecute(r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) (*CompressedScreenshotResponse, *http.Response, error) - - /* - TakeCompressedScreenshotDeprecated [DEPRECATED] Take compressed screenshot - - Take a compressed screenshot with format, quality, and scale options - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeCompressedScreenshotDeprecatedRequest - - Deprecated - */ - TakeCompressedScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeCompressedScreenshotDeprecatedRequest - - // TakeCompressedScreenshotDeprecatedExecute executes the request - // @return CompressedScreenshotResponse - // Deprecated - TakeCompressedScreenshotDeprecatedExecute(r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) (*CompressedScreenshotResponse, *http.Response, error) - - /* - TakeRegionScreenshotDeprecated [DEPRECATED] Take region screenshot - - Take a screenshot of a specific region - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeRegionScreenshotDeprecatedRequest - - Deprecated - */ - TakeRegionScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeRegionScreenshotDeprecatedRequest - - // TakeRegionScreenshotDeprecatedExecute executes the request - // @return RegionScreenshotResponse - // Deprecated - TakeRegionScreenshotDeprecatedExecute(r ToolboxAPITakeRegionScreenshotDeprecatedRequest) (*RegionScreenshotResponse, *http.Response, error) - - /* - TakeScreenshotDeprecated [DEPRECATED] Take screenshot - - Take a screenshot of the entire screen - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeScreenshotDeprecatedRequest - - Deprecated - */ - TakeScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeScreenshotDeprecatedRequest - - // TakeScreenshotDeprecatedExecute executes the request - // @return ScreenshotResponse - // Deprecated - TakeScreenshotDeprecatedExecute(r ToolboxAPITakeScreenshotDeprecatedRequest) (*ScreenshotResponse, *http.Response, error) - - /* - TypeTextDeprecated [DEPRECATED] Type text - - Type text using keyboard - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITypeTextDeprecatedRequest - - Deprecated - */ - TypeTextDeprecated(ctx context.Context, sandboxId string) ToolboxAPITypeTextDeprecatedRequest - - // TypeTextDeprecatedExecute executes the request - // Deprecated - TypeTextDeprecatedExecute(r ToolboxAPITypeTextDeprecatedRequest) (*http.Response, error) - - /* - UploadFileDeprecated [DEPRECATED] Upload file - - Upload file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIUploadFileDeprecatedRequest - - Deprecated - */ - UploadFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIUploadFileDeprecatedRequest - - // UploadFileDeprecatedExecute executes the request - // Deprecated - UploadFileDeprecatedExecute(r ToolboxAPIUploadFileDeprecatedRequest) (*http.Response, error) - - /* - UploadFilesDeprecated [DEPRECATED] Upload multiple files - - Upload multiple files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIUploadFilesDeprecatedRequest - - Deprecated - */ - UploadFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIUploadFilesDeprecatedRequest - - // UploadFilesDeprecatedExecute executes the request - // Deprecated - UploadFilesDeprecatedExecute(r ToolboxAPIUploadFilesDeprecatedRequest) (*http.Response, error) -} - -// ToolboxAPIService ToolboxAPI service -type ToolboxAPIService service - -type ToolboxAPIClickMouseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - mouseClickRequest *MouseClickRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIClickMouseDeprecatedRequest) MouseClickRequest(mouseClickRequest MouseClickRequest) ToolboxAPIClickMouseDeprecatedRequest { - r.mouseClickRequest = &mouseClickRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIClickMouseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIClickMouseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIClickMouseDeprecatedRequest) Execute() (*MouseClickResponse, *http.Response, error) { - return r.ApiService.ClickMouseDeprecatedExecute(r) -} - -/* -ClickMouseDeprecated [DEPRECATED] Click mouse - -Click mouse at specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIClickMouseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ClickMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIClickMouseDeprecatedRequest { - return ToolboxAPIClickMouseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MouseClickResponse -// Deprecated -func (a *ToolboxAPIService) ClickMouseDeprecatedExecute(r ToolboxAPIClickMouseDeprecatedRequest) (*MouseClickResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MouseClickResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ClickMouseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/mouse/click" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.mouseClickRequest == nil { - return localVarReturnValue, nil, reportError("mouseClickRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.mouseClickRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPICreateFolderDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - mode *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPICreateFolderDeprecatedRequest) Path(path string) ToolboxAPICreateFolderDeprecatedRequest { - r.path = &path - return r -} - -func (r ToolboxAPICreateFolderDeprecatedRequest) Mode(mode string) ToolboxAPICreateFolderDeprecatedRequest { - r.mode = &mode - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPICreateFolderDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPICreateFolderDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPICreateFolderDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.CreateFolderDeprecatedExecute(r) -} - -/* -CreateFolderDeprecated [DEPRECATED] Create folder - -Create folder inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreateFolderDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) CreateFolderDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreateFolderDeprecatedRequest { - return ToolboxAPICreateFolderDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) CreateFolderDeprecatedExecute(r ToolboxAPICreateFolderDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.CreateFolderDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/folder" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return nil, reportError("path is required and must be specified") - } - if r.mode == nil { - return nil, reportError("mode is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "mode", r.mode, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPICreatePTYSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - ptyCreateRequest *PtyCreateRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPICreatePTYSessionDeprecatedRequest) PtyCreateRequest(ptyCreateRequest PtyCreateRequest) ToolboxAPICreatePTYSessionDeprecatedRequest { - r.ptyCreateRequest = &ptyCreateRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPICreatePTYSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPICreatePTYSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPICreatePTYSessionDeprecatedRequest) Execute() (*PtyCreateResponse, *http.Response, error) { - return r.ApiService.CreatePTYSessionDeprecatedExecute(r) -} - -/* -CreatePTYSessionDeprecated [DEPRECATED] Create PTY session - -Create a new PTY session in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreatePTYSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) CreatePTYSessionDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreatePTYSessionDeprecatedRequest { - return ToolboxAPICreatePTYSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return PtyCreateResponse -// Deprecated -func (a *ToolboxAPIService) CreatePTYSessionDeprecatedExecute(r ToolboxAPICreatePTYSessionDeprecatedRequest) (*PtyCreateResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PtyCreateResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.CreatePTYSessionDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/pty" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.ptyCreateRequest == nil { - return localVarReturnValue, nil, reportError("ptyCreateRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.ptyCreateRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPICreateSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - createSessionRequest *CreateSessionRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPICreateSessionDeprecatedRequest) CreateSessionRequest(createSessionRequest CreateSessionRequest) ToolboxAPICreateSessionDeprecatedRequest { - r.createSessionRequest = &createSessionRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPICreateSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPICreateSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPICreateSessionDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.CreateSessionDeprecatedExecute(r) -} - -/* -CreateSessionDeprecated [DEPRECATED] Create session - -Create a new session in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPICreateSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) CreateSessionDeprecated(ctx context.Context, sandboxId string) ToolboxAPICreateSessionDeprecatedRequest { - return ToolboxAPICreateSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) CreateSessionDeprecatedExecute(r ToolboxAPICreateSessionDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.CreateSessionDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.createSessionRequest == nil { - return nil, reportError("createSessionRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.createSessionRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIDeleteFileDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string - recursive *bool -} - -func (r ToolboxAPIDeleteFileDeprecatedRequest) Path(path string) ToolboxAPIDeleteFileDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDeleteFileDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDeleteFileDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDeleteFileDeprecatedRequest) Recursive(recursive bool) ToolboxAPIDeleteFileDeprecatedRequest { - r.recursive = &recursive - return r -} - -func (r ToolboxAPIDeleteFileDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.DeleteFileDeprecatedExecute(r) -} - -/* -DeleteFileDeprecated [DEPRECATED] Delete file - -Delete file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDeleteFileDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DeleteFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDeleteFileDeprecatedRequest { - return ToolboxAPIDeleteFileDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) DeleteFileDeprecatedExecute(r ToolboxAPIDeleteFileDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DeleteFileDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return nil, reportError("path is required and must be specified") - } - - if r.recursive != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "recursive", r.recursive, "form", "") - } - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIDeletePTYSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDeletePTYSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDeletePTYSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDeletePTYSessionDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.DeletePTYSessionDeprecatedExecute(r) -} - -/* -DeletePTYSessionDeprecated [DEPRECATED] Delete PTY session - -Delete a PTY session and terminate the associated process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIDeletePTYSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DeletePTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIDeletePTYSessionDeprecatedRequest { - return ToolboxAPIDeletePTYSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) DeletePTYSessionDeprecatedExecute(r ToolboxAPIDeletePTYSessionDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DeletePTYSessionDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/pty/{sessionId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIDeleteSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDeleteSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDeleteSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDeleteSessionDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.DeleteSessionDeprecatedExecute(r) -} - -/* -DeleteSessionDeprecated [DEPRECATED] Delete session - -Delete a specific session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIDeleteSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DeleteSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIDeleteSessionDeprecatedRequest { - return ToolboxAPIDeleteSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) DeleteSessionDeprecatedExecute(r ToolboxAPIDeleteSessionDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DeleteSessionDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session/{sessionId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIDownloadFileDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIDownloadFileDeprecatedRequest) Path(path string) ToolboxAPIDownloadFileDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDownloadFileDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDownloadFileDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDownloadFileDeprecatedRequest) Execute() (*os.File, *http.Response, error) { - return r.ApiService.DownloadFileDeprecatedExecute(r) -} - -/* -DownloadFileDeprecated [DEPRECATED] Download file - -Download file from sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDownloadFileDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DownloadFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDownloadFileDeprecatedRequest { - return ToolboxAPIDownloadFileDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return *os.File -// Deprecated -func (a *ToolboxAPIService) DownloadFileDeprecatedExecute(r ToolboxAPIDownloadFileDeprecatedRequest) (*os.File, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *os.File - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DownloadFileDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/download" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIDownloadFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - downloadFiles *DownloadFiles - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIDownloadFilesDeprecatedRequest) DownloadFiles(downloadFiles DownloadFiles) ToolboxAPIDownloadFilesDeprecatedRequest { - r.downloadFiles = &downloadFiles - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDownloadFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDownloadFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDownloadFilesDeprecatedRequest) Execute() (*os.File, *http.Response, error) { - return r.ApiService.DownloadFilesDeprecatedExecute(r) -} - -/* -DownloadFilesDeprecated [DEPRECATED] Download multiple files - -Streams back a multipart/form-data bundle of the requested paths - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDownloadFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DownloadFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDownloadFilesDeprecatedRequest { - return ToolboxAPIDownloadFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return *os.File -// Deprecated -func (a *ToolboxAPIService) DownloadFilesDeprecatedExecute(r ToolboxAPIDownloadFilesDeprecatedRequest) (*os.File, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *os.File - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DownloadFilesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/bulk-download" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.downloadFiles == nil { - return localVarReturnValue, nil, reportError("downloadFiles is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.downloadFiles - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIDragMouseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - mouseDragRequest *MouseDragRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIDragMouseDeprecatedRequest) MouseDragRequest(mouseDragRequest MouseDragRequest) ToolboxAPIDragMouseDeprecatedRequest { - r.mouseDragRequest = &mouseDragRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIDragMouseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIDragMouseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIDragMouseDeprecatedRequest) Execute() (*MouseDragResponse, *http.Response, error) { - return r.ApiService.DragMouseDeprecatedExecute(r) -} - -/* -DragMouseDeprecated [DEPRECATED] Drag mouse - -Drag mouse from start to end coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIDragMouseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) DragMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIDragMouseDeprecatedRequest { - return ToolboxAPIDragMouseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MouseDragResponse -// Deprecated -func (a *ToolboxAPIService) DragMouseDeprecatedExecute(r ToolboxAPIDragMouseDeprecatedRequest) (*MouseDragResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MouseDragResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.DragMouseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/mouse/drag" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.mouseDragRequest == nil { - return localVarReturnValue, nil, reportError("mouseDragRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.mouseDragRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIExecuteCommandDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - executeRequest *ExecuteRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIExecuteCommandDeprecatedRequest) ExecuteRequest(executeRequest ExecuteRequest) ToolboxAPIExecuteCommandDeprecatedRequest { - r.executeRequest = &executeRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIExecuteCommandDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIExecuteCommandDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIExecuteCommandDeprecatedRequest) Execute() (*ExecuteResponse, *http.Response, error) { - return r.ApiService.ExecuteCommandDeprecatedExecute(r) -} - -/* -ExecuteCommandDeprecated [DEPRECATED] Execute command - -Execute command synchronously inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIExecuteCommandDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ExecuteCommandDeprecated(ctx context.Context, sandboxId string) ToolboxAPIExecuteCommandDeprecatedRequest { - return ToolboxAPIExecuteCommandDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ExecuteResponse -// Deprecated -func (a *ToolboxAPIService) ExecuteCommandDeprecatedExecute(r ToolboxAPIExecuteCommandDeprecatedRequest) (*ExecuteResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ExecuteResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ExecuteCommandDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/execute" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.executeRequest == nil { - return localVarReturnValue, nil, reportError("executeRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.executeRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIExecuteSessionCommandDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - sessionExecuteRequest *SessionExecuteRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIExecuteSessionCommandDeprecatedRequest) SessionExecuteRequest(sessionExecuteRequest SessionExecuteRequest) ToolboxAPIExecuteSessionCommandDeprecatedRequest { - r.sessionExecuteRequest = &sessionExecuteRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIExecuteSessionCommandDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIExecuteSessionCommandDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIExecuteSessionCommandDeprecatedRequest) Execute() (*SessionExecuteResponse, *http.Response, error) { - return r.ApiService.ExecuteSessionCommandDeprecatedExecute(r) -} - -/* -ExecuteSessionCommandDeprecated [DEPRECATED] Execute command in session - -Execute a command in a specific session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIExecuteSessionCommandDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ExecuteSessionCommandDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIExecuteSessionCommandDeprecatedRequest { - return ToolboxAPIExecuteSessionCommandDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// @return SessionExecuteResponse -// Deprecated -func (a *ToolboxAPIService) ExecuteSessionCommandDeprecatedExecute(r ToolboxAPIExecuteSessionCommandDeprecatedRequest) (*SessionExecuteResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SessionExecuteResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ExecuteSessionCommandDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session/{sessionId}/exec" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.sessionExecuteRequest == nil { - return localVarReturnValue, nil, reportError("sessionExecuteRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.sessionExecuteRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIFindInFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - pattern *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIFindInFilesDeprecatedRequest) Path(path string) ToolboxAPIFindInFilesDeprecatedRequest { - r.path = &path - return r -} - -func (r ToolboxAPIFindInFilesDeprecatedRequest) Pattern(pattern string) ToolboxAPIFindInFilesDeprecatedRequest { - r.pattern = &pattern - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIFindInFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIFindInFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIFindInFilesDeprecatedRequest) Execute() ([]Match, *http.Response, error) { - return r.ApiService.FindInFilesDeprecatedExecute(r) -} - -/* -FindInFilesDeprecated [DEPRECATED] Search for text/pattern in files - -Search for text/pattern inside sandbox files - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIFindInFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) FindInFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIFindInFilesDeprecatedRequest { - return ToolboxAPIFindInFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []Match -// Deprecated -func (a *ToolboxAPIService) FindInFilesDeprecatedExecute(r ToolboxAPIFindInFilesDeprecatedRequest) ([]Match, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []Match - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.FindInFilesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/find" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - if r.pattern == nil { - return localVarReturnValue, nil, reportError("pattern is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "pattern", r.pattern, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetComputerUseStatusDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetComputerUseStatusDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetComputerUseStatusDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetComputerUseStatusDeprecatedRequest) Execute() (*ComputerUseStatusResponse, *http.Response, error) { - return r.ApiService.GetComputerUseStatusDeprecatedExecute(r) -} - -/* -GetComputerUseStatusDeprecated [DEPRECATED] Get computer use status - -Get status of all VNC desktop processes - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetComputerUseStatusDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetComputerUseStatusDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetComputerUseStatusDeprecatedRequest { - return ToolboxAPIGetComputerUseStatusDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ComputerUseStatusResponse -// Deprecated -func (a *ToolboxAPIService) GetComputerUseStatusDeprecatedExecute(r ToolboxAPIGetComputerUseStatusDeprecatedRequest) (*ComputerUseStatusResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ComputerUseStatusResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetComputerUseStatusDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/status" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetDisplayInfoDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetDisplayInfoDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetDisplayInfoDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetDisplayInfoDeprecatedRequest) Execute() (*DisplayInfoResponse, *http.Response, error) { - return r.ApiService.GetDisplayInfoDeprecatedExecute(r) -} - -/* -GetDisplayInfoDeprecated [DEPRECATED] Get display info - -Get information about displays - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetDisplayInfoDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetDisplayInfoDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetDisplayInfoDeprecatedRequest { - return ToolboxAPIGetDisplayInfoDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return DisplayInfoResponse -// Deprecated -func (a *ToolboxAPIService) GetDisplayInfoDeprecatedExecute(r ToolboxAPIGetDisplayInfoDeprecatedRequest) (*DisplayInfoResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *DisplayInfoResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetDisplayInfoDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/display/info" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetFileInfoDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGetFileInfoDeprecatedRequest) Path(path string) ToolboxAPIGetFileInfoDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetFileInfoDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetFileInfoDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetFileInfoDeprecatedRequest) Execute() (*FileInfo, *http.Response, error) { - return r.ApiService.GetFileInfoDeprecatedExecute(r) -} - -/* -GetFileInfoDeprecated [DEPRECATED] Get file info - -Get file info inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetFileInfoDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetFileInfoDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetFileInfoDeprecatedRequest { - return ToolboxAPIGetFileInfoDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return FileInfo -// Deprecated -func (a *ToolboxAPIService) GetFileInfoDeprecatedExecute(r ToolboxAPIGetFileInfoDeprecatedRequest) (*FileInfo, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *FileInfo - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetFileInfoDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/info" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetMousePositionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetMousePositionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetMousePositionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetMousePositionDeprecatedRequest) Execute() (*MousePosition, *http.Response, error) { - return r.ApiService.GetMousePositionDeprecatedExecute(r) -} - -/* -GetMousePositionDeprecated [DEPRECATED] Get mouse position - -Get current mouse cursor position - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetMousePositionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetMousePositionDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetMousePositionDeprecatedRequest { - return ToolboxAPIGetMousePositionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MousePosition -// Deprecated -func (a *ToolboxAPIService) GetMousePositionDeprecatedExecute(r ToolboxAPIGetMousePositionDeprecatedRequest) (*MousePosition, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MousePosition - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetMousePositionDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/mouse/position" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetPTYSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetPTYSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetPTYSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetPTYSessionDeprecatedRequest) Execute() (*PtySessionInfo, *http.Response, error) { - return r.ApiService.GetPTYSessionDeprecatedExecute(r) -} - -/* -GetPTYSessionDeprecated [DEPRECATED] Get PTY session - -Get PTY session information by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIGetPTYSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetPTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIGetPTYSessionDeprecatedRequest { - return ToolboxAPIGetPTYSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// @return PtySessionInfo -// Deprecated -func (a *ToolboxAPIService) GetPTYSessionDeprecatedExecute(r ToolboxAPIGetPTYSessionDeprecatedRequest) (*PtySessionInfo, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PtySessionInfo - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetPTYSessionDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/pty/{sessionId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetProcessErrorsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - processName string - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetProcessErrorsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetProcessErrorsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetProcessErrorsDeprecatedRequest) Execute() (*ProcessErrorsResponse, *http.Response, error) { - return r.ApiService.GetProcessErrorsDeprecatedExecute(r) -} - -/* -GetProcessErrorsDeprecated [DEPRECATED] Get process errors - -Get error logs for a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessErrorsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetProcessErrorsDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessErrorsDeprecatedRequest { - return ToolboxAPIGetProcessErrorsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - processName: processName, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ProcessErrorsResponse -// Deprecated -func (a *ToolboxAPIService) GetProcessErrorsDeprecatedExecute(r ToolboxAPIGetProcessErrorsDeprecatedRequest) (*ProcessErrorsResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ProcessErrorsResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetProcessErrorsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/errors" - localVarPath = strings.Replace(localVarPath, "{"+"processName"+"}", url.PathEscape(parameterValueToString(r.processName, "processName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetProcessLogsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - processName string - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetProcessLogsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetProcessLogsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetProcessLogsDeprecatedRequest) Execute() (*ProcessLogsResponse, *http.Response, error) { - return r.ApiService.GetProcessLogsDeprecatedExecute(r) -} - -/* -GetProcessLogsDeprecated [DEPRECATED] Get process logs - -Get logs for a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessLogsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetProcessLogsDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessLogsDeprecatedRequest { - return ToolboxAPIGetProcessLogsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - processName: processName, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ProcessLogsResponse -// Deprecated -func (a *ToolboxAPIService) GetProcessLogsDeprecatedExecute(r ToolboxAPIGetProcessLogsDeprecatedRequest) (*ProcessLogsResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ProcessLogsResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetProcessLogsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/logs" - localVarPath = strings.Replace(localVarPath, "{"+"processName"+"}", url.PathEscape(parameterValueToString(r.processName, "processName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetProcessStatusDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - processName string - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetProcessStatusDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetProcessStatusDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetProcessStatusDeprecatedRequest) Execute() (*ProcessStatusResponse, *http.Response, error) { - return r.ApiService.GetProcessStatusDeprecatedExecute(r) -} - -/* -GetProcessStatusDeprecated [DEPRECATED] Get process status - -Get status of a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIGetProcessStatusDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetProcessStatusDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIGetProcessStatusDeprecatedRequest { - return ToolboxAPIGetProcessStatusDeprecatedRequest{ - ApiService: a, - ctx: ctx, - processName: processName, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ProcessStatusResponse -// Deprecated -func (a *ToolboxAPIService) GetProcessStatusDeprecatedExecute(r ToolboxAPIGetProcessStatusDeprecatedRequest) (*ProcessStatusResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ProcessStatusResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetProcessStatusDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/status" - localVarPath = strings.Replace(localVarPath, "{"+"processName"+"}", url.PathEscape(parameterValueToString(r.processName, "processName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetProjectDirDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetProjectDirDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetProjectDirDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetProjectDirDeprecatedRequest) Execute() (*ProjectDirResponse, *http.Response, error) { - return r.ApiService.GetProjectDirDeprecatedExecute(r) -} - -/* -GetProjectDirDeprecated [DEPRECATED] Get sandbox project dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetProjectDirDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetProjectDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetProjectDirDeprecatedRequest { - return ToolboxAPIGetProjectDirDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ProjectDirResponse -// Deprecated -func (a *ToolboxAPIService) GetProjectDirDeprecatedExecute(r ToolboxAPIGetProjectDirDeprecatedRequest) (*ProjectDirResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ProjectDirResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetProjectDirDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/project-dir" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetSessionCommandDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - commandId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetSessionCommandDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetSessionCommandDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetSessionCommandDeprecatedRequest) Execute() (*Command, *http.Response, error) { - return r.ApiService.GetSessionCommandDeprecatedExecute(r) -} - -/* -GetSessionCommandDeprecated [DEPRECATED] Get session command - -Get session command by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @param commandId - @return ToolboxAPIGetSessionCommandDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetSessionCommandDeprecated(ctx context.Context, sandboxId string, sessionId string, commandId string) ToolboxAPIGetSessionCommandDeprecatedRequest { - return ToolboxAPIGetSessionCommandDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - commandId: commandId, - } -} - -// Execute executes the request -// @return Command -// Deprecated -func (a *ToolboxAPIService) GetSessionCommandDeprecatedExecute(r ToolboxAPIGetSessionCommandDeprecatedRequest) (*Command, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Command - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetSessionCommandDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session/{sessionId}/command/{commandId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"commandId"+"}", url.PathEscape(parameterValueToString(r.commandId, "commandId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetSessionCommandLogsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - commandId string - xBoxLiteOrganizationID *string - follow *bool -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetSessionCommandLogsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetSessionCommandLogsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Whether to stream the logs -func (r ToolboxAPIGetSessionCommandLogsDeprecatedRequest) Follow(follow bool) ToolboxAPIGetSessionCommandLogsDeprecatedRequest { - r.follow = &follow - return r -} - -func (r ToolboxAPIGetSessionCommandLogsDeprecatedRequest) Execute() (string, *http.Response, error) { - return r.ApiService.GetSessionCommandLogsDeprecatedExecute(r) -} - -/* -GetSessionCommandLogsDeprecated [DEPRECATED] Get command logs - -Get logs for a specific command in a session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @param commandId - @return ToolboxAPIGetSessionCommandLogsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetSessionCommandLogsDeprecated(ctx context.Context, sandboxId string, sessionId string, commandId string) ToolboxAPIGetSessionCommandLogsDeprecatedRequest { - return ToolboxAPIGetSessionCommandLogsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - commandId: commandId, - } -} - -// Execute executes the request -// @return string -// Deprecated -func (a *ToolboxAPIService) GetSessionCommandLogsDeprecatedExecute(r ToolboxAPIGetSessionCommandLogsDeprecatedRequest) (string, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue string - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetSessionCommandLogsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session/{sessionId}/command/{commandId}/logs" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"commandId"+"}", url.PathEscape(parameterValueToString(r.commandId, "commandId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.follow != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "follow", r.follow, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"text/plain"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetSessionDeprecatedRequest) Execute() (*Session, *http.Response, error) { - return r.ApiService.GetSessionDeprecatedExecute(r) -} - -/* -GetSessionDeprecated [DEPRECATED] Get session - -Get session by ID - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIGetSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIGetSessionDeprecatedRequest { - return ToolboxAPIGetSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// @return Session -// Deprecated -func (a *ToolboxAPIService) GetSessionDeprecatedExecute(r ToolboxAPIGetSessionDeprecatedRequest) (*Session, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Session - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetSessionDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session/{sessionId}" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetUserHomeDirDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetUserHomeDirDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetUserHomeDirDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetUserHomeDirDeprecatedRequest) Execute() (*UserHomeDirResponse, *http.Response, error) { - return r.ApiService.GetUserHomeDirDeprecatedExecute(r) -} - -/* -GetUserHomeDirDeprecated [DEPRECATED] Get sandbox user home dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetUserHomeDirDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetUserHomeDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetUserHomeDirDeprecatedRequest { - return ToolboxAPIGetUserHomeDirDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return UserHomeDirResponse -// Deprecated -func (a *ToolboxAPIService) GetUserHomeDirDeprecatedExecute(r ToolboxAPIGetUserHomeDirDeprecatedRequest) (*UserHomeDirResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *UserHomeDirResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetUserHomeDirDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/user-home-dir" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetWindowsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetWindowsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetWindowsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetWindowsDeprecatedRequest) Execute() (*WindowsResponse, *http.Response, error) { - return r.ApiService.GetWindowsDeprecatedExecute(r) -} - -/* -GetWindowsDeprecated [DEPRECATED] Get windows - -Get list of open windows - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetWindowsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetWindowsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetWindowsDeprecatedRequest { - return ToolboxAPIGetWindowsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return WindowsResponse -// Deprecated -func (a *ToolboxAPIService) GetWindowsDeprecatedExecute(r ToolboxAPIGetWindowsDeprecatedRequest) (*WindowsResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *WindowsResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetWindowsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/display/windows" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGetWorkDirDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGetWorkDirDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGetWorkDirDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGetWorkDirDeprecatedRequest) Execute() (*WorkDirResponse, *http.Response, error) { - return r.ApiService.GetWorkDirDeprecatedExecute(r) -} - -/* -GetWorkDirDeprecated [DEPRECATED] Get sandbox work-dir - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGetWorkDirDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GetWorkDirDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGetWorkDirDeprecatedRequest { - return ToolboxAPIGetWorkDirDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return WorkDirResponse -// Deprecated -func (a *ToolboxAPIService) GetWorkDirDeprecatedExecute(r ToolboxAPIGetWorkDirDeprecatedRequest) (*WorkDirResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *WorkDirResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GetWorkDirDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/work-dir" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGitAddFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitAddRequest *GitAddRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitAddFilesDeprecatedRequest) GitAddRequest(gitAddRequest GitAddRequest) ToolboxAPIGitAddFilesDeprecatedRequest { - r.gitAddRequest = &gitAddRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitAddFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitAddFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitAddFilesDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitAddFilesDeprecatedExecute(r) -} - -/* -GitAddFilesDeprecated [DEPRECATED] Add files - -Add files to git commit - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitAddFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitAddFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitAddFilesDeprecatedRequest { - return ToolboxAPIGitAddFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitAddFilesDeprecatedExecute(r ToolboxAPIGitAddFilesDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitAddFilesDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/add" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitAddRequest == nil { - return nil, reportError("gitAddRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitAddRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitCheckoutBranchDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitCheckoutRequest *GitCheckoutRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitCheckoutBranchDeprecatedRequest) GitCheckoutRequest(gitCheckoutRequest GitCheckoutRequest) ToolboxAPIGitCheckoutBranchDeprecatedRequest { - r.gitCheckoutRequest = &gitCheckoutRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitCheckoutBranchDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitCheckoutBranchDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitCheckoutBranchDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitCheckoutBranchDeprecatedExecute(r) -} - -/* -GitCheckoutBranchDeprecated [DEPRECATED] Checkout branch - -Checkout branch or commit in git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCheckoutBranchDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitCheckoutBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCheckoutBranchDeprecatedRequest { - return ToolboxAPIGitCheckoutBranchDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitCheckoutBranchDeprecatedExecute(r ToolboxAPIGitCheckoutBranchDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitCheckoutBranchDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/checkout" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitCheckoutRequest == nil { - return nil, reportError("gitCheckoutRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitCheckoutRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitCloneRepositoryDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitCloneRequest *GitCloneRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitCloneRepositoryDeprecatedRequest) GitCloneRequest(gitCloneRequest GitCloneRequest) ToolboxAPIGitCloneRepositoryDeprecatedRequest { - r.gitCloneRequest = &gitCloneRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitCloneRepositoryDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitCloneRepositoryDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitCloneRepositoryDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitCloneRepositoryDeprecatedExecute(r) -} - -/* -GitCloneRepositoryDeprecated [DEPRECATED] Clone repository - -Clone git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCloneRepositoryDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitCloneRepositoryDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCloneRepositoryDeprecatedRequest { - return ToolboxAPIGitCloneRepositoryDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitCloneRepositoryDeprecatedExecute(r ToolboxAPIGitCloneRepositoryDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitCloneRepositoryDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/clone" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitCloneRequest == nil { - return nil, reportError("gitCloneRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitCloneRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitCommitChangesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitCommitRequest *GitCommitRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitCommitChangesDeprecatedRequest) GitCommitRequest(gitCommitRequest GitCommitRequest) ToolboxAPIGitCommitChangesDeprecatedRequest { - r.gitCommitRequest = &gitCommitRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitCommitChangesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitCommitChangesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitCommitChangesDeprecatedRequest) Execute() (*GitCommitResponse, *http.Response, error) { - return r.ApiService.GitCommitChangesDeprecatedExecute(r) -} - -/* -GitCommitChangesDeprecated [DEPRECATED] Commit changes - -Commit changes to git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCommitChangesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitCommitChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCommitChangesDeprecatedRequest { - return ToolboxAPIGitCommitChangesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return GitCommitResponse -// Deprecated -func (a *ToolboxAPIService) GitCommitChangesDeprecatedExecute(r ToolboxAPIGitCommitChangesDeprecatedRequest) (*GitCommitResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *GitCommitResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitCommitChangesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/commit" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitCommitRequest == nil { - return localVarReturnValue, nil, reportError("gitCommitRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitCommitRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGitCreateBranchDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitBranchRequest *GitBranchRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitCreateBranchDeprecatedRequest) GitBranchRequest(gitBranchRequest GitBranchRequest) ToolboxAPIGitCreateBranchDeprecatedRequest { - r.gitBranchRequest = &gitBranchRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitCreateBranchDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitCreateBranchDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitCreateBranchDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitCreateBranchDeprecatedExecute(r) -} - -/* -GitCreateBranchDeprecated [DEPRECATED] Create branch - -Create branch on git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitCreateBranchDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitCreateBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitCreateBranchDeprecatedRequest { - return ToolboxAPIGitCreateBranchDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitCreateBranchDeprecatedExecute(r ToolboxAPIGitCreateBranchDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitCreateBranchDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/branches" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitBranchRequest == nil { - return nil, reportError("gitBranchRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitBranchRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitDeleteBranchDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitDeleteBranchRequest *GitDeleteBranchRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitDeleteBranchDeprecatedRequest) GitDeleteBranchRequest(gitDeleteBranchRequest GitDeleteBranchRequest) ToolboxAPIGitDeleteBranchDeprecatedRequest { - r.gitDeleteBranchRequest = &gitDeleteBranchRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitDeleteBranchDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitDeleteBranchDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitDeleteBranchDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitDeleteBranchDeprecatedExecute(r) -} - -/* -GitDeleteBranchDeprecated [DEPRECATED] Delete branch - -Delete branch on git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitDeleteBranchDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitDeleteBranchDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitDeleteBranchDeprecatedRequest { - return ToolboxAPIGitDeleteBranchDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitDeleteBranchDeprecatedExecute(r ToolboxAPIGitDeleteBranchDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitDeleteBranchDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/branches" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitDeleteBranchRequest == nil { - return nil, reportError("gitDeleteBranchRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitDeleteBranchRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitGetHistoryDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitGetHistoryDeprecatedRequest) Path(path string) ToolboxAPIGitGetHistoryDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitGetHistoryDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitGetHistoryDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitGetHistoryDeprecatedRequest) Execute() ([]GitCommitInfo, *http.Response, error) { - return r.ApiService.GitGetHistoryDeprecatedExecute(r) -} - -/* -GitGetHistoryDeprecated [DEPRECATED] Get commit history - -Get commit history from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitGetHistoryDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitGetHistoryDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitGetHistoryDeprecatedRequest { - return ToolboxAPIGitGetHistoryDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []GitCommitInfo -// Deprecated -func (a *ToolboxAPIService) GitGetHistoryDeprecatedExecute(r ToolboxAPIGitGetHistoryDeprecatedRequest) ([]GitCommitInfo, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []GitCommitInfo - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitGetHistoryDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/history" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGitGetStatusDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitGetStatusDeprecatedRequest) Path(path string) ToolboxAPIGitGetStatusDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitGetStatusDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitGetStatusDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitGetStatusDeprecatedRequest) Execute() (*GitStatus, *http.Response, error) { - return r.ApiService.GitGetStatusDeprecatedExecute(r) -} - -/* -GitGetStatusDeprecated [DEPRECATED] Get git status - -Get status from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitGetStatusDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitGetStatusDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitGetStatusDeprecatedRequest { - return ToolboxAPIGitGetStatusDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return GitStatus -// Deprecated -func (a *ToolboxAPIService) GitGetStatusDeprecatedExecute(r ToolboxAPIGitGetStatusDeprecatedRequest) (*GitStatus, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *GitStatus - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitGetStatusDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/status" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGitListBranchesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitListBranchesDeprecatedRequest) Path(path string) ToolboxAPIGitListBranchesDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitListBranchesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitListBranchesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitListBranchesDeprecatedRequest) Execute() (*ListBranchResponse, *http.Response, error) { - return r.ApiService.GitListBranchesDeprecatedExecute(r) -} - -/* -GitListBranchesDeprecated [DEPRECATED] Get branch list - -Get branch list from git repository - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitListBranchesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitListBranchesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitListBranchesDeprecatedRequest { - return ToolboxAPIGitListBranchesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ListBranchResponse -// Deprecated -func (a *ToolboxAPIService) GitListBranchesDeprecatedExecute(r ToolboxAPIGitListBranchesDeprecatedRequest) (*ListBranchResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ListBranchResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitListBranchesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/branches" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIGitPullChangesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitRepoRequest *GitRepoRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitPullChangesDeprecatedRequest) GitRepoRequest(gitRepoRequest GitRepoRequest) ToolboxAPIGitPullChangesDeprecatedRequest { - r.gitRepoRequest = &gitRepoRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitPullChangesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitPullChangesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitPullChangesDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitPullChangesDeprecatedExecute(r) -} - -/* -GitPullChangesDeprecated [DEPRECATED] Pull changes - -Pull changes from remote - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitPullChangesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitPullChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitPullChangesDeprecatedRequest { - return ToolboxAPIGitPullChangesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitPullChangesDeprecatedExecute(r ToolboxAPIGitPullChangesDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitPullChangesDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/pull" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitRepoRequest == nil { - return nil, reportError("gitRepoRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitRepoRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIGitPushChangesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - gitRepoRequest *GitRepoRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIGitPushChangesDeprecatedRequest) GitRepoRequest(gitRepoRequest GitRepoRequest) ToolboxAPIGitPushChangesDeprecatedRequest { - r.gitRepoRequest = &gitRepoRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIGitPushChangesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIGitPushChangesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIGitPushChangesDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GitPushChangesDeprecatedExecute(r) -} - -/* -GitPushChangesDeprecated [DEPRECATED] Push changes - -Push changes to remote - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIGitPushChangesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) GitPushChangesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIGitPushChangesDeprecatedRequest { - return ToolboxAPIGitPushChangesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) GitPushChangesDeprecatedExecute(r ToolboxAPIGitPushChangesDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.GitPushChangesDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/git/push" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.gitRepoRequest == nil { - return nil, reportError("gitRepoRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.gitRepoRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIListFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string - path *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIListFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIListFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIListFilesDeprecatedRequest) Path(path string) ToolboxAPIListFilesDeprecatedRequest { - r.path = &path - return r -} - -func (r ToolboxAPIListFilesDeprecatedRequest) Execute() ([]FileInfo, *http.Response, error) { - return r.ApiService.ListFilesDeprecatedExecute(r) -} - -/* -ListFilesDeprecated [DEPRECATED] List files - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ListFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListFilesDeprecatedRequest { - return ToolboxAPIListFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []FileInfo -// Deprecated -func (a *ToolboxAPIService) ListFilesDeprecatedExecute(r ToolboxAPIListFilesDeprecatedRequest) ([]FileInfo, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []FileInfo - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ListFilesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.path != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIListPTYSessionsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIListPTYSessionsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIListPTYSessionsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIListPTYSessionsDeprecatedRequest) Execute() (*PtyListResponse, *http.Response, error) { - return r.ApiService.ListPTYSessionsDeprecatedExecute(r) -} - -/* -ListPTYSessionsDeprecated [DEPRECATED] List PTY sessions - -List all active PTY sessions in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListPTYSessionsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ListPTYSessionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListPTYSessionsDeprecatedRequest { - return ToolboxAPIListPTYSessionsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return PtyListResponse -// Deprecated -func (a *ToolboxAPIService) ListPTYSessionsDeprecatedExecute(r ToolboxAPIListPTYSessionsDeprecatedRequest) (*PtyListResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PtyListResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ListPTYSessionsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/pty" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIListSessionsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIListSessionsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIListSessionsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIListSessionsDeprecatedRequest) Execute() ([]Session, *http.Response, error) { - return r.ApiService.ListSessionsDeprecatedExecute(r) -} - -/* -ListSessionsDeprecated [DEPRECATED] List sessions - -List all active sessions in the sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIListSessionsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ListSessionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPIListSessionsDeprecatedRequest { - return ToolboxAPIListSessionsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []Session -// Deprecated -func (a *ToolboxAPIService) ListSessionsDeprecatedExecute(r ToolboxAPIListSessionsDeprecatedRequest) ([]Session, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []Session - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ListSessionsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/session" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPILspCompletionsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - lspCompletionParams *LspCompletionParams - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspCompletionsDeprecatedRequest) LspCompletionParams(lspCompletionParams LspCompletionParams) ToolboxAPILspCompletionsDeprecatedRequest { - r.lspCompletionParams = &lspCompletionParams - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspCompletionsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspCompletionsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspCompletionsDeprecatedRequest) Execute() (*CompletionList, *http.Response, error) { - return r.ApiService.LspCompletionsDeprecatedExecute(r) -} - -/* -LspCompletionsDeprecated [DEPRECATED] Get Lsp Completions - -The Completion request is sent from the client to the server to compute completion items at a given cursor position. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspCompletionsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspCompletionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspCompletionsDeprecatedRequest { - return ToolboxAPILspCompletionsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return CompletionList -// Deprecated -func (a *ToolboxAPIService) LspCompletionsDeprecatedExecute(r ToolboxAPILspCompletionsDeprecatedRequest) (*CompletionList, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *CompletionList - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspCompletionsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/completions" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.lspCompletionParams == nil { - return localVarReturnValue, nil, reportError("lspCompletionParams is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.lspCompletionParams - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPILspDidCloseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - lspDocumentRequest *LspDocumentRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspDidCloseDeprecatedRequest) LspDocumentRequest(lspDocumentRequest LspDocumentRequest) ToolboxAPILspDidCloseDeprecatedRequest { - r.lspDocumentRequest = &lspDocumentRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspDidCloseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspDidCloseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspDidCloseDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.LspDidCloseDeprecatedExecute(r) -} - -/* -LspDidCloseDeprecated [DEPRECATED] Call Lsp DidClose - -The document close notification is sent from the client to the server when the document got closed in the client. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDidCloseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspDidCloseDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDidCloseDeprecatedRequest { - return ToolboxAPILspDidCloseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) LspDidCloseDeprecatedExecute(r ToolboxAPILspDidCloseDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspDidCloseDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/did-close" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.lspDocumentRequest == nil { - return nil, reportError("lspDocumentRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.lspDocumentRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPILspDidOpenDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - lspDocumentRequest *LspDocumentRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspDidOpenDeprecatedRequest) LspDocumentRequest(lspDocumentRequest LspDocumentRequest) ToolboxAPILspDidOpenDeprecatedRequest { - r.lspDocumentRequest = &lspDocumentRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspDidOpenDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspDidOpenDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspDidOpenDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.LspDidOpenDeprecatedExecute(r) -} - -/* -LspDidOpenDeprecated [DEPRECATED] Call Lsp DidOpen - -The document open notification is sent from the client to the server to signal newly opened text documents. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDidOpenDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspDidOpenDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDidOpenDeprecatedRequest { - return ToolboxAPILspDidOpenDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) LspDidOpenDeprecatedExecute(r ToolboxAPILspDidOpenDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspDidOpenDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/did-open" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.lspDocumentRequest == nil { - return nil, reportError("lspDocumentRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.lspDocumentRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPILspDocumentSymbolsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - languageId *string - pathToProject *string - uri *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspDocumentSymbolsDeprecatedRequest) LanguageId(languageId string) ToolboxAPILspDocumentSymbolsDeprecatedRequest { - r.languageId = &languageId - return r -} - -func (r ToolboxAPILspDocumentSymbolsDeprecatedRequest) PathToProject(pathToProject string) ToolboxAPILspDocumentSymbolsDeprecatedRequest { - r.pathToProject = &pathToProject - return r -} - -func (r ToolboxAPILspDocumentSymbolsDeprecatedRequest) Uri(uri string) ToolboxAPILspDocumentSymbolsDeprecatedRequest { - r.uri = &uri - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspDocumentSymbolsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspDocumentSymbolsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspDocumentSymbolsDeprecatedRequest) Execute() ([]LspSymbol, *http.Response, error) { - return r.ApiService.LspDocumentSymbolsDeprecatedExecute(r) -} - -/* -LspDocumentSymbolsDeprecated [DEPRECATED] Call Lsp DocumentSymbols - -The document symbol request is sent from the client to the server. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspDocumentSymbolsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspDocumentSymbolsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspDocumentSymbolsDeprecatedRequest { - return ToolboxAPILspDocumentSymbolsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []LspSymbol -// Deprecated -func (a *ToolboxAPIService) LspDocumentSymbolsDeprecatedExecute(r ToolboxAPILspDocumentSymbolsDeprecatedRequest) ([]LspSymbol, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []LspSymbol - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspDocumentSymbolsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/document-symbols" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.languageId == nil { - return localVarReturnValue, nil, reportError("languageId is required and must be specified") - } - if r.pathToProject == nil { - return localVarReturnValue, nil, reportError("pathToProject is required and must be specified") - } - if r.uri == nil { - return localVarReturnValue, nil, reportError("uri is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "languageId", r.languageId, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "pathToProject", r.pathToProject, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "uri", r.uri, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPILspStartDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - lspServerRequest *LspServerRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspStartDeprecatedRequest) LspServerRequest(lspServerRequest LspServerRequest) ToolboxAPILspStartDeprecatedRequest { - r.lspServerRequest = &lspServerRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspStartDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspStartDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspStartDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.LspStartDeprecatedExecute(r) -} - -/* -LspStartDeprecated [DEPRECATED] Start Lsp server - -Start Lsp server process inside sandbox project - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspStartDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspStartDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspStartDeprecatedRequest { - return ToolboxAPILspStartDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) LspStartDeprecatedExecute(r ToolboxAPILspStartDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspStartDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/start" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.lspServerRequest == nil { - return nil, reportError("lspServerRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.lspServerRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPILspStopDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - lspServerRequest *LspServerRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspStopDeprecatedRequest) LspServerRequest(lspServerRequest LspServerRequest) ToolboxAPILspStopDeprecatedRequest { - r.lspServerRequest = &lspServerRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspStopDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspStopDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspStopDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.LspStopDeprecatedExecute(r) -} - -/* -LspStopDeprecated [DEPRECATED] Stop Lsp server - -Stop Lsp server process inside sandbox project - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspStopDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspStopDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspStopDeprecatedRequest { - return ToolboxAPILspStopDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) LspStopDeprecatedExecute(r ToolboxAPILspStopDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspStopDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/stop" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.lspServerRequest == nil { - return nil, reportError("lspServerRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.lspServerRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPILspWorkspaceSymbolsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - languageId *string - pathToProject *string - query *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) LanguageId(languageId string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest { - r.languageId = &languageId - return r -} - -func (r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) PathToProject(pathToProject string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest { - r.pathToProject = &pathToProject - return r -} - -func (r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) Query(query string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest { - r.query = &query - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) Execute() ([]LspSymbol, *http.Response, error) { - return r.ApiService.LspWorkspaceSymbolsDeprecatedExecute(r) -} - -/* -LspWorkspaceSymbolsDeprecated [DEPRECATED] Call Lsp WorkspaceSymbols - -The workspace symbol request is sent from the client to the server to list project-wide symbols matching the query string. - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPILspWorkspaceSymbolsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) LspWorkspaceSymbolsDeprecated(ctx context.Context, sandboxId string) ToolboxAPILspWorkspaceSymbolsDeprecatedRequest { - return ToolboxAPILspWorkspaceSymbolsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []LspSymbol -// Deprecated -func (a *ToolboxAPIService) LspWorkspaceSymbolsDeprecatedExecute(r ToolboxAPILspWorkspaceSymbolsDeprecatedRequest) ([]LspSymbol, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []LspSymbol - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.LspWorkspaceSymbolsDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/lsp/workspace-symbols" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.languageId == nil { - return localVarReturnValue, nil, reportError("languageId is required and must be specified") - } - if r.pathToProject == nil { - return localVarReturnValue, nil, reportError("pathToProject is required and must be specified") - } - if r.query == nil { - return localVarReturnValue, nil, reportError("query is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "languageId", r.languageId, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "pathToProject", r.pathToProject, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "query", r.query, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIMoveFileDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - source *string - destination *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIMoveFileDeprecatedRequest) Source(source string) ToolboxAPIMoveFileDeprecatedRequest { - r.source = &source - return r -} - -func (r ToolboxAPIMoveFileDeprecatedRequest) Destination(destination string) ToolboxAPIMoveFileDeprecatedRequest { - r.destination = &destination - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIMoveFileDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIMoveFileDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIMoveFileDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.MoveFileDeprecatedExecute(r) -} - -/* -MoveFileDeprecated [DEPRECATED] Move file - -Move file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIMoveFileDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) MoveFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIMoveFileDeprecatedRequest { - return ToolboxAPIMoveFileDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) MoveFileDeprecatedExecute(r ToolboxAPIMoveFileDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.MoveFileDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/move" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.source == nil { - return nil, reportError("source is required and must be specified") - } - if r.destination == nil { - return nil, reportError("destination is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "source", r.source, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "destination", r.destination, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIMoveMouseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - mouseMoveRequest *MouseMoveRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIMoveMouseDeprecatedRequest) MouseMoveRequest(mouseMoveRequest MouseMoveRequest) ToolboxAPIMoveMouseDeprecatedRequest { - r.mouseMoveRequest = &mouseMoveRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIMoveMouseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIMoveMouseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIMoveMouseDeprecatedRequest) Execute() (*MouseMoveResponse, *http.Response, error) { - return r.ApiService.MoveMouseDeprecatedExecute(r) -} - -/* -MoveMouseDeprecated [DEPRECATED] Move mouse - -Move mouse cursor to specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIMoveMouseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) MoveMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIMoveMouseDeprecatedRequest { - return ToolboxAPIMoveMouseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MouseMoveResponse -// Deprecated -func (a *ToolboxAPIService) MoveMouseDeprecatedExecute(r ToolboxAPIMoveMouseDeprecatedRequest) (*MouseMoveResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MouseMoveResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.MoveMouseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/mouse/move" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.mouseMoveRequest == nil { - return localVarReturnValue, nil, reportError("mouseMoveRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.mouseMoveRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIPressHotkeyDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - keyboardHotkeyRequest *KeyboardHotkeyRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIPressHotkeyDeprecatedRequest) KeyboardHotkeyRequest(keyboardHotkeyRequest KeyboardHotkeyRequest) ToolboxAPIPressHotkeyDeprecatedRequest { - r.keyboardHotkeyRequest = &keyboardHotkeyRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIPressHotkeyDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIPressHotkeyDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIPressHotkeyDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.PressHotkeyDeprecatedExecute(r) -} - -/* -PressHotkeyDeprecated [DEPRECATED] Press hotkey - -Press a hotkey combination - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIPressHotkeyDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) PressHotkeyDeprecated(ctx context.Context, sandboxId string) ToolboxAPIPressHotkeyDeprecatedRequest { - return ToolboxAPIPressHotkeyDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) PressHotkeyDeprecatedExecute(r ToolboxAPIPressHotkeyDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.PressHotkeyDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/keyboard/hotkey" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.keyboardHotkeyRequest == nil { - return nil, reportError("keyboardHotkeyRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.keyboardHotkeyRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIPressKeyDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - keyboardPressRequest *KeyboardPressRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIPressKeyDeprecatedRequest) KeyboardPressRequest(keyboardPressRequest KeyboardPressRequest) ToolboxAPIPressKeyDeprecatedRequest { - r.keyboardPressRequest = &keyboardPressRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIPressKeyDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIPressKeyDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIPressKeyDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.PressKeyDeprecatedExecute(r) -} - -/* -PressKeyDeprecated [DEPRECATED] Press key - -Press a key with optional modifiers - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIPressKeyDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) PressKeyDeprecated(ctx context.Context, sandboxId string) ToolboxAPIPressKeyDeprecatedRequest { - return ToolboxAPIPressKeyDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) PressKeyDeprecatedExecute(r ToolboxAPIPressKeyDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.PressKeyDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/keyboard/key" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.keyboardPressRequest == nil { - return nil, reportError("keyboardPressRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.keyboardPressRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIReplaceInFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - replaceRequest *ReplaceRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIReplaceInFilesDeprecatedRequest) ReplaceRequest(replaceRequest ReplaceRequest) ToolboxAPIReplaceInFilesDeprecatedRequest { - r.replaceRequest = &replaceRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIReplaceInFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIReplaceInFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIReplaceInFilesDeprecatedRequest) Execute() ([]ReplaceResult, *http.Response, error) { - return r.ApiService.ReplaceInFilesDeprecatedExecute(r) -} - -/* -ReplaceInFilesDeprecated [DEPRECATED] Replace in files - -Replace text/pattern in multiple files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIReplaceInFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ReplaceInFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIReplaceInFilesDeprecatedRequest { - return ToolboxAPIReplaceInFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return []ReplaceResult -// Deprecated -func (a *ToolboxAPIService) ReplaceInFilesDeprecatedExecute(r ToolboxAPIReplaceInFilesDeprecatedRequest) ([]ReplaceResult, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []ReplaceResult - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ReplaceInFilesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/replace" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.replaceRequest == nil { - return localVarReturnValue, nil, reportError("replaceRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.replaceRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIResizePTYSessionDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - sessionId string - ptyResizeRequest *PtyResizeRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIResizePTYSessionDeprecatedRequest) PtyResizeRequest(ptyResizeRequest PtyResizeRequest) ToolboxAPIResizePTYSessionDeprecatedRequest { - r.ptyResizeRequest = &ptyResizeRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIResizePTYSessionDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIResizePTYSessionDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIResizePTYSessionDeprecatedRequest) Execute() (*PtySessionInfo, *http.Response, error) { - return r.ApiService.ResizePTYSessionDeprecatedExecute(r) -} - -/* -ResizePTYSessionDeprecated [DEPRECATED] Resize PTY session - -Resize a PTY session - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @param sessionId - @return ToolboxAPIResizePTYSessionDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ResizePTYSessionDeprecated(ctx context.Context, sandboxId string, sessionId string) ToolboxAPIResizePTYSessionDeprecatedRequest { - return ToolboxAPIResizePTYSessionDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - sessionId: sessionId, - } -} - -// Execute executes the request -// @return PtySessionInfo -// Deprecated -func (a *ToolboxAPIService) ResizePTYSessionDeprecatedExecute(r ToolboxAPIResizePTYSessionDeprecatedRequest) (*PtySessionInfo, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *PtySessionInfo - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ResizePTYSessionDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/process/pty/{sessionId}/resize" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sessionId"+"}", url.PathEscape(parameterValueToString(r.sessionId, "sessionId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.ptyResizeRequest == nil { - return localVarReturnValue, nil, reportError("ptyResizeRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.ptyResizeRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIRestartProcessDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - processName string - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIRestartProcessDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIRestartProcessDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIRestartProcessDeprecatedRequest) Execute() (*ProcessRestartResponse, *http.Response, error) { - return r.ApiService.RestartProcessDeprecatedExecute(r) -} - -/* -RestartProcessDeprecated [DEPRECATED] Restart process - -Restart a specific VNC process - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param processName - @param sandboxId - @return ToolboxAPIRestartProcessDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) RestartProcessDeprecated(ctx context.Context, processName string, sandboxId string) ToolboxAPIRestartProcessDeprecatedRequest { - return ToolboxAPIRestartProcessDeprecatedRequest{ - ApiService: a, - ctx: ctx, - processName: processName, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ProcessRestartResponse -// Deprecated -func (a *ToolboxAPIService) RestartProcessDeprecatedExecute(r ToolboxAPIRestartProcessDeprecatedRequest) (*ProcessRestartResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ProcessRestartResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.RestartProcessDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/process/{processName}/restart" - localVarPath = strings.Replace(localVarPath, "{"+"processName"+"}", url.PathEscape(parameterValueToString(r.processName, "processName")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIScrollMouseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - mouseScrollRequest *MouseScrollRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPIScrollMouseDeprecatedRequest) MouseScrollRequest(mouseScrollRequest MouseScrollRequest) ToolboxAPIScrollMouseDeprecatedRequest { - r.mouseScrollRequest = &mouseScrollRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIScrollMouseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIScrollMouseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIScrollMouseDeprecatedRequest) Execute() (*MouseScrollResponse, *http.Response, error) { - return r.ApiService.ScrollMouseDeprecatedExecute(r) -} - -/* -ScrollMouseDeprecated [DEPRECATED] Scroll mouse - -Scroll mouse at specified coordinates - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIScrollMouseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) ScrollMouseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIScrollMouseDeprecatedRequest { - return ToolboxAPIScrollMouseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return MouseScrollResponse -// Deprecated -func (a *ToolboxAPIService) ScrollMouseDeprecatedExecute(r ToolboxAPIScrollMouseDeprecatedRequest) (*MouseScrollResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *MouseScrollResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.ScrollMouseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/mouse/scroll" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.mouseScrollRequest == nil { - return localVarReturnValue, nil, reportError("mouseScrollRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.mouseScrollRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPISearchFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - pattern *string - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPISearchFilesDeprecatedRequest) Path(path string) ToolboxAPISearchFilesDeprecatedRequest { - r.path = &path - return r -} - -func (r ToolboxAPISearchFilesDeprecatedRequest) Pattern(pattern string) ToolboxAPISearchFilesDeprecatedRequest { - r.pattern = &pattern - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPISearchFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPISearchFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPISearchFilesDeprecatedRequest) Execute() (*SearchFilesResponse, *http.Response, error) { - return r.ApiService.SearchFilesDeprecatedExecute(r) -} - -/* -SearchFilesDeprecated [DEPRECATED] Search files - -Search for files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPISearchFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) SearchFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPISearchFilesDeprecatedRequest { - return ToolboxAPISearchFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return SearchFilesResponse -// Deprecated -func (a *ToolboxAPIService) SearchFilesDeprecatedExecute(r ToolboxAPISearchFilesDeprecatedRequest) (*SearchFilesResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SearchFilesResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.SearchFilesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/search" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return localVarReturnValue, nil, reportError("path is required and must be specified") - } - if r.pattern == nil { - return localVarReturnValue, nil, reportError("pattern is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "pattern", r.pattern, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPISetFilePermissionsDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string - owner *string - group *string - mode *string -} - -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) Path(path string) ToolboxAPISetFilePermissionsDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPISetFilePermissionsDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) Owner(owner string) ToolboxAPISetFilePermissionsDeprecatedRequest { - r.owner = &owner - return r -} - -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) Group(group string) ToolboxAPISetFilePermissionsDeprecatedRequest { - r.group = &group - return r -} - -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) Mode(mode string) ToolboxAPISetFilePermissionsDeprecatedRequest { - r.mode = &mode - return r -} - -func (r ToolboxAPISetFilePermissionsDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.SetFilePermissionsDeprecatedExecute(r) -} - -/* -SetFilePermissionsDeprecated [DEPRECATED] Set file permissions - -Set file owner/group/permissions inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPISetFilePermissionsDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) SetFilePermissionsDeprecated(ctx context.Context, sandboxId string) ToolboxAPISetFilePermissionsDeprecatedRequest { - return ToolboxAPISetFilePermissionsDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) SetFilePermissionsDeprecatedExecute(r ToolboxAPISetFilePermissionsDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.SetFilePermissionsDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/permissions" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - if r.owner != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "owner", r.owner, "form", "") - } - if r.group != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "group", r.group, "form", "") - } - if r.mode != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "mode", r.mode, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIStartComputerUseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIStartComputerUseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIStartComputerUseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIStartComputerUseDeprecatedRequest) Execute() (*ComputerUseStartResponse, *http.Response, error) { - return r.ApiService.StartComputerUseDeprecatedExecute(r) -} - -/* -StartComputerUseDeprecated [DEPRECATED] Start computer use processes - -Start all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc) - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIStartComputerUseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) StartComputerUseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIStartComputerUseDeprecatedRequest { - return ToolboxAPIStartComputerUseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ComputerUseStartResponse -// Deprecated -func (a *ToolboxAPIService) StartComputerUseDeprecatedExecute(r ToolboxAPIStartComputerUseDeprecatedRequest) (*ComputerUseStartResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ComputerUseStartResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.StartComputerUseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/start" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPIStopComputerUseDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIStopComputerUseDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIStopComputerUseDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIStopComputerUseDeprecatedRequest) Execute() (*ComputerUseStopResponse, *http.Response, error) { - return r.ApiService.StopComputerUseDeprecatedExecute(r) -} - -/* -StopComputerUseDeprecated [DEPRECATED] Stop computer use processes - -Stop all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc) - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIStopComputerUseDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) StopComputerUseDeprecated(ctx context.Context, sandboxId string) ToolboxAPIStopComputerUseDeprecatedRequest { - return ToolboxAPIStopComputerUseDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ComputerUseStopResponse -// Deprecated -func (a *ToolboxAPIService) StopComputerUseDeprecatedExecute(r ToolboxAPIStopComputerUseDeprecatedRequest) (*ComputerUseStopResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ComputerUseStopResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.StopComputerUseDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/stop" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - height *float32 - width *float32 - y *float32 - x *float32 - xBoxLiteOrganizationID *string - scale *float32 - quality *float32 - format *string - showCursor *bool -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Height(height float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.height = &height - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Width(width float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.width = &width - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Y(y float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.y = &y - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) X(x float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.x = &x - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Scale(scale float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.scale = &scale - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Quality(quality float32) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.quality = &quality - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Format(format string) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.format = &format - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) ShowCursor(showCursor bool) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - r.showCursor = &showCursor - return r -} - -func (r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) Execute() (*CompressedScreenshotResponse, *http.Response, error) { - return r.ApiService.TakeCompressedRegionScreenshotDeprecatedExecute(r) -} - -/* -TakeCompressedRegionScreenshotDeprecated [DEPRECATED] Take compressed region screenshot - -Take a compressed screenshot of a specific region - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) TakeCompressedRegionScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest { - return ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return CompressedScreenshotResponse -// Deprecated -func (a *ToolboxAPIService) TakeCompressedRegionScreenshotDeprecatedExecute(r ToolboxAPITakeCompressedRegionScreenshotDeprecatedRequest) (*CompressedScreenshotResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *CompressedScreenshotResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.TakeCompressedRegionScreenshotDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/screenshot/region/compressed" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.height == nil { - return localVarReturnValue, nil, reportError("height is required and must be specified") - } - if r.width == nil { - return localVarReturnValue, nil, reportError("width is required and must be specified") - } - if r.y == nil { - return localVarReturnValue, nil, reportError("y is required and must be specified") - } - if r.x == nil { - return localVarReturnValue, nil, reportError("x is required and must be specified") - } - - if r.scale != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "scale", r.scale, "form", "") - } - if r.quality != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "quality", r.quality, "form", "") - } - if r.format != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "format", r.format, "form", "") - } - if r.showCursor != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "show_cursor", r.showCursor, "form", "") - } - parameterAddToHeaderOrQuery(localVarQueryParams, "height", r.height, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "width", r.width, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "y", r.y, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "x", r.x, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPITakeCompressedScreenshotDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string - scale *float32 - quality *float32 - format *string - showCursor *bool -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) Scale(scale float32) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - r.scale = &scale - return r -} - -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) Quality(quality float32) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - r.quality = &quality - return r -} - -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) Format(format string) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - r.format = &format - return r -} - -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) ShowCursor(showCursor bool) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - r.showCursor = &showCursor - return r -} - -func (r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) Execute() (*CompressedScreenshotResponse, *http.Response, error) { - return r.ApiService.TakeCompressedScreenshotDeprecatedExecute(r) -} - -/* -TakeCompressedScreenshotDeprecated [DEPRECATED] Take compressed screenshot - -Take a compressed screenshot with format, quality, and scale options - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeCompressedScreenshotDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) TakeCompressedScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeCompressedScreenshotDeprecatedRequest { - return ToolboxAPITakeCompressedScreenshotDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return CompressedScreenshotResponse -// Deprecated -func (a *ToolboxAPIService) TakeCompressedScreenshotDeprecatedExecute(r ToolboxAPITakeCompressedScreenshotDeprecatedRequest) (*CompressedScreenshotResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *CompressedScreenshotResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.TakeCompressedScreenshotDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/screenshot/compressed" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.scale != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "scale", r.scale, "form", "") - } - if r.quality != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "quality", r.quality, "form", "") - } - if r.format != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "format", r.format, "form", "") - } - if r.showCursor != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "show_cursor", r.showCursor, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPITakeRegionScreenshotDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - height *float32 - width *float32 - y *float32 - x *float32 - xBoxLiteOrganizationID *string - showCursor *bool -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) Height(height float32) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.height = &height - return r -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) Width(width float32) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.width = &width - return r -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) Y(y float32) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.y = &y - return r -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) X(x float32) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.x = &x - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) ShowCursor(showCursor bool) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - r.showCursor = &showCursor - return r -} - -func (r ToolboxAPITakeRegionScreenshotDeprecatedRequest) Execute() (*RegionScreenshotResponse, *http.Response, error) { - return r.ApiService.TakeRegionScreenshotDeprecatedExecute(r) -} - -/* -TakeRegionScreenshotDeprecated [DEPRECATED] Take region screenshot - -Take a screenshot of a specific region - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeRegionScreenshotDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) TakeRegionScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeRegionScreenshotDeprecatedRequest { - return ToolboxAPITakeRegionScreenshotDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return RegionScreenshotResponse -// Deprecated -func (a *ToolboxAPIService) TakeRegionScreenshotDeprecatedExecute(r ToolboxAPITakeRegionScreenshotDeprecatedRequest) (*RegionScreenshotResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *RegionScreenshotResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.TakeRegionScreenshotDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/screenshot/region" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.height == nil { - return localVarReturnValue, nil, reportError("height is required and must be specified") - } - if r.width == nil { - return localVarReturnValue, nil, reportError("width is required and must be specified") - } - if r.y == nil { - return localVarReturnValue, nil, reportError("y is required and must be specified") - } - if r.x == nil { - return localVarReturnValue, nil, reportError("x is required and must be specified") - } - - if r.showCursor != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "show_cursor", r.showCursor, "form", "") - } - parameterAddToHeaderOrQuery(localVarQueryParams, "height", r.height, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "width", r.width, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "y", r.y, "form", "") - parameterAddToHeaderOrQuery(localVarQueryParams, "x", r.x, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPITakeScreenshotDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string - showCursor *bool -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPITakeScreenshotDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPITakeScreenshotDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPITakeScreenshotDeprecatedRequest) ShowCursor(showCursor bool) ToolboxAPITakeScreenshotDeprecatedRequest { - r.showCursor = &showCursor - return r -} - -func (r ToolboxAPITakeScreenshotDeprecatedRequest) Execute() (*ScreenshotResponse, *http.Response, error) { - return r.ApiService.TakeScreenshotDeprecatedExecute(r) -} - -/* -TakeScreenshotDeprecated [DEPRECATED] Take screenshot - -Take a screenshot of the entire screen - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITakeScreenshotDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) TakeScreenshotDeprecated(ctx context.Context, sandboxId string) ToolboxAPITakeScreenshotDeprecatedRequest { - return ToolboxAPITakeScreenshotDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// @return ScreenshotResponse -// Deprecated -func (a *ToolboxAPIService) TakeScreenshotDeprecatedExecute(r ToolboxAPITakeScreenshotDeprecatedRequest) (*ScreenshotResponse, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *ScreenshotResponse - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.TakeScreenshotDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/screenshot" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.showCursor != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "show_cursor", r.showCursor, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type ToolboxAPITypeTextDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - keyboardTypeRequest *KeyboardTypeRequest - xBoxLiteOrganizationID *string -} - -func (r ToolboxAPITypeTextDeprecatedRequest) KeyboardTypeRequest(keyboardTypeRequest KeyboardTypeRequest) ToolboxAPITypeTextDeprecatedRequest { - r.keyboardTypeRequest = &keyboardTypeRequest - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPITypeTextDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPITypeTextDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPITypeTextDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.TypeTextDeprecatedExecute(r) -} - -/* -TypeTextDeprecated [DEPRECATED] Type text - -Type text using keyboard - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPITypeTextDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) TypeTextDeprecated(ctx context.Context, sandboxId string) ToolboxAPITypeTextDeprecatedRequest { - return ToolboxAPITypeTextDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) TypeTextDeprecatedExecute(r ToolboxAPITypeTextDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.TypeTextDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/computeruse/keyboard/type" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.keyboardTypeRequest == nil { - return nil, reportError("keyboardTypeRequest is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.keyboardTypeRequest - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIUploadFileDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - path *string - xBoxLiteOrganizationID *string - file *os.File -} - -func (r ToolboxAPIUploadFileDeprecatedRequest) Path(path string) ToolboxAPIUploadFileDeprecatedRequest { - r.path = &path - return r -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIUploadFileDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIUploadFileDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIUploadFileDeprecatedRequest) File(file *os.File) ToolboxAPIUploadFileDeprecatedRequest { - r.file = file - return r -} - -func (r ToolboxAPIUploadFileDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.UploadFileDeprecatedExecute(r) -} - -/* -UploadFileDeprecated [DEPRECATED] Upload file - -Upload file inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIUploadFileDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) UploadFileDeprecated(ctx context.Context, sandboxId string) ToolboxAPIUploadFileDeprecatedRequest { - return ToolboxAPIUploadFileDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) UploadFileDeprecatedExecute(r ToolboxAPIUploadFileDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.UploadFileDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/upload" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.path == nil { - return nil, reportError("path is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "path", r.path, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"multipart/form-data"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - var fileLocalVarFormFileName string - var fileLocalVarFileName string - var fileLocalVarFileBytes []byte - - fileLocalVarFormFileName = "file" - fileLocalVarFile := r.file - - if fileLocalVarFile != nil { - fbs, _ := io.ReadAll(fileLocalVarFile) - - fileLocalVarFileBytes = fbs - fileLocalVarFileName = fileLocalVarFile.Name() - fileLocalVarFile.Close() - formFiles = append(formFiles, formFile{fileBytes: fileLocalVarFileBytes, fileName: fileLocalVarFileName, formFileName: fileLocalVarFormFileName}) - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type ToolboxAPIUploadFilesDeprecatedRequest struct { - ctx context.Context - ApiService ToolboxAPI - sandboxId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r ToolboxAPIUploadFilesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) ToolboxAPIUploadFilesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r ToolboxAPIUploadFilesDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.UploadFilesDeprecatedExecute(r) -} - -/* -UploadFilesDeprecated [DEPRECATED] Upload multiple files - -Upload multiple files inside sandbox - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param sandboxId - @return ToolboxAPIUploadFilesDeprecatedRequest - -Deprecated -*/ -func (a *ToolboxAPIService) UploadFilesDeprecated(ctx context.Context, sandboxId string) ToolboxAPIUploadFilesDeprecatedRequest { - return ToolboxAPIUploadFilesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - sandboxId: sandboxId, - } -} - -// Execute executes the request -// Deprecated -func (a *ToolboxAPIService) UploadFilesDeprecatedExecute(r ToolboxAPIUploadFilesDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ToolboxAPIService.UploadFilesDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/toolbox/{sandboxId}/toolbox/files/bulk-upload" - localVarPath = strings.Replace(localVarPath, "{"+"sandboxId"+"}", url.PathEscape(parameterValueToString(r.sandboxId, "sandboxId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"multipart/form-data"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/api_workspace.go b/apps/api-client-go/api_workspace.go deleted file mode 100644 index 207111d82..000000000 --- a/apps/api-client-go/api_workspace.go +++ /dev/null @@ -1,1834 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "bytes" - "context" - "io" - "net/http" - "net/url" - "strings" -) - - -type WorkspaceAPI interface { - - /* - ArchiveWorkspaceDeprecated [DEPRECATED] Archive workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId - @return WorkspaceAPIArchiveWorkspaceDeprecatedRequest - - Deprecated - */ - ArchiveWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIArchiveWorkspaceDeprecatedRequest - - // ArchiveWorkspaceDeprecatedExecute executes the request - // Deprecated - ArchiveWorkspaceDeprecatedExecute(r WorkspaceAPIArchiveWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - CreateBackupWorkspaceDeprecated [DEPRECATED] Create workspace backup - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPICreateBackupWorkspaceDeprecatedRequest - - Deprecated - */ - CreateBackupWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPICreateBackupWorkspaceDeprecatedRequest - - // CreateBackupWorkspaceDeprecatedExecute executes the request - // @return Workspace - // Deprecated - CreateBackupWorkspaceDeprecatedExecute(r WorkspaceAPICreateBackupWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) - - /* - CreateWorkspaceDeprecated [DEPRECATED] Create a new workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return WorkspaceAPICreateWorkspaceDeprecatedRequest - - Deprecated - */ - CreateWorkspaceDeprecated(ctx context.Context) WorkspaceAPICreateWorkspaceDeprecatedRequest - - // CreateWorkspaceDeprecatedExecute executes the request - // @return Workspace - // Deprecated - CreateWorkspaceDeprecatedExecute(r WorkspaceAPICreateWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) - - /* - DeleteWorkspaceDeprecated [DEPRECATED] Delete workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIDeleteWorkspaceDeprecatedRequest - - Deprecated - */ - DeleteWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIDeleteWorkspaceDeprecatedRequest - - // DeleteWorkspaceDeprecatedExecute executes the request - // Deprecated - DeleteWorkspaceDeprecatedExecute(r WorkspaceAPIDeleteWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - GetBuildLogsWorkspaceDeprecated [DEPRECATED] Get build logs - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest - - Deprecated - */ - GetBuildLogsWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest - - // GetBuildLogsWorkspaceDeprecatedExecute executes the request - // Deprecated - GetBuildLogsWorkspaceDeprecatedExecute(r WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - GetPortPreviewUrlWorkspaceDeprecated [DEPRECATED] Get preview URL for a workspace port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param port Port number to get preview URL for - @return WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest - - Deprecated - */ - GetPortPreviewUrlWorkspaceDeprecated(ctx context.Context, workspaceId string, port float32) WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest - - // GetPortPreviewUrlWorkspaceDeprecatedExecute executes the request - // @return WorkspacePortPreviewUrl - // Deprecated - GetPortPreviewUrlWorkspaceDeprecatedExecute(r WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest) (*WorkspacePortPreviewUrl, *http.Response, error) - - /* - GetWorkspaceDeprecated [DEPRECATED] Get workspace details - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIGetWorkspaceDeprecatedRequest - - Deprecated - */ - GetWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIGetWorkspaceDeprecatedRequest - - // GetWorkspaceDeprecatedExecute executes the request - // @return Workspace - // Deprecated - GetWorkspaceDeprecatedExecute(r WorkspaceAPIGetWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) - - /* - ListWorkspacesDeprecated [DEPRECATED] List all workspaces - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return WorkspaceAPIListWorkspacesDeprecatedRequest - - Deprecated - */ - ListWorkspacesDeprecated(ctx context.Context) WorkspaceAPIListWorkspacesDeprecatedRequest - - // ListWorkspacesDeprecatedExecute executes the request - // @return []Workspace - // Deprecated - ListWorkspacesDeprecatedExecute(r WorkspaceAPIListWorkspacesDeprecatedRequest) ([]Workspace, *http.Response, error) - - /* - ReplaceLabelsWorkspaceDeprecated [DEPRECATED] Replace workspace labels - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest - - Deprecated - */ - ReplaceLabelsWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest - - // ReplaceLabelsWorkspaceDeprecatedExecute executes the request - // @return SandboxLabels - // Deprecated - ReplaceLabelsWorkspaceDeprecatedExecute(r WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest) (*SandboxLabels, *http.Response, error) - - /* - SetAutoArchiveIntervalWorkspaceDeprecated [DEPRECATED] Set workspace auto-archive interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param interval Auto-archive interval in minutes (0 means the maximum interval will be used) - @return WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest - - Deprecated - */ - SetAutoArchiveIntervalWorkspaceDeprecated(ctx context.Context, workspaceId string, interval float32) WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest - - // SetAutoArchiveIntervalWorkspaceDeprecatedExecute executes the request - // Deprecated - SetAutoArchiveIntervalWorkspaceDeprecatedExecute(r WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - SetAutostopIntervalWorkspaceDeprecated [DEPRECATED] Set workspace auto-stop interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param interval Auto-stop interval in minutes (0 to disable) - @return WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest - - Deprecated - */ - SetAutostopIntervalWorkspaceDeprecated(ctx context.Context, workspaceId string, interval float32) WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest - - // SetAutostopIntervalWorkspaceDeprecatedExecute executes the request - // Deprecated - SetAutostopIntervalWorkspaceDeprecatedExecute(r WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - StartWorkspaceDeprecated [DEPRECATED] Start workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIStartWorkspaceDeprecatedRequest - - Deprecated - */ - StartWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIStartWorkspaceDeprecatedRequest - - // StartWorkspaceDeprecatedExecute executes the request - // Deprecated - StartWorkspaceDeprecatedExecute(r WorkspaceAPIStartWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - StopWorkspaceDeprecated [DEPRECATED] Stop workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIStopWorkspaceDeprecatedRequest - - Deprecated - */ - StopWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIStopWorkspaceDeprecatedRequest - - // StopWorkspaceDeprecatedExecute executes the request - // Deprecated - StopWorkspaceDeprecatedExecute(r WorkspaceAPIStopWorkspaceDeprecatedRequest) (*http.Response, error) - - /* - UpdatePublicStatusWorkspaceDeprecated [DEPRECATED] Update public status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param isPublic Public status to set - @return WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest - - Deprecated - */ - UpdatePublicStatusWorkspaceDeprecated(ctx context.Context, workspaceId string, isPublic bool) WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest - - // UpdatePublicStatusWorkspaceDeprecatedExecute executes the request - // Deprecated - UpdatePublicStatusWorkspaceDeprecatedExecute(r WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest) (*http.Response, error) -} - -// WorkspaceAPIService WorkspaceAPI service -type WorkspaceAPIService service - -type WorkspaceAPIArchiveWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIArchiveWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIArchiveWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIArchiveWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.ArchiveWorkspaceDeprecatedExecute(r) -} - -/* -ArchiveWorkspaceDeprecated [DEPRECATED] Archive workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId - @return WorkspaceAPIArchiveWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) ArchiveWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIArchiveWorkspaceDeprecatedRequest { - return WorkspaceAPIArchiveWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) ArchiveWorkspaceDeprecatedExecute(r WorkspaceAPIArchiveWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.ArchiveWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/archive" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPICreateBackupWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPICreateBackupWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPICreateBackupWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPICreateBackupWorkspaceDeprecatedRequest) Execute() (*Workspace, *http.Response, error) { - return r.ApiService.CreateBackupWorkspaceDeprecatedExecute(r) -} - -/* -CreateBackupWorkspaceDeprecated [DEPRECATED] Create workspace backup - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPICreateBackupWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) CreateBackupWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPICreateBackupWorkspaceDeprecatedRequest { - return WorkspaceAPICreateBackupWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// @return Workspace -// Deprecated -func (a *WorkspaceAPIService) CreateBackupWorkspaceDeprecatedExecute(r WorkspaceAPICreateBackupWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Workspace - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.CreateBackupWorkspaceDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/backup" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPICreateWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - createWorkspace *CreateWorkspace - xBoxLiteOrganizationID *string -} - -func (r WorkspaceAPICreateWorkspaceDeprecatedRequest) CreateWorkspace(createWorkspace CreateWorkspace) WorkspaceAPICreateWorkspaceDeprecatedRequest { - r.createWorkspace = &createWorkspace - return r -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPICreateWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPICreateWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPICreateWorkspaceDeprecatedRequest) Execute() (*Workspace, *http.Response, error) { - return r.ApiService.CreateWorkspaceDeprecatedExecute(r) -} - -/* -CreateWorkspaceDeprecated [DEPRECATED] Create a new workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return WorkspaceAPICreateWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) CreateWorkspaceDeprecated(ctx context.Context) WorkspaceAPICreateWorkspaceDeprecatedRequest { - return WorkspaceAPICreateWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return Workspace -// Deprecated -func (a *WorkspaceAPIService) CreateWorkspaceDeprecatedExecute(r WorkspaceAPICreateWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Workspace - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.CreateWorkspaceDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.createWorkspace == nil { - return localVarReturnValue, nil, reportError("createWorkspace is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.createWorkspace - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPIDeleteWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - force *bool - xBoxLiteOrganizationID *string -} - -func (r WorkspaceAPIDeleteWorkspaceDeprecatedRequest) Force(force bool) WorkspaceAPIDeleteWorkspaceDeprecatedRequest { - r.force = &force - return r -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIDeleteWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIDeleteWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIDeleteWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.DeleteWorkspaceDeprecatedExecute(r) -} - -/* -DeleteWorkspaceDeprecated [DEPRECATED] Delete workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIDeleteWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) DeleteWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIDeleteWorkspaceDeprecatedRequest { - return WorkspaceAPIDeleteWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) DeleteWorkspaceDeprecatedExecute(r WorkspaceAPIDeleteWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodDelete - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.DeleteWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.force == nil { - return nil, reportError("force is required and must be specified") - } - - parameterAddToHeaderOrQuery(localVarQueryParams, "force", r.force, "form", "") - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string - follow *bool -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Whether to follow the logs stream -func (r WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest) Follow(follow bool) WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest { - r.follow = &follow - return r -} - -func (r WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.GetBuildLogsWorkspaceDeprecatedExecute(r) -} - -/* -GetBuildLogsWorkspaceDeprecated [DEPRECATED] Get build logs - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) GetBuildLogsWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest { - return WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) GetBuildLogsWorkspaceDeprecatedExecute(r WorkspaceAPIGetBuildLogsWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.GetBuildLogsWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/build-logs" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.follow != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "follow", r.follow, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - port float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest) Execute() (*WorkspacePortPreviewUrl, *http.Response, error) { - return r.ApiService.GetPortPreviewUrlWorkspaceDeprecatedExecute(r) -} - -/* -GetPortPreviewUrlWorkspaceDeprecated [DEPRECATED] Get preview URL for a workspace port - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param port Port number to get preview URL for - @return WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) GetPortPreviewUrlWorkspaceDeprecated(ctx context.Context, workspaceId string, port float32) WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest { - return WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - port: port, - } -} - -// Execute executes the request -// @return WorkspacePortPreviewUrl -// Deprecated -func (a *WorkspaceAPIService) GetPortPreviewUrlWorkspaceDeprecatedExecute(r WorkspaceAPIGetPortPreviewUrlWorkspaceDeprecatedRequest) (*WorkspacePortPreviewUrl, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *WorkspacePortPreviewUrl - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.GetPortPreviewUrlWorkspaceDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/ports/{port}/preview-url" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"port"+"}", url.PathEscape(parameterValueToString(r.port, "port")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPIGetWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string - verbose *bool -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIGetWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIGetWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Include verbose output -func (r WorkspaceAPIGetWorkspaceDeprecatedRequest) Verbose(verbose bool) WorkspaceAPIGetWorkspaceDeprecatedRequest { - r.verbose = &verbose - return r -} - -func (r WorkspaceAPIGetWorkspaceDeprecatedRequest) Execute() (*Workspace, *http.Response, error) { - return r.ApiService.GetWorkspaceDeprecatedExecute(r) -} - -/* -GetWorkspaceDeprecated [DEPRECATED] Get workspace details - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIGetWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) GetWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIGetWorkspaceDeprecatedRequest { - return WorkspaceAPIGetWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// @return Workspace -// Deprecated -func (a *WorkspaceAPIService) GetWorkspaceDeprecatedExecute(r WorkspaceAPIGetWorkspaceDeprecatedRequest) (*Workspace, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *Workspace - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.GetWorkspaceDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.verbose != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPIListWorkspacesDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - xBoxLiteOrganizationID *string - verbose *bool - labels *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIListWorkspacesDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIListWorkspacesDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -// Include verbose output -func (r WorkspaceAPIListWorkspacesDeprecatedRequest) Verbose(verbose bool) WorkspaceAPIListWorkspacesDeprecatedRequest { - r.verbose = &verbose - return r -} - -// JSON encoded labels to filter by -func (r WorkspaceAPIListWorkspacesDeprecatedRequest) Labels(labels string) WorkspaceAPIListWorkspacesDeprecatedRequest { - r.labels = &labels - return r -} - -func (r WorkspaceAPIListWorkspacesDeprecatedRequest) Execute() ([]Workspace, *http.Response, error) { - return r.ApiService.ListWorkspacesDeprecatedExecute(r) -} - -/* -ListWorkspacesDeprecated [DEPRECATED] List all workspaces - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @return WorkspaceAPIListWorkspacesDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) ListWorkspacesDeprecated(ctx context.Context) WorkspaceAPIListWorkspacesDeprecatedRequest { - return WorkspaceAPIListWorkspacesDeprecatedRequest{ - ApiService: a, - ctx: ctx, - } -} - -// Execute executes the request -// @return []Workspace -// Deprecated -func (a *WorkspaceAPIService) ListWorkspacesDeprecatedExecute(r WorkspaceAPIListWorkspacesDeprecatedRequest) ([]Workspace, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodGet - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue []Workspace - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.ListWorkspacesDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace" - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - if r.verbose != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "verbose", r.verbose, "form", "") - } - if r.labels != nil { - parameterAddToHeaderOrQuery(localVarQueryParams, "labels", r.labels, "form", "") - } - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - sandboxLabels *SandboxLabels - xBoxLiteOrganizationID *string -} - -func (r WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest) SandboxLabels(sandboxLabels SandboxLabels) WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest { - r.sandboxLabels = &sandboxLabels - return r -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest) Execute() (*SandboxLabels, *http.Response, error) { - return r.ApiService.ReplaceLabelsWorkspaceDeprecatedExecute(r) -} - -/* -ReplaceLabelsWorkspaceDeprecated [DEPRECATED] Replace workspace labels - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) ReplaceLabelsWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest { - return WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// @return SandboxLabels -// Deprecated -func (a *WorkspaceAPIService) ReplaceLabelsWorkspaceDeprecatedExecute(r WorkspaceAPIReplaceLabelsWorkspaceDeprecatedRequest) (*SandboxLabels, *http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPut - localVarPostBody interface{} - formFiles []formFile - localVarReturnValue *SandboxLabels - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.ReplaceLabelsWorkspaceDeprecated") - if err != nil { - return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/labels" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - if r.sandboxLabels == nil { - return localVarReturnValue, nil, reportError("sandboxLabels is required and must be specified") - } - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{"application/json"} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{"application/json"} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - // body params - localVarPostBody = r.sandboxLabels - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return localVarReturnValue, nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarReturnValue, localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) - if err != nil { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: err.Error(), - } - return localVarReturnValue, localVarHTTPResponse, newErr - } - - return localVarReturnValue, localVarHTTPResponse, nil -} - -type WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - interval float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.SetAutoArchiveIntervalWorkspaceDeprecatedExecute(r) -} - -/* -SetAutoArchiveIntervalWorkspaceDeprecated [DEPRECATED] Set workspace auto-archive interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param interval Auto-archive interval in minutes (0 means the maximum interval will be used) - @return WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) SetAutoArchiveIntervalWorkspaceDeprecated(ctx context.Context, workspaceId string, interval float32) WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest { - return WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - interval: interval, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) SetAutoArchiveIntervalWorkspaceDeprecatedExecute(r WorkspaceAPISetAutoArchiveIntervalWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.SetAutoArchiveIntervalWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/autoarchive/{interval}" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - interval float32 - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.SetAutostopIntervalWorkspaceDeprecatedExecute(r) -} - -/* -SetAutostopIntervalWorkspaceDeprecated [DEPRECATED] Set workspace auto-stop interval - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param interval Auto-stop interval in minutes (0 to disable) - @return WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) SetAutostopIntervalWorkspaceDeprecated(ctx context.Context, workspaceId string, interval float32) WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest { - return WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - interval: interval, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) SetAutostopIntervalWorkspaceDeprecatedExecute(r WorkspaceAPISetAutostopIntervalWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.SetAutostopIntervalWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/autostop/{interval}" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"interval"+"}", url.PathEscape(parameterValueToString(r.interval, "interval")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPIStartWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIStartWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIStartWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIStartWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.StartWorkspaceDeprecatedExecute(r) -} - -/* -StartWorkspaceDeprecated [DEPRECATED] Start workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIStartWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) StartWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIStartWorkspaceDeprecatedRequest { - return WorkspaceAPIStartWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) StartWorkspaceDeprecatedExecute(r WorkspaceAPIStartWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.StartWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/start" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPIStopWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIStopWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIStopWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIStopWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.StopWorkspaceDeprecatedExecute(r) -} - -/* -StopWorkspaceDeprecated [DEPRECATED] Stop workspace - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @return WorkspaceAPIStopWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) StopWorkspaceDeprecated(ctx context.Context, workspaceId string) WorkspaceAPIStopWorkspaceDeprecatedRequest { - return WorkspaceAPIStopWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) StopWorkspaceDeprecatedExecute(r WorkspaceAPIStopWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.StopWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/stop" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} - -type WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest struct { - ctx context.Context - ApiService WorkspaceAPI - workspaceId string - isPublic bool - xBoxLiteOrganizationID *string -} - -// Use with JWT to specify the organization ID -func (r WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest) XBoxLiteOrganizationID(xBoxLiteOrganizationID string) WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest { - r.xBoxLiteOrganizationID = &xBoxLiteOrganizationID - return r -} - -func (r WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest) Execute() (*http.Response, error) { - return r.ApiService.UpdatePublicStatusWorkspaceDeprecatedExecute(r) -} - -/* -UpdatePublicStatusWorkspaceDeprecated [DEPRECATED] Update public status - - @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). - @param workspaceId ID of the workspace - @param isPublic Public status to set - @return WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest - -Deprecated -*/ -func (a *WorkspaceAPIService) UpdatePublicStatusWorkspaceDeprecated(ctx context.Context, workspaceId string, isPublic bool) WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest { - return WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest{ - ApiService: a, - ctx: ctx, - workspaceId: workspaceId, - isPublic: isPublic, - } -} - -// Execute executes the request -// Deprecated -func (a *WorkspaceAPIService) UpdatePublicStatusWorkspaceDeprecatedExecute(r WorkspaceAPIUpdatePublicStatusWorkspaceDeprecatedRequest) (*http.Response, error) { - var ( - localVarHTTPMethod = http.MethodPost - localVarPostBody interface{} - formFiles []formFile - ) - - localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "WorkspaceAPIService.UpdatePublicStatusWorkspaceDeprecated") - if err != nil { - return nil, &GenericOpenAPIError{error: err.Error()} - } - - localVarPath := localBasePath + "/workspace/{workspaceId}/public/{isPublic}" - localVarPath = strings.Replace(localVarPath, "{"+"workspaceId"+"}", url.PathEscape(parameterValueToString(r.workspaceId, "workspaceId")), -1) - localVarPath = strings.Replace(localVarPath, "{"+"isPublic"+"}", url.PathEscape(parameterValueToString(r.isPublic, "isPublic")), -1) - - localVarHeaderParams := make(map[string]string) - localVarQueryParams := url.Values{} - localVarFormParams := url.Values{} - - // to determine the Content-Type header - localVarHTTPContentTypes := []string{} - - // set Content-Type header - localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) - if localVarHTTPContentType != "" { - localVarHeaderParams["Content-Type"] = localVarHTTPContentType - } - - // to determine the Accept header - localVarHTTPHeaderAccepts := []string{} - - // set Accept header - localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) - if localVarHTTPHeaderAccept != "" { - localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept - } - if r.xBoxLiteOrganizationID != nil { - parameterAddToHeaderOrQuery(localVarHeaderParams, "X-BoxLite-Organization-ID", r.xBoxLiteOrganizationID, "simple", "") - } - req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) - if err != nil { - return nil, err - } - - localVarHTTPResponse, err := a.client.callAPI(req) - if err != nil || localVarHTTPResponse == nil { - return localVarHTTPResponse, err - } - - localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) - localVarHTTPResponse.Body.Close() - localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) - if err != nil { - return localVarHTTPResponse, err - } - - if localVarHTTPResponse.StatusCode >= 300 { - newErr := &GenericOpenAPIError{ - body: localVarBody, - error: localVarHTTPResponse.Status, - } - return localVarHTTPResponse, newErr - } - - return localVarHTTPResponse, nil -} diff --git a/apps/api-client-go/client.go b/apps/api-client-go/client.go index ce2c556fc..7ae5fadbb 100644 --- a/apps/api-client-go/client.go +++ b/apps/api-client-go/client.go @@ -56,9 +56,11 @@ type APIClient struct { AuditAPI AuditAPI - ConfigAPI ConfigAPI + AuthAPI AuthAPI + + BoxAPI BoxAPI - DockerRegistryAPI DockerRegistryAPI + ConfigAPI ConfigAPI HealthAPI HealthAPI @@ -74,19 +76,11 @@ type APIClient struct { RunnersAPI RunnersAPI - SandboxAPI SandboxAPI - - SnapshotsAPI SnapshotsAPI - - ToolboxAPI ToolboxAPI - UsersAPI UsersAPI VolumesAPI VolumesAPI WebhooksAPI WebhooksAPI - - WorkspaceAPI WorkspaceAPI } type service struct { @@ -108,8 +102,9 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.AdminAPI = (*AdminAPIService)(&c.common) c.ApiKeysAPI = (*ApiKeysAPIService)(&c.common) c.AuditAPI = (*AuditAPIService)(&c.common) + c.AuthAPI = (*AuthAPIService)(&c.common) + c.BoxAPI = (*BoxAPIService)(&c.common) c.ConfigAPI = (*ConfigAPIService)(&c.common) - c.DockerRegistryAPI = (*DockerRegistryAPIService)(&c.common) c.HealthAPI = (*HealthAPIService)(&c.common) c.JobsAPI = (*JobsAPIService)(&c.common) c.ObjectStorageAPI = (*ObjectStorageAPIService)(&c.common) @@ -117,13 +112,9 @@ func NewAPIClient(cfg *Configuration) *APIClient { c.PreviewAPI = (*PreviewAPIService)(&c.common) c.RegionsAPI = (*RegionsAPIService)(&c.common) c.RunnersAPI = (*RunnersAPIService)(&c.common) - c.SandboxAPI = (*SandboxAPIService)(&c.common) - c.SnapshotsAPI = (*SnapshotsAPIService)(&c.common) - c.ToolboxAPI = (*ToolboxAPIService)(&c.common) c.UsersAPI = (*UsersAPIService)(&c.common) c.VolumesAPI = (*VolumesAPIService)(&c.common) c.WebhooksAPI = (*WebhooksAPIService)(&c.common) - c.WorkspaceAPI = (*WorkspaceAPIService)(&c.common) return c } @@ -494,6 +485,15 @@ func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err err *s = string(b) return nil } + if r, ok := v.(*io.Reader); ok { + *r = bytes.NewReader(b) + return nil + } + // Must stay before the JSON branch: json.Unmarshal would base64-decode into *[]byte. + if p, ok := v.(*[]byte); ok { + *p = b + return nil + } if f, ok := v.(*os.File); ok { f, err = os.CreateTemp("", "HttpClientFile") if err != nil { @@ -547,10 +547,7 @@ func addFile(w *multipart.Writer, fieldName, path string) error { if err != nil { return err } - err = file.Close() - if err != nil { - return err - } + defer file.Close() part, err := w.CreateFormFile(fieldName, filepath.Base(path)) if err != nil { diff --git a/apps/api-client-go/go.mod b/apps/api-client-go/go.mod index 61651b825..bc9a83cb6 100644 --- a/apps/api-client-go/go.mod +++ b/apps/api-client-go/go.mod @@ -1,6 +1,6 @@ module github.com/boxlite-ai/boxlite/libs/api-client-go -go 1.18 +go 1.23 require ( ) diff --git a/apps/api-client-go/model_account_provider.go b/apps/api-client-go/model_account_provider.go index c6489f199..ec49dd521 100644 --- a/apps/api-client-go/model_account_provider.go +++ b/apps/api-client-go/model_account_provider.go @@ -194,3 +194,5 @@ func (v *NullableAccountProvider) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_admin_box_item.go b/apps/api-client-go/model_admin_box_item.go new file mode 100644 index 000000000..a3f724e7e --- /dev/null +++ b/apps/api-client-go/model_admin_box_item.go @@ -0,0 +1,394 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminBoxItem type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminBoxItem{} + +// AdminBoxItem struct for AdminBoxItem +type AdminBoxItem struct { + // Box ID + Id string `json:"id"` + // Organization ID + OrganizationId string `json:"organizationId"` + State BoxState `json:"state"` + // Runner ID the box is assigned to + RunnerId *string `json:"runnerId,omitempty"` + // Allocated CPU (vCPUs) + Cpu float32 `json:"cpu"` + // Allocated memory in GiB + MemoryGiB *float32 `json:"memoryGiB,omitempty"` + // Creation timestamp + CreatedAt string `json:"createdAt"` + Owner AdminBoxOwner `json:"owner"` + AdditionalProperties map[string]interface{} +} + +type _AdminBoxItem AdminBoxItem + +// NewAdminBoxItem instantiates a new AdminBoxItem object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminBoxItem(id string, organizationId string, state BoxState, cpu float32, createdAt string, owner AdminBoxOwner) *AdminBoxItem { + this := AdminBoxItem{} + this.Id = id + this.OrganizationId = organizationId + this.State = state + this.Cpu = cpu + this.CreatedAt = createdAt + this.Owner = owner + return &this +} + +// NewAdminBoxItemWithDefaults instantiates a new AdminBoxItem object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminBoxItemWithDefaults() *AdminBoxItem { + this := AdminBoxItem{} + return &this +} + +// GetId returns the Id field value +func (o *AdminBoxItem) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminBoxItem) SetId(v string) { + o.Id = v +} + +// GetOrganizationId returns the OrganizationId field value +func (o *AdminBoxItem) GetOrganizationId() string { + if o == nil { + var ret string + return ret + } + + return o.OrganizationId +} + +// GetOrganizationIdOk returns a tuple with the OrganizationId field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetOrganizationIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.OrganizationId, true +} + +// SetOrganizationId sets field value +func (o *AdminBoxItem) SetOrganizationId(v string) { + o.OrganizationId = v +} + +// GetState returns the State field value +func (o *AdminBoxItem) GetState() BoxState { + if o == nil { + var ret BoxState + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetStateOk() (*BoxState, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminBoxItem) SetState(v BoxState) { + o.State = v +} + +// GetRunnerId returns the RunnerId field value if set, zero value otherwise. +func (o *AdminBoxItem) GetRunnerId() string { + if o == nil || IsNil(o.RunnerId) { + var ret string + return ret + } + return *o.RunnerId +} + +// GetRunnerIdOk returns a tuple with the RunnerId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetRunnerIdOk() (*string, bool) { + if o == nil || IsNil(o.RunnerId) { + return nil, false + } + return o.RunnerId, true +} + +// HasRunnerId returns a boolean if a field has been set. +func (o *AdminBoxItem) HasRunnerId() bool { + if o != nil && !IsNil(o.RunnerId) { + return true + } + + return false +} + +// SetRunnerId gets a reference to the given string and assigns it to the RunnerId field. +func (o *AdminBoxItem) SetRunnerId(v string) { + o.RunnerId = &v +} + +// GetCpu returns the Cpu field value +func (o *AdminBoxItem) GetCpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Cpu +} + +// GetCpuOk returns a tuple with the Cpu field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetCpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Cpu, true +} + +// SetCpu sets field value +func (o *AdminBoxItem) SetCpu(v float32) { + o.Cpu = v +} + +// GetMemoryGiB returns the MemoryGiB field value if set, zero value otherwise. +func (o *AdminBoxItem) GetMemoryGiB() float32 { + if o == nil || IsNil(o.MemoryGiB) { + var ret float32 + return ret + } + return *o.MemoryGiB +} + +// GetMemoryGiBOk returns a tuple with the MemoryGiB field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetMemoryGiBOk() (*float32, bool) { + if o == nil || IsNil(o.MemoryGiB) { + return nil, false + } + return o.MemoryGiB, true +} + +// HasMemoryGiB returns a boolean if a field has been set. +func (o *AdminBoxItem) HasMemoryGiB() bool { + if o != nil && !IsNil(o.MemoryGiB) { + return true + } + + return false +} + +// SetMemoryGiB gets a reference to the given float32 and assigns it to the MemoryGiB field. +func (o *AdminBoxItem) SetMemoryGiB(v float32) { + o.MemoryGiB = &v +} + +// GetCreatedAt returns the CreatedAt field value +func (o *AdminBoxItem) GetCreatedAt() string { + if o == nil { + var ret string + return ret + } + + return o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetCreatedAtOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.CreatedAt, true +} + +// SetCreatedAt sets field value +func (o *AdminBoxItem) SetCreatedAt(v string) { + o.CreatedAt = v +} + +// GetOwner returns the Owner field value +func (o *AdminBoxItem) GetOwner() AdminBoxOwner { + if o == nil { + var ret AdminBoxOwner + return ret + } + + return o.Owner +} + +// GetOwnerOk returns a tuple with the Owner field value +// and a boolean to check if the value has been set. +func (o *AdminBoxItem) GetOwnerOk() (*AdminBoxOwner, bool) { + if o == nil { + return nil, false + } + return &o.Owner, true +} + +// SetOwner sets field value +func (o *AdminBoxItem) SetOwner(v AdminBoxOwner) { + o.Owner = v +} + +func (o AdminBoxItem) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminBoxItem) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["organizationId"] = o.OrganizationId + toSerialize["state"] = o.State + if !IsNil(o.RunnerId) { + toSerialize["runnerId"] = o.RunnerId + } + toSerialize["cpu"] = o.Cpu + if !IsNil(o.MemoryGiB) { + toSerialize["memoryGiB"] = o.MemoryGiB + } + toSerialize["createdAt"] = o.CreatedAt + toSerialize["owner"] = o.Owner + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminBoxItem) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "organizationId", + "state", + "cpu", + "createdAt", + "owner", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminBoxItem := _AdminBoxItem{} + + err = json.Unmarshal(data, &varAdminBoxItem) + + if err != nil { + return err + } + + *o = AdminBoxItem(varAdminBoxItem) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "organizationId") + delete(additionalProperties, "state") + delete(additionalProperties, "runnerId") + delete(additionalProperties, "cpu") + delete(additionalProperties, "memoryGiB") + delete(additionalProperties, "createdAt") + delete(additionalProperties, "owner") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminBoxItem struct { + value *AdminBoxItem + isSet bool +} + +func (v NullableAdminBoxItem) Get() *AdminBoxItem { + return v.value +} + +func (v *NullableAdminBoxItem) Set(val *AdminBoxItem) { + v.value = val + v.isSet = true +} + +func (v NullableAdminBoxItem) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminBoxItem) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminBoxItem(val *AdminBoxItem) *NullableAdminBoxItem { + return &NullableAdminBoxItem{value: val, isSet: true} +} + +func (v NullableAdminBoxItem) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminBoxItem) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_box_owner.go b/apps/api-client-go/model_admin_box_owner.go new file mode 100644 index 000000000..bc179f590 --- /dev/null +++ b/apps/api-client-go/model_admin_box_owner.go @@ -0,0 +1,298 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminBoxOwner type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminBoxOwner{} + +// AdminBoxOwner struct for AdminBoxOwner +type AdminBoxOwner struct { + // Creator/user ID behind this owner group when available + UserId *string `json:"userId,omitempty"` + // Display name for the owner group + Name string `json:"name"` + // Owner email for personal organizations, blank when unavailable + Email string `json:"email"` + // Organization name backing this box + OrgName string `json:"orgName"` + // Whether this is a personal organization + Personal bool `json:"personal"` + AdditionalProperties map[string]interface{} +} + +type _AdminBoxOwner AdminBoxOwner + +// NewAdminBoxOwner instantiates a new AdminBoxOwner object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminBoxOwner(name string, email string, orgName string, personal bool) *AdminBoxOwner { + this := AdminBoxOwner{} + this.Name = name + this.Email = email + this.OrgName = orgName + this.Personal = personal + return &this +} + +// NewAdminBoxOwnerWithDefaults instantiates a new AdminBoxOwner object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminBoxOwnerWithDefaults() *AdminBoxOwner { + this := AdminBoxOwner{} + return &this +} + +// GetUserId returns the UserId field value if set, zero value otherwise. +func (o *AdminBoxOwner) GetUserId() string { + if o == nil || IsNil(o.UserId) { + var ret string + return ret + } + return *o.UserId +} + +// GetUserIdOk returns a tuple with the UserId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminBoxOwner) GetUserIdOk() (*string, bool) { + if o == nil || IsNil(o.UserId) { + return nil, false + } + return o.UserId, true +} + +// HasUserId returns a boolean if a field has been set. +func (o *AdminBoxOwner) HasUserId() bool { + if o != nil && !IsNil(o.UserId) { + return true + } + + return false +} + +// SetUserId gets a reference to the given string and assigns it to the UserId field. +func (o *AdminBoxOwner) SetUserId(v string) { + o.UserId = &v +} + +// GetName returns the Name field value +func (o *AdminBoxOwner) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *AdminBoxOwner) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *AdminBoxOwner) SetName(v string) { + o.Name = v +} + +// GetEmail returns the Email field value +func (o *AdminBoxOwner) GetEmail() string { + if o == nil { + var ret string + return ret + } + + return o.Email +} + +// GetEmailOk returns a tuple with the Email field value +// and a boolean to check if the value has been set. +func (o *AdminBoxOwner) GetEmailOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Email, true +} + +// SetEmail sets field value +func (o *AdminBoxOwner) SetEmail(v string) { + o.Email = v +} + +// GetOrgName returns the OrgName field value +func (o *AdminBoxOwner) GetOrgName() string { + if o == nil { + var ret string + return ret + } + + return o.OrgName +} + +// GetOrgNameOk returns a tuple with the OrgName field value +// and a boolean to check if the value has been set. +func (o *AdminBoxOwner) GetOrgNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.OrgName, true +} + +// SetOrgName sets field value +func (o *AdminBoxOwner) SetOrgName(v string) { + o.OrgName = v +} + +// GetPersonal returns the Personal field value +func (o *AdminBoxOwner) GetPersonal() bool { + if o == nil { + var ret bool + return ret + } + + return o.Personal +} + +// GetPersonalOk returns a tuple with the Personal field value +// and a boolean to check if the value has been set. +func (o *AdminBoxOwner) GetPersonalOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Personal, true +} + +// SetPersonal sets field value +func (o *AdminBoxOwner) SetPersonal(v bool) { + o.Personal = v +} + +func (o AdminBoxOwner) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminBoxOwner) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.UserId) { + toSerialize["userId"] = o.UserId + } + toSerialize["name"] = o.Name + toSerialize["email"] = o.Email + toSerialize["orgName"] = o.OrgName + toSerialize["personal"] = o.Personal + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminBoxOwner) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "name", + "email", + "orgName", + "personal", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminBoxOwner := _AdminBoxOwner{} + + err = json.Unmarshal(data, &varAdminBoxOwner) + + if err != nil { + return err + } + + *o = AdminBoxOwner(varAdminBoxOwner) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "userId") + delete(additionalProperties, "name") + delete(additionalProperties, "email") + delete(additionalProperties, "orgName") + delete(additionalProperties, "personal") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminBoxOwner struct { + value *AdminBoxOwner + isSet bool +} + +func (v NullableAdminBoxOwner) Get() *AdminBoxOwner { + return v.value +} + +func (v *NullableAdminBoxOwner) Set(val *AdminBoxOwner) { + v.value = val + v.isSet = true +} + +func (v NullableAdminBoxOwner) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminBoxOwner) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminBoxOwner(val *AdminBoxOwner) *NullableAdminBoxOwner { + return &NullableAdminBoxOwner{value: val, isSet: true} +} + +func (v NullableAdminBoxOwner) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminBoxOwner) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_create_runner.go b/apps/api-client-go/model_admin_create_runner.go index 8eebc70ed..6207317d7 100644 --- a/apps/api-client-go/model_admin_create_runner.go +++ b/apps/api-client-go/model_admin_create_runner.go @@ -481,3 +481,5 @@ func (v *NullableAdminCreateRunner) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_admin_machine_item.go b/apps/api-client-go/model_admin_machine_item.go new file mode 100644 index 000000000..5bb455837 --- /dev/null +++ b/apps/api-client-go/model_admin_machine_item.go @@ -0,0 +1,320 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminMachineItem type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminMachineItem{} + +// AdminMachineItem struct for AdminMachineItem +type AdminMachineItem struct { + // Runner / host ID + Host string `json:"host"` + // Region ID + Region string `json:"region"` + // CPU oversell ratio (allocatedCpu / totalCpu); 0 when capacity is 0 + OversellCpu float32 `json:"oversellCpu"` + // CPU utilisation waterline (0–100) + CpuWaterline float32 `json:"cpuWaterline"` + // Memory utilisation waterline (0–100) + MemWaterline float32 `json:"memWaterline"` + // Number of currently started boxes on this runner + Boxes float32 `json:"boxes"` + AdditionalProperties map[string]interface{} +} + +type _AdminMachineItem AdminMachineItem + +// NewAdminMachineItem instantiates a new AdminMachineItem object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminMachineItem(host string, region string, oversellCpu float32, cpuWaterline float32, memWaterline float32, boxes float32) *AdminMachineItem { + this := AdminMachineItem{} + this.Host = host + this.Region = region + this.OversellCpu = oversellCpu + this.CpuWaterline = cpuWaterline + this.MemWaterline = memWaterline + this.Boxes = boxes + return &this +} + +// NewAdminMachineItemWithDefaults instantiates a new AdminMachineItem object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminMachineItemWithDefaults() *AdminMachineItem { + this := AdminMachineItem{} + return &this +} + +// GetHost returns the Host field value +func (o *AdminMachineItem) GetHost() string { + if o == nil { + var ret string + return ret + } + + return o.Host +} + +// GetHostOk returns a tuple with the Host field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetHostOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Host, true +} + +// SetHost sets field value +func (o *AdminMachineItem) SetHost(v string) { + o.Host = v +} + +// GetRegion returns the Region field value +func (o *AdminMachineItem) GetRegion() string { + if o == nil { + var ret string + return ret + } + + return o.Region +} + +// GetRegionOk returns a tuple with the Region field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetRegionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Region, true +} + +// SetRegion sets field value +func (o *AdminMachineItem) SetRegion(v string) { + o.Region = v +} + +// GetOversellCpu returns the OversellCpu field value +func (o *AdminMachineItem) GetOversellCpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.OversellCpu +} + +// GetOversellCpuOk returns a tuple with the OversellCpu field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetOversellCpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.OversellCpu, true +} + +// SetOversellCpu sets field value +func (o *AdminMachineItem) SetOversellCpu(v float32) { + o.OversellCpu = v +} + +// GetCpuWaterline returns the CpuWaterline field value +func (o *AdminMachineItem) GetCpuWaterline() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.CpuWaterline +} + +// GetCpuWaterlineOk returns a tuple with the CpuWaterline field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetCpuWaterlineOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.CpuWaterline, true +} + +// SetCpuWaterline sets field value +func (o *AdminMachineItem) SetCpuWaterline(v float32) { + o.CpuWaterline = v +} + +// GetMemWaterline returns the MemWaterline field value +func (o *AdminMachineItem) GetMemWaterline() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.MemWaterline +} + +// GetMemWaterlineOk returns a tuple with the MemWaterline field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetMemWaterlineOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.MemWaterline, true +} + +// SetMemWaterline sets field value +func (o *AdminMachineItem) SetMemWaterline(v float32) { + o.MemWaterline = v +} + +// GetBoxes returns the Boxes field value +func (o *AdminMachineItem) GetBoxes() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Boxes +} + +// GetBoxesOk returns a tuple with the Boxes field value +// and a boolean to check if the value has been set. +func (o *AdminMachineItem) GetBoxesOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Boxes, true +} + +// SetBoxes sets field value +func (o *AdminMachineItem) SetBoxes(v float32) { + o.Boxes = v +} + +func (o AdminMachineItem) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminMachineItem) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["host"] = o.Host + toSerialize["region"] = o.Region + toSerialize["oversellCpu"] = o.OversellCpu + toSerialize["cpuWaterline"] = o.CpuWaterline + toSerialize["memWaterline"] = o.MemWaterline + toSerialize["boxes"] = o.Boxes + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminMachineItem) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "host", + "region", + "oversellCpu", + "cpuWaterline", + "memWaterline", + "boxes", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminMachineItem := _AdminMachineItem{} + + err = json.Unmarshal(data, &varAdminMachineItem) + + if err != nil { + return err + } + + *o = AdminMachineItem(varAdminMachineItem) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "host") + delete(additionalProperties, "region") + delete(additionalProperties, "oversellCpu") + delete(additionalProperties, "cpuWaterline") + delete(additionalProperties, "memWaterline") + delete(additionalProperties, "boxes") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminMachineItem struct { + value *AdminMachineItem + isSet bool +} + +func (v NullableAdminMachineItem) Get() *AdminMachineItem { + return v.value +} + +func (v *NullableAdminMachineItem) Set(val *AdminMachineItem) { + v.value = val + v.isSet = true +} + +func (v NullableAdminMachineItem) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminMachineItem) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminMachineItem(val *AdminMachineItem) *NullableAdminMachineItem { + return &NullableAdminMachineItem{value: val, isSet: true} +} + +func (v NullableAdminMachineItem) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminMachineItem) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_audit_log.go b/apps/api-client-go/model_admin_observability_audit_log.go new file mode 100644 index 000000000..c7b89f9d7 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_audit_log.go @@ -0,0 +1,545 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "time" + "fmt" +) + +// checks if the AdminObservabilityAuditLog type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityAuditLog{} + +// AdminObservabilityAuditLog struct for AdminObservabilityAuditLog +type AdminObservabilityAuditLog struct { + Id string `json:"id"` + ActorId string `json:"actorId"` + ActorEmail string `json:"actorEmail"` + OrganizationId *string `json:"organizationId,omitempty"` + Action string `json:"action"` + TargetType *string `json:"targetType,omitempty"` + TargetId *string `json:"targetId,omitempty"` + StatusCode *float32 `json:"statusCode,omitempty"` + ErrorMessage *string `json:"errorMessage,omitempty"` + Source *string `json:"source,omitempty"` + Metadata map[string]interface{} `json:"metadata,omitempty"` + CreatedAt time.Time `json:"createdAt"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityAuditLog AdminObservabilityAuditLog + +// NewAdminObservabilityAuditLog instantiates a new AdminObservabilityAuditLog object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityAuditLog(id string, actorId string, actorEmail string, action string, createdAt time.Time) *AdminObservabilityAuditLog { + this := AdminObservabilityAuditLog{} + this.Id = id + this.ActorId = actorId + this.ActorEmail = actorEmail + this.Action = action + this.CreatedAt = createdAt + return &this +} + +// NewAdminObservabilityAuditLogWithDefaults instantiates a new AdminObservabilityAuditLog object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityAuditLogWithDefaults() *AdminObservabilityAuditLog { + this := AdminObservabilityAuditLog{} + return &this +} + +// GetId returns the Id field value +func (o *AdminObservabilityAuditLog) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminObservabilityAuditLog) SetId(v string) { + o.Id = v +} + +// GetActorId returns the ActorId field value +func (o *AdminObservabilityAuditLog) GetActorId() string { + if o == nil { + var ret string + return ret + } + + return o.ActorId +} + +// GetActorIdOk returns a tuple with the ActorId field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetActorIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ActorId, true +} + +// SetActorId sets field value +func (o *AdminObservabilityAuditLog) SetActorId(v string) { + o.ActorId = v +} + +// GetActorEmail returns the ActorEmail field value +func (o *AdminObservabilityAuditLog) GetActorEmail() string { + if o == nil { + var ret string + return ret + } + + return o.ActorEmail +} + +// GetActorEmailOk returns a tuple with the ActorEmail field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetActorEmailOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ActorEmail, true +} + +// SetActorEmail sets field value +func (o *AdminObservabilityAuditLog) SetActorEmail(v string) { + o.ActorEmail = v +} + +// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetOrganizationId() string { + if o == nil || IsNil(o.OrganizationId) { + var ret string + return ret + } + return *o.OrganizationId +} + +// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetOrganizationIdOk() (*string, bool) { + if o == nil || IsNil(o.OrganizationId) { + return nil, false + } + return o.OrganizationId, true +} + +// HasOrganizationId returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasOrganizationId() bool { + if o != nil && !IsNil(o.OrganizationId) { + return true + } + + return false +} + +// SetOrganizationId gets a reference to the given string and assigns it to the OrganizationId field. +func (o *AdminObservabilityAuditLog) SetOrganizationId(v string) { + o.OrganizationId = &v +} + +// GetAction returns the Action field value +func (o *AdminObservabilityAuditLog) GetAction() string { + if o == nil { + var ret string + return ret + } + + return o.Action +} + +// GetActionOk returns a tuple with the Action field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetActionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Action, true +} + +// SetAction sets field value +func (o *AdminObservabilityAuditLog) SetAction(v string) { + o.Action = v +} + +// GetTargetType returns the TargetType field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetTargetType() string { + if o == nil || IsNil(o.TargetType) { + var ret string + return ret + } + return *o.TargetType +} + +// GetTargetTypeOk returns a tuple with the TargetType field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetTargetTypeOk() (*string, bool) { + if o == nil || IsNil(o.TargetType) { + return nil, false + } + return o.TargetType, true +} + +// HasTargetType returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasTargetType() bool { + if o != nil && !IsNil(o.TargetType) { + return true + } + + return false +} + +// SetTargetType gets a reference to the given string and assigns it to the TargetType field. +func (o *AdminObservabilityAuditLog) SetTargetType(v string) { + o.TargetType = &v +} + +// GetTargetId returns the TargetId field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetTargetId() string { + if o == nil || IsNil(o.TargetId) { + var ret string + return ret + } + return *o.TargetId +} + +// GetTargetIdOk returns a tuple with the TargetId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetTargetIdOk() (*string, bool) { + if o == nil || IsNil(o.TargetId) { + return nil, false + } + return o.TargetId, true +} + +// HasTargetId returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasTargetId() bool { + if o != nil && !IsNil(o.TargetId) { + return true + } + + return false +} + +// SetTargetId gets a reference to the given string and assigns it to the TargetId field. +func (o *AdminObservabilityAuditLog) SetTargetId(v string) { + o.TargetId = &v +} + +// GetStatusCode returns the StatusCode field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetStatusCode() float32 { + if o == nil || IsNil(o.StatusCode) { + var ret float32 + return ret + } + return *o.StatusCode +} + +// GetStatusCodeOk returns a tuple with the StatusCode field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetStatusCodeOk() (*float32, bool) { + if o == nil || IsNil(o.StatusCode) { + return nil, false + } + return o.StatusCode, true +} + +// HasStatusCode returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasStatusCode() bool { + if o != nil && !IsNil(o.StatusCode) { + return true + } + + return false +} + +// SetStatusCode gets a reference to the given float32 and assigns it to the StatusCode field. +func (o *AdminObservabilityAuditLog) SetStatusCode(v float32) { + o.StatusCode = &v +} + +// GetErrorMessage returns the ErrorMessage field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetErrorMessage() string { + if o == nil || IsNil(o.ErrorMessage) { + var ret string + return ret + } + return *o.ErrorMessage +} + +// GetErrorMessageOk returns a tuple with the ErrorMessage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetErrorMessageOk() (*string, bool) { + if o == nil || IsNil(o.ErrorMessage) { + return nil, false + } + return o.ErrorMessage, true +} + +// HasErrorMessage returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasErrorMessage() bool { + if o != nil && !IsNil(o.ErrorMessage) { + return true + } + + return false +} + +// SetErrorMessage gets a reference to the given string and assigns it to the ErrorMessage field. +func (o *AdminObservabilityAuditLog) SetErrorMessage(v string) { + o.ErrorMessage = &v +} + +// GetSource returns the Source field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetSource() string { + if o == nil || IsNil(o.Source) { + var ret string + return ret + } + return *o.Source +} + +// GetSourceOk returns a tuple with the Source field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetSourceOk() (*string, bool) { + if o == nil || IsNil(o.Source) { + return nil, false + } + return o.Source, true +} + +// HasSource returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasSource() bool { + if o != nil && !IsNil(o.Source) { + return true + } + + return false +} + +// SetSource gets a reference to the given string and assigns it to the Source field. +func (o *AdminObservabilityAuditLog) SetSource(v string) { + o.Source = &v +} + +// GetMetadata returns the Metadata field value if set, zero value otherwise. +func (o *AdminObservabilityAuditLog) GetMetadata() map[string]interface{} { + if o == nil || IsNil(o.Metadata) { + var ret map[string]interface{} + return ret + } + return o.Metadata +} + +// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetMetadataOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Metadata) { + return map[string]interface{}{}, false + } + return o.Metadata, true +} + +// HasMetadata returns a boolean if a field has been set. +func (o *AdminObservabilityAuditLog) HasMetadata() bool { + if o != nil && !IsNil(o.Metadata) { + return true + } + + return false +} + +// SetMetadata gets a reference to the given map[string]interface{} and assigns it to the Metadata field. +func (o *AdminObservabilityAuditLog) SetMetadata(v map[string]interface{}) { + o.Metadata = v +} + +// GetCreatedAt returns the CreatedAt field value +func (o *AdminObservabilityAuditLog) GetCreatedAt() time.Time { + if o == nil { + var ret time.Time + return ret + } + + return o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityAuditLog) GetCreatedAtOk() (*time.Time, bool) { + if o == nil { + return nil, false + } + return &o.CreatedAt, true +} + +// SetCreatedAt sets field value +func (o *AdminObservabilityAuditLog) SetCreatedAt(v time.Time) { + o.CreatedAt = v +} + +func (o AdminObservabilityAuditLog) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityAuditLog) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["actorId"] = o.ActorId + toSerialize["actorEmail"] = o.ActorEmail + if !IsNil(o.OrganizationId) { + toSerialize["organizationId"] = o.OrganizationId + } + toSerialize["action"] = o.Action + if !IsNil(o.TargetType) { + toSerialize["targetType"] = o.TargetType + } + if !IsNil(o.TargetId) { + toSerialize["targetId"] = o.TargetId + } + if !IsNil(o.StatusCode) { + toSerialize["statusCode"] = o.StatusCode + } + if !IsNil(o.ErrorMessage) { + toSerialize["errorMessage"] = o.ErrorMessage + } + if !IsNil(o.Source) { + toSerialize["source"] = o.Source + } + if !IsNil(o.Metadata) { + toSerialize["metadata"] = o.Metadata + } + toSerialize["createdAt"] = o.CreatedAt + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityAuditLog) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "actorId", + "actorEmail", + "action", + "createdAt", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityAuditLog := _AdminObservabilityAuditLog{} + + err = json.Unmarshal(data, &varAdminObservabilityAuditLog) + + if err != nil { + return err + } + + *o = AdminObservabilityAuditLog(varAdminObservabilityAuditLog) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "actorId") + delete(additionalProperties, "actorEmail") + delete(additionalProperties, "organizationId") + delete(additionalProperties, "action") + delete(additionalProperties, "targetType") + delete(additionalProperties, "targetId") + delete(additionalProperties, "statusCode") + delete(additionalProperties, "errorMessage") + delete(additionalProperties, "source") + delete(additionalProperties, "metadata") + delete(additionalProperties, "createdAt") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityAuditLog struct { + value *AdminObservabilityAuditLog + isSet bool +} + +func (v NullableAdminObservabilityAuditLog) Get() *AdminObservabilityAuditLog { + return v.value +} + +func (v *NullableAdminObservabilityAuditLog) Set(val *AdminObservabilityAuditLog) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityAuditLog) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityAuditLog) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityAuditLog(val *AdminObservabilityAuditLog) *NullableAdminObservabilityAuditLog { + return &NullableAdminObservabilityAuditLog{value: val, isSet: true} +} + +func (v NullableAdminObservabilityAuditLog) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityAuditLog) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_backend_status_dto.go b/apps/api-client-go/model_admin_observability_backend_status_dto.go new file mode 100644 index 000000000..3eb578c18 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_backend_status_dto.go @@ -0,0 +1,236 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityBackendStatusDto type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityBackendStatusDto{} + +// AdminObservabilityBackendStatusDto struct for AdminObservabilityBackendStatusDto +type AdminObservabilityBackendStatusDto struct { + // Whether ClickHouse/ClickStack query configuration is present + Configured bool `json:"configured"` + State string `json:"state"` + Message *string `json:"message,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityBackendStatusDto AdminObservabilityBackendStatusDto + +// NewAdminObservabilityBackendStatusDto instantiates a new AdminObservabilityBackendStatusDto object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityBackendStatusDto(configured bool, state string) *AdminObservabilityBackendStatusDto { + this := AdminObservabilityBackendStatusDto{} + this.Configured = configured + this.State = state + return &this +} + +// NewAdminObservabilityBackendStatusDtoWithDefaults instantiates a new AdminObservabilityBackendStatusDto object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityBackendStatusDtoWithDefaults() *AdminObservabilityBackendStatusDto { + this := AdminObservabilityBackendStatusDto{} + return &this +} + +// GetConfigured returns the Configured field value +func (o *AdminObservabilityBackendStatusDto) GetConfigured() bool { + if o == nil { + var ret bool + return ret + } + + return o.Configured +} + +// GetConfiguredOk returns a tuple with the Configured field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityBackendStatusDto) GetConfiguredOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Configured, true +} + +// SetConfigured sets field value +func (o *AdminObservabilityBackendStatusDto) SetConfigured(v bool) { + o.Configured = v +} + +// GetState returns the State field value +func (o *AdminObservabilityBackendStatusDto) GetState() string { + if o == nil { + var ret string + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityBackendStatusDto) GetStateOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminObservabilityBackendStatusDto) SetState(v string) { + o.State = v +} + +// GetMessage returns the Message field value if set, zero value otherwise. +func (o *AdminObservabilityBackendStatusDto) GetMessage() string { + if o == nil || IsNil(o.Message) { + var ret string + return ret + } + return *o.Message +} + +// GetMessageOk returns a tuple with the Message field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityBackendStatusDto) GetMessageOk() (*string, bool) { + if o == nil || IsNil(o.Message) { + return nil, false + } + return o.Message, true +} + +// HasMessage returns a boolean if a field has been set. +func (o *AdminObservabilityBackendStatusDto) HasMessage() bool { + if o != nil && !IsNil(o.Message) { + return true + } + + return false +} + +// SetMessage gets a reference to the given string and assigns it to the Message field. +func (o *AdminObservabilityBackendStatusDto) SetMessage(v string) { + o.Message = &v +} + +func (o AdminObservabilityBackendStatusDto) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityBackendStatusDto) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["configured"] = o.Configured + toSerialize["state"] = o.State + if !IsNil(o.Message) { + toSerialize["message"] = o.Message + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityBackendStatusDto) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "configured", + "state", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityBackendStatusDto := _AdminObservabilityBackendStatusDto{} + + err = json.Unmarshal(data, &varAdminObservabilityBackendStatusDto) + + if err != nil { + return err + } + + *o = AdminObservabilityBackendStatusDto(varAdminObservabilityBackendStatusDto) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "configured") + delete(additionalProperties, "state") + delete(additionalProperties, "message") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityBackendStatusDto struct { + value *AdminObservabilityBackendStatusDto + isSet bool +} + +func (v NullableAdminObservabilityBackendStatusDto) Get() *AdminObservabilityBackendStatusDto { + return v.value +} + +func (v *NullableAdminObservabilityBackendStatusDto) Set(val *AdminObservabilityBackendStatusDto) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityBackendStatusDto) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityBackendStatusDto) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityBackendStatusDto(val *AdminObservabilityBackendStatusDto) *NullableAdminObservabilityBackendStatusDto { + return &NullableAdminObservabilityBackendStatusDto{value: val, isSet: true} +} + +func (v NullableAdminObservabilityBackendStatusDto) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityBackendStatusDto) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_click_stack_links.go b/apps/api-client-go/model_admin_observability_click_stack_links.go new file mode 100644 index 000000000..facee9544 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_click_stack_links.go @@ -0,0 +1,502 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityClickStackLinks type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityClickStackLinks{} + +// AdminObservabilityClickStackLinks struct for AdminObservabilityClickStackLinks +type AdminObservabilityClickStackLinks struct { + Configured bool `json:"configured"` + Message *string `json:"message,omitempty"` + MissingSources []string `json:"missingSources,omitempty"` + SourceSetup []AdminObservabilityClickStackSourceSetup `json:"sourceSetup,omitempty"` + LogsUrl *string `json:"logsUrl,omitempty"` + DashboardUrl *string `json:"dashboardUrl,omitempty"` + TracesUrl *string `json:"tracesUrl,omitempty"` + MetricsUrl *string `json:"metricsUrl,omitempty"` + Query *string `json:"query,omitempty"` + QueryContext map[string]interface{} `json:"queryContext,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityClickStackLinks AdminObservabilityClickStackLinks + +// NewAdminObservabilityClickStackLinks instantiates a new AdminObservabilityClickStackLinks object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityClickStackLinks(configured bool) *AdminObservabilityClickStackLinks { + this := AdminObservabilityClickStackLinks{} + this.Configured = configured + return &this +} + +// NewAdminObservabilityClickStackLinksWithDefaults instantiates a new AdminObservabilityClickStackLinks object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityClickStackLinksWithDefaults() *AdminObservabilityClickStackLinks { + this := AdminObservabilityClickStackLinks{} + return &this +} + +// GetConfigured returns the Configured field value +func (o *AdminObservabilityClickStackLinks) GetConfigured() bool { + if o == nil { + var ret bool + return ret + } + + return o.Configured +} + +// GetConfiguredOk returns a tuple with the Configured field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetConfiguredOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Configured, true +} + +// SetConfigured sets field value +func (o *AdminObservabilityClickStackLinks) SetConfigured(v bool) { + o.Configured = v +} + +// GetMessage returns the Message field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetMessage() string { + if o == nil || IsNil(o.Message) { + var ret string + return ret + } + return *o.Message +} + +// GetMessageOk returns a tuple with the Message field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetMessageOk() (*string, bool) { + if o == nil || IsNil(o.Message) { + return nil, false + } + return o.Message, true +} + +// HasMessage returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasMessage() bool { + if o != nil && !IsNil(o.Message) { + return true + } + + return false +} + +// SetMessage gets a reference to the given string and assigns it to the Message field. +func (o *AdminObservabilityClickStackLinks) SetMessage(v string) { + o.Message = &v +} + +// GetMissingSources returns the MissingSources field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetMissingSources() []string { + if o == nil || IsNil(o.MissingSources) { + var ret []string + return ret + } + return o.MissingSources +} + +// GetMissingSourcesOk returns a tuple with the MissingSources field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetMissingSourcesOk() ([]string, bool) { + if o == nil || IsNil(o.MissingSources) { + return nil, false + } + return o.MissingSources, true +} + +// HasMissingSources returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasMissingSources() bool { + if o != nil && !IsNil(o.MissingSources) { + return true + } + + return false +} + +// SetMissingSources gets a reference to the given []string and assigns it to the MissingSources field. +func (o *AdminObservabilityClickStackLinks) SetMissingSources(v []string) { + o.MissingSources = v +} + +// GetSourceSetup returns the SourceSetup field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetSourceSetup() []AdminObservabilityClickStackSourceSetup { + if o == nil || IsNil(o.SourceSetup) { + var ret []AdminObservabilityClickStackSourceSetup + return ret + } + return o.SourceSetup +} + +// GetSourceSetupOk returns a tuple with the SourceSetup field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetSourceSetupOk() ([]AdminObservabilityClickStackSourceSetup, bool) { + if o == nil || IsNil(o.SourceSetup) { + return nil, false + } + return o.SourceSetup, true +} + +// HasSourceSetup returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasSourceSetup() bool { + if o != nil && !IsNil(o.SourceSetup) { + return true + } + + return false +} + +// SetSourceSetup gets a reference to the given []AdminObservabilityClickStackSourceSetup and assigns it to the SourceSetup field. +func (o *AdminObservabilityClickStackLinks) SetSourceSetup(v []AdminObservabilityClickStackSourceSetup) { + o.SourceSetup = v +} + +// GetLogsUrl returns the LogsUrl field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetLogsUrl() string { + if o == nil || IsNil(o.LogsUrl) { + var ret string + return ret + } + return *o.LogsUrl +} + +// GetLogsUrlOk returns a tuple with the LogsUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetLogsUrlOk() (*string, bool) { + if o == nil || IsNil(o.LogsUrl) { + return nil, false + } + return o.LogsUrl, true +} + +// HasLogsUrl returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasLogsUrl() bool { + if o != nil && !IsNil(o.LogsUrl) { + return true + } + + return false +} + +// SetLogsUrl gets a reference to the given string and assigns it to the LogsUrl field. +func (o *AdminObservabilityClickStackLinks) SetLogsUrl(v string) { + o.LogsUrl = &v +} + +// GetDashboardUrl returns the DashboardUrl field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetDashboardUrl() string { + if o == nil || IsNil(o.DashboardUrl) { + var ret string + return ret + } + return *o.DashboardUrl +} + +// GetDashboardUrlOk returns a tuple with the DashboardUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetDashboardUrlOk() (*string, bool) { + if o == nil || IsNil(o.DashboardUrl) { + return nil, false + } + return o.DashboardUrl, true +} + +// HasDashboardUrl returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasDashboardUrl() bool { + if o != nil && !IsNil(o.DashboardUrl) { + return true + } + + return false +} + +// SetDashboardUrl gets a reference to the given string and assigns it to the DashboardUrl field. +func (o *AdminObservabilityClickStackLinks) SetDashboardUrl(v string) { + o.DashboardUrl = &v +} + +// GetTracesUrl returns the TracesUrl field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetTracesUrl() string { + if o == nil || IsNil(o.TracesUrl) { + var ret string + return ret + } + return *o.TracesUrl +} + +// GetTracesUrlOk returns a tuple with the TracesUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetTracesUrlOk() (*string, bool) { + if o == nil || IsNil(o.TracesUrl) { + return nil, false + } + return o.TracesUrl, true +} + +// HasTracesUrl returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasTracesUrl() bool { + if o != nil && !IsNil(o.TracesUrl) { + return true + } + + return false +} + +// SetTracesUrl gets a reference to the given string and assigns it to the TracesUrl field. +func (o *AdminObservabilityClickStackLinks) SetTracesUrl(v string) { + o.TracesUrl = &v +} + +// GetMetricsUrl returns the MetricsUrl field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetMetricsUrl() string { + if o == nil || IsNil(o.MetricsUrl) { + var ret string + return ret + } + return *o.MetricsUrl +} + +// GetMetricsUrlOk returns a tuple with the MetricsUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetMetricsUrlOk() (*string, bool) { + if o == nil || IsNil(o.MetricsUrl) { + return nil, false + } + return o.MetricsUrl, true +} + +// HasMetricsUrl returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasMetricsUrl() bool { + if o != nil && !IsNil(o.MetricsUrl) { + return true + } + + return false +} + +// SetMetricsUrl gets a reference to the given string and assigns it to the MetricsUrl field. +func (o *AdminObservabilityClickStackLinks) SetMetricsUrl(v string) { + o.MetricsUrl = &v +} + +// GetQuery returns the Query field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetQuery() string { + if o == nil || IsNil(o.Query) { + var ret string + return ret + } + return *o.Query +} + +// GetQueryOk returns a tuple with the Query field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetQueryOk() (*string, bool) { + if o == nil || IsNil(o.Query) { + return nil, false + } + return o.Query, true +} + +// HasQuery returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasQuery() bool { + if o != nil && !IsNil(o.Query) { + return true + } + + return false +} + +// SetQuery gets a reference to the given string and assigns it to the Query field. +func (o *AdminObservabilityClickStackLinks) SetQuery(v string) { + o.Query = &v +} + +// GetQueryContext returns the QueryContext field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackLinks) GetQueryContext() map[string]interface{} { + if o == nil || IsNil(o.QueryContext) { + var ret map[string]interface{} + return ret + } + return o.QueryContext +} + +// GetQueryContextOk returns a tuple with the QueryContext field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackLinks) GetQueryContextOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.QueryContext) { + return map[string]interface{}{}, false + } + return o.QueryContext, true +} + +// HasQueryContext returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackLinks) HasQueryContext() bool { + if o != nil && !IsNil(o.QueryContext) { + return true + } + + return false +} + +// SetQueryContext gets a reference to the given map[string]interface{} and assigns it to the QueryContext field. +func (o *AdminObservabilityClickStackLinks) SetQueryContext(v map[string]interface{}) { + o.QueryContext = v +} + +func (o AdminObservabilityClickStackLinks) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityClickStackLinks) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["configured"] = o.Configured + if !IsNil(o.Message) { + toSerialize["message"] = o.Message + } + if !IsNil(o.MissingSources) { + toSerialize["missingSources"] = o.MissingSources + } + if !IsNil(o.SourceSetup) { + toSerialize["sourceSetup"] = o.SourceSetup + } + if !IsNil(o.LogsUrl) { + toSerialize["logsUrl"] = o.LogsUrl + } + if !IsNil(o.DashboardUrl) { + toSerialize["dashboardUrl"] = o.DashboardUrl + } + if !IsNil(o.TracesUrl) { + toSerialize["tracesUrl"] = o.TracesUrl + } + if !IsNil(o.MetricsUrl) { + toSerialize["metricsUrl"] = o.MetricsUrl + } + if !IsNil(o.Query) { + toSerialize["query"] = o.Query + } + if !IsNil(o.QueryContext) { + toSerialize["queryContext"] = o.QueryContext + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityClickStackLinks) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "configured", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityClickStackLinks := _AdminObservabilityClickStackLinks{} + + err = json.Unmarshal(data, &varAdminObservabilityClickStackLinks) + + if err != nil { + return err + } + + *o = AdminObservabilityClickStackLinks(varAdminObservabilityClickStackLinks) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "configured") + delete(additionalProperties, "message") + delete(additionalProperties, "missingSources") + delete(additionalProperties, "sourceSetup") + delete(additionalProperties, "logsUrl") + delete(additionalProperties, "dashboardUrl") + delete(additionalProperties, "tracesUrl") + delete(additionalProperties, "metricsUrl") + delete(additionalProperties, "query") + delete(additionalProperties, "queryContext") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityClickStackLinks struct { + value *AdminObservabilityClickStackLinks + isSet bool +} + +func (v NullableAdminObservabilityClickStackLinks) Get() *AdminObservabilityClickStackLinks { + return v.value +} + +func (v *NullableAdminObservabilityClickStackLinks) Set(val *AdminObservabilityClickStackLinks) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityClickStackLinks) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityClickStackLinks) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityClickStackLinks(val *AdminObservabilityClickStackLinks) *NullableAdminObservabilityClickStackLinks { + return &NullableAdminObservabilityClickStackLinks{value: val, isSet: true} +} + +func (v NullableAdminObservabilityClickStackLinks) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityClickStackLinks) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_click_stack_source_setup.go b/apps/api-client-go/model_admin_observability_click_stack_source_setup.go new file mode 100644 index 000000000..0fff97682 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_click_stack_source_setup.go @@ -0,0 +1,462 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityClickStackSourceSetup type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityClickStackSourceSetup{} + +// AdminObservabilityClickStackSourceSetup struct for AdminObservabilityClickStackSourceSetup +type AdminObservabilityClickStackSourceSetup struct { + Kind string `json:"kind"` + EnvVar string `json:"envVar"` + Name string `json:"name"` + DataType string `json:"dataType"` + Database string `json:"database"` + Table *string `json:"table,omitempty"` + TimestampColumn string `json:"timestampColumn"` + DefaultSelect *string `json:"defaultSelect,omitempty"` + Fields map[string]interface{} `json:"fields,omitempty"` + MetricTables map[string]interface{} `json:"metricTables,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityClickStackSourceSetup AdminObservabilityClickStackSourceSetup + +// NewAdminObservabilityClickStackSourceSetup instantiates a new AdminObservabilityClickStackSourceSetup object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityClickStackSourceSetup(kind string, envVar string, name string, dataType string, database string, timestampColumn string) *AdminObservabilityClickStackSourceSetup { + this := AdminObservabilityClickStackSourceSetup{} + this.Kind = kind + this.EnvVar = envVar + this.Name = name + this.DataType = dataType + this.Database = database + this.TimestampColumn = timestampColumn + return &this +} + +// NewAdminObservabilityClickStackSourceSetupWithDefaults instantiates a new AdminObservabilityClickStackSourceSetup object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityClickStackSourceSetupWithDefaults() *AdminObservabilityClickStackSourceSetup { + this := AdminObservabilityClickStackSourceSetup{} + return &this +} + +// GetKind returns the Kind field value +func (o *AdminObservabilityClickStackSourceSetup) GetKind() string { + if o == nil { + var ret string + return ret + } + + return o.Kind +} + +// GetKindOk returns a tuple with the Kind field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetKindOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Kind, true +} + +// SetKind sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetKind(v string) { + o.Kind = v +} + +// GetEnvVar returns the EnvVar field value +func (o *AdminObservabilityClickStackSourceSetup) GetEnvVar() string { + if o == nil { + var ret string + return ret + } + + return o.EnvVar +} + +// GetEnvVarOk returns a tuple with the EnvVar field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetEnvVarOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.EnvVar, true +} + +// SetEnvVar sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetEnvVar(v string) { + o.EnvVar = v +} + +// GetName returns the Name field value +func (o *AdminObservabilityClickStackSourceSetup) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetName(v string) { + o.Name = v +} + +// GetDataType returns the DataType field value +func (o *AdminObservabilityClickStackSourceSetup) GetDataType() string { + if o == nil { + var ret string + return ret + } + + return o.DataType +} + +// GetDataTypeOk returns a tuple with the DataType field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetDataTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.DataType, true +} + +// SetDataType sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetDataType(v string) { + o.DataType = v +} + +// GetDatabase returns the Database field value +func (o *AdminObservabilityClickStackSourceSetup) GetDatabase() string { + if o == nil { + var ret string + return ret + } + + return o.Database +} + +// GetDatabaseOk returns a tuple with the Database field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetDatabaseOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Database, true +} + +// SetDatabase sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetDatabase(v string) { + o.Database = v +} + +// GetTable returns the Table field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackSourceSetup) GetTable() string { + if o == nil || IsNil(o.Table) { + var ret string + return ret + } + return *o.Table +} + +// GetTableOk returns a tuple with the Table field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetTableOk() (*string, bool) { + if o == nil || IsNil(o.Table) { + return nil, false + } + return o.Table, true +} + +// HasTable returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackSourceSetup) HasTable() bool { + if o != nil && !IsNil(o.Table) { + return true + } + + return false +} + +// SetTable gets a reference to the given string and assigns it to the Table field. +func (o *AdminObservabilityClickStackSourceSetup) SetTable(v string) { + o.Table = &v +} + +// GetTimestampColumn returns the TimestampColumn field value +func (o *AdminObservabilityClickStackSourceSetup) GetTimestampColumn() string { + if o == nil { + var ret string + return ret + } + + return o.TimestampColumn +} + +// GetTimestampColumnOk returns a tuple with the TimestampColumn field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetTimestampColumnOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.TimestampColumn, true +} + +// SetTimestampColumn sets field value +func (o *AdminObservabilityClickStackSourceSetup) SetTimestampColumn(v string) { + o.TimestampColumn = v +} + +// GetDefaultSelect returns the DefaultSelect field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackSourceSetup) GetDefaultSelect() string { + if o == nil || IsNil(o.DefaultSelect) { + var ret string + return ret + } + return *o.DefaultSelect +} + +// GetDefaultSelectOk returns a tuple with the DefaultSelect field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetDefaultSelectOk() (*string, bool) { + if o == nil || IsNil(o.DefaultSelect) { + return nil, false + } + return o.DefaultSelect, true +} + +// HasDefaultSelect returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackSourceSetup) HasDefaultSelect() bool { + if o != nil && !IsNil(o.DefaultSelect) { + return true + } + + return false +} + +// SetDefaultSelect gets a reference to the given string and assigns it to the DefaultSelect field. +func (o *AdminObservabilityClickStackSourceSetup) SetDefaultSelect(v string) { + o.DefaultSelect = &v +} + +// GetFields returns the Fields field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackSourceSetup) GetFields() map[string]interface{} { + if o == nil || IsNil(o.Fields) { + var ret map[string]interface{} + return ret + } + return o.Fields +} + +// GetFieldsOk returns a tuple with the Fields field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetFieldsOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Fields) { + return map[string]interface{}{}, false + } + return o.Fields, true +} + +// HasFields returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackSourceSetup) HasFields() bool { + if o != nil && !IsNil(o.Fields) { + return true + } + + return false +} + +// SetFields gets a reference to the given map[string]interface{} and assigns it to the Fields field. +func (o *AdminObservabilityClickStackSourceSetup) SetFields(v map[string]interface{}) { + o.Fields = v +} + +// GetMetricTables returns the MetricTables field value if set, zero value otherwise. +func (o *AdminObservabilityClickStackSourceSetup) GetMetricTables() map[string]interface{} { + if o == nil || IsNil(o.MetricTables) { + var ret map[string]interface{} + return ret + } + return o.MetricTables +} + +// GetMetricTablesOk returns a tuple with the MetricTables field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityClickStackSourceSetup) GetMetricTablesOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.MetricTables) { + return map[string]interface{}{}, false + } + return o.MetricTables, true +} + +// HasMetricTables returns a boolean if a field has been set. +func (o *AdminObservabilityClickStackSourceSetup) HasMetricTables() bool { + if o != nil && !IsNil(o.MetricTables) { + return true + } + + return false +} + +// SetMetricTables gets a reference to the given map[string]interface{} and assigns it to the MetricTables field. +func (o *AdminObservabilityClickStackSourceSetup) SetMetricTables(v map[string]interface{}) { + o.MetricTables = v +} + +func (o AdminObservabilityClickStackSourceSetup) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityClickStackSourceSetup) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["kind"] = o.Kind + toSerialize["envVar"] = o.EnvVar + toSerialize["name"] = o.Name + toSerialize["dataType"] = o.DataType + toSerialize["database"] = o.Database + if !IsNil(o.Table) { + toSerialize["table"] = o.Table + } + toSerialize["timestampColumn"] = o.TimestampColumn + if !IsNil(o.DefaultSelect) { + toSerialize["defaultSelect"] = o.DefaultSelect + } + if !IsNil(o.Fields) { + toSerialize["fields"] = o.Fields + } + if !IsNil(o.MetricTables) { + toSerialize["metricTables"] = o.MetricTables + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityClickStackSourceSetup) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "kind", + "envVar", + "name", + "dataType", + "database", + "timestampColumn", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityClickStackSourceSetup := _AdminObservabilityClickStackSourceSetup{} + + err = json.Unmarshal(data, &varAdminObservabilityClickStackSourceSetup) + + if err != nil { + return err + } + + *o = AdminObservabilityClickStackSourceSetup(varAdminObservabilityClickStackSourceSetup) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "kind") + delete(additionalProperties, "envVar") + delete(additionalProperties, "name") + delete(additionalProperties, "dataType") + delete(additionalProperties, "database") + delete(additionalProperties, "table") + delete(additionalProperties, "timestampColumn") + delete(additionalProperties, "defaultSelect") + delete(additionalProperties, "fields") + delete(additionalProperties, "metricTables") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityClickStackSourceSetup struct { + value *AdminObservabilityClickStackSourceSetup + isSet bool +} + +func (v NullableAdminObservabilityClickStackSourceSetup) Get() *AdminObservabilityClickStackSourceSetup { + return v.value +} + +func (v *NullableAdminObservabilityClickStackSourceSetup) Set(val *AdminObservabilityClickStackSourceSetup) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityClickStackSourceSetup) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityClickStackSourceSetup) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityClickStackSourceSetup(val *AdminObservabilityClickStackSourceSetup) *NullableAdminObservabilityClickStackSourceSetup { + return &NullableAdminObservabilityClickStackSourceSetup{value: val, isSet: true} +} + +func (v NullableAdminObservabilityClickStackSourceSetup) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityClickStackSourceSetup) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_commands.go b/apps/api-client-go/model_admin_observability_commands.go new file mode 100644 index 000000000..601b62fc2 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_commands.go @@ -0,0 +1,198 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityCommands type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityCommands{} + +// AdminObservabilityCommands struct for AdminObservabilityCommands +type AdminObservabilityCommands struct { + Api string `json:"api"` + AiAgentPrompt string `json:"aiAgentPrompt"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityCommands AdminObservabilityCommands + +// NewAdminObservabilityCommands instantiates a new AdminObservabilityCommands object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityCommands(api string, aiAgentPrompt string) *AdminObservabilityCommands { + this := AdminObservabilityCommands{} + this.Api = api + this.AiAgentPrompt = aiAgentPrompt + return &this +} + +// NewAdminObservabilityCommandsWithDefaults instantiates a new AdminObservabilityCommands object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityCommandsWithDefaults() *AdminObservabilityCommands { + this := AdminObservabilityCommands{} + return &this +} + +// GetApi returns the Api field value +func (o *AdminObservabilityCommands) GetApi() string { + if o == nil { + var ret string + return ret + } + + return o.Api +} + +// GetApiOk returns a tuple with the Api field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCommands) GetApiOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Api, true +} + +// SetApi sets field value +func (o *AdminObservabilityCommands) SetApi(v string) { + o.Api = v +} + +// GetAiAgentPrompt returns the AiAgentPrompt field value +func (o *AdminObservabilityCommands) GetAiAgentPrompt() string { + if o == nil { + var ret string + return ret + } + + return o.AiAgentPrompt +} + +// GetAiAgentPromptOk returns a tuple with the AiAgentPrompt field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCommands) GetAiAgentPromptOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.AiAgentPrompt, true +} + +// SetAiAgentPrompt sets field value +func (o *AdminObservabilityCommands) SetAiAgentPrompt(v string) { + o.AiAgentPrompt = v +} + +func (o AdminObservabilityCommands) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityCommands) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["api"] = o.Api + toSerialize["aiAgentPrompt"] = o.AiAgentPrompt + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityCommands) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "api", + "aiAgentPrompt", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityCommands := _AdminObservabilityCommands{} + + err = json.Unmarshal(data, &varAdminObservabilityCommands) + + if err != nil { + return err + } + + *o = AdminObservabilityCommands(varAdminObservabilityCommands) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "api") + delete(additionalProperties, "aiAgentPrompt") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityCommands struct { + value *AdminObservabilityCommands + isSet bool +} + +func (v NullableAdminObservabilityCommands) Get() *AdminObservabilityCommands { + return v.value +} + +func (v *NullableAdminObservabilityCommands) Set(val *AdminObservabilityCommands) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityCommands) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityCommands) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityCommands(val *AdminObservabilityCommands) *NullableAdminObservabilityCommands { + return &NullableAdminObservabilityCommands{value: val, isSet: true} +} + +func (v NullableAdminObservabilityCommands) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityCommands) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_correlation.go b/apps/api-client-go/model_admin_observability_correlation.go new file mode 100644 index 000000000..1c640909f --- /dev/null +++ b/apps/api-client-go/model_admin_observability_correlation.go @@ -0,0 +1,459 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityCorrelation type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityCorrelation{} + +// AdminObservabilityCorrelation struct for AdminObservabilityCorrelation +type AdminObservabilityCorrelation struct { + TraceIds []string `json:"traceIds"` + OrgIds []string `json:"orgIds"` + UserIds []string `json:"userIds"` + BoxIds []string `json:"boxIds"` + RunnerIds []string `json:"runnerIds"` + MachineIds []string `json:"machineIds"` + RequestIds []string `json:"requestIds"` + OperationIds []string `json:"operationIds"` + ExecutionIds []string `json:"executionIds"` + JobIds []string `json:"jobIds"` + ServiceNames []string `json:"serviceNames"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityCorrelation AdminObservabilityCorrelation + +// NewAdminObservabilityCorrelation instantiates a new AdminObservabilityCorrelation object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityCorrelation(traceIds []string, orgIds []string, userIds []string, boxIds []string, runnerIds []string, machineIds []string, requestIds []string, operationIds []string, executionIds []string, jobIds []string, serviceNames []string) *AdminObservabilityCorrelation { + this := AdminObservabilityCorrelation{} + this.TraceIds = traceIds + this.OrgIds = orgIds + this.UserIds = userIds + this.BoxIds = boxIds + this.RunnerIds = runnerIds + this.MachineIds = machineIds + this.RequestIds = requestIds + this.OperationIds = operationIds + this.ExecutionIds = executionIds + this.JobIds = jobIds + this.ServiceNames = serviceNames + return &this +} + +// NewAdminObservabilityCorrelationWithDefaults instantiates a new AdminObservabilityCorrelation object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityCorrelationWithDefaults() *AdminObservabilityCorrelation { + this := AdminObservabilityCorrelation{} + return &this +} + +// GetTraceIds returns the TraceIds field value +func (o *AdminObservabilityCorrelation) GetTraceIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.TraceIds +} + +// GetTraceIdsOk returns a tuple with the TraceIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetTraceIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.TraceIds, true +} + +// SetTraceIds sets field value +func (o *AdminObservabilityCorrelation) SetTraceIds(v []string) { + o.TraceIds = v +} + +// GetOrgIds returns the OrgIds field value +func (o *AdminObservabilityCorrelation) GetOrgIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.OrgIds +} + +// GetOrgIdsOk returns a tuple with the OrgIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetOrgIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.OrgIds, true +} + +// SetOrgIds sets field value +func (o *AdminObservabilityCorrelation) SetOrgIds(v []string) { + o.OrgIds = v +} + +// GetUserIds returns the UserIds field value +func (o *AdminObservabilityCorrelation) GetUserIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.UserIds +} + +// GetUserIdsOk returns a tuple with the UserIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetUserIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.UserIds, true +} + +// SetUserIds sets field value +func (o *AdminObservabilityCorrelation) SetUserIds(v []string) { + o.UserIds = v +} + +// GetBoxIds returns the BoxIds field value +func (o *AdminObservabilityCorrelation) GetBoxIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.BoxIds +} + +// GetBoxIdsOk returns a tuple with the BoxIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetBoxIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.BoxIds, true +} + +// SetBoxIds sets field value +func (o *AdminObservabilityCorrelation) SetBoxIds(v []string) { + o.BoxIds = v +} + +// GetRunnerIds returns the RunnerIds field value +func (o *AdminObservabilityCorrelation) GetRunnerIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.RunnerIds +} + +// GetRunnerIdsOk returns a tuple with the RunnerIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetRunnerIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.RunnerIds, true +} + +// SetRunnerIds sets field value +func (o *AdminObservabilityCorrelation) SetRunnerIds(v []string) { + o.RunnerIds = v +} + +// GetMachineIds returns the MachineIds field value +func (o *AdminObservabilityCorrelation) GetMachineIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.MachineIds +} + +// GetMachineIdsOk returns a tuple with the MachineIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetMachineIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.MachineIds, true +} + +// SetMachineIds sets field value +func (o *AdminObservabilityCorrelation) SetMachineIds(v []string) { + o.MachineIds = v +} + +// GetRequestIds returns the RequestIds field value +func (o *AdminObservabilityCorrelation) GetRequestIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.RequestIds +} + +// GetRequestIdsOk returns a tuple with the RequestIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetRequestIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.RequestIds, true +} + +// SetRequestIds sets field value +func (o *AdminObservabilityCorrelation) SetRequestIds(v []string) { + o.RequestIds = v +} + +// GetOperationIds returns the OperationIds field value +func (o *AdminObservabilityCorrelation) GetOperationIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.OperationIds +} + +// GetOperationIdsOk returns a tuple with the OperationIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetOperationIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.OperationIds, true +} + +// SetOperationIds sets field value +func (o *AdminObservabilityCorrelation) SetOperationIds(v []string) { + o.OperationIds = v +} + +// GetExecutionIds returns the ExecutionIds field value +func (o *AdminObservabilityCorrelation) GetExecutionIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.ExecutionIds +} + +// GetExecutionIdsOk returns a tuple with the ExecutionIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetExecutionIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.ExecutionIds, true +} + +// SetExecutionIds sets field value +func (o *AdminObservabilityCorrelation) SetExecutionIds(v []string) { + o.ExecutionIds = v +} + +// GetJobIds returns the JobIds field value +func (o *AdminObservabilityCorrelation) GetJobIds() []string { + if o == nil { + var ret []string + return ret + } + + return o.JobIds +} + +// GetJobIdsOk returns a tuple with the JobIds field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetJobIdsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.JobIds, true +} + +// SetJobIds sets field value +func (o *AdminObservabilityCorrelation) SetJobIds(v []string) { + o.JobIds = v +} + +// GetServiceNames returns the ServiceNames field value +func (o *AdminObservabilityCorrelation) GetServiceNames() []string { + if o == nil { + var ret []string + return ret + } + + return o.ServiceNames +} + +// GetServiceNamesOk returns a tuple with the ServiceNames field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityCorrelation) GetServiceNamesOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.ServiceNames, true +} + +// SetServiceNames sets field value +func (o *AdminObservabilityCorrelation) SetServiceNames(v []string) { + o.ServiceNames = v +} + +func (o AdminObservabilityCorrelation) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityCorrelation) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["traceIds"] = o.TraceIds + toSerialize["orgIds"] = o.OrgIds + toSerialize["userIds"] = o.UserIds + toSerialize["boxIds"] = o.BoxIds + toSerialize["runnerIds"] = o.RunnerIds + toSerialize["machineIds"] = o.MachineIds + toSerialize["requestIds"] = o.RequestIds + toSerialize["operationIds"] = o.OperationIds + toSerialize["executionIds"] = o.ExecutionIds + toSerialize["jobIds"] = o.JobIds + toSerialize["serviceNames"] = o.ServiceNames + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityCorrelation) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "traceIds", + "orgIds", + "userIds", + "boxIds", + "runnerIds", + "machineIds", + "requestIds", + "operationIds", + "executionIds", + "jobIds", + "serviceNames", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityCorrelation := _AdminObservabilityCorrelation{} + + err = json.Unmarshal(data, &varAdminObservabilityCorrelation) + + if err != nil { + return err + } + + *o = AdminObservabilityCorrelation(varAdminObservabilityCorrelation) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "traceIds") + delete(additionalProperties, "orgIds") + delete(additionalProperties, "userIds") + delete(additionalProperties, "boxIds") + delete(additionalProperties, "runnerIds") + delete(additionalProperties, "machineIds") + delete(additionalProperties, "requestIds") + delete(additionalProperties, "operationIds") + delete(additionalProperties, "executionIds") + delete(additionalProperties, "jobIds") + delete(additionalProperties, "serviceNames") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityCorrelation struct { + value *AdminObservabilityCorrelation + isSet bool +} + +func (v NullableAdminObservabilityCorrelation) Get() *AdminObservabilityCorrelation { + return v.value +} + +func (v *NullableAdminObservabilityCorrelation) Set(val *AdminObservabilityCorrelation) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityCorrelation) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityCorrelation) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityCorrelation(val *AdminObservabilityCorrelation) *NullableAdminObservabilityCorrelation { + return &NullableAdminObservabilityCorrelation{value: val, isSet: true} +} + +func (v NullableAdminObservabilityCorrelation) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityCorrelation) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_external_links.go b/apps/api-client-go/model_admin_observability_external_links.go new file mode 100644 index 000000000..bbc8bfd23 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_external_links.go @@ -0,0 +1,169 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityExternalLinks type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityExternalLinks{} + +// AdminObservabilityExternalLinks struct for AdminObservabilityExternalLinks +type AdminObservabilityExternalLinks struct { + Clickstack AdminObservabilityClickStackLinks `json:"clickstack"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityExternalLinks AdminObservabilityExternalLinks + +// NewAdminObservabilityExternalLinks instantiates a new AdminObservabilityExternalLinks object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityExternalLinks(clickstack AdminObservabilityClickStackLinks) *AdminObservabilityExternalLinks { + this := AdminObservabilityExternalLinks{} + this.Clickstack = clickstack + return &this +} + +// NewAdminObservabilityExternalLinksWithDefaults instantiates a new AdminObservabilityExternalLinks object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityExternalLinksWithDefaults() *AdminObservabilityExternalLinks { + this := AdminObservabilityExternalLinks{} + return &this +} + +// GetClickstack returns the Clickstack field value +func (o *AdminObservabilityExternalLinks) GetClickstack() AdminObservabilityClickStackLinks { + if o == nil { + var ret AdminObservabilityClickStackLinks + return ret + } + + return o.Clickstack +} + +// GetClickstackOk returns a tuple with the Clickstack field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityExternalLinks) GetClickstackOk() (*AdminObservabilityClickStackLinks, bool) { + if o == nil { + return nil, false + } + return &o.Clickstack, true +} + +// SetClickstack sets field value +func (o *AdminObservabilityExternalLinks) SetClickstack(v AdminObservabilityClickStackLinks) { + o.Clickstack = v +} + +func (o AdminObservabilityExternalLinks) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityExternalLinks) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["clickstack"] = o.Clickstack + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityExternalLinks) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "clickstack", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityExternalLinks := _AdminObservabilityExternalLinks{} + + err = json.Unmarshal(data, &varAdminObservabilityExternalLinks) + + if err != nil { + return err + } + + *o = AdminObservabilityExternalLinks(varAdminObservabilityExternalLinks) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "clickstack") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityExternalLinks struct { + value *AdminObservabilityExternalLinks + isSet bool +} + +func (v NullableAdminObservabilityExternalLinks) Get() *AdminObservabilityExternalLinks { + return v.value +} + +func (v *NullableAdminObservabilityExternalLinks) Set(val *AdminObservabilityExternalLinks) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityExternalLinks) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityExternalLinks) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityExternalLinks(val *AdminObservabilityExternalLinks) *NullableAdminObservabilityExternalLinks { + return &NullableAdminObservabilityExternalLinks{value: val, isSet: true} +} + +func (v NullableAdminObservabilityExternalLinks) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityExternalLinks) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_investigate_response.go b/apps/api-client-go/model_admin_observability_investigate_response.go new file mode 100644 index 000000000..cfcf68aba --- /dev/null +++ b/apps/api-client-go/model_admin_observability_investigate_response.go @@ -0,0 +1,604 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityInvestigateResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityInvestigateResponse{} + +// AdminObservabilityInvestigateResponse struct for AdminObservabilityInvestigateResponse +type AdminObservabilityInvestigateResponse struct { + Resource AdminObservabilityResourceSummary `json:"resource"` + Correlation AdminObservabilityCorrelation `json:"correlation"` + Sources []AdminObservabilitySourceStatus `json:"sources"` + TraceSpans []TraceSpan `json:"traceSpans"` + Logs []LogEntry `json:"logs"` + Metrics MetricsResponse `json:"metrics"` + Boxes []AdminBoxItem `json:"boxes"` + Runners []AdminRunnerItem `json:"runners"` + Machines []AdminMachineItem `json:"machines"` + AuditLogs []AdminObservabilityAuditLog `json:"auditLogs"` + Xlogs []AdminObservabilityXLog `json:"xlogs"` + S3Objects []AdminObservabilityS3Object `json:"s3Objects"` + Timeline []AdminObservabilityTimelineEvent `json:"timeline"` + Operations []AdminObservabilityOperation `json:"operations"` + Commands AdminObservabilityCommands `json:"commands"` + ExternalLinks AdminObservabilityExternalLinks `json:"externalLinks"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityInvestigateResponse AdminObservabilityInvestigateResponse + +// NewAdminObservabilityInvestigateResponse instantiates a new AdminObservabilityInvestigateResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityInvestigateResponse(resource AdminObservabilityResourceSummary, correlation AdminObservabilityCorrelation, sources []AdminObservabilitySourceStatus, traceSpans []TraceSpan, logs []LogEntry, metrics MetricsResponse, boxes []AdminBoxItem, runners []AdminRunnerItem, machines []AdminMachineItem, auditLogs []AdminObservabilityAuditLog, xlogs []AdminObservabilityXLog, s3Objects []AdminObservabilityS3Object, timeline []AdminObservabilityTimelineEvent, operations []AdminObservabilityOperation, commands AdminObservabilityCommands, externalLinks AdminObservabilityExternalLinks) *AdminObservabilityInvestigateResponse { + this := AdminObservabilityInvestigateResponse{} + this.Resource = resource + this.Correlation = correlation + this.Sources = sources + this.TraceSpans = traceSpans + this.Logs = logs + this.Metrics = metrics + this.Boxes = boxes + this.Runners = runners + this.Machines = machines + this.AuditLogs = auditLogs + this.Xlogs = xlogs + this.S3Objects = s3Objects + this.Timeline = timeline + this.Operations = operations + this.Commands = commands + this.ExternalLinks = externalLinks + return &this +} + +// NewAdminObservabilityInvestigateResponseWithDefaults instantiates a new AdminObservabilityInvestigateResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityInvestigateResponseWithDefaults() *AdminObservabilityInvestigateResponse { + this := AdminObservabilityInvestigateResponse{} + return &this +} + +// GetResource returns the Resource field value +func (o *AdminObservabilityInvestigateResponse) GetResource() AdminObservabilityResourceSummary { + if o == nil { + var ret AdminObservabilityResourceSummary + return ret + } + + return o.Resource +} + +// GetResourceOk returns a tuple with the Resource field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetResourceOk() (*AdminObservabilityResourceSummary, bool) { + if o == nil { + return nil, false + } + return &o.Resource, true +} + +// SetResource sets field value +func (o *AdminObservabilityInvestigateResponse) SetResource(v AdminObservabilityResourceSummary) { + o.Resource = v +} + +// GetCorrelation returns the Correlation field value +func (o *AdminObservabilityInvestigateResponse) GetCorrelation() AdminObservabilityCorrelation { + if o == nil { + var ret AdminObservabilityCorrelation + return ret + } + + return o.Correlation +} + +// GetCorrelationOk returns a tuple with the Correlation field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetCorrelationOk() (*AdminObservabilityCorrelation, bool) { + if o == nil { + return nil, false + } + return &o.Correlation, true +} + +// SetCorrelation sets field value +func (o *AdminObservabilityInvestigateResponse) SetCorrelation(v AdminObservabilityCorrelation) { + o.Correlation = v +} + +// GetSources returns the Sources field value +func (o *AdminObservabilityInvestigateResponse) GetSources() []AdminObservabilitySourceStatus { + if o == nil { + var ret []AdminObservabilitySourceStatus + return ret + } + + return o.Sources +} + +// GetSourcesOk returns a tuple with the Sources field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetSourcesOk() ([]AdminObservabilitySourceStatus, bool) { + if o == nil { + return nil, false + } + return o.Sources, true +} + +// SetSources sets field value +func (o *AdminObservabilityInvestigateResponse) SetSources(v []AdminObservabilitySourceStatus) { + o.Sources = v +} + +// GetTraceSpans returns the TraceSpans field value +func (o *AdminObservabilityInvestigateResponse) GetTraceSpans() []TraceSpan { + if o == nil { + var ret []TraceSpan + return ret + } + + return o.TraceSpans +} + +// GetTraceSpansOk returns a tuple with the TraceSpans field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetTraceSpansOk() ([]TraceSpan, bool) { + if o == nil { + return nil, false + } + return o.TraceSpans, true +} + +// SetTraceSpans sets field value +func (o *AdminObservabilityInvestigateResponse) SetTraceSpans(v []TraceSpan) { + o.TraceSpans = v +} + +// GetLogs returns the Logs field value +func (o *AdminObservabilityInvestigateResponse) GetLogs() []LogEntry { + if o == nil { + var ret []LogEntry + return ret + } + + return o.Logs +} + +// GetLogsOk returns a tuple with the Logs field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetLogsOk() ([]LogEntry, bool) { + if o == nil { + return nil, false + } + return o.Logs, true +} + +// SetLogs sets field value +func (o *AdminObservabilityInvestigateResponse) SetLogs(v []LogEntry) { + o.Logs = v +} + +// GetMetrics returns the Metrics field value +func (o *AdminObservabilityInvestigateResponse) GetMetrics() MetricsResponse { + if o == nil { + var ret MetricsResponse + return ret + } + + return o.Metrics +} + +// GetMetricsOk returns a tuple with the Metrics field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetMetricsOk() (*MetricsResponse, bool) { + if o == nil { + return nil, false + } + return &o.Metrics, true +} + +// SetMetrics sets field value +func (o *AdminObservabilityInvestigateResponse) SetMetrics(v MetricsResponse) { + o.Metrics = v +} + +// GetBoxes returns the Boxes field value +func (o *AdminObservabilityInvestigateResponse) GetBoxes() []AdminBoxItem { + if o == nil { + var ret []AdminBoxItem + return ret + } + + return o.Boxes +} + +// GetBoxesOk returns a tuple with the Boxes field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetBoxesOk() ([]AdminBoxItem, bool) { + if o == nil { + return nil, false + } + return o.Boxes, true +} + +// SetBoxes sets field value +func (o *AdminObservabilityInvestigateResponse) SetBoxes(v []AdminBoxItem) { + o.Boxes = v +} + +// GetRunners returns the Runners field value +func (o *AdminObservabilityInvestigateResponse) GetRunners() []AdminRunnerItem { + if o == nil { + var ret []AdminRunnerItem + return ret + } + + return o.Runners +} + +// GetRunnersOk returns a tuple with the Runners field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetRunnersOk() ([]AdminRunnerItem, bool) { + if o == nil { + return nil, false + } + return o.Runners, true +} + +// SetRunners sets field value +func (o *AdminObservabilityInvestigateResponse) SetRunners(v []AdminRunnerItem) { + o.Runners = v +} + +// GetMachines returns the Machines field value +func (o *AdminObservabilityInvestigateResponse) GetMachines() []AdminMachineItem { + if o == nil { + var ret []AdminMachineItem + return ret + } + + return o.Machines +} + +// GetMachinesOk returns a tuple with the Machines field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetMachinesOk() ([]AdminMachineItem, bool) { + if o == nil { + return nil, false + } + return o.Machines, true +} + +// SetMachines sets field value +func (o *AdminObservabilityInvestigateResponse) SetMachines(v []AdminMachineItem) { + o.Machines = v +} + +// GetAuditLogs returns the AuditLogs field value +func (o *AdminObservabilityInvestigateResponse) GetAuditLogs() []AdminObservabilityAuditLog { + if o == nil { + var ret []AdminObservabilityAuditLog + return ret + } + + return o.AuditLogs +} + +// GetAuditLogsOk returns a tuple with the AuditLogs field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetAuditLogsOk() ([]AdminObservabilityAuditLog, bool) { + if o == nil { + return nil, false + } + return o.AuditLogs, true +} + +// SetAuditLogs sets field value +func (o *AdminObservabilityInvestigateResponse) SetAuditLogs(v []AdminObservabilityAuditLog) { + o.AuditLogs = v +} + +// GetXlogs returns the Xlogs field value +func (o *AdminObservabilityInvestigateResponse) GetXlogs() []AdminObservabilityXLog { + if o == nil { + var ret []AdminObservabilityXLog + return ret + } + + return o.Xlogs +} + +// GetXlogsOk returns a tuple with the Xlogs field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetXlogsOk() ([]AdminObservabilityXLog, bool) { + if o == nil { + return nil, false + } + return o.Xlogs, true +} + +// SetXlogs sets field value +func (o *AdminObservabilityInvestigateResponse) SetXlogs(v []AdminObservabilityXLog) { + o.Xlogs = v +} + +// GetS3Objects returns the S3Objects field value +func (o *AdminObservabilityInvestigateResponse) GetS3Objects() []AdminObservabilityS3Object { + if o == nil { + var ret []AdminObservabilityS3Object + return ret + } + + return o.S3Objects +} + +// GetS3ObjectsOk returns a tuple with the S3Objects field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetS3ObjectsOk() ([]AdminObservabilityS3Object, bool) { + if o == nil { + return nil, false + } + return o.S3Objects, true +} + +// SetS3Objects sets field value +func (o *AdminObservabilityInvestigateResponse) SetS3Objects(v []AdminObservabilityS3Object) { + o.S3Objects = v +} + +// GetTimeline returns the Timeline field value +func (o *AdminObservabilityInvestigateResponse) GetTimeline() []AdminObservabilityTimelineEvent { + if o == nil { + var ret []AdminObservabilityTimelineEvent + return ret + } + + return o.Timeline +} + +// GetTimelineOk returns a tuple with the Timeline field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetTimelineOk() ([]AdminObservabilityTimelineEvent, bool) { + if o == nil { + return nil, false + } + return o.Timeline, true +} + +// SetTimeline sets field value +func (o *AdminObservabilityInvestigateResponse) SetTimeline(v []AdminObservabilityTimelineEvent) { + o.Timeline = v +} + +// GetOperations returns the Operations field value +func (o *AdminObservabilityInvestigateResponse) GetOperations() []AdminObservabilityOperation { + if o == nil { + var ret []AdminObservabilityOperation + return ret + } + + return o.Operations +} + +// GetOperationsOk returns a tuple with the Operations field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetOperationsOk() ([]AdminObservabilityOperation, bool) { + if o == nil { + return nil, false + } + return o.Operations, true +} + +// SetOperations sets field value +func (o *AdminObservabilityInvestigateResponse) SetOperations(v []AdminObservabilityOperation) { + o.Operations = v +} + +// GetCommands returns the Commands field value +func (o *AdminObservabilityInvestigateResponse) GetCommands() AdminObservabilityCommands { + if o == nil { + var ret AdminObservabilityCommands + return ret + } + + return o.Commands +} + +// GetCommandsOk returns a tuple with the Commands field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetCommandsOk() (*AdminObservabilityCommands, bool) { + if o == nil { + return nil, false + } + return &o.Commands, true +} + +// SetCommands sets field value +func (o *AdminObservabilityInvestigateResponse) SetCommands(v AdminObservabilityCommands) { + o.Commands = v +} + +// GetExternalLinks returns the ExternalLinks field value +func (o *AdminObservabilityInvestigateResponse) GetExternalLinks() AdminObservabilityExternalLinks { + if o == nil { + var ret AdminObservabilityExternalLinks + return ret + } + + return o.ExternalLinks +} + +// GetExternalLinksOk returns a tuple with the ExternalLinks field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityInvestigateResponse) GetExternalLinksOk() (*AdminObservabilityExternalLinks, bool) { + if o == nil { + return nil, false + } + return &o.ExternalLinks, true +} + +// SetExternalLinks sets field value +func (o *AdminObservabilityInvestigateResponse) SetExternalLinks(v AdminObservabilityExternalLinks) { + o.ExternalLinks = v +} + +func (o AdminObservabilityInvestigateResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityInvestigateResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["resource"] = o.Resource + toSerialize["correlation"] = o.Correlation + toSerialize["sources"] = o.Sources + toSerialize["traceSpans"] = o.TraceSpans + toSerialize["logs"] = o.Logs + toSerialize["metrics"] = o.Metrics + toSerialize["boxes"] = o.Boxes + toSerialize["runners"] = o.Runners + toSerialize["machines"] = o.Machines + toSerialize["auditLogs"] = o.AuditLogs + toSerialize["xlogs"] = o.Xlogs + toSerialize["s3Objects"] = o.S3Objects + toSerialize["timeline"] = o.Timeline + toSerialize["operations"] = o.Operations + toSerialize["commands"] = o.Commands + toSerialize["externalLinks"] = o.ExternalLinks + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityInvestigateResponse) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "resource", + "correlation", + "sources", + "traceSpans", + "logs", + "metrics", + "boxes", + "runners", + "machines", + "auditLogs", + "xlogs", + "s3Objects", + "timeline", + "operations", + "commands", + "externalLinks", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityInvestigateResponse := _AdminObservabilityInvestigateResponse{} + + err = json.Unmarshal(data, &varAdminObservabilityInvestigateResponse) + + if err != nil { + return err + } + + *o = AdminObservabilityInvestigateResponse(varAdminObservabilityInvestigateResponse) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "resource") + delete(additionalProperties, "correlation") + delete(additionalProperties, "sources") + delete(additionalProperties, "traceSpans") + delete(additionalProperties, "logs") + delete(additionalProperties, "metrics") + delete(additionalProperties, "boxes") + delete(additionalProperties, "runners") + delete(additionalProperties, "machines") + delete(additionalProperties, "auditLogs") + delete(additionalProperties, "xlogs") + delete(additionalProperties, "s3Objects") + delete(additionalProperties, "timeline") + delete(additionalProperties, "operations") + delete(additionalProperties, "commands") + delete(additionalProperties, "externalLinks") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityInvestigateResponse struct { + value *AdminObservabilityInvestigateResponse + isSet bool +} + +func (v NullableAdminObservabilityInvestigateResponse) Get() *AdminObservabilityInvestigateResponse { + return v.value +} + +func (v *NullableAdminObservabilityInvestigateResponse) Set(val *AdminObservabilityInvestigateResponse) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityInvestigateResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityInvestigateResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityInvestigateResponse(val *AdminObservabilityInvestigateResponse) *NullableAdminObservabilityInvestigateResponse { + return &NullableAdminObservabilityInvestigateResponse{value: val, isSet: true} +} + +func (v NullableAdminObservabilityInvestigateResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityInvestigateResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_layer_signals_dto.go b/apps/api-client-go/model_admin_observability_layer_signals_dto.go new file mode 100644 index 000000000..75d5ba6c5 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_layer_signals_dto.go @@ -0,0 +1,227 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityLayerSignalsDto type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityLayerSignalsDto{} + +// AdminObservabilityLayerSignalsDto struct for AdminObservabilityLayerSignalsDto +type AdminObservabilityLayerSignalsDto struct { + Logs string `json:"logs"` + Traces string `json:"traces"` + Metrics string `json:"metrics"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityLayerSignalsDto AdminObservabilityLayerSignalsDto + +// NewAdminObservabilityLayerSignalsDto instantiates a new AdminObservabilityLayerSignalsDto object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityLayerSignalsDto(logs string, traces string, metrics string) *AdminObservabilityLayerSignalsDto { + this := AdminObservabilityLayerSignalsDto{} + this.Logs = logs + this.Traces = traces + this.Metrics = metrics + return &this +} + +// NewAdminObservabilityLayerSignalsDtoWithDefaults instantiates a new AdminObservabilityLayerSignalsDto object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityLayerSignalsDtoWithDefaults() *AdminObservabilityLayerSignalsDto { + this := AdminObservabilityLayerSignalsDto{} + return &this +} + +// GetLogs returns the Logs field value +func (o *AdminObservabilityLayerSignalsDto) GetLogs() string { + if o == nil { + var ret string + return ret + } + + return o.Logs +} + +// GetLogsOk returns a tuple with the Logs field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerSignalsDto) GetLogsOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Logs, true +} + +// SetLogs sets field value +func (o *AdminObservabilityLayerSignalsDto) SetLogs(v string) { + o.Logs = v +} + +// GetTraces returns the Traces field value +func (o *AdminObservabilityLayerSignalsDto) GetTraces() string { + if o == nil { + var ret string + return ret + } + + return o.Traces +} + +// GetTracesOk returns a tuple with the Traces field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerSignalsDto) GetTracesOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Traces, true +} + +// SetTraces sets field value +func (o *AdminObservabilityLayerSignalsDto) SetTraces(v string) { + o.Traces = v +} + +// GetMetrics returns the Metrics field value +func (o *AdminObservabilityLayerSignalsDto) GetMetrics() string { + if o == nil { + var ret string + return ret + } + + return o.Metrics +} + +// GetMetricsOk returns a tuple with the Metrics field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerSignalsDto) GetMetricsOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Metrics, true +} + +// SetMetrics sets field value +func (o *AdminObservabilityLayerSignalsDto) SetMetrics(v string) { + o.Metrics = v +} + +func (o AdminObservabilityLayerSignalsDto) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityLayerSignalsDto) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["logs"] = o.Logs + toSerialize["traces"] = o.Traces + toSerialize["metrics"] = o.Metrics + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityLayerSignalsDto) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "logs", + "traces", + "metrics", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityLayerSignalsDto := _AdminObservabilityLayerSignalsDto{} + + err = json.Unmarshal(data, &varAdminObservabilityLayerSignalsDto) + + if err != nil { + return err + } + + *o = AdminObservabilityLayerSignalsDto(varAdminObservabilityLayerSignalsDto) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "logs") + delete(additionalProperties, "traces") + delete(additionalProperties, "metrics") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityLayerSignalsDto struct { + value *AdminObservabilityLayerSignalsDto + isSet bool +} + +func (v NullableAdminObservabilityLayerSignalsDto) Get() *AdminObservabilityLayerSignalsDto { + return v.value +} + +func (v *NullableAdminObservabilityLayerSignalsDto) Set(val *AdminObservabilityLayerSignalsDto) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityLayerSignalsDto) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityLayerSignalsDto) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityLayerSignalsDto(val *AdminObservabilityLayerSignalsDto) *NullableAdminObservabilityLayerSignalsDto { + return &NullableAdminObservabilityLayerSignalsDto{value: val, isSet: true} +} + +func (v NullableAdminObservabilityLayerSignalsDto) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityLayerSignalsDto) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_layer_status_dto.go b/apps/api-client-go/model_admin_observability_layer_status_dto.go new file mode 100644 index 000000000..c6e17b93c --- /dev/null +++ b/apps/api-client-go/model_admin_observability_layer_status_dto.go @@ -0,0 +1,264 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityLayerStatusDto type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityLayerStatusDto{} + +// AdminObservabilityLayerStatusDto struct for AdminObservabilityLayerStatusDto +type AdminObservabilityLayerStatusDto struct { + Layer string `json:"layer"` + State string `json:"state"` + Signals AdminObservabilityLayerSignalsDto `json:"signals"` + LastSeen *string `json:"lastSeen,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityLayerStatusDto AdminObservabilityLayerStatusDto + +// NewAdminObservabilityLayerStatusDto instantiates a new AdminObservabilityLayerStatusDto object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityLayerStatusDto(layer string, state string, signals AdminObservabilityLayerSignalsDto) *AdminObservabilityLayerStatusDto { + this := AdminObservabilityLayerStatusDto{} + this.Layer = layer + this.State = state + this.Signals = signals + return &this +} + +// NewAdminObservabilityLayerStatusDtoWithDefaults instantiates a new AdminObservabilityLayerStatusDto object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityLayerStatusDtoWithDefaults() *AdminObservabilityLayerStatusDto { + this := AdminObservabilityLayerStatusDto{} + return &this +} + +// GetLayer returns the Layer field value +func (o *AdminObservabilityLayerStatusDto) GetLayer() string { + if o == nil { + var ret string + return ret + } + + return o.Layer +} + +// GetLayerOk returns a tuple with the Layer field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerStatusDto) GetLayerOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Layer, true +} + +// SetLayer sets field value +func (o *AdminObservabilityLayerStatusDto) SetLayer(v string) { + o.Layer = v +} + +// GetState returns the State field value +func (o *AdminObservabilityLayerStatusDto) GetState() string { + if o == nil { + var ret string + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerStatusDto) GetStateOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminObservabilityLayerStatusDto) SetState(v string) { + o.State = v +} + +// GetSignals returns the Signals field value +func (o *AdminObservabilityLayerStatusDto) GetSignals() AdminObservabilityLayerSignalsDto { + if o == nil { + var ret AdminObservabilityLayerSignalsDto + return ret + } + + return o.Signals +} + +// GetSignalsOk returns a tuple with the Signals field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerStatusDto) GetSignalsOk() (*AdminObservabilityLayerSignalsDto, bool) { + if o == nil { + return nil, false + } + return &o.Signals, true +} + +// SetSignals sets field value +func (o *AdminObservabilityLayerStatusDto) SetSignals(v AdminObservabilityLayerSignalsDto) { + o.Signals = v +} + +// GetLastSeen returns the LastSeen field value if set, zero value otherwise. +func (o *AdminObservabilityLayerStatusDto) GetLastSeen() string { + if o == nil || IsNil(o.LastSeen) { + var ret string + return ret + } + return *o.LastSeen +} + +// GetLastSeenOk returns a tuple with the LastSeen field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityLayerStatusDto) GetLastSeenOk() (*string, bool) { + if o == nil || IsNil(o.LastSeen) { + return nil, false + } + return o.LastSeen, true +} + +// HasLastSeen returns a boolean if a field has been set. +func (o *AdminObservabilityLayerStatusDto) HasLastSeen() bool { + if o != nil && !IsNil(o.LastSeen) { + return true + } + + return false +} + +// SetLastSeen gets a reference to the given string and assigns it to the LastSeen field. +func (o *AdminObservabilityLayerStatusDto) SetLastSeen(v string) { + o.LastSeen = &v +} + +func (o AdminObservabilityLayerStatusDto) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityLayerStatusDto) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["layer"] = o.Layer + toSerialize["state"] = o.State + toSerialize["signals"] = o.Signals + if !IsNil(o.LastSeen) { + toSerialize["lastSeen"] = o.LastSeen + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityLayerStatusDto) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "layer", + "state", + "signals", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityLayerStatusDto := _AdminObservabilityLayerStatusDto{} + + err = json.Unmarshal(data, &varAdminObservabilityLayerStatusDto) + + if err != nil { + return err + } + + *o = AdminObservabilityLayerStatusDto(varAdminObservabilityLayerStatusDto) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "layer") + delete(additionalProperties, "state") + delete(additionalProperties, "signals") + delete(additionalProperties, "lastSeen") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityLayerStatusDto struct { + value *AdminObservabilityLayerStatusDto + isSet bool +} + +func (v NullableAdminObservabilityLayerStatusDto) Get() *AdminObservabilityLayerStatusDto { + return v.value +} + +func (v *NullableAdminObservabilityLayerStatusDto) Set(val *AdminObservabilityLayerStatusDto) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityLayerStatusDto) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityLayerStatusDto) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityLayerStatusDto(val *AdminObservabilityLayerStatusDto) *NullableAdminObservabilityLayerStatusDto { + return &NullableAdminObservabilityLayerStatusDto{value: val, isSet: true} +} + +func (v NullableAdminObservabilityLayerStatusDto) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityLayerStatusDto) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_operation.go b/apps/api-client-go/model_admin_observability_operation.go new file mode 100644 index 000000000..63fda86a9 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_operation.go @@ -0,0 +1,351 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityOperation type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityOperation{} + +// AdminObservabilityOperation struct for AdminObservabilityOperation +type AdminObservabilityOperation struct { + Id string `json:"id"` + Label string `json:"label"` + State string `json:"state"` + Method string `json:"method"` + Path string `json:"path"` + Reason string `json:"reason"` + TargetId *string `json:"targetId,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityOperation AdminObservabilityOperation + +// NewAdminObservabilityOperation instantiates a new AdminObservabilityOperation object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityOperation(id string, label string, state string, method string, path string, reason string) *AdminObservabilityOperation { + this := AdminObservabilityOperation{} + this.Id = id + this.Label = label + this.State = state + this.Method = method + this.Path = path + this.Reason = reason + return &this +} + +// NewAdminObservabilityOperationWithDefaults instantiates a new AdminObservabilityOperation object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityOperationWithDefaults() *AdminObservabilityOperation { + this := AdminObservabilityOperation{} + return &this +} + +// GetId returns the Id field value +func (o *AdminObservabilityOperation) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminObservabilityOperation) SetId(v string) { + o.Id = v +} + +// GetLabel returns the Label field value +func (o *AdminObservabilityOperation) GetLabel() string { + if o == nil { + var ret string + return ret + } + + return o.Label +} + +// GetLabelOk returns a tuple with the Label field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetLabelOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Label, true +} + +// SetLabel sets field value +func (o *AdminObservabilityOperation) SetLabel(v string) { + o.Label = v +} + +// GetState returns the State field value +func (o *AdminObservabilityOperation) GetState() string { + if o == nil { + var ret string + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetStateOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminObservabilityOperation) SetState(v string) { + o.State = v +} + +// GetMethod returns the Method field value +func (o *AdminObservabilityOperation) GetMethod() string { + if o == nil { + var ret string + return ret + } + + return o.Method +} + +// GetMethodOk returns a tuple with the Method field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetMethodOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Method, true +} + +// SetMethod sets field value +func (o *AdminObservabilityOperation) SetMethod(v string) { + o.Method = v +} + +// GetPath returns the Path field value +func (o *AdminObservabilityOperation) GetPath() string { + if o == nil { + var ret string + return ret + } + + return o.Path +} + +// GetPathOk returns a tuple with the Path field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetPathOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Path, true +} + +// SetPath sets field value +func (o *AdminObservabilityOperation) SetPath(v string) { + o.Path = v +} + +// GetReason returns the Reason field value +func (o *AdminObservabilityOperation) GetReason() string { + if o == nil { + var ret string + return ret + } + + return o.Reason +} + +// GetReasonOk returns a tuple with the Reason field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetReasonOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Reason, true +} + +// SetReason sets field value +func (o *AdminObservabilityOperation) SetReason(v string) { + o.Reason = v +} + +// GetTargetId returns the TargetId field value if set, zero value otherwise. +func (o *AdminObservabilityOperation) GetTargetId() string { + if o == nil || IsNil(o.TargetId) { + var ret string + return ret + } + return *o.TargetId +} + +// GetTargetIdOk returns a tuple with the TargetId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityOperation) GetTargetIdOk() (*string, bool) { + if o == nil || IsNil(o.TargetId) { + return nil, false + } + return o.TargetId, true +} + +// HasTargetId returns a boolean if a field has been set. +func (o *AdminObservabilityOperation) HasTargetId() bool { + if o != nil && !IsNil(o.TargetId) { + return true + } + + return false +} + +// SetTargetId gets a reference to the given string and assigns it to the TargetId field. +func (o *AdminObservabilityOperation) SetTargetId(v string) { + o.TargetId = &v +} + +func (o AdminObservabilityOperation) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityOperation) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["label"] = o.Label + toSerialize["state"] = o.State + toSerialize["method"] = o.Method + toSerialize["path"] = o.Path + toSerialize["reason"] = o.Reason + if !IsNil(o.TargetId) { + toSerialize["targetId"] = o.TargetId + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityOperation) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "label", + "state", + "method", + "path", + "reason", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityOperation := _AdminObservabilityOperation{} + + err = json.Unmarshal(data, &varAdminObservabilityOperation) + + if err != nil { + return err + } + + *o = AdminObservabilityOperation(varAdminObservabilityOperation) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "label") + delete(additionalProperties, "state") + delete(additionalProperties, "method") + delete(additionalProperties, "path") + delete(additionalProperties, "reason") + delete(additionalProperties, "targetId") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityOperation struct { + value *AdminObservabilityOperation + isSet bool +} + +func (v NullableAdminObservabilityOperation) Get() *AdminObservabilityOperation { + return v.value +} + +func (v *NullableAdminObservabilityOperation) Set(val *AdminObservabilityOperation) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityOperation) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityOperation) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityOperation(val *AdminObservabilityOperation) *NullableAdminObservabilityOperation { + return &NullableAdminObservabilityOperation{value: val, isSet: true} +} + +func (v NullableAdminObservabilityOperation) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityOperation) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_resource_summary.go b/apps/api-client-go/model_admin_observability_resource_summary.go new file mode 100644 index 000000000..c1a6c79a3 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_resource_summary.go @@ -0,0 +1,383 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityResourceSummary type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityResourceSummary{} + +// AdminObservabilityResourceSummary struct for AdminObservabilityResourceSummary +type AdminObservabilityResourceSummary struct { + Type string `json:"type"` + Title string `json:"title"` + Subtitle *string `json:"subtitle,omitempty"` + State *string `json:"state,omitempty"` + Owner *string `json:"owner,omitempty"` + Identifiers map[string]interface{} `json:"identifiers,omitempty"` + TimeRange map[string]interface{} `json:"timeRange,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityResourceSummary AdminObservabilityResourceSummary + +// NewAdminObservabilityResourceSummary instantiates a new AdminObservabilityResourceSummary object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityResourceSummary(type_ string, title string) *AdminObservabilityResourceSummary { + this := AdminObservabilityResourceSummary{} + this.Type = type_ + this.Title = title + return &this +} + +// NewAdminObservabilityResourceSummaryWithDefaults instantiates a new AdminObservabilityResourceSummary object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityResourceSummaryWithDefaults() *AdminObservabilityResourceSummary { + this := AdminObservabilityResourceSummary{} + return &this +} + +// GetType returns the Type field value +func (o *AdminObservabilityResourceSummary) GetType() string { + if o == nil { + var ret string + return ret + } + + return o.Type +} + +// GetTypeOk returns a tuple with the Type field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Type, true +} + +// SetType sets field value +func (o *AdminObservabilityResourceSummary) SetType(v string) { + o.Type = v +} + +// GetTitle returns the Title field value +func (o *AdminObservabilityResourceSummary) GetTitle() string { + if o == nil { + var ret string + return ret + } + + return o.Title +} + +// GetTitleOk returns a tuple with the Title field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetTitleOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Title, true +} + +// SetTitle sets field value +func (o *AdminObservabilityResourceSummary) SetTitle(v string) { + o.Title = v +} + +// GetSubtitle returns the Subtitle field value if set, zero value otherwise. +func (o *AdminObservabilityResourceSummary) GetSubtitle() string { + if o == nil || IsNil(o.Subtitle) { + var ret string + return ret + } + return *o.Subtitle +} + +// GetSubtitleOk returns a tuple with the Subtitle field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetSubtitleOk() (*string, bool) { + if o == nil || IsNil(o.Subtitle) { + return nil, false + } + return o.Subtitle, true +} + +// HasSubtitle returns a boolean if a field has been set. +func (o *AdminObservabilityResourceSummary) HasSubtitle() bool { + if o != nil && !IsNil(o.Subtitle) { + return true + } + + return false +} + +// SetSubtitle gets a reference to the given string and assigns it to the Subtitle field. +func (o *AdminObservabilityResourceSummary) SetSubtitle(v string) { + o.Subtitle = &v +} + +// GetState returns the State field value if set, zero value otherwise. +func (o *AdminObservabilityResourceSummary) GetState() string { + if o == nil || IsNil(o.State) { + var ret string + return ret + } + return *o.State +} + +// GetStateOk returns a tuple with the State field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetStateOk() (*string, bool) { + if o == nil || IsNil(o.State) { + return nil, false + } + return o.State, true +} + +// HasState returns a boolean if a field has been set. +func (o *AdminObservabilityResourceSummary) HasState() bool { + if o != nil && !IsNil(o.State) { + return true + } + + return false +} + +// SetState gets a reference to the given string and assigns it to the State field. +func (o *AdminObservabilityResourceSummary) SetState(v string) { + o.State = &v +} + +// GetOwner returns the Owner field value if set, zero value otherwise. +func (o *AdminObservabilityResourceSummary) GetOwner() string { + if o == nil || IsNil(o.Owner) { + var ret string + return ret + } + return *o.Owner +} + +// GetOwnerOk returns a tuple with the Owner field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetOwnerOk() (*string, bool) { + if o == nil || IsNil(o.Owner) { + return nil, false + } + return o.Owner, true +} + +// HasOwner returns a boolean if a field has been set. +func (o *AdminObservabilityResourceSummary) HasOwner() bool { + if o != nil && !IsNil(o.Owner) { + return true + } + + return false +} + +// SetOwner gets a reference to the given string and assigns it to the Owner field. +func (o *AdminObservabilityResourceSummary) SetOwner(v string) { + o.Owner = &v +} + +// GetIdentifiers returns the Identifiers field value if set, zero value otherwise. +func (o *AdminObservabilityResourceSummary) GetIdentifiers() map[string]interface{} { + if o == nil || IsNil(o.Identifiers) { + var ret map[string]interface{} + return ret + } + return o.Identifiers +} + +// GetIdentifiersOk returns a tuple with the Identifiers field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetIdentifiersOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Identifiers) { + return map[string]interface{}{}, false + } + return o.Identifiers, true +} + +// HasIdentifiers returns a boolean if a field has been set. +func (o *AdminObservabilityResourceSummary) HasIdentifiers() bool { + if o != nil && !IsNil(o.Identifiers) { + return true + } + + return false +} + +// SetIdentifiers gets a reference to the given map[string]interface{} and assigns it to the Identifiers field. +func (o *AdminObservabilityResourceSummary) SetIdentifiers(v map[string]interface{}) { + o.Identifiers = v +} + +// GetTimeRange returns the TimeRange field value if set, zero value otherwise. +func (o *AdminObservabilityResourceSummary) GetTimeRange() map[string]interface{} { + if o == nil || IsNil(o.TimeRange) { + var ret map[string]interface{} + return ret + } + return o.TimeRange +} + +// GetTimeRangeOk returns a tuple with the TimeRange field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityResourceSummary) GetTimeRangeOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.TimeRange) { + return map[string]interface{}{}, false + } + return o.TimeRange, true +} + +// HasTimeRange returns a boolean if a field has been set. +func (o *AdminObservabilityResourceSummary) HasTimeRange() bool { + if o != nil && !IsNil(o.TimeRange) { + return true + } + + return false +} + +// SetTimeRange gets a reference to the given map[string]interface{} and assigns it to the TimeRange field. +func (o *AdminObservabilityResourceSummary) SetTimeRange(v map[string]interface{}) { + o.TimeRange = v +} + +func (o AdminObservabilityResourceSummary) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityResourceSummary) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["type"] = o.Type + toSerialize["title"] = o.Title + if !IsNil(o.Subtitle) { + toSerialize["subtitle"] = o.Subtitle + } + if !IsNil(o.State) { + toSerialize["state"] = o.State + } + if !IsNil(o.Owner) { + toSerialize["owner"] = o.Owner + } + if !IsNil(o.Identifiers) { + toSerialize["identifiers"] = o.Identifiers + } + if !IsNil(o.TimeRange) { + toSerialize["timeRange"] = o.TimeRange + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityResourceSummary) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "type", + "title", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityResourceSummary := _AdminObservabilityResourceSummary{} + + err = json.Unmarshal(data, &varAdminObservabilityResourceSummary) + + if err != nil { + return err + } + + *o = AdminObservabilityResourceSummary(varAdminObservabilityResourceSummary) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "type") + delete(additionalProperties, "title") + delete(additionalProperties, "subtitle") + delete(additionalProperties, "state") + delete(additionalProperties, "owner") + delete(additionalProperties, "identifiers") + delete(additionalProperties, "timeRange") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityResourceSummary struct { + value *AdminObservabilityResourceSummary + isSet bool +} + +func (v NullableAdminObservabilityResourceSummary) Get() *AdminObservabilityResourceSummary { + return v.value +} + +func (v *NullableAdminObservabilityResourceSummary) Set(val *AdminObservabilityResourceSummary) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityResourceSummary) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityResourceSummary) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityResourceSummary(val *AdminObservabilityResourceSummary) *NullableAdminObservabilityResourceSummary { + return &NullableAdminObservabilityResourceSummary{value: val, isSet: true} +} + +func (v NullableAdminObservabilityResourceSummary) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityResourceSummary) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_s3_object.go b/apps/api-client-go/model_admin_observability_s3_object.go new file mode 100644 index 000000000..55bc704d4 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_s3_object.go @@ -0,0 +1,347 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "time" + "fmt" +) + +// checks if the AdminObservabilityS3Object type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityS3Object{} + +// AdminObservabilityS3Object struct for AdminObservabilityS3Object +type AdminObservabilityS3Object struct { + Bucket string `json:"bucket"` + Key string `json:"key"` + Size *float32 `json:"size,omitempty"` + LastModified *time.Time `json:"lastModified,omitempty"` + Etag *string `json:"etag,omitempty"` + MatchedBy *string `json:"matchedBy,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityS3Object AdminObservabilityS3Object + +// NewAdminObservabilityS3Object instantiates a new AdminObservabilityS3Object object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityS3Object(bucket string, key string) *AdminObservabilityS3Object { + this := AdminObservabilityS3Object{} + this.Bucket = bucket + this.Key = key + return &this +} + +// NewAdminObservabilityS3ObjectWithDefaults instantiates a new AdminObservabilityS3Object object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityS3ObjectWithDefaults() *AdminObservabilityS3Object { + this := AdminObservabilityS3Object{} + return &this +} + +// GetBucket returns the Bucket field value +func (o *AdminObservabilityS3Object) GetBucket() string { + if o == nil { + var ret string + return ret + } + + return o.Bucket +} + +// GetBucketOk returns a tuple with the Bucket field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetBucketOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Bucket, true +} + +// SetBucket sets field value +func (o *AdminObservabilityS3Object) SetBucket(v string) { + o.Bucket = v +} + +// GetKey returns the Key field value +func (o *AdminObservabilityS3Object) GetKey() string { + if o == nil { + var ret string + return ret + } + + return o.Key +} + +// GetKeyOk returns a tuple with the Key field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetKeyOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Key, true +} + +// SetKey sets field value +func (o *AdminObservabilityS3Object) SetKey(v string) { + o.Key = v +} + +// GetSize returns the Size field value if set, zero value otherwise. +func (o *AdminObservabilityS3Object) GetSize() float32 { + if o == nil || IsNil(o.Size) { + var ret float32 + return ret + } + return *o.Size +} + +// GetSizeOk returns a tuple with the Size field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetSizeOk() (*float32, bool) { + if o == nil || IsNil(o.Size) { + return nil, false + } + return o.Size, true +} + +// HasSize returns a boolean if a field has been set. +func (o *AdminObservabilityS3Object) HasSize() bool { + if o != nil && !IsNil(o.Size) { + return true + } + + return false +} + +// SetSize gets a reference to the given float32 and assigns it to the Size field. +func (o *AdminObservabilityS3Object) SetSize(v float32) { + o.Size = &v +} + +// GetLastModified returns the LastModified field value if set, zero value otherwise. +func (o *AdminObservabilityS3Object) GetLastModified() time.Time { + if o == nil || IsNil(o.LastModified) { + var ret time.Time + return ret + } + return *o.LastModified +} + +// GetLastModifiedOk returns a tuple with the LastModified field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetLastModifiedOk() (*time.Time, bool) { + if o == nil || IsNil(o.LastModified) { + return nil, false + } + return o.LastModified, true +} + +// HasLastModified returns a boolean if a field has been set. +func (o *AdminObservabilityS3Object) HasLastModified() bool { + if o != nil && !IsNil(o.LastModified) { + return true + } + + return false +} + +// SetLastModified gets a reference to the given time.Time and assigns it to the LastModified field. +func (o *AdminObservabilityS3Object) SetLastModified(v time.Time) { + o.LastModified = &v +} + +// GetEtag returns the Etag field value if set, zero value otherwise. +func (o *AdminObservabilityS3Object) GetEtag() string { + if o == nil || IsNil(o.Etag) { + var ret string + return ret + } + return *o.Etag +} + +// GetEtagOk returns a tuple with the Etag field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetEtagOk() (*string, bool) { + if o == nil || IsNil(o.Etag) { + return nil, false + } + return o.Etag, true +} + +// HasEtag returns a boolean if a field has been set. +func (o *AdminObservabilityS3Object) HasEtag() bool { + if o != nil && !IsNil(o.Etag) { + return true + } + + return false +} + +// SetEtag gets a reference to the given string and assigns it to the Etag field. +func (o *AdminObservabilityS3Object) SetEtag(v string) { + o.Etag = &v +} + +// GetMatchedBy returns the MatchedBy field value if set, zero value otherwise. +func (o *AdminObservabilityS3Object) GetMatchedBy() string { + if o == nil || IsNil(o.MatchedBy) { + var ret string + return ret + } + return *o.MatchedBy +} + +// GetMatchedByOk returns a tuple with the MatchedBy field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityS3Object) GetMatchedByOk() (*string, bool) { + if o == nil || IsNil(o.MatchedBy) { + return nil, false + } + return o.MatchedBy, true +} + +// HasMatchedBy returns a boolean if a field has been set. +func (o *AdminObservabilityS3Object) HasMatchedBy() bool { + if o != nil && !IsNil(o.MatchedBy) { + return true + } + + return false +} + +// SetMatchedBy gets a reference to the given string and assigns it to the MatchedBy field. +func (o *AdminObservabilityS3Object) SetMatchedBy(v string) { + o.MatchedBy = &v +} + +func (o AdminObservabilityS3Object) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityS3Object) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["bucket"] = o.Bucket + toSerialize["key"] = o.Key + if !IsNil(o.Size) { + toSerialize["size"] = o.Size + } + if !IsNil(o.LastModified) { + toSerialize["lastModified"] = o.LastModified + } + if !IsNil(o.Etag) { + toSerialize["etag"] = o.Etag + } + if !IsNil(o.MatchedBy) { + toSerialize["matchedBy"] = o.MatchedBy + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityS3Object) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "bucket", + "key", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityS3Object := _AdminObservabilityS3Object{} + + err = json.Unmarshal(data, &varAdminObservabilityS3Object) + + if err != nil { + return err + } + + *o = AdminObservabilityS3Object(varAdminObservabilityS3Object) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "bucket") + delete(additionalProperties, "key") + delete(additionalProperties, "size") + delete(additionalProperties, "lastModified") + delete(additionalProperties, "etag") + delete(additionalProperties, "matchedBy") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityS3Object struct { + value *AdminObservabilityS3Object + isSet bool +} + +func (v NullableAdminObservabilityS3Object) Get() *AdminObservabilityS3Object { + return v.value +} + +func (v *NullableAdminObservabilityS3Object) Set(val *AdminObservabilityS3Object) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityS3Object) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityS3Object) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityS3Object(val *AdminObservabilityS3Object) *NullableAdminObservabilityS3Object { + return &NullableAdminObservabilityS3Object{value: val, isSet: true} +} + +func (v NullableAdminObservabilityS3Object) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityS3Object) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_source_status.go b/apps/api-client-go/model_admin_observability_source_status.go new file mode 100644 index 000000000..e6af26323 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_source_status.go @@ -0,0 +1,272 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilitySourceStatus type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilitySourceStatus{} + +// AdminObservabilitySourceStatus struct for AdminObservabilitySourceStatus +type AdminObservabilitySourceStatus struct { + Source string `json:"source"` + State string `json:"state"` + Message *string `json:"message,omitempty"` + Count *float32 `json:"count,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilitySourceStatus AdminObservabilitySourceStatus + +// NewAdminObservabilitySourceStatus instantiates a new AdminObservabilitySourceStatus object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilitySourceStatus(source string, state string) *AdminObservabilitySourceStatus { + this := AdminObservabilitySourceStatus{} + this.Source = source + this.State = state + return &this +} + +// NewAdminObservabilitySourceStatusWithDefaults instantiates a new AdminObservabilitySourceStatus object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilitySourceStatusWithDefaults() *AdminObservabilitySourceStatus { + this := AdminObservabilitySourceStatus{} + return &this +} + +// GetSource returns the Source field value +func (o *AdminObservabilitySourceStatus) GetSource() string { + if o == nil { + var ret string + return ret + } + + return o.Source +} + +// GetSourceOk returns a tuple with the Source field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilitySourceStatus) GetSourceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Source, true +} + +// SetSource sets field value +func (o *AdminObservabilitySourceStatus) SetSource(v string) { + o.Source = v +} + +// GetState returns the State field value +func (o *AdminObservabilitySourceStatus) GetState() string { + if o == nil { + var ret string + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilitySourceStatus) GetStateOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminObservabilitySourceStatus) SetState(v string) { + o.State = v +} + +// GetMessage returns the Message field value if set, zero value otherwise. +func (o *AdminObservabilitySourceStatus) GetMessage() string { + if o == nil || IsNil(o.Message) { + var ret string + return ret + } + return *o.Message +} + +// GetMessageOk returns a tuple with the Message field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilitySourceStatus) GetMessageOk() (*string, bool) { + if o == nil || IsNil(o.Message) { + return nil, false + } + return o.Message, true +} + +// HasMessage returns a boolean if a field has been set. +func (o *AdminObservabilitySourceStatus) HasMessage() bool { + if o != nil && !IsNil(o.Message) { + return true + } + + return false +} + +// SetMessage gets a reference to the given string and assigns it to the Message field. +func (o *AdminObservabilitySourceStatus) SetMessage(v string) { + o.Message = &v +} + +// GetCount returns the Count field value if set, zero value otherwise. +func (o *AdminObservabilitySourceStatus) GetCount() float32 { + if o == nil || IsNil(o.Count) { + var ret float32 + return ret + } + return *o.Count +} + +// GetCountOk returns a tuple with the Count field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilitySourceStatus) GetCountOk() (*float32, bool) { + if o == nil || IsNil(o.Count) { + return nil, false + } + return o.Count, true +} + +// HasCount returns a boolean if a field has been set. +func (o *AdminObservabilitySourceStatus) HasCount() bool { + if o != nil && !IsNil(o.Count) { + return true + } + + return false +} + +// SetCount gets a reference to the given float32 and assigns it to the Count field. +func (o *AdminObservabilitySourceStatus) SetCount(v float32) { + o.Count = &v +} + +func (o AdminObservabilitySourceStatus) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilitySourceStatus) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["source"] = o.Source + toSerialize["state"] = o.State + if !IsNil(o.Message) { + toSerialize["message"] = o.Message + } + if !IsNil(o.Count) { + toSerialize["count"] = o.Count + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilitySourceStatus) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "source", + "state", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilitySourceStatus := _AdminObservabilitySourceStatus{} + + err = json.Unmarshal(data, &varAdminObservabilitySourceStatus) + + if err != nil { + return err + } + + *o = AdminObservabilitySourceStatus(varAdminObservabilitySourceStatus) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "source") + delete(additionalProperties, "state") + delete(additionalProperties, "message") + delete(additionalProperties, "count") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilitySourceStatus struct { + value *AdminObservabilitySourceStatus + isSet bool +} + +func (v NullableAdminObservabilitySourceStatus) Get() *AdminObservabilitySourceStatus { + return v.value +} + +func (v *NullableAdminObservabilitySourceStatus) Set(val *AdminObservabilitySourceStatus) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilitySourceStatus) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilitySourceStatus) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilitySourceStatus(val *AdminObservabilitySourceStatus) *NullableAdminObservabilitySourceStatus { + return &NullableAdminObservabilitySourceStatus{value: val, isSet: true} +} + +func (v NullableAdminObservabilitySourceStatus) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilitySourceStatus) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_status_dto.go b/apps/api-client-go/model_admin_observability_status_dto.go new file mode 100644 index 000000000..7a8c19614 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_status_dto.go @@ -0,0 +1,198 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityStatusDto type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityStatusDto{} + +// AdminObservabilityStatusDto struct for AdminObservabilityStatusDto +type AdminObservabilityStatusDto struct { + Backend AdminObservabilityBackendStatusDto `json:"backend"` + Layers []AdminObservabilityLayerStatusDto `json:"layers"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityStatusDto AdminObservabilityStatusDto + +// NewAdminObservabilityStatusDto instantiates a new AdminObservabilityStatusDto object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityStatusDto(backend AdminObservabilityBackendStatusDto, layers []AdminObservabilityLayerStatusDto) *AdminObservabilityStatusDto { + this := AdminObservabilityStatusDto{} + this.Backend = backend + this.Layers = layers + return &this +} + +// NewAdminObservabilityStatusDtoWithDefaults instantiates a new AdminObservabilityStatusDto object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityStatusDtoWithDefaults() *AdminObservabilityStatusDto { + this := AdminObservabilityStatusDto{} + return &this +} + +// GetBackend returns the Backend field value +func (o *AdminObservabilityStatusDto) GetBackend() AdminObservabilityBackendStatusDto { + if o == nil { + var ret AdminObservabilityBackendStatusDto + return ret + } + + return o.Backend +} + +// GetBackendOk returns a tuple with the Backend field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityStatusDto) GetBackendOk() (*AdminObservabilityBackendStatusDto, bool) { + if o == nil { + return nil, false + } + return &o.Backend, true +} + +// SetBackend sets field value +func (o *AdminObservabilityStatusDto) SetBackend(v AdminObservabilityBackendStatusDto) { + o.Backend = v +} + +// GetLayers returns the Layers field value +func (o *AdminObservabilityStatusDto) GetLayers() []AdminObservabilityLayerStatusDto { + if o == nil { + var ret []AdminObservabilityLayerStatusDto + return ret + } + + return o.Layers +} + +// GetLayersOk returns a tuple with the Layers field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityStatusDto) GetLayersOk() ([]AdminObservabilityLayerStatusDto, bool) { + if o == nil { + return nil, false + } + return o.Layers, true +} + +// SetLayers sets field value +func (o *AdminObservabilityStatusDto) SetLayers(v []AdminObservabilityLayerStatusDto) { + o.Layers = v +} + +func (o AdminObservabilityStatusDto) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityStatusDto) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["backend"] = o.Backend + toSerialize["layers"] = o.Layers + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityStatusDto) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "backend", + "layers", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityStatusDto := _AdminObservabilityStatusDto{} + + err = json.Unmarshal(data, &varAdminObservabilityStatusDto) + + if err != nil { + return err + } + + *o = AdminObservabilityStatusDto(varAdminObservabilityStatusDto) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "backend") + delete(additionalProperties, "layers") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityStatusDto struct { + value *AdminObservabilityStatusDto + isSet bool +} + +func (v NullableAdminObservabilityStatusDto) Get() *AdminObservabilityStatusDto { + return v.value +} + +func (v *NullableAdminObservabilityStatusDto) Set(val *AdminObservabilityStatusDto) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityStatusDto) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityStatusDto) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityStatusDto(val *AdminObservabilityStatusDto) *NullableAdminObservabilityStatusDto { + return &NullableAdminObservabilityStatusDto{value: val, isSet: true} +} + +func (v NullableAdminObservabilityStatusDto) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityStatusDto) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_timeline_event.go b/apps/api-client-go/model_admin_observability_timeline_event.go new file mode 100644 index 000000000..a1514c907 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_timeline_event.go @@ -0,0 +1,338 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityTimelineEvent type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityTimelineEvent{} + +// AdminObservabilityTimelineEvent struct for AdminObservabilityTimelineEvent +type AdminObservabilityTimelineEvent struct { + Timestamp string `json:"timestamp"` + Source string `json:"source"` + Title string `json:"title"` + Detail *string `json:"detail,omitempty"` + Severity *string `json:"severity,omitempty"` + Identifiers map[string]interface{} `json:"identifiers,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityTimelineEvent AdminObservabilityTimelineEvent + +// NewAdminObservabilityTimelineEvent instantiates a new AdminObservabilityTimelineEvent object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityTimelineEvent(timestamp string, source string, title string) *AdminObservabilityTimelineEvent { + this := AdminObservabilityTimelineEvent{} + this.Timestamp = timestamp + this.Source = source + this.Title = title + return &this +} + +// NewAdminObservabilityTimelineEventWithDefaults instantiates a new AdminObservabilityTimelineEvent object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityTimelineEventWithDefaults() *AdminObservabilityTimelineEvent { + this := AdminObservabilityTimelineEvent{} + return &this +} + +// GetTimestamp returns the Timestamp field value +func (o *AdminObservabilityTimelineEvent) GetTimestamp() string { + if o == nil { + var ret string + return ret + } + + return o.Timestamp +} + +// GetTimestampOk returns a tuple with the Timestamp field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetTimestampOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Timestamp, true +} + +// SetTimestamp sets field value +func (o *AdminObservabilityTimelineEvent) SetTimestamp(v string) { + o.Timestamp = v +} + +// GetSource returns the Source field value +func (o *AdminObservabilityTimelineEvent) GetSource() string { + if o == nil { + var ret string + return ret + } + + return o.Source +} + +// GetSourceOk returns a tuple with the Source field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetSourceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Source, true +} + +// SetSource sets field value +func (o *AdminObservabilityTimelineEvent) SetSource(v string) { + o.Source = v +} + +// GetTitle returns the Title field value +func (o *AdminObservabilityTimelineEvent) GetTitle() string { + if o == nil { + var ret string + return ret + } + + return o.Title +} + +// GetTitleOk returns a tuple with the Title field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetTitleOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Title, true +} + +// SetTitle sets field value +func (o *AdminObservabilityTimelineEvent) SetTitle(v string) { + o.Title = v +} + +// GetDetail returns the Detail field value if set, zero value otherwise. +func (o *AdminObservabilityTimelineEvent) GetDetail() string { + if o == nil || IsNil(o.Detail) { + var ret string + return ret + } + return *o.Detail +} + +// GetDetailOk returns a tuple with the Detail field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetDetailOk() (*string, bool) { + if o == nil || IsNil(o.Detail) { + return nil, false + } + return o.Detail, true +} + +// HasDetail returns a boolean if a field has been set. +func (o *AdminObservabilityTimelineEvent) HasDetail() bool { + if o != nil && !IsNil(o.Detail) { + return true + } + + return false +} + +// SetDetail gets a reference to the given string and assigns it to the Detail field. +func (o *AdminObservabilityTimelineEvent) SetDetail(v string) { + o.Detail = &v +} + +// GetSeverity returns the Severity field value if set, zero value otherwise. +func (o *AdminObservabilityTimelineEvent) GetSeverity() string { + if o == nil || IsNil(o.Severity) { + var ret string + return ret + } + return *o.Severity +} + +// GetSeverityOk returns a tuple with the Severity field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetSeverityOk() (*string, bool) { + if o == nil || IsNil(o.Severity) { + return nil, false + } + return o.Severity, true +} + +// HasSeverity returns a boolean if a field has been set. +func (o *AdminObservabilityTimelineEvent) HasSeverity() bool { + if o != nil && !IsNil(o.Severity) { + return true + } + + return false +} + +// SetSeverity gets a reference to the given string and assigns it to the Severity field. +func (o *AdminObservabilityTimelineEvent) SetSeverity(v string) { + o.Severity = &v +} + +// GetIdentifiers returns the Identifiers field value if set, zero value otherwise. +func (o *AdminObservabilityTimelineEvent) GetIdentifiers() map[string]interface{} { + if o == nil || IsNil(o.Identifiers) { + var ret map[string]interface{} + return ret + } + return o.Identifiers +} + +// GetIdentifiersOk returns a tuple with the Identifiers field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityTimelineEvent) GetIdentifiersOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Identifiers) { + return map[string]interface{}{}, false + } + return o.Identifiers, true +} + +// HasIdentifiers returns a boolean if a field has been set. +func (o *AdminObservabilityTimelineEvent) HasIdentifiers() bool { + if o != nil && !IsNil(o.Identifiers) { + return true + } + + return false +} + +// SetIdentifiers gets a reference to the given map[string]interface{} and assigns it to the Identifiers field. +func (o *AdminObservabilityTimelineEvent) SetIdentifiers(v map[string]interface{}) { + o.Identifiers = v +} + +func (o AdminObservabilityTimelineEvent) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityTimelineEvent) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["timestamp"] = o.Timestamp + toSerialize["source"] = o.Source + toSerialize["title"] = o.Title + if !IsNil(o.Detail) { + toSerialize["detail"] = o.Detail + } + if !IsNil(o.Severity) { + toSerialize["severity"] = o.Severity + } + if !IsNil(o.Identifiers) { + toSerialize["identifiers"] = o.Identifiers + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityTimelineEvent) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "timestamp", + "source", + "title", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityTimelineEvent := _AdminObservabilityTimelineEvent{} + + err = json.Unmarshal(data, &varAdminObservabilityTimelineEvent) + + if err != nil { + return err + } + + *o = AdminObservabilityTimelineEvent(varAdminObservabilityTimelineEvent) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "timestamp") + delete(additionalProperties, "source") + delete(additionalProperties, "title") + delete(additionalProperties, "detail") + delete(additionalProperties, "severity") + delete(additionalProperties, "identifiers") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityTimelineEvent struct { + value *AdminObservabilityTimelineEvent + isSet bool +} + +func (v NullableAdminObservabilityTimelineEvent) Get() *AdminObservabilityTimelineEvent { + return v.value +} + +func (v *NullableAdminObservabilityTimelineEvent) Set(val *AdminObservabilityTimelineEvent) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityTimelineEvent) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityTimelineEvent) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityTimelineEvent(val *AdminObservabilityTimelineEvent) *NullableAdminObservabilityTimelineEvent { + return &NullableAdminObservabilityTimelineEvent{value: val, isSet: true} +} + +func (v NullableAdminObservabilityTimelineEvent) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityTimelineEvent) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_observability_x_log.go b/apps/api-client-go/model_admin_observability_x_log.go new file mode 100644 index 000000000..ec2b8ab81 --- /dev/null +++ b/apps/api-client-go/model_admin_observability_x_log.go @@ -0,0 +1,515 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminObservabilityXLog type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminObservabilityXLog{} + +// AdminObservabilityXLog struct for AdminObservabilityXLog +type AdminObservabilityXLog struct { + Source string `json:"source"` + Timestamp string `json:"timestamp"` + ServiceName string `json:"serviceName"` + Body string `json:"body"` + SeverityText *string `json:"severityText,omitempty"` + TraceId *string `json:"traceId,omitempty"` + SpanId *string `json:"spanId,omitempty"` + ExecutionId *string `json:"executionId,omitempty"` + JobId *string `json:"jobId,omitempty"` + Stream *string `json:"stream,omitempty"` + Attributes map[string]interface{} `json:"attributes,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminObservabilityXLog AdminObservabilityXLog + +// NewAdminObservabilityXLog instantiates a new AdminObservabilityXLog object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminObservabilityXLog(source string, timestamp string, serviceName string, body string) *AdminObservabilityXLog { + this := AdminObservabilityXLog{} + this.Source = source + this.Timestamp = timestamp + this.ServiceName = serviceName + this.Body = body + return &this +} + +// NewAdminObservabilityXLogWithDefaults instantiates a new AdminObservabilityXLog object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminObservabilityXLogWithDefaults() *AdminObservabilityXLog { + this := AdminObservabilityXLog{} + return &this +} + +// GetSource returns the Source field value +func (o *AdminObservabilityXLog) GetSource() string { + if o == nil { + var ret string + return ret + } + + return o.Source +} + +// GetSourceOk returns a tuple with the Source field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetSourceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Source, true +} + +// SetSource sets field value +func (o *AdminObservabilityXLog) SetSource(v string) { + o.Source = v +} + +// GetTimestamp returns the Timestamp field value +func (o *AdminObservabilityXLog) GetTimestamp() string { + if o == nil { + var ret string + return ret + } + + return o.Timestamp +} + +// GetTimestampOk returns a tuple with the Timestamp field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetTimestampOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Timestamp, true +} + +// SetTimestamp sets field value +func (o *AdminObservabilityXLog) SetTimestamp(v string) { + o.Timestamp = v +} + +// GetServiceName returns the ServiceName field value +func (o *AdminObservabilityXLog) GetServiceName() string { + if o == nil { + var ret string + return ret + } + + return o.ServiceName +} + +// GetServiceNameOk returns a tuple with the ServiceName field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetServiceNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ServiceName, true +} + +// SetServiceName sets field value +func (o *AdminObservabilityXLog) SetServiceName(v string) { + o.ServiceName = v +} + +// GetBody returns the Body field value +func (o *AdminObservabilityXLog) GetBody() string { + if o == nil { + var ret string + return ret + } + + return o.Body +} + +// GetBodyOk returns a tuple with the Body field value +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetBodyOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Body, true +} + +// SetBody sets field value +func (o *AdminObservabilityXLog) SetBody(v string) { + o.Body = v +} + +// GetSeverityText returns the SeverityText field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetSeverityText() string { + if o == nil || IsNil(o.SeverityText) { + var ret string + return ret + } + return *o.SeverityText +} + +// GetSeverityTextOk returns a tuple with the SeverityText field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetSeverityTextOk() (*string, bool) { + if o == nil || IsNil(o.SeverityText) { + return nil, false + } + return o.SeverityText, true +} + +// HasSeverityText returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasSeverityText() bool { + if o != nil && !IsNil(o.SeverityText) { + return true + } + + return false +} + +// SetSeverityText gets a reference to the given string and assigns it to the SeverityText field. +func (o *AdminObservabilityXLog) SetSeverityText(v string) { + o.SeverityText = &v +} + +// GetTraceId returns the TraceId field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetTraceId() string { + if o == nil || IsNil(o.TraceId) { + var ret string + return ret + } + return *o.TraceId +} + +// GetTraceIdOk returns a tuple with the TraceId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetTraceIdOk() (*string, bool) { + if o == nil || IsNil(o.TraceId) { + return nil, false + } + return o.TraceId, true +} + +// HasTraceId returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasTraceId() bool { + if o != nil && !IsNil(o.TraceId) { + return true + } + + return false +} + +// SetTraceId gets a reference to the given string and assigns it to the TraceId field. +func (o *AdminObservabilityXLog) SetTraceId(v string) { + o.TraceId = &v +} + +// GetSpanId returns the SpanId field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetSpanId() string { + if o == nil || IsNil(o.SpanId) { + var ret string + return ret + } + return *o.SpanId +} + +// GetSpanIdOk returns a tuple with the SpanId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetSpanIdOk() (*string, bool) { + if o == nil || IsNil(o.SpanId) { + return nil, false + } + return o.SpanId, true +} + +// HasSpanId returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasSpanId() bool { + if o != nil && !IsNil(o.SpanId) { + return true + } + + return false +} + +// SetSpanId gets a reference to the given string and assigns it to the SpanId field. +func (o *AdminObservabilityXLog) SetSpanId(v string) { + o.SpanId = &v +} + +// GetExecutionId returns the ExecutionId field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetExecutionId() string { + if o == nil || IsNil(o.ExecutionId) { + var ret string + return ret + } + return *o.ExecutionId +} + +// GetExecutionIdOk returns a tuple with the ExecutionId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetExecutionIdOk() (*string, bool) { + if o == nil || IsNil(o.ExecutionId) { + return nil, false + } + return o.ExecutionId, true +} + +// HasExecutionId returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasExecutionId() bool { + if o != nil && !IsNil(o.ExecutionId) { + return true + } + + return false +} + +// SetExecutionId gets a reference to the given string and assigns it to the ExecutionId field. +func (o *AdminObservabilityXLog) SetExecutionId(v string) { + o.ExecutionId = &v +} + +// GetJobId returns the JobId field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetJobId() string { + if o == nil || IsNil(o.JobId) { + var ret string + return ret + } + return *o.JobId +} + +// GetJobIdOk returns a tuple with the JobId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetJobIdOk() (*string, bool) { + if o == nil || IsNil(o.JobId) { + return nil, false + } + return o.JobId, true +} + +// HasJobId returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasJobId() bool { + if o != nil && !IsNil(o.JobId) { + return true + } + + return false +} + +// SetJobId gets a reference to the given string and assigns it to the JobId field. +func (o *AdminObservabilityXLog) SetJobId(v string) { + o.JobId = &v +} + +// GetStream returns the Stream field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetStream() string { + if o == nil || IsNil(o.Stream) { + var ret string + return ret + } + return *o.Stream +} + +// GetStreamOk returns a tuple with the Stream field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetStreamOk() (*string, bool) { + if o == nil || IsNil(o.Stream) { + return nil, false + } + return o.Stream, true +} + +// HasStream returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasStream() bool { + if o != nil && !IsNil(o.Stream) { + return true + } + + return false +} + +// SetStream gets a reference to the given string and assigns it to the Stream field. +func (o *AdminObservabilityXLog) SetStream(v string) { + o.Stream = &v +} + +// GetAttributes returns the Attributes field value if set, zero value otherwise. +func (o *AdminObservabilityXLog) GetAttributes() map[string]interface{} { + if o == nil || IsNil(o.Attributes) { + var ret map[string]interface{} + return ret + } + return o.Attributes +} + +// GetAttributesOk returns a tuple with the Attributes field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminObservabilityXLog) GetAttributesOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.Attributes) { + return map[string]interface{}{}, false + } + return o.Attributes, true +} + +// HasAttributes returns a boolean if a field has been set. +func (o *AdminObservabilityXLog) HasAttributes() bool { + if o != nil && !IsNil(o.Attributes) { + return true + } + + return false +} + +// SetAttributes gets a reference to the given map[string]interface{} and assigns it to the Attributes field. +func (o *AdminObservabilityXLog) SetAttributes(v map[string]interface{}) { + o.Attributes = v +} + +func (o AdminObservabilityXLog) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminObservabilityXLog) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["source"] = o.Source + toSerialize["timestamp"] = o.Timestamp + toSerialize["serviceName"] = o.ServiceName + toSerialize["body"] = o.Body + if !IsNil(o.SeverityText) { + toSerialize["severityText"] = o.SeverityText + } + if !IsNil(o.TraceId) { + toSerialize["traceId"] = o.TraceId + } + if !IsNil(o.SpanId) { + toSerialize["spanId"] = o.SpanId + } + if !IsNil(o.ExecutionId) { + toSerialize["executionId"] = o.ExecutionId + } + if !IsNil(o.JobId) { + toSerialize["jobId"] = o.JobId + } + if !IsNil(o.Stream) { + toSerialize["stream"] = o.Stream + } + if !IsNil(o.Attributes) { + toSerialize["attributes"] = o.Attributes + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminObservabilityXLog) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "source", + "timestamp", + "serviceName", + "body", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminObservabilityXLog := _AdminObservabilityXLog{} + + err = json.Unmarshal(data, &varAdminObservabilityXLog) + + if err != nil { + return err + } + + *o = AdminObservabilityXLog(varAdminObservabilityXLog) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "source") + delete(additionalProperties, "timestamp") + delete(additionalProperties, "serviceName") + delete(additionalProperties, "body") + delete(additionalProperties, "severityText") + delete(additionalProperties, "traceId") + delete(additionalProperties, "spanId") + delete(additionalProperties, "executionId") + delete(additionalProperties, "jobId") + delete(additionalProperties, "stream") + delete(additionalProperties, "attributes") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminObservabilityXLog struct { + value *AdminObservabilityXLog + isSet bool +} + +func (v NullableAdminObservabilityXLog) Get() *AdminObservabilityXLog { + return v.value +} + +func (v *NullableAdminObservabilityXLog) Set(val *AdminObservabilityXLog) { + v.value = val + v.isSet = true +} + +func (v NullableAdminObservabilityXLog) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminObservabilityXLog) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminObservabilityXLog(val *AdminObservabilityXLog) *NullableAdminObservabilityXLog { + return &NullableAdminObservabilityXLog{value: val, isSet: true} +} + +func (v NullableAdminObservabilityXLog) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminObservabilityXLog) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_overview.go b/apps/api-client-go/model_admin_overview.go new file mode 100644 index 000000000..270db5eb5 --- /dev/null +++ b/apps/api-client-go/model_admin_overview.go @@ -0,0 +1,287 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminOverview type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminOverview{} + +// AdminOverview struct for AdminOverview +type AdminOverview struct { + // Total number of users + Users float32 `json:"users"` + // Number of active (started) boxes + ActiveBoxes float32 `json:"activeBoxes"` + Boxes AdminOverviewBoxes `json:"boxes"` + Runners AdminOverviewRunners `json:"runners"` + Cluster AdminOverviewCluster `json:"cluster"` + AdditionalProperties map[string]interface{} +} + +type _AdminOverview AdminOverview + +// NewAdminOverview instantiates a new AdminOverview object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminOverview(users float32, activeBoxes float32, boxes AdminOverviewBoxes, runners AdminOverviewRunners, cluster AdminOverviewCluster) *AdminOverview { + this := AdminOverview{} + this.Users = users + this.ActiveBoxes = activeBoxes + this.Boxes = boxes + this.Runners = runners + this.Cluster = cluster + return &this +} + +// NewAdminOverviewWithDefaults instantiates a new AdminOverview object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminOverviewWithDefaults() *AdminOverview { + this := AdminOverview{} + return &this +} + +// GetUsers returns the Users field value +func (o *AdminOverview) GetUsers() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Users +} + +// GetUsersOk returns a tuple with the Users field value +// and a boolean to check if the value has been set. +func (o *AdminOverview) GetUsersOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Users, true +} + +// SetUsers sets field value +func (o *AdminOverview) SetUsers(v float32) { + o.Users = v +} + +// GetActiveBoxes returns the ActiveBoxes field value +func (o *AdminOverview) GetActiveBoxes() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.ActiveBoxes +} + +// GetActiveBoxesOk returns a tuple with the ActiveBoxes field value +// and a boolean to check if the value has been set. +func (o *AdminOverview) GetActiveBoxesOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.ActiveBoxes, true +} + +// SetActiveBoxes sets field value +func (o *AdminOverview) SetActiveBoxes(v float32) { + o.ActiveBoxes = v +} + +// GetBoxes returns the Boxes field value +func (o *AdminOverview) GetBoxes() AdminOverviewBoxes { + if o == nil { + var ret AdminOverviewBoxes + return ret + } + + return o.Boxes +} + +// GetBoxesOk returns a tuple with the Boxes field value +// and a boolean to check if the value has been set. +func (o *AdminOverview) GetBoxesOk() (*AdminOverviewBoxes, bool) { + if o == nil { + return nil, false + } + return &o.Boxes, true +} + +// SetBoxes sets field value +func (o *AdminOverview) SetBoxes(v AdminOverviewBoxes) { + o.Boxes = v +} + +// GetRunners returns the Runners field value +func (o *AdminOverview) GetRunners() AdminOverviewRunners { + if o == nil { + var ret AdminOverviewRunners + return ret + } + + return o.Runners +} + +// GetRunnersOk returns a tuple with the Runners field value +// and a boolean to check if the value has been set. +func (o *AdminOverview) GetRunnersOk() (*AdminOverviewRunners, bool) { + if o == nil { + return nil, false + } + return &o.Runners, true +} + +// SetRunners sets field value +func (o *AdminOverview) SetRunners(v AdminOverviewRunners) { + o.Runners = v +} + +// GetCluster returns the Cluster field value +func (o *AdminOverview) GetCluster() AdminOverviewCluster { + if o == nil { + var ret AdminOverviewCluster + return ret + } + + return o.Cluster +} + +// GetClusterOk returns a tuple with the Cluster field value +// and a boolean to check if the value has been set. +func (o *AdminOverview) GetClusterOk() (*AdminOverviewCluster, bool) { + if o == nil { + return nil, false + } + return &o.Cluster, true +} + +// SetCluster sets field value +func (o *AdminOverview) SetCluster(v AdminOverviewCluster) { + o.Cluster = v +} + +func (o AdminOverview) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminOverview) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["users"] = o.Users + toSerialize["activeBoxes"] = o.ActiveBoxes + toSerialize["boxes"] = o.Boxes + toSerialize["runners"] = o.Runners + toSerialize["cluster"] = o.Cluster + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminOverview) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "users", + "activeBoxes", + "boxes", + "runners", + "cluster", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminOverview := _AdminOverview{} + + err = json.Unmarshal(data, &varAdminOverview) + + if err != nil { + return err + } + + *o = AdminOverview(varAdminOverview) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "users") + delete(additionalProperties, "activeBoxes") + delete(additionalProperties, "boxes") + delete(additionalProperties, "runners") + delete(additionalProperties, "cluster") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminOverview struct { + value *AdminOverview + isSet bool +} + +func (v NullableAdminOverview) Get() *AdminOverview { + return v.value +} + +func (v *NullableAdminOverview) Set(val *AdminOverview) { + v.value = val + v.isSet = true +} + +func (v NullableAdminOverview) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminOverview) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminOverview(val *AdminOverview) *NullableAdminOverview { + return &NullableAdminOverview{value: val, isSet: true} +} + +func (v NullableAdminOverview) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminOverview) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_overview_boxes.go b/apps/api-client-go/model_admin_overview_boxes.go new file mode 100644 index 000000000..8e45ca7c4 --- /dev/null +++ b/apps/api-client-go/model_admin_overview_boxes.go @@ -0,0 +1,200 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminOverviewBoxes type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminOverviewBoxes{} + +// AdminOverviewBoxes struct for AdminOverviewBoxes +type AdminOverviewBoxes struct { + // Total number of boxes across all states + Total float32 `json:"total"` + // Box counts keyed by BoxState + ByState map[string]float32 `json:"byState"` + AdditionalProperties map[string]interface{} +} + +type _AdminOverviewBoxes AdminOverviewBoxes + +// NewAdminOverviewBoxes instantiates a new AdminOverviewBoxes object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminOverviewBoxes(total float32, byState map[string]float32) *AdminOverviewBoxes { + this := AdminOverviewBoxes{} + this.Total = total + this.ByState = byState + return &this +} + +// NewAdminOverviewBoxesWithDefaults instantiates a new AdminOverviewBoxes object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminOverviewBoxesWithDefaults() *AdminOverviewBoxes { + this := AdminOverviewBoxes{} + return &this +} + +// GetTotal returns the Total field value +func (o *AdminOverviewBoxes) GetTotal() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Total +} + +// GetTotalOk returns a tuple with the Total field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewBoxes) GetTotalOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Total, true +} + +// SetTotal sets field value +func (o *AdminOverviewBoxes) SetTotal(v float32) { + o.Total = v +} + +// GetByState returns the ByState field value +func (o *AdminOverviewBoxes) GetByState() map[string]float32 { + if o == nil { + var ret map[string]float32 + return ret + } + + return o.ByState +} + +// GetByStateOk returns a tuple with the ByState field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewBoxes) GetByStateOk() (*map[string]float32, bool) { + if o == nil { + return nil, false + } + return &o.ByState, true +} + +// SetByState sets field value +func (o *AdminOverviewBoxes) SetByState(v map[string]float32) { + o.ByState = v +} + +func (o AdminOverviewBoxes) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminOverviewBoxes) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["total"] = o.Total + toSerialize["byState"] = o.ByState + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminOverviewBoxes) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "total", + "byState", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminOverviewBoxes := _AdminOverviewBoxes{} + + err = json.Unmarshal(data, &varAdminOverviewBoxes) + + if err != nil { + return err + } + + *o = AdminOverviewBoxes(varAdminOverviewBoxes) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "total") + delete(additionalProperties, "byState") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminOverviewBoxes struct { + value *AdminOverviewBoxes + isSet bool +} + +func (v NullableAdminOverviewBoxes) Get() *AdminOverviewBoxes { + return v.value +} + +func (v *NullableAdminOverviewBoxes) Set(val *AdminOverviewBoxes) { + v.value = val + v.isSet = true +} + +func (v NullableAdminOverviewBoxes) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminOverviewBoxes) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminOverviewBoxes(val *AdminOverviewBoxes) *NullableAdminOverviewBoxes { + return &NullableAdminOverviewBoxes{value: val, isSet: true} +} + +func (v NullableAdminOverviewBoxes) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminOverviewBoxes) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_overview_cluster.go b/apps/api-client-go/model_admin_overview_cluster.go new file mode 100644 index 000000000..6a4ee8c4c --- /dev/null +++ b/apps/api-client-go/model_admin_overview_cluster.go @@ -0,0 +1,200 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminOverviewCluster type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminOverviewCluster{} + +// AdminOverviewCluster struct for AdminOverviewCluster +type AdminOverviewCluster struct { + // Average CPU utilisation across all runners (0–1) + CpuUtil float32 `json:"cpuUtil"` + // Average CPU oversell ratio (allocated / capacity); 0 when no capacity + Oversell float32 `json:"oversell"` + AdditionalProperties map[string]interface{} +} + +type _AdminOverviewCluster AdminOverviewCluster + +// NewAdminOverviewCluster instantiates a new AdminOverviewCluster object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminOverviewCluster(cpuUtil float32, oversell float32) *AdminOverviewCluster { + this := AdminOverviewCluster{} + this.CpuUtil = cpuUtil + this.Oversell = oversell + return &this +} + +// NewAdminOverviewClusterWithDefaults instantiates a new AdminOverviewCluster object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminOverviewClusterWithDefaults() *AdminOverviewCluster { + this := AdminOverviewCluster{} + return &this +} + +// GetCpuUtil returns the CpuUtil field value +func (o *AdminOverviewCluster) GetCpuUtil() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.CpuUtil +} + +// GetCpuUtilOk returns a tuple with the CpuUtil field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewCluster) GetCpuUtilOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.CpuUtil, true +} + +// SetCpuUtil sets field value +func (o *AdminOverviewCluster) SetCpuUtil(v float32) { + o.CpuUtil = v +} + +// GetOversell returns the Oversell field value +func (o *AdminOverviewCluster) GetOversell() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Oversell +} + +// GetOversellOk returns a tuple with the Oversell field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewCluster) GetOversellOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Oversell, true +} + +// SetOversell sets field value +func (o *AdminOverviewCluster) SetOversell(v float32) { + o.Oversell = v +} + +func (o AdminOverviewCluster) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminOverviewCluster) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["cpuUtil"] = o.CpuUtil + toSerialize["oversell"] = o.Oversell + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminOverviewCluster) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "cpuUtil", + "oversell", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminOverviewCluster := _AdminOverviewCluster{} + + err = json.Unmarshal(data, &varAdminOverviewCluster) + + if err != nil { + return err + } + + *o = AdminOverviewCluster(varAdminOverviewCluster) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "cpuUtil") + delete(additionalProperties, "oversell") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminOverviewCluster struct { + value *AdminOverviewCluster + isSet bool +} + +func (v NullableAdminOverviewCluster) Get() *AdminOverviewCluster { + return v.value +} + +func (v *NullableAdminOverviewCluster) Set(val *AdminOverviewCluster) { + v.value = val + v.isSet = true +} + +func (v NullableAdminOverviewCluster) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminOverviewCluster) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminOverviewCluster(val *AdminOverviewCluster) *NullableAdminOverviewCluster { + return &NullableAdminOverviewCluster{value: val, isSet: true} +} + +func (v NullableAdminOverviewCluster) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminOverviewCluster) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_overview_runners.go b/apps/api-client-go/model_admin_overview_runners.go new file mode 100644 index 000000000..28f8520b4 --- /dev/null +++ b/apps/api-client-go/model_admin_overview_runners.go @@ -0,0 +1,230 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminOverviewRunners type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminOverviewRunners{} + +// AdminOverviewRunners struct for AdminOverviewRunners +type AdminOverviewRunners struct { + // Number of online (ready) runners + Online float32 `json:"online"` + // Total number of runners + Total float32 `json:"total"` + // Number of draining runners + Draining float32 `json:"draining"` + AdditionalProperties map[string]interface{} +} + +type _AdminOverviewRunners AdminOverviewRunners + +// NewAdminOverviewRunners instantiates a new AdminOverviewRunners object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminOverviewRunners(online float32, total float32, draining float32) *AdminOverviewRunners { + this := AdminOverviewRunners{} + this.Online = online + this.Total = total + this.Draining = draining + return &this +} + +// NewAdminOverviewRunnersWithDefaults instantiates a new AdminOverviewRunners object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminOverviewRunnersWithDefaults() *AdminOverviewRunners { + this := AdminOverviewRunners{} + return &this +} + +// GetOnline returns the Online field value +func (o *AdminOverviewRunners) GetOnline() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Online +} + +// GetOnlineOk returns a tuple with the Online field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewRunners) GetOnlineOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Online, true +} + +// SetOnline sets field value +func (o *AdminOverviewRunners) SetOnline(v float32) { + o.Online = v +} + +// GetTotal returns the Total field value +func (o *AdminOverviewRunners) GetTotal() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Total +} + +// GetTotalOk returns a tuple with the Total field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewRunners) GetTotalOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Total, true +} + +// SetTotal sets field value +func (o *AdminOverviewRunners) SetTotal(v float32) { + o.Total = v +} + +// GetDraining returns the Draining field value +func (o *AdminOverviewRunners) GetDraining() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Draining +} + +// GetDrainingOk returns a tuple with the Draining field value +// and a boolean to check if the value has been set. +func (o *AdminOverviewRunners) GetDrainingOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Draining, true +} + +// SetDraining sets field value +func (o *AdminOverviewRunners) SetDraining(v float32) { + o.Draining = v +} + +func (o AdminOverviewRunners) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminOverviewRunners) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["online"] = o.Online + toSerialize["total"] = o.Total + toSerialize["draining"] = o.Draining + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminOverviewRunners) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "online", + "total", + "draining", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminOverviewRunners := _AdminOverviewRunners{} + + err = json.Unmarshal(data, &varAdminOverviewRunners) + + if err != nil { + return err + } + + *o = AdminOverviewRunners(varAdminOverviewRunners) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "online") + delete(additionalProperties, "total") + delete(additionalProperties, "draining") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminOverviewRunners struct { + value *AdminOverviewRunners + isSet bool +} + +func (v NullableAdminOverviewRunners) Get() *AdminOverviewRunners { + return v.value +} + +func (v *NullableAdminOverviewRunners) Set(val *AdminOverviewRunners) { + v.value = val + v.isSet = true +} + +func (v NullableAdminOverviewRunners) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminOverviewRunners) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminOverviewRunners(val *AdminOverviewRunners) *NullableAdminOverviewRunners { + return &NullableAdminOverviewRunners{value: val, isSet: true} +} + +func (v NullableAdminOverviewRunners) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminOverviewRunners) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_runner.go b/apps/api-client-go/model_admin_runner.go new file mode 100644 index 000000000..74c6e7c63 --- /dev/null +++ b/apps/api-client-go/model_admin_runner.go @@ -0,0 +1,1150 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminRunner type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminRunner{} + +// AdminRunner struct for AdminRunner +type AdminRunner struct { + // The ID of the runner + Id string `json:"id"` + // The domain of the runner + Domain *string `json:"domain,omitempty"` + // The API URL of the runner + ApiUrl *string `json:"apiUrl,omitempty"` + // The proxy URL of the runner + ProxyUrl *string `json:"proxyUrl,omitempty"` + // The CPU capacity of the runner + Cpu float32 `json:"cpu"` + // The memory capacity of the runner in GiB + Memory float32 `json:"memory"` + // The disk capacity of the runner in GiB + Disk float32 `json:"disk"` + // The GPU capacity of the runner + Gpu *float32 `json:"gpu,omitempty"` + // The type of GPU + GpuType *string `json:"gpuType,omitempty"` + // The class of the runner + Class BoxClass `json:"class"` + // Current CPU usage percentage + CurrentCpuUsagePercentage *float32 `json:"currentCpuUsagePercentage,omitempty"` + // Current RAM usage percentage + CurrentMemoryUsagePercentage *float32 `json:"currentMemoryUsagePercentage,omitempty"` + // Current disk usage percentage + CurrentDiskUsagePercentage *float32 `json:"currentDiskUsagePercentage,omitempty"` + // Current allocated CPU + CurrentAllocatedCpu *float32 `json:"currentAllocatedCpu,omitempty"` + // Current allocated memory in GiB + CurrentAllocatedMemoryGiB *float32 `json:"currentAllocatedMemoryGiB,omitempty"` + // Current allocated disk in GiB + CurrentAllocatedDiskGiB *float32 `json:"currentAllocatedDiskGiB,omitempty"` + // Current number of started boxes + CurrentStartedBoxes *float32 `json:"currentStartedBoxes,omitempty"` + // Runner availability score + AvailabilityScore *float32 `json:"availabilityScore,omitempty"` + // The region of the runner + Region string `json:"region"` + // The name of the runner + Name string `json:"name"` + // The state of the runner + State RunnerState `json:"state"` + // The last time the runner was checked + LastChecked *string `json:"lastChecked,omitempty"` + // Whether the runner is unschedulable + Unschedulable bool `json:"unschedulable"` + // The creation timestamp of the runner + CreatedAt string `json:"createdAt"` + // The last update timestamp of the runner + UpdatedAt string `json:"updatedAt"` + // The version of the runner (deprecated in favor of apiVersion) + // Deprecated + Version string `json:"version"` + // The api version of the runner + // Deprecated + ApiVersion string `json:"apiVersion"` + // The app version of the runner + // Deprecated + AppVersion *string `json:"appVersion,omitempty"` + // The region type of the runner + RegionType *RegionType `json:"regionType,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _AdminRunner AdminRunner + +// NewAdminRunner instantiates a new AdminRunner object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminRunner(id string, cpu float32, memory float32, disk float32, class BoxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string) *AdminRunner { + this := AdminRunner{} + this.Id = id + this.Cpu = cpu + this.Memory = memory + this.Disk = disk + this.Class = class + this.Region = region + this.Name = name + this.State = state + this.Unschedulable = unschedulable + this.CreatedAt = createdAt + this.UpdatedAt = updatedAt + this.Version = version + this.ApiVersion = apiVersion + return &this +} + +// NewAdminRunnerWithDefaults instantiates a new AdminRunner object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminRunnerWithDefaults() *AdminRunner { + this := AdminRunner{} + return &this +} + +// GetId returns the Id field value +func (o *AdminRunner) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminRunner) SetId(v string) { + o.Id = v +} + +// GetDomain returns the Domain field value if set, zero value otherwise. +func (o *AdminRunner) GetDomain() string { + if o == nil || IsNil(o.Domain) { + var ret string + return ret + } + return *o.Domain +} + +// GetDomainOk returns a tuple with the Domain field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetDomainOk() (*string, bool) { + if o == nil || IsNil(o.Domain) { + return nil, false + } + return o.Domain, true +} + +// HasDomain returns a boolean if a field has been set. +func (o *AdminRunner) HasDomain() bool { + if o != nil && !IsNil(o.Domain) { + return true + } + + return false +} + +// SetDomain gets a reference to the given string and assigns it to the Domain field. +func (o *AdminRunner) SetDomain(v string) { + o.Domain = &v +} + +// GetApiUrl returns the ApiUrl field value if set, zero value otherwise. +func (o *AdminRunner) GetApiUrl() string { + if o == nil || IsNil(o.ApiUrl) { + var ret string + return ret + } + return *o.ApiUrl +} + +// GetApiUrlOk returns a tuple with the ApiUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetApiUrlOk() (*string, bool) { + if o == nil || IsNil(o.ApiUrl) { + return nil, false + } + return o.ApiUrl, true +} + +// HasApiUrl returns a boolean if a field has been set. +func (o *AdminRunner) HasApiUrl() bool { + if o != nil && !IsNil(o.ApiUrl) { + return true + } + + return false +} + +// SetApiUrl gets a reference to the given string and assigns it to the ApiUrl field. +func (o *AdminRunner) SetApiUrl(v string) { + o.ApiUrl = &v +} + +// GetProxyUrl returns the ProxyUrl field value if set, zero value otherwise. +func (o *AdminRunner) GetProxyUrl() string { + if o == nil || IsNil(o.ProxyUrl) { + var ret string + return ret + } + return *o.ProxyUrl +} + +// GetProxyUrlOk returns a tuple with the ProxyUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetProxyUrlOk() (*string, bool) { + if o == nil || IsNil(o.ProxyUrl) { + return nil, false + } + return o.ProxyUrl, true +} + +// HasProxyUrl returns a boolean if a field has been set. +func (o *AdminRunner) HasProxyUrl() bool { + if o != nil && !IsNil(o.ProxyUrl) { + return true + } + + return false +} + +// SetProxyUrl gets a reference to the given string and assigns it to the ProxyUrl field. +func (o *AdminRunner) SetProxyUrl(v string) { + o.ProxyUrl = &v +} + +// GetCpu returns the Cpu field value +func (o *AdminRunner) GetCpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Cpu +} + +// GetCpuOk returns a tuple with the Cpu field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Cpu, true +} + +// SetCpu sets field value +func (o *AdminRunner) SetCpu(v float32) { + o.Cpu = v +} + +// GetMemory returns the Memory field value +func (o *AdminRunner) GetMemory() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Memory +} + +// GetMemoryOk returns a tuple with the Memory field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetMemoryOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Memory, true +} + +// SetMemory sets field value +func (o *AdminRunner) SetMemory(v float32) { + o.Memory = v +} + +// GetDisk returns the Disk field value +func (o *AdminRunner) GetDisk() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Disk +} + +// GetDiskOk returns a tuple with the Disk field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetDiskOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Disk, true +} + +// SetDisk sets field value +func (o *AdminRunner) SetDisk(v float32) { + o.Disk = v +} + +// GetGpu returns the Gpu field value if set, zero value otherwise. +func (o *AdminRunner) GetGpu() float32 { + if o == nil || IsNil(o.Gpu) { + var ret float32 + return ret + } + return *o.Gpu +} + +// GetGpuOk returns a tuple with the Gpu field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetGpuOk() (*float32, bool) { + if o == nil || IsNil(o.Gpu) { + return nil, false + } + return o.Gpu, true +} + +// HasGpu returns a boolean if a field has been set. +func (o *AdminRunner) HasGpu() bool { + if o != nil && !IsNil(o.Gpu) { + return true + } + + return false +} + +// SetGpu gets a reference to the given float32 and assigns it to the Gpu field. +func (o *AdminRunner) SetGpu(v float32) { + o.Gpu = &v +} + +// GetGpuType returns the GpuType field value if set, zero value otherwise. +func (o *AdminRunner) GetGpuType() string { + if o == nil || IsNil(o.GpuType) { + var ret string + return ret + } + return *o.GpuType +} + +// GetGpuTypeOk returns a tuple with the GpuType field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetGpuTypeOk() (*string, bool) { + if o == nil || IsNil(o.GpuType) { + return nil, false + } + return o.GpuType, true +} + +// HasGpuType returns a boolean if a field has been set. +func (o *AdminRunner) HasGpuType() bool { + if o != nil && !IsNil(o.GpuType) { + return true + } + + return false +} + +// SetGpuType gets a reference to the given string and assigns it to the GpuType field. +func (o *AdminRunner) SetGpuType(v string) { + o.GpuType = &v +} + +// GetClass returns the Class field value +func (o *AdminRunner) GetClass() BoxClass { + if o == nil { + var ret BoxClass + return ret + } + + return o.Class +} + +// GetClassOk returns a tuple with the Class field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetClassOk() (*BoxClass, bool) { + if o == nil { + return nil, false + } + return &o.Class, true +} + +// SetClass sets field value +func (o *AdminRunner) SetClass(v BoxClass) { + o.Class = v +} + +// GetCurrentCpuUsagePercentage returns the CurrentCpuUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentCpuUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentCpuUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentCpuUsagePercentage +} + +// GetCurrentCpuUsagePercentageOk returns a tuple with the CurrentCpuUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentCpuUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentCpuUsagePercentage) { + return nil, false + } + return o.CurrentCpuUsagePercentage, true +} + +// HasCurrentCpuUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentCpuUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentCpuUsagePercentage) { + return true + } + + return false +} + +// SetCurrentCpuUsagePercentage gets a reference to the given float32 and assigns it to the CurrentCpuUsagePercentage field. +func (o *AdminRunner) SetCurrentCpuUsagePercentage(v float32) { + o.CurrentCpuUsagePercentage = &v +} + +// GetCurrentMemoryUsagePercentage returns the CurrentMemoryUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentMemoryUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentMemoryUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentMemoryUsagePercentage +} + +// GetCurrentMemoryUsagePercentageOk returns a tuple with the CurrentMemoryUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentMemoryUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentMemoryUsagePercentage) { + return nil, false + } + return o.CurrentMemoryUsagePercentage, true +} + +// HasCurrentMemoryUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentMemoryUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentMemoryUsagePercentage) { + return true + } + + return false +} + +// SetCurrentMemoryUsagePercentage gets a reference to the given float32 and assigns it to the CurrentMemoryUsagePercentage field. +func (o *AdminRunner) SetCurrentMemoryUsagePercentage(v float32) { + o.CurrentMemoryUsagePercentage = &v +} + +// GetCurrentDiskUsagePercentage returns the CurrentDiskUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentDiskUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentDiskUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentDiskUsagePercentage +} + +// GetCurrentDiskUsagePercentageOk returns a tuple with the CurrentDiskUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentDiskUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentDiskUsagePercentage) { + return nil, false + } + return o.CurrentDiskUsagePercentage, true +} + +// HasCurrentDiskUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentDiskUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentDiskUsagePercentage) { + return true + } + + return false +} + +// SetCurrentDiskUsagePercentage gets a reference to the given float32 and assigns it to the CurrentDiskUsagePercentage field. +func (o *AdminRunner) SetCurrentDiskUsagePercentage(v float32) { + o.CurrentDiskUsagePercentage = &v +} + +// GetCurrentAllocatedCpu returns the CurrentAllocatedCpu field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentAllocatedCpu() float32 { + if o == nil || IsNil(o.CurrentAllocatedCpu) { + var ret float32 + return ret + } + return *o.CurrentAllocatedCpu +} + +// GetCurrentAllocatedCpuOk returns a tuple with the CurrentAllocatedCpu field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentAllocatedCpuOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedCpu) { + return nil, false + } + return o.CurrentAllocatedCpu, true +} + +// HasCurrentAllocatedCpu returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentAllocatedCpu() bool { + if o != nil && !IsNil(o.CurrentAllocatedCpu) { + return true + } + + return false +} + +// SetCurrentAllocatedCpu gets a reference to the given float32 and assigns it to the CurrentAllocatedCpu field. +func (o *AdminRunner) SetCurrentAllocatedCpu(v float32) { + o.CurrentAllocatedCpu = &v +} + +// GetCurrentAllocatedMemoryGiB returns the CurrentAllocatedMemoryGiB field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentAllocatedMemoryGiB() float32 { + if o == nil || IsNil(o.CurrentAllocatedMemoryGiB) { + var ret float32 + return ret + } + return *o.CurrentAllocatedMemoryGiB +} + +// GetCurrentAllocatedMemoryGiBOk returns a tuple with the CurrentAllocatedMemoryGiB field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentAllocatedMemoryGiBOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedMemoryGiB) { + return nil, false + } + return o.CurrentAllocatedMemoryGiB, true +} + +// HasCurrentAllocatedMemoryGiB returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentAllocatedMemoryGiB() bool { + if o != nil && !IsNil(o.CurrentAllocatedMemoryGiB) { + return true + } + + return false +} + +// SetCurrentAllocatedMemoryGiB gets a reference to the given float32 and assigns it to the CurrentAllocatedMemoryGiB field. +func (o *AdminRunner) SetCurrentAllocatedMemoryGiB(v float32) { + o.CurrentAllocatedMemoryGiB = &v +} + +// GetCurrentAllocatedDiskGiB returns the CurrentAllocatedDiskGiB field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentAllocatedDiskGiB() float32 { + if o == nil || IsNil(o.CurrentAllocatedDiskGiB) { + var ret float32 + return ret + } + return *o.CurrentAllocatedDiskGiB +} + +// GetCurrentAllocatedDiskGiBOk returns a tuple with the CurrentAllocatedDiskGiB field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentAllocatedDiskGiBOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedDiskGiB) { + return nil, false + } + return o.CurrentAllocatedDiskGiB, true +} + +// HasCurrentAllocatedDiskGiB returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentAllocatedDiskGiB() bool { + if o != nil && !IsNil(o.CurrentAllocatedDiskGiB) { + return true + } + + return false +} + +// SetCurrentAllocatedDiskGiB gets a reference to the given float32 and assigns it to the CurrentAllocatedDiskGiB field. +func (o *AdminRunner) SetCurrentAllocatedDiskGiB(v float32) { + o.CurrentAllocatedDiskGiB = &v +} + +// GetCurrentStartedBoxes returns the CurrentStartedBoxes field value if set, zero value otherwise. +func (o *AdminRunner) GetCurrentStartedBoxes() float32 { + if o == nil || IsNil(o.CurrentStartedBoxes) { + var ret float32 + return ret + } + return *o.CurrentStartedBoxes +} + +// GetCurrentStartedBoxesOk returns a tuple with the CurrentStartedBoxes field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCurrentStartedBoxesOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentStartedBoxes) { + return nil, false + } + return o.CurrentStartedBoxes, true +} + +// HasCurrentStartedBoxes returns a boolean if a field has been set. +func (o *AdminRunner) HasCurrentStartedBoxes() bool { + if o != nil && !IsNil(o.CurrentStartedBoxes) { + return true + } + + return false +} + +// SetCurrentStartedBoxes gets a reference to the given float32 and assigns it to the CurrentStartedBoxes field. +func (o *AdminRunner) SetCurrentStartedBoxes(v float32) { + o.CurrentStartedBoxes = &v +} + +// GetAvailabilityScore returns the AvailabilityScore field value if set, zero value otherwise. +func (o *AdminRunner) GetAvailabilityScore() float32 { + if o == nil || IsNil(o.AvailabilityScore) { + var ret float32 + return ret + } + return *o.AvailabilityScore +} + +// GetAvailabilityScoreOk returns a tuple with the AvailabilityScore field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetAvailabilityScoreOk() (*float32, bool) { + if o == nil || IsNil(o.AvailabilityScore) { + return nil, false + } + return o.AvailabilityScore, true +} + +// HasAvailabilityScore returns a boolean if a field has been set. +func (o *AdminRunner) HasAvailabilityScore() bool { + if o != nil && !IsNil(o.AvailabilityScore) { + return true + } + + return false +} + +// SetAvailabilityScore gets a reference to the given float32 and assigns it to the AvailabilityScore field. +func (o *AdminRunner) SetAvailabilityScore(v float32) { + o.AvailabilityScore = &v +} + +// GetRegion returns the Region field value +func (o *AdminRunner) GetRegion() string { + if o == nil { + var ret string + return ret + } + + return o.Region +} + +// GetRegionOk returns a tuple with the Region field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetRegionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Region, true +} + +// SetRegion sets field value +func (o *AdminRunner) SetRegion(v string) { + o.Region = v +} + +// GetName returns the Name field value +func (o *AdminRunner) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *AdminRunner) SetName(v string) { + o.Name = v +} + +// GetState returns the State field value +func (o *AdminRunner) GetState() RunnerState { + if o == nil { + var ret RunnerState + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetStateOk() (*RunnerState, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminRunner) SetState(v RunnerState) { + o.State = v +} + +// GetLastChecked returns the LastChecked field value if set, zero value otherwise. +func (o *AdminRunner) GetLastChecked() string { + if o == nil || IsNil(o.LastChecked) { + var ret string + return ret + } + return *o.LastChecked +} + +// GetLastCheckedOk returns a tuple with the LastChecked field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetLastCheckedOk() (*string, bool) { + if o == nil || IsNil(o.LastChecked) { + return nil, false + } + return o.LastChecked, true +} + +// HasLastChecked returns a boolean if a field has been set. +func (o *AdminRunner) HasLastChecked() bool { + if o != nil && !IsNil(o.LastChecked) { + return true + } + + return false +} + +// SetLastChecked gets a reference to the given string and assigns it to the LastChecked field. +func (o *AdminRunner) SetLastChecked(v string) { + o.LastChecked = &v +} + +// GetUnschedulable returns the Unschedulable field value +func (o *AdminRunner) GetUnschedulable() bool { + if o == nil { + var ret bool + return ret + } + + return o.Unschedulable +} + +// GetUnschedulableOk returns a tuple with the Unschedulable field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetUnschedulableOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Unschedulable, true +} + +// SetUnschedulable sets field value +func (o *AdminRunner) SetUnschedulable(v bool) { + o.Unschedulable = v +} + +// GetCreatedAt returns the CreatedAt field value +func (o *AdminRunner) GetCreatedAt() string { + if o == nil { + var ret string + return ret + } + + return o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetCreatedAtOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.CreatedAt, true +} + +// SetCreatedAt sets field value +func (o *AdminRunner) SetCreatedAt(v string) { + o.CreatedAt = v +} + +// GetUpdatedAt returns the UpdatedAt field value +func (o *AdminRunner) GetUpdatedAt() string { + if o == nil { + var ret string + return ret + } + + return o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetUpdatedAtOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.UpdatedAt, true +} + +// SetUpdatedAt sets field value +func (o *AdminRunner) SetUpdatedAt(v string) { + o.UpdatedAt = v +} + +// GetVersion returns the Version field value +// Deprecated +func (o *AdminRunner) GetVersion() string { + if o == nil { + var ret string + return ret + } + + return o.Version +} + +// GetVersionOk returns a tuple with the Version field value +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunner) GetVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Version, true +} + +// SetVersion sets field value +// Deprecated +func (o *AdminRunner) SetVersion(v string) { + o.Version = v +} + +// GetApiVersion returns the ApiVersion field value +// Deprecated +func (o *AdminRunner) GetApiVersion() string { + if o == nil { + var ret string + return ret + } + + return o.ApiVersion +} + +// GetApiVersionOk returns a tuple with the ApiVersion field value +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunner) GetApiVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ApiVersion, true +} + +// SetApiVersion sets field value +// Deprecated +func (o *AdminRunner) SetApiVersion(v string) { + o.ApiVersion = v +} + +// GetAppVersion returns the AppVersion field value if set, zero value otherwise. +// Deprecated +func (o *AdminRunner) GetAppVersion() string { + if o == nil || IsNil(o.AppVersion) { + var ret string + return ret + } + return *o.AppVersion +} + +// GetAppVersionOk returns a tuple with the AppVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunner) GetAppVersionOk() (*string, bool) { + if o == nil || IsNil(o.AppVersion) { + return nil, false + } + return o.AppVersion, true +} + +// HasAppVersion returns a boolean if a field has been set. +func (o *AdminRunner) HasAppVersion() bool { + if o != nil && !IsNil(o.AppVersion) { + return true + } + + return false +} + +// SetAppVersion gets a reference to the given string and assigns it to the AppVersion field. +// Deprecated +func (o *AdminRunner) SetAppVersion(v string) { + o.AppVersion = &v +} + +// GetRegionType returns the RegionType field value if set, zero value otherwise. +func (o *AdminRunner) GetRegionType() RegionType { + if o == nil || IsNil(o.RegionType) { + var ret RegionType + return ret + } + return *o.RegionType +} + +// GetRegionTypeOk returns a tuple with the RegionType field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunner) GetRegionTypeOk() (*RegionType, bool) { + if o == nil || IsNil(o.RegionType) { + return nil, false + } + return o.RegionType, true +} + +// HasRegionType returns a boolean if a field has been set. +func (o *AdminRunner) HasRegionType() bool { + if o != nil && !IsNil(o.RegionType) { + return true + } + + return false +} + +// SetRegionType gets a reference to the given RegionType and assigns it to the RegionType field. +func (o *AdminRunner) SetRegionType(v RegionType) { + o.RegionType = &v +} + +func (o AdminRunner) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminRunner) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + if !IsNil(o.Domain) { + toSerialize["domain"] = o.Domain + } + if !IsNil(o.ApiUrl) { + toSerialize["apiUrl"] = o.ApiUrl + } + if !IsNil(o.ProxyUrl) { + toSerialize["proxyUrl"] = o.ProxyUrl + } + toSerialize["cpu"] = o.Cpu + toSerialize["memory"] = o.Memory + toSerialize["disk"] = o.Disk + if !IsNil(o.Gpu) { + toSerialize["gpu"] = o.Gpu + } + if !IsNil(o.GpuType) { + toSerialize["gpuType"] = o.GpuType + } + toSerialize["class"] = o.Class + if !IsNil(o.CurrentCpuUsagePercentage) { + toSerialize["currentCpuUsagePercentage"] = o.CurrentCpuUsagePercentage + } + if !IsNil(o.CurrentMemoryUsagePercentage) { + toSerialize["currentMemoryUsagePercentage"] = o.CurrentMemoryUsagePercentage + } + if !IsNil(o.CurrentDiskUsagePercentage) { + toSerialize["currentDiskUsagePercentage"] = o.CurrentDiskUsagePercentage + } + if !IsNil(o.CurrentAllocatedCpu) { + toSerialize["currentAllocatedCpu"] = o.CurrentAllocatedCpu + } + if !IsNil(o.CurrentAllocatedMemoryGiB) { + toSerialize["currentAllocatedMemoryGiB"] = o.CurrentAllocatedMemoryGiB + } + if !IsNil(o.CurrentAllocatedDiskGiB) { + toSerialize["currentAllocatedDiskGiB"] = o.CurrentAllocatedDiskGiB + } + if !IsNil(o.CurrentStartedBoxes) { + toSerialize["currentStartedBoxes"] = o.CurrentStartedBoxes + } + if !IsNil(o.AvailabilityScore) { + toSerialize["availabilityScore"] = o.AvailabilityScore + } + toSerialize["region"] = o.Region + toSerialize["name"] = o.Name + toSerialize["state"] = o.State + if !IsNil(o.LastChecked) { + toSerialize["lastChecked"] = o.LastChecked + } + toSerialize["unschedulable"] = o.Unschedulable + toSerialize["createdAt"] = o.CreatedAt + toSerialize["updatedAt"] = o.UpdatedAt + toSerialize["version"] = o.Version + toSerialize["apiVersion"] = o.ApiVersion + if !IsNil(o.AppVersion) { + toSerialize["appVersion"] = o.AppVersion + } + if !IsNil(o.RegionType) { + toSerialize["regionType"] = o.RegionType + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminRunner) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "cpu", + "memory", + "disk", + "class", + "region", + "name", + "state", + "unschedulable", + "createdAt", + "updatedAt", + "version", + "apiVersion", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminRunner := _AdminRunner{} + + err = json.Unmarshal(data, &varAdminRunner) + + if err != nil { + return err + } + + *o = AdminRunner(varAdminRunner) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "domain") + delete(additionalProperties, "apiUrl") + delete(additionalProperties, "proxyUrl") + delete(additionalProperties, "cpu") + delete(additionalProperties, "memory") + delete(additionalProperties, "disk") + delete(additionalProperties, "gpu") + delete(additionalProperties, "gpuType") + delete(additionalProperties, "class") + delete(additionalProperties, "currentCpuUsagePercentage") + delete(additionalProperties, "currentMemoryUsagePercentage") + delete(additionalProperties, "currentDiskUsagePercentage") + delete(additionalProperties, "currentAllocatedCpu") + delete(additionalProperties, "currentAllocatedMemoryGiB") + delete(additionalProperties, "currentAllocatedDiskGiB") + delete(additionalProperties, "currentStartedBoxes") + delete(additionalProperties, "availabilityScore") + delete(additionalProperties, "region") + delete(additionalProperties, "name") + delete(additionalProperties, "state") + delete(additionalProperties, "lastChecked") + delete(additionalProperties, "unschedulable") + delete(additionalProperties, "createdAt") + delete(additionalProperties, "updatedAt") + delete(additionalProperties, "version") + delete(additionalProperties, "apiVersion") + delete(additionalProperties, "appVersion") + delete(additionalProperties, "regionType") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminRunner struct { + value *AdminRunner + isSet bool +} + +func (v NullableAdminRunner) Get() *AdminRunner { + return v.value +} + +func (v *NullableAdminRunner) Set(val *AdminRunner) { + v.value = val + v.isSet = true +} + +func (v NullableAdminRunner) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminRunner) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminRunner(val *AdminRunner) *NullableAdminRunner { + return &NullableAdminRunner{value: val, isSet: true} +} + +func (v NullableAdminRunner) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminRunner) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_runner_item.go b/apps/api-client-go/model_admin_runner_item.go new file mode 100644 index 000000000..9600e6931 --- /dev/null +++ b/apps/api-client-go/model_admin_runner_item.go @@ -0,0 +1,1180 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminRunnerItem type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminRunnerItem{} + +// AdminRunnerItem struct for AdminRunnerItem +type AdminRunnerItem struct { + // The ID of the runner + Id string `json:"id"` + // The domain of the runner + Domain *string `json:"domain,omitempty"` + // The API URL of the runner + ApiUrl *string `json:"apiUrl,omitempty"` + // The proxy URL of the runner + ProxyUrl *string `json:"proxyUrl,omitempty"` + // The CPU capacity of the runner + Cpu float32 `json:"cpu"` + // The memory capacity of the runner in GiB + Memory float32 `json:"memory"` + // The disk capacity of the runner in GiB + Disk float32 `json:"disk"` + // The GPU capacity of the runner + Gpu *float32 `json:"gpu,omitempty"` + // The type of GPU + GpuType *string `json:"gpuType,omitempty"` + // The class of the runner + Class BoxClass `json:"class"` + // Current CPU usage percentage + CurrentCpuUsagePercentage *float32 `json:"currentCpuUsagePercentage,omitempty"` + // Current RAM usage percentage + CurrentMemoryUsagePercentage *float32 `json:"currentMemoryUsagePercentage,omitempty"` + // Current disk usage percentage + CurrentDiskUsagePercentage *float32 `json:"currentDiskUsagePercentage,omitempty"` + // Current allocated CPU + CurrentAllocatedCpu *float32 `json:"currentAllocatedCpu,omitempty"` + // Current allocated memory in GiB + CurrentAllocatedMemoryGiB *float32 `json:"currentAllocatedMemoryGiB,omitempty"` + // Current allocated disk in GiB + CurrentAllocatedDiskGiB *float32 `json:"currentAllocatedDiskGiB,omitempty"` + // Current number of started boxes + CurrentStartedBoxes *float32 `json:"currentStartedBoxes,omitempty"` + // Runner availability score + AvailabilityScore *float32 `json:"availabilityScore,omitempty"` + // The region of the runner + Region string `json:"region"` + // The name of the runner + Name string `json:"name"` + // The state of the runner + State RunnerState `json:"state"` + // The last time the runner was checked + LastChecked *string `json:"lastChecked,omitempty"` + // Whether the runner is unschedulable + Unschedulable bool `json:"unschedulable"` + // The creation timestamp of the runner + CreatedAt string `json:"createdAt"` + // The last update timestamp of the runner + UpdatedAt string `json:"updatedAt"` + // The version of the runner (deprecated in favor of apiVersion) + // Deprecated + Version string `json:"version"` + // The api version of the runner + // Deprecated + ApiVersion string `json:"apiVersion"` + // The app version of the runner + // Deprecated + AppVersion *string `json:"appVersion,omitempty"` + // The region type of the runner + RegionType *RegionType `json:"regionType,omitempty"` + // Whether the runner is currently draining + Draining bool `json:"draining"` + AdditionalProperties map[string]interface{} +} + +type _AdminRunnerItem AdminRunnerItem + +// NewAdminRunnerItem instantiates a new AdminRunnerItem object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminRunnerItem(id string, cpu float32, memory float32, disk float32, class BoxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string, draining bool) *AdminRunnerItem { + this := AdminRunnerItem{} + this.Id = id + this.Cpu = cpu + this.Memory = memory + this.Disk = disk + this.Class = class + this.Region = region + this.Name = name + this.State = state + this.Unschedulable = unschedulable + this.CreatedAt = createdAt + this.UpdatedAt = updatedAt + this.Version = version + this.ApiVersion = apiVersion + this.Draining = draining + return &this +} + +// NewAdminRunnerItemWithDefaults instantiates a new AdminRunnerItem object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminRunnerItemWithDefaults() *AdminRunnerItem { + this := AdminRunnerItem{} + return &this +} + +// GetId returns the Id field value +func (o *AdminRunnerItem) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminRunnerItem) SetId(v string) { + o.Id = v +} + +// GetDomain returns the Domain field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetDomain() string { + if o == nil || IsNil(o.Domain) { + var ret string + return ret + } + return *o.Domain +} + +// GetDomainOk returns a tuple with the Domain field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetDomainOk() (*string, bool) { + if o == nil || IsNil(o.Domain) { + return nil, false + } + return o.Domain, true +} + +// HasDomain returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasDomain() bool { + if o != nil && !IsNil(o.Domain) { + return true + } + + return false +} + +// SetDomain gets a reference to the given string and assigns it to the Domain field. +func (o *AdminRunnerItem) SetDomain(v string) { + o.Domain = &v +} + +// GetApiUrl returns the ApiUrl field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetApiUrl() string { + if o == nil || IsNil(o.ApiUrl) { + var ret string + return ret + } + return *o.ApiUrl +} + +// GetApiUrlOk returns a tuple with the ApiUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetApiUrlOk() (*string, bool) { + if o == nil || IsNil(o.ApiUrl) { + return nil, false + } + return o.ApiUrl, true +} + +// HasApiUrl returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasApiUrl() bool { + if o != nil && !IsNil(o.ApiUrl) { + return true + } + + return false +} + +// SetApiUrl gets a reference to the given string and assigns it to the ApiUrl field. +func (o *AdminRunnerItem) SetApiUrl(v string) { + o.ApiUrl = &v +} + +// GetProxyUrl returns the ProxyUrl field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetProxyUrl() string { + if o == nil || IsNil(o.ProxyUrl) { + var ret string + return ret + } + return *o.ProxyUrl +} + +// GetProxyUrlOk returns a tuple with the ProxyUrl field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetProxyUrlOk() (*string, bool) { + if o == nil || IsNil(o.ProxyUrl) { + return nil, false + } + return o.ProxyUrl, true +} + +// HasProxyUrl returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasProxyUrl() bool { + if o != nil && !IsNil(o.ProxyUrl) { + return true + } + + return false +} + +// SetProxyUrl gets a reference to the given string and assigns it to the ProxyUrl field. +func (o *AdminRunnerItem) SetProxyUrl(v string) { + o.ProxyUrl = &v +} + +// GetCpu returns the Cpu field value +func (o *AdminRunnerItem) GetCpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Cpu +} + +// GetCpuOk returns a tuple with the Cpu field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Cpu, true +} + +// SetCpu sets field value +func (o *AdminRunnerItem) SetCpu(v float32) { + o.Cpu = v +} + +// GetMemory returns the Memory field value +func (o *AdminRunnerItem) GetMemory() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Memory +} + +// GetMemoryOk returns a tuple with the Memory field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetMemoryOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Memory, true +} + +// SetMemory sets field value +func (o *AdminRunnerItem) SetMemory(v float32) { + o.Memory = v +} + +// GetDisk returns the Disk field value +func (o *AdminRunnerItem) GetDisk() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Disk +} + +// GetDiskOk returns a tuple with the Disk field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetDiskOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Disk, true +} + +// SetDisk sets field value +func (o *AdminRunnerItem) SetDisk(v float32) { + o.Disk = v +} + +// GetGpu returns the Gpu field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetGpu() float32 { + if o == nil || IsNil(o.Gpu) { + var ret float32 + return ret + } + return *o.Gpu +} + +// GetGpuOk returns a tuple with the Gpu field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetGpuOk() (*float32, bool) { + if o == nil || IsNil(o.Gpu) { + return nil, false + } + return o.Gpu, true +} + +// HasGpu returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasGpu() bool { + if o != nil && !IsNil(o.Gpu) { + return true + } + + return false +} + +// SetGpu gets a reference to the given float32 and assigns it to the Gpu field. +func (o *AdminRunnerItem) SetGpu(v float32) { + o.Gpu = &v +} + +// GetGpuType returns the GpuType field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetGpuType() string { + if o == nil || IsNil(o.GpuType) { + var ret string + return ret + } + return *o.GpuType +} + +// GetGpuTypeOk returns a tuple with the GpuType field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetGpuTypeOk() (*string, bool) { + if o == nil || IsNil(o.GpuType) { + return nil, false + } + return o.GpuType, true +} + +// HasGpuType returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasGpuType() bool { + if o != nil && !IsNil(o.GpuType) { + return true + } + + return false +} + +// SetGpuType gets a reference to the given string and assigns it to the GpuType field. +func (o *AdminRunnerItem) SetGpuType(v string) { + o.GpuType = &v +} + +// GetClass returns the Class field value +func (o *AdminRunnerItem) GetClass() BoxClass { + if o == nil { + var ret BoxClass + return ret + } + + return o.Class +} + +// GetClassOk returns a tuple with the Class field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetClassOk() (*BoxClass, bool) { + if o == nil { + return nil, false + } + return &o.Class, true +} + +// SetClass sets field value +func (o *AdminRunnerItem) SetClass(v BoxClass) { + o.Class = v +} + +// GetCurrentCpuUsagePercentage returns the CurrentCpuUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentCpuUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentCpuUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentCpuUsagePercentage +} + +// GetCurrentCpuUsagePercentageOk returns a tuple with the CurrentCpuUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentCpuUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentCpuUsagePercentage) { + return nil, false + } + return o.CurrentCpuUsagePercentage, true +} + +// HasCurrentCpuUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentCpuUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentCpuUsagePercentage) { + return true + } + + return false +} + +// SetCurrentCpuUsagePercentage gets a reference to the given float32 and assigns it to the CurrentCpuUsagePercentage field. +func (o *AdminRunnerItem) SetCurrentCpuUsagePercentage(v float32) { + o.CurrentCpuUsagePercentage = &v +} + +// GetCurrentMemoryUsagePercentage returns the CurrentMemoryUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentMemoryUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentMemoryUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentMemoryUsagePercentage +} + +// GetCurrentMemoryUsagePercentageOk returns a tuple with the CurrentMemoryUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentMemoryUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentMemoryUsagePercentage) { + return nil, false + } + return o.CurrentMemoryUsagePercentage, true +} + +// HasCurrentMemoryUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentMemoryUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentMemoryUsagePercentage) { + return true + } + + return false +} + +// SetCurrentMemoryUsagePercentage gets a reference to the given float32 and assigns it to the CurrentMemoryUsagePercentage field. +func (o *AdminRunnerItem) SetCurrentMemoryUsagePercentage(v float32) { + o.CurrentMemoryUsagePercentage = &v +} + +// GetCurrentDiskUsagePercentage returns the CurrentDiskUsagePercentage field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentDiskUsagePercentage() float32 { + if o == nil || IsNil(o.CurrentDiskUsagePercentage) { + var ret float32 + return ret + } + return *o.CurrentDiskUsagePercentage +} + +// GetCurrentDiskUsagePercentageOk returns a tuple with the CurrentDiskUsagePercentage field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentDiskUsagePercentageOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentDiskUsagePercentage) { + return nil, false + } + return o.CurrentDiskUsagePercentage, true +} + +// HasCurrentDiskUsagePercentage returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentDiskUsagePercentage() bool { + if o != nil && !IsNil(o.CurrentDiskUsagePercentage) { + return true + } + + return false +} + +// SetCurrentDiskUsagePercentage gets a reference to the given float32 and assigns it to the CurrentDiskUsagePercentage field. +func (o *AdminRunnerItem) SetCurrentDiskUsagePercentage(v float32) { + o.CurrentDiskUsagePercentage = &v +} + +// GetCurrentAllocatedCpu returns the CurrentAllocatedCpu field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentAllocatedCpu() float32 { + if o == nil || IsNil(o.CurrentAllocatedCpu) { + var ret float32 + return ret + } + return *o.CurrentAllocatedCpu +} + +// GetCurrentAllocatedCpuOk returns a tuple with the CurrentAllocatedCpu field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentAllocatedCpuOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedCpu) { + return nil, false + } + return o.CurrentAllocatedCpu, true +} + +// HasCurrentAllocatedCpu returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentAllocatedCpu() bool { + if o != nil && !IsNil(o.CurrentAllocatedCpu) { + return true + } + + return false +} + +// SetCurrentAllocatedCpu gets a reference to the given float32 and assigns it to the CurrentAllocatedCpu field. +func (o *AdminRunnerItem) SetCurrentAllocatedCpu(v float32) { + o.CurrentAllocatedCpu = &v +} + +// GetCurrentAllocatedMemoryGiB returns the CurrentAllocatedMemoryGiB field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentAllocatedMemoryGiB() float32 { + if o == nil || IsNil(o.CurrentAllocatedMemoryGiB) { + var ret float32 + return ret + } + return *o.CurrentAllocatedMemoryGiB +} + +// GetCurrentAllocatedMemoryGiBOk returns a tuple with the CurrentAllocatedMemoryGiB field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentAllocatedMemoryGiBOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedMemoryGiB) { + return nil, false + } + return o.CurrentAllocatedMemoryGiB, true +} + +// HasCurrentAllocatedMemoryGiB returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentAllocatedMemoryGiB() bool { + if o != nil && !IsNil(o.CurrentAllocatedMemoryGiB) { + return true + } + + return false +} + +// SetCurrentAllocatedMemoryGiB gets a reference to the given float32 and assigns it to the CurrentAllocatedMemoryGiB field. +func (o *AdminRunnerItem) SetCurrentAllocatedMemoryGiB(v float32) { + o.CurrentAllocatedMemoryGiB = &v +} + +// GetCurrentAllocatedDiskGiB returns the CurrentAllocatedDiskGiB field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentAllocatedDiskGiB() float32 { + if o == nil || IsNil(o.CurrentAllocatedDiskGiB) { + var ret float32 + return ret + } + return *o.CurrentAllocatedDiskGiB +} + +// GetCurrentAllocatedDiskGiBOk returns a tuple with the CurrentAllocatedDiskGiB field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentAllocatedDiskGiBOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentAllocatedDiskGiB) { + return nil, false + } + return o.CurrentAllocatedDiskGiB, true +} + +// HasCurrentAllocatedDiskGiB returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentAllocatedDiskGiB() bool { + if o != nil && !IsNil(o.CurrentAllocatedDiskGiB) { + return true + } + + return false +} + +// SetCurrentAllocatedDiskGiB gets a reference to the given float32 and assigns it to the CurrentAllocatedDiskGiB field. +func (o *AdminRunnerItem) SetCurrentAllocatedDiskGiB(v float32) { + o.CurrentAllocatedDiskGiB = &v +} + +// GetCurrentStartedBoxes returns the CurrentStartedBoxes field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetCurrentStartedBoxes() float32 { + if o == nil || IsNil(o.CurrentStartedBoxes) { + var ret float32 + return ret + } + return *o.CurrentStartedBoxes +} + +// GetCurrentStartedBoxesOk returns a tuple with the CurrentStartedBoxes field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCurrentStartedBoxesOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentStartedBoxes) { + return nil, false + } + return o.CurrentStartedBoxes, true +} + +// HasCurrentStartedBoxes returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasCurrentStartedBoxes() bool { + if o != nil && !IsNil(o.CurrentStartedBoxes) { + return true + } + + return false +} + +// SetCurrentStartedBoxes gets a reference to the given float32 and assigns it to the CurrentStartedBoxes field. +func (o *AdminRunnerItem) SetCurrentStartedBoxes(v float32) { + o.CurrentStartedBoxes = &v +} + +// GetAvailabilityScore returns the AvailabilityScore field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetAvailabilityScore() float32 { + if o == nil || IsNil(o.AvailabilityScore) { + var ret float32 + return ret + } + return *o.AvailabilityScore +} + +// GetAvailabilityScoreOk returns a tuple with the AvailabilityScore field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetAvailabilityScoreOk() (*float32, bool) { + if o == nil || IsNil(o.AvailabilityScore) { + return nil, false + } + return o.AvailabilityScore, true +} + +// HasAvailabilityScore returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasAvailabilityScore() bool { + if o != nil && !IsNil(o.AvailabilityScore) { + return true + } + + return false +} + +// SetAvailabilityScore gets a reference to the given float32 and assigns it to the AvailabilityScore field. +func (o *AdminRunnerItem) SetAvailabilityScore(v float32) { + o.AvailabilityScore = &v +} + +// GetRegion returns the Region field value +func (o *AdminRunnerItem) GetRegion() string { + if o == nil { + var ret string + return ret + } + + return o.Region +} + +// GetRegionOk returns a tuple with the Region field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetRegionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Region, true +} + +// SetRegion sets field value +func (o *AdminRunnerItem) SetRegion(v string) { + o.Region = v +} + +// GetName returns the Name field value +func (o *AdminRunnerItem) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *AdminRunnerItem) SetName(v string) { + o.Name = v +} + +// GetState returns the State field value +func (o *AdminRunnerItem) GetState() RunnerState { + if o == nil { + var ret RunnerState + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetStateOk() (*RunnerState, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *AdminRunnerItem) SetState(v RunnerState) { + o.State = v +} + +// GetLastChecked returns the LastChecked field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetLastChecked() string { + if o == nil || IsNil(o.LastChecked) { + var ret string + return ret + } + return *o.LastChecked +} + +// GetLastCheckedOk returns a tuple with the LastChecked field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetLastCheckedOk() (*string, bool) { + if o == nil || IsNil(o.LastChecked) { + return nil, false + } + return o.LastChecked, true +} + +// HasLastChecked returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasLastChecked() bool { + if o != nil && !IsNil(o.LastChecked) { + return true + } + + return false +} + +// SetLastChecked gets a reference to the given string and assigns it to the LastChecked field. +func (o *AdminRunnerItem) SetLastChecked(v string) { + o.LastChecked = &v +} + +// GetUnschedulable returns the Unschedulable field value +func (o *AdminRunnerItem) GetUnschedulable() bool { + if o == nil { + var ret bool + return ret + } + + return o.Unschedulable +} + +// GetUnschedulableOk returns a tuple with the Unschedulable field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetUnschedulableOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Unschedulable, true +} + +// SetUnschedulable sets field value +func (o *AdminRunnerItem) SetUnschedulable(v bool) { + o.Unschedulable = v +} + +// GetCreatedAt returns the CreatedAt field value +func (o *AdminRunnerItem) GetCreatedAt() string { + if o == nil { + var ret string + return ret + } + + return o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetCreatedAtOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.CreatedAt, true +} + +// SetCreatedAt sets field value +func (o *AdminRunnerItem) SetCreatedAt(v string) { + o.CreatedAt = v +} + +// GetUpdatedAt returns the UpdatedAt field value +func (o *AdminRunnerItem) GetUpdatedAt() string { + if o == nil { + var ret string + return ret + } + + return o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetUpdatedAtOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.UpdatedAt, true +} + +// SetUpdatedAt sets field value +func (o *AdminRunnerItem) SetUpdatedAt(v string) { + o.UpdatedAt = v +} + +// GetVersion returns the Version field value +// Deprecated +func (o *AdminRunnerItem) GetVersion() string { + if o == nil { + var ret string + return ret + } + + return o.Version +} + +// GetVersionOk returns a tuple with the Version field value +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunnerItem) GetVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Version, true +} + +// SetVersion sets field value +// Deprecated +func (o *AdminRunnerItem) SetVersion(v string) { + o.Version = v +} + +// GetApiVersion returns the ApiVersion field value +// Deprecated +func (o *AdminRunnerItem) GetApiVersion() string { + if o == nil { + var ret string + return ret + } + + return o.ApiVersion +} + +// GetApiVersionOk returns a tuple with the ApiVersion field value +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunnerItem) GetApiVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ApiVersion, true +} + +// SetApiVersion sets field value +// Deprecated +func (o *AdminRunnerItem) SetApiVersion(v string) { + o.ApiVersion = v +} + +// GetAppVersion returns the AppVersion field value if set, zero value otherwise. +// Deprecated +func (o *AdminRunnerItem) GetAppVersion() string { + if o == nil || IsNil(o.AppVersion) { + var ret string + return ret + } + return *o.AppVersion +} + +// GetAppVersionOk returns a tuple with the AppVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +// Deprecated +func (o *AdminRunnerItem) GetAppVersionOk() (*string, bool) { + if o == nil || IsNil(o.AppVersion) { + return nil, false + } + return o.AppVersion, true +} + +// HasAppVersion returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasAppVersion() bool { + if o != nil && !IsNil(o.AppVersion) { + return true + } + + return false +} + +// SetAppVersion gets a reference to the given string and assigns it to the AppVersion field. +// Deprecated +func (o *AdminRunnerItem) SetAppVersion(v string) { + o.AppVersion = &v +} + +// GetRegionType returns the RegionType field value if set, zero value otherwise. +func (o *AdminRunnerItem) GetRegionType() RegionType { + if o == nil || IsNil(o.RegionType) { + var ret RegionType + return ret + } + return *o.RegionType +} + +// GetRegionTypeOk returns a tuple with the RegionType field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetRegionTypeOk() (*RegionType, bool) { + if o == nil || IsNil(o.RegionType) { + return nil, false + } + return o.RegionType, true +} + +// HasRegionType returns a boolean if a field has been set. +func (o *AdminRunnerItem) HasRegionType() bool { + if o != nil && !IsNil(o.RegionType) { + return true + } + + return false +} + +// SetRegionType gets a reference to the given RegionType and assigns it to the RegionType field. +func (o *AdminRunnerItem) SetRegionType(v RegionType) { + o.RegionType = &v +} + +// GetDraining returns the Draining field value +func (o *AdminRunnerItem) GetDraining() bool { + if o == nil { + var ret bool + return ret + } + + return o.Draining +} + +// GetDrainingOk returns a tuple with the Draining field value +// and a boolean to check if the value has been set. +func (o *AdminRunnerItem) GetDrainingOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Draining, true +} + +// SetDraining sets field value +func (o *AdminRunnerItem) SetDraining(v bool) { + o.Draining = v +} + +func (o AdminRunnerItem) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminRunnerItem) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + if !IsNil(o.Domain) { + toSerialize["domain"] = o.Domain + } + if !IsNil(o.ApiUrl) { + toSerialize["apiUrl"] = o.ApiUrl + } + if !IsNil(o.ProxyUrl) { + toSerialize["proxyUrl"] = o.ProxyUrl + } + toSerialize["cpu"] = o.Cpu + toSerialize["memory"] = o.Memory + toSerialize["disk"] = o.Disk + if !IsNil(o.Gpu) { + toSerialize["gpu"] = o.Gpu + } + if !IsNil(o.GpuType) { + toSerialize["gpuType"] = o.GpuType + } + toSerialize["class"] = o.Class + if !IsNil(o.CurrentCpuUsagePercentage) { + toSerialize["currentCpuUsagePercentage"] = o.CurrentCpuUsagePercentage + } + if !IsNil(o.CurrentMemoryUsagePercentage) { + toSerialize["currentMemoryUsagePercentage"] = o.CurrentMemoryUsagePercentage + } + if !IsNil(o.CurrentDiskUsagePercentage) { + toSerialize["currentDiskUsagePercentage"] = o.CurrentDiskUsagePercentage + } + if !IsNil(o.CurrentAllocatedCpu) { + toSerialize["currentAllocatedCpu"] = o.CurrentAllocatedCpu + } + if !IsNil(o.CurrentAllocatedMemoryGiB) { + toSerialize["currentAllocatedMemoryGiB"] = o.CurrentAllocatedMemoryGiB + } + if !IsNil(o.CurrentAllocatedDiskGiB) { + toSerialize["currentAllocatedDiskGiB"] = o.CurrentAllocatedDiskGiB + } + if !IsNil(o.CurrentStartedBoxes) { + toSerialize["currentStartedBoxes"] = o.CurrentStartedBoxes + } + if !IsNil(o.AvailabilityScore) { + toSerialize["availabilityScore"] = o.AvailabilityScore + } + toSerialize["region"] = o.Region + toSerialize["name"] = o.Name + toSerialize["state"] = o.State + if !IsNil(o.LastChecked) { + toSerialize["lastChecked"] = o.LastChecked + } + toSerialize["unschedulable"] = o.Unschedulable + toSerialize["createdAt"] = o.CreatedAt + toSerialize["updatedAt"] = o.UpdatedAt + toSerialize["version"] = o.Version + toSerialize["apiVersion"] = o.ApiVersion + if !IsNil(o.AppVersion) { + toSerialize["appVersion"] = o.AppVersion + } + if !IsNil(o.RegionType) { + toSerialize["regionType"] = o.RegionType + } + toSerialize["draining"] = o.Draining + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminRunnerItem) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "cpu", + "memory", + "disk", + "class", + "region", + "name", + "state", + "unschedulable", + "createdAt", + "updatedAt", + "version", + "apiVersion", + "draining", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminRunnerItem := _AdminRunnerItem{} + + err = json.Unmarshal(data, &varAdminRunnerItem) + + if err != nil { + return err + } + + *o = AdminRunnerItem(varAdminRunnerItem) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "domain") + delete(additionalProperties, "apiUrl") + delete(additionalProperties, "proxyUrl") + delete(additionalProperties, "cpu") + delete(additionalProperties, "memory") + delete(additionalProperties, "disk") + delete(additionalProperties, "gpu") + delete(additionalProperties, "gpuType") + delete(additionalProperties, "class") + delete(additionalProperties, "currentCpuUsagePercentage") + delete(additionalProperties, "currentMemoryUsagePercentage") + delete(additionalProperties, "currentDiskUsagePercentage") + delete(additionalProperties, "currentAllocatedCpu") + delete(additionalProperties, "currentAllocatedMemoryGiB") + delete(additionalProperties, "currentAllocatedDiskGiB") + delete(additionalProperties, "currentStartedBoxes") + delete(additionalProperties, "availabilityScore") + delete(additionalProperties, "region") + delete(additionalProperties, "name") + delete(additionalProperties, "state") + delete(additionalProperties, "lastChecked") + delete(additionalProperties, "unschedulable") + delete(additionalProperties, "createdAt") + delete(additionalProperties, "updatedAt") + delete(additionalProperties, "version") + delete(additionalProperties, "apiVersion") + delete(additionalProperties, "appVersion") + delete(additionalProperties, "regionType") + delete(additionalProperties, "draining") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminRunnerItem struct { + value *AdminRunnerItem + isSet bool +} + +func (v NullableAdminRunnerItem) Get() *AdminRunnerItem { + return v.value +} + +func (v *NullableAdminRunnerItem) Set(val *AdminRunnerItem) { + v.value = val + v.isSet = true +} + +func (v NullableAdminRunnerItem) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminRunnerItem) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminRunnerItem(val *AdminRunnerItem) *NullableAdminRunnerItem { + return &NullableAdminRunnerItem{value: val, isSet: true} +} + +func (v NullableAdminRunnerItem) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminRunnerItem) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_admin_user_item.go b/apps/api-client-go/model_admin_user_item.go new file mode 100644 index 000000000..60d72a0ab --- /dev/null +++ b/apps/api-client-go/model_admin_user_item.go @@ -0,0 +1,259 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the AdminUserItem type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &AdminUserItem{} + +// AdminUserItem struct for AdminUserItem +type AdminUserItem struct { + // User ID + Id string `json:"id"` + // User email + Email string `json:"email"` + // Display name + Name string `json:"name"` + Role SystemRole `json:"role"` + AdditionalProperties map[string]interface{} +} + +type _AdminUserItem AdminUserItem + +// NewAdminUserItem instantiates a new AdminUserItem object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewAdminUserItem(id string, email string, name string, role SystemRole) *AdminUserItem { + this := AdminUserItem{} + this.Id = id + this.Email = email + this.Name = name + this.Role = role + return &this +} + +// NewAdminUserItemWithDefaults instantiates a new AdminUserItem object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewAdminUserItemWithDefaults() *AdminUserItem { + this := AdminUserItem{} + return &this +} + +// GetId returns the Id field value +func (o *AdminUserItem) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *AdminUserItem) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *AdminUserItem) SetId(v string) { + o.Id = v +} + +// GetEmail returns the Email field value +func (o *AdminUserItem) GetEmail() string { + if o == nil { + var ret string + return ret + } + + return o.Email +} + +// GetEmailOk returns a tuple with the Email field value +// and a boolean to check if the value has been set. +func (o *AdminUserItem) GetEmailOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Email, true +} + +// SetEmail sets field value +func (o *AdminUserItem) SetEmail(v string) { + o.Email = v +} + +// GetName returns the Name field value +func (o *AdminUserItem) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *AdminUserItem) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *AdminUserItem) SetName(v string) { + o.Name = v +} + +// GetRole returns the Role field value +func (o *AdminUserItem) GetRole() SystemRole { + if o == nil { + var ret SystemRole + return ret + } + + return o.Role +} + +// GetRoleOk returns a tuple with the Role field value +// and a boolean to check if the value has been set. +func (o *AdminUserItem) GetRoleOk() (*SystemRole, bool) { + if o == nil { + return nil, false + } + return &o.Role, true +} + +// SetRole sets field value +func (o *AdminUserItem) SetRole(v SystemRole) { + o.Role = v +} + +func (o AdminUserItem) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o AdminUserItem) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["email"] = o.Email + toSerialize["name"] = o.Name + toSerialize["role"] = o.Role + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *AdminUserItem) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "email", + "name", + "role", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varAdminUserItem := _AdminUserItem{} + + err = json.Unmarshal(data, &varAdminUserItem) + + if err != nil { + return err + } + + *o = AdminUserItem(varAdminUserItem) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "email") + delete(additionalProperties, "name") + delete(additionalProperties, "role") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableAdminUserItem struct { + value *AdminUserItem + isSet bool +} + +func (v NullableAdminUserItem) Get() *AdminUserItem { + return v.value +} + +func (v *NullableAdminUserItem) Set(val *AdminUserItem) { + v.value = val + v.isSet = true +} + +func (v NullableAdminUserItem) IsSet() bool { + return v.isSet +} + +func (v *NullableAdminUserItem) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableAdminUserItem(val *AdminUserItem) *NullableAdminUserItem { + return &NullableAdminUserItem{value: val, isSet: true} +} + +func (v NullableAdminUserItem) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableAdminUserItem) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_announcement.go b/apps/api-client-go/model_announcement.go index de8f39f54..2d009d4da 100644 --- a/apps/api-client-go/model_announcement.go +++ b/apps/api-client-go/model_announcement.go @@ -204,3 +204,5 @@ func (v *NullableAnnouncement) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_api_key_list.go b/apps/api-client-go/model_api_key_list.go index f8e725cc3..ba2cc9c76 100644 --- a/apps/api-client-go/model_api_key_list.go +++ b/apps/api-client-go/model_api_key_list.go @@ -351,3 +351,5 @@ func (v *NullableApiKeyList) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_api_key_response.go b/apps/api-client-go/model_api_key_response.go index 0b6907921..0f9f18f1c 100644 --- a/apps/api-client-go/model_api_key_response.go +++ b/apps/api-client-go/model_api_key_response.go @@ -289,3 +289,5 @@ func (v *NullableApiKeyResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_audit_log.go b/apps/api-client-go/model_audit_log.go index 9ed93cdea..0c91b88f6 100644 --- a/apps/api-client-go/model_audit_log.go +++ b/apps/api-client-go/model_audit_log.go @@ -615,3 +615,5 @@ func (v *NullableAuditLog) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_box.go b/apps/api-client-go/model_box.go new file mode 100644 index 000000000..67a9326c7 --- /dev/null +++ b/apps/api-client-go/model_box.go @@ -0,0 +1,1096 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the Box type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &Box{} + +// Box struct for Box +type Box struct { + // The public 12-character Box ID + Id string `json:"id"` + // The organization ID of the box + OrganizationId string `json:"organizationId"` + // The name of the box + Name string `json:"name"` + // The user associated with the project + User string `json:"user"` + // Environment variables for the box + Env map[string]string `json:"env"` + // Labels for the box + Labels map[string]string `json:"labels"` + // Whether the box http preview is public + Public bool `json:"public"` + // Whether to block all network access for the box + NetworkBlockAll bool `json:"networkBlockAll"` + // Comma-separated list of allowed CIDR network addresses for the box + NetworkAllowList *string `json:"networkAllowList,omitempty"` + // The target environment for the box + Target string `json:"target"` + // The image used for the box + Image *string `json:"image,omitempty"` + // The CPU quota for the box + Cpu float32 `json:"cpu"` + // The GPU quota for the box + Gpu float32 `json:"gpu"` + // The memory quota for the box + Memory float32 `json:"memory"` + // The disk quota for the box + Disk float32 `json:"disk"` + // The state of the box + State *BoxState `json:"state,omitempty"` + // The desired state of the box + DesiredState *BoxDesiredState `json:"desiredState,omitempty"` + // The error reason of the box + ErrorReason *string `json:"errorReason,omitempty"` + // Whether the box error is recoverable. + Recoverable *bool `json:"recoverable,omitempty"` + // Auto-stop interval in minutes (0 means disabled) + AutoStopInterval *float32 `json:"autoStopInterval,omitempty"` + // Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) + AutoDeleteInterval *float32 `json:"autoDeleteInterval,omitempty"` + // Array of volumes attached to the box + Volumes []BoxVolume `json:"volumes,omitempty"` + // The creation timestamp of the box + CreatedAt *string `json:"createdAt,omitempty"` + // The last update timestamp of the box + UpdatedAt *string `json:"updatedAt,omitempty"` + // The class of the box + // Deprecated + Class *string `json:"class,omitempty"` + // The version of the daemon running in the box + DaemonVersion *string `json:"daemonVersion,omitempty"` + // The runner ID of the box + RunnerId *string `json:"runnerId,omitempty"` + // The toolbox proxy URL for the box + ToolboxProxyUrl string `json:"toolboxProxyUrl"` + AdditionalProperties map[string]interface{} +} + +type _Box Box + +// NewBox instantiates a new Box object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBox(id string, organizationId string, name string, user string, env map[string]string, labels map[string]string, public bool, networkBlockAll bool, target string, cpu float32, gpu float32, memory float32, disk float32, toolboxProxyUrl string) *Box { + this := Box{} + this.Id = id + this.OrganizationId = organizationId + this.Name = name + this.User = user + this.Env = env + this.Labels = labels + this.Public = public + this.NetworkBlockAll = networkBlockAll + this.Target = target + this.Cpu = cpu + this.Gpu = gpu + this.Memory = memory + this.Disk = disk + this.ToolboxProxyUrl = toolboxProxyUrl + return &this +} + +// NewBoxWithDefaults instantiates a new Box object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBoxWithDefaults() *Box { + this := Box{} + return &this +} + +// GetId returns the Id field value +func (o *Box) GetId() string { + if o == nil { + var ret string + return ret + } + + return o.Id +} + +// GetIdOk returns a tuple with the Id field value +// and a boolean to check if the value has been set. +func (o *Box) GetIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Id, true +} + +// SetId sets field value +func (o *Box) SetId(v string) { + o.Id = v +} + +// GetOrganizationId returns the OrganizationId field value +func (o *Box) GetOrganizationId() string { + if o == nil { + var ret string + return ret + } + + return o.OrganizationId +} + +// GetOrganizationIdOk returns a tuple with the OrganizationId field value +// and a boolean to check if the value has been set. +func (o *Box) GetOrganizationIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.OrganizationId, true +} + +// SetOrganizationId sets field value +func (o *Box) SetOrganizationId(v string) { + o.OrganizationId = v +} + +// GetName returns the Name field value +func (o *Box) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *Box) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *Box) SetName(v string) { + o.Name = v +} + +// GetUser returns the User field value +func (o *Box) GetUser() string { + if o == nil { + var ret string + return ret + } + + return o.User +} + +// GetUserOk returns a tuple with the User field value +// and a boolean to check if the value has been set. +func (o *Box) GetUserOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.User, true +} + +// SetUser sets field value +func (o *Box) SetUser(v string) { + o.User = v +} + +// GetEnv returns the Env field value +func (o *Box) GetEnv() map[string]string { + if o == nil { + var ret map[string]string + return ret + } + + return o.Env +} + +// GetEnvOk returns a tuple with the Env field value +// and a boolean to check if the value has been set. +func (o *Box) GetEnvOk() (*map[string]string, bool) { + if o == nil { + return nil, false + } + return &o.Env, true +} + +// SetEnv sets field value +func (o *Box) SetEnv(v map[string]string) { + o.Env = v +} + +// GetLabels returns the Labels field value +func (o *Box) GetLabels() map[string]string { + if o == nil { + var ret map[string]string + return ret + } + + return o.Labels +} + +// GetLabelsOk returns a tuple with the Labels field value +// and a boolean to check if the value has been set. +func (o *Box) GetLabelsOk() (*map[string]string, bool) { + if o == nil { + return nil, false + } + return &o.Labels, true +} + +// SetLabels sets field value +func (o *Box) SetLabels(v map[string]string) { + o.Labels = v +} + +// GetPublic returns the Public field value +func (o *Box) GetPublic() bool { + if o == nil { + var ret bool + return ret + } + + return o.Public +} + +// GetPublicOk returns a tuple with the Public field value +// and a boolean to check if the value has been set. +func (o *Box) GetPublicOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.Public, true +} + +// SetPublic sets field value +func (o *Box) SetPublic(v bool) { + o.Public = v +} + +// GetNetworkBlockAll returns the NetworkBlockAll field value +func (o *Box) GetNetworkBlockAll() bool { + if o == nil { + var ret bool + return ret + } + + return o.NetworkBlockAll +} + +// GetNetworkBlockAllOk returns a tuple with the NetworkBlockAll field value +// and a boolean to check if the value has been set. +func (o *Box) GetNetworkBlockAllOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.NetworkBlockAll, true +} + +// SetNetworkBlockAll sets field value +func (o *Box) SetNetworkBlockAll(v bool) { + o.NetworkBlockAll = v +} + +// GetNetworkAllowList returns the NetworkAllowList field value if set, zero value otherwise. +func (o *Box) GetNetworkAllowList() string { + if o == nil || IsNil(o.NetworkAllowList) { + var ret string + return ret + } + return *o.NetworkAllowList +} + +// GetNetworkAllowListOk returns a tuple with the NetworkAllowList field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetNetworkAllowListOk() (*string, bool) { + if o == nil || IsNil(o.NetworkAllowList) { + return nil, false + } + return o.NetworkAllowList, true +} + +// HasNetworkAllowList returns a boolean if a field has been set. +func (o *Box) HasNetworkAllowList() bool { + if o != nil && !IsNil(o.NetworkAllowList) { + return true + } + + return false +} + +// SetNetworkAllowList gets a reference to the given string and assigns it to the NetworkAllowList field. +func (o *Box) SetNetworkAllowList(v string) { + o.NetworkAllowList = &v +} + +// GetTarget returns the Target field value +func (o *Box) GetTarget() string { + if o == nil { + var ret string + return ret + } + + return o.Target +} + +// GetTargetOk returns a tuple with the Target field value +// and a boolean to check if the value has been set. +func (o *Box) GetTargetOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Target, true +} + +// SetTarget sets field value +func (o *Box) SetTarget(v string) { + o.Target = v +} + +// GetImage returns the Image field value if set, zero value otherwise. +func (o *Box) GetImage() string { + if o == nil || IsNil(o.Image) { + var ret string + return ret + } + return *o.Image +} + +// GetImageOk returns a tuple with the Image field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetImageOk() (*string, bool) { + if o == nil || IsNil(o.Image) { + return nil, false + } + return o.Image, true +} + +// HasImage returns a boolean if a field has been set. +func (o *Box) HasImage() bool { + if o != nil && !IsNil(o.Image) { + return true + } + + return false +} + +// SetImage gets a reference to the given string and assigns it to the Image field. +func (o *Box) SetImage(v string) { + o.Image = &v +} + +// GetCpu returns the Cpu field value +func (o *Box) GetCpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Cpu +} + +// GetCpuOk returns a tuple with the Cpu field value +// and a boolean to check if the value has been set. +func (o *Box) GetCpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Cpu, true +} + +// SetCpu sets field value +func (o *Box) SetCpu(v float32) { + o.Cpu = v +} + +// GetGpu returns the Gpu field value +func (o *Box) GetGpu() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Gpu +} + +// GetGpuOk returns a tuple with the Gpu field value +// and a boolean to check if the value has been set. +func (o *Box) GetGpuOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Gpu, true +} + +// SetGpu sets field value +func (o *Box) SetGpu(v float32) { + o.Gpu = v +} + +// GetMemory returns the Memory field value +func (o *Box) GetMemory() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Memory +} + +// GetMemoryOk returns a tuple with the Memory field value +// and a boolean to check if the value has been set. +func (o *Box) GetMemoryOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Memory, true +} + +// SetMemory sets field value +func (o *Box) SetMemory(v float32) { + o.Memory = v +} + +// GetDisk returns the Disk field value +func (o *Box) GetDisk() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Disk +} + +// GetDiskOk returns a tuple with the Disk field value +// and a boolean to check if the value has been set. +func (o *Box) GetDiskOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Disk, true +} + +// SetDisk sets field value +func (o *Box) SetDisk(v float32) { + o.Disk = v +} + +// GetState returns the State field value if set, zero value otherwise. +func (o *Box) GetState() BoxState { + if o == nil || IsNil(o.State) { + var ret BoxState + return ret + } + return *o.State +} + +// GetStateOk returns a tuple with the State field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetStateOk() (*BoxState, bool) { + if o == nil || IsNil(o.State) { + return nil, false + } + return o.State, true +} + +// HasState returns a boolean if a field has been set. +func (o *Box) HasState() bool { + if o != nil && !IsNil(o.State) { + return true + } + + return false +} + +// SetState gets a reference to the given BoxState and assigns it to the State field. +func (o *Box) SetState(v BoxState) { + o.State = &v +} + +// GetDesiredState returns the DesiredState field value if set, zero value otherwise. +func (o *Box) GetDesiredState() BoxDesiredState { + if o == nil || IsNil(o.DesiredState) { + var ret BoxDesiredState + return ret + } + return *o.DesiredState +} + +// GetDesiredStateOk returns a tuple with the DesiredState field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetDesiredStateOk() (*BoxDesiredState, bool) { + if o == nil || IsNil(o.DesiredState) { + return nil, false + } + return o.DesiredState, true +} + +// HasDesiredState returns a boolean if a field has been set. +func (o *Box) HasDesiredState() bool { + if o != nil && !IsNil(o.DesiredState) { + return true + } + + return false +} + +// SetDesiredState gets a reference to the given BoxDesiredState and assigns it to the DesiredState field. +func (o *Box) SetDesiredState(v BoxDesiredState) { + o.DesiredState = &v +} + +// GetErrorReason returns the ErrorReason field value if set, zero value otherwise. +func (o *Box) GetErrorReason() string { + if o == nil || IsNil(o.ErrorReason) { + var ret string + return ret + } + return *o.ErrorReason +} + +// GetErrorReasonOk returns a tuple with the ErrorReason field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetErrorReasonOk() (*string, bool) { + if o == nil || IsNil(o.ErrorReason) { + return nil, false + } + return o.ErrorReason, true +} + +// HasErrorReason returns a boolean if a field has been set. +func (o *Box) HasErrorReason() bool { + if o != nil && !IsNil(o.ErrorReason) { + return true + } + + return false +} + +// SetErrorReason gets a reference to the given string and assigns it to the ErrorReason field. +func (o *Box) SetErrorReason(v string) { + o.ErrorReason = &v +} + +// GetRecoverable returns the Recoverable field value if set, zero value otherwise. +func (o *Box) GetRecoverable() bool { + if o == nil || IsNil(o.Recoverable) { + var ret bool + return ret + } + return *o.Recoverable +} + +// GetRecoverableOk returns a tuple with the Recoverable field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetRecoverableOk() (*bool, bool) { + if o == nil || IsNil(o.Recoverable) { + return nil, false + } + return o.Recoverable, true +} + +// HasRecoverable returns a boolean if a field has been set. +func (o *Box) HasRecoverable() bool { + if o != nil && !IsNil(o.Recoverable) { + return true + } + + return false +} + +// SetRecoverable gets a reference to the given bool and assigns it to the Recoverable field. +func (o *Box) SetRecoverable(v bool) { + o.Recoverable = &v +} + +// GetAutoStopInterval returns the AutoStopInterval field value if set, zero value otherwise. +func (o *Box) GetAutoStopInterval() float32 { + if o == nil || IsNil(o.AutoStopInterval) { + var ret float32 + return ret + } + return *o.AutoStopInterval +} + +// GetAutoStopIntervalOk returns a tuple with the AutoStopInterval field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetAutoStopIntervalOk() (*float32, bool) { + if o == nil || IsNil(o.AutoStopInterval) { + return nil, false + } + return o.AutoStopInterval, true +} + +// HasAutoStopInterval returns a boolean if a field has been set. +func (o *Box) HasAutoStopInterval() bool { + if o != nil && !IsNil(o.AutoStopInterval) { + return true + } + + return false +} + +// SetAutoStopInterval gets a reference to the given float32 and assigns it to the AutoStopInterval field. +func (o *Box) SetAutoStopInterval(v float32) { + o.AutoStopInterval = &v +} + +// GetAutoDeleteInterval returns the AutoDeleteInterval field value if set, zero value otherwise. +func (o *Box) GetAutoDeleteInterval() float32 { + if o == nil || IsNil(o.AutoDeleteInterval) { + var ret float32 + return ret + } + return *o.AutoDeleteInterval +} + +// GetAutoDeleteIntervalOk returns a tuple with the AutoDeleteInterval field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetAutoDeleteIntervalOk() (*float32, bool) { + if o == nil || IsNil(o.AutoDeleteInterval) { + return nil, false + } + return o.AutoDeleteInterval, true +} + +// HasAutoDeleteInterval returns a boolean if a field has been set. +func (o *Box) HasAutoDeleteInterval() bool { + if o != nil && !IsNil(o.AutoDeleteInterval) { + return true + } + + return false +} + +// SetAutoDeleteInterval gets a reference to the given float32 and assigns it to the AutoDeleteInterval field. +func (o *Box) SetAutoDeleteInterval(v float32) { + o.AutoDeleteInterval = &v +} + +// GetVolumes returns the Volumes field value if set, zero value otherwise. +func (o *Box) GetVolumes() []BoxVolume { + if o == nil || IsNil(o.Volumes) { + var ret []BoxVolume + return ret + } + return o.Volumes +} + +// GetVolumesOk returns a tuple with the Volumes field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetVolumesOk() ([]BoxVolume, bool) { + if o == nil || IsNil(o.Volumes) { + return nil, false + } + return o.Volumes, true +} + +// HasVolumes returns a boolean if a field has been set. +func (o *Box) HasVolumes() bool { + if o != nil && !IsNil(o.Volumes) { + return true + } + + return false +} + +// SetVolumes gets a reference to the given []BoxVolume and assigns it to the Volumes field. +func (o *Box) SetVolumes(v []BoxVolume) { + o.Volumes = v +} + +// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. +func (o *Box) GetCreatedAt() string { + if o == nil || IsNil(o.CreatedAt) { + var ret string + return ret + } + return *o.CreatedAt +} + +// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetCreatedAtOk() (*string, bool) { + if o == nil || IsNil(o.CreatedAt) { + return nil, false + } + return o.CreatedAt, true +} + +// HasCreatedAt returns a boolean if a field has been set. +func (o *Box) HasCreatedAt() bool { + if o != nil && !IsNil(o.CreatedAt) { + return true + } + + return false +} + +// SetCreatedAt gets a reference to the given string and assigns it to the CreatedAt field. +func (o *Box) SetCreatedAt(v string) { + o.CreatedAt = &v +} + +// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. +func (o *Box) GetUpdatedAt() string { + if o == nil || IsNil(o.UpdatedAt) { + var ret string + return ret + } + return *o.UpdatedAt +} + +// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetUpdatedAtOk() (*string, bool) { + if o == nil || IsNil(o.UpdatedAt) { + return nil, false + } + return o.UpdatedAt, true +} + +// HasUpdatedAt returns a boolean if a field has been set. +func (o *Box) HasUpdatedAt() bool { + if o != nil && !IsNil(o.UpdatedAt) { + return true + } + + return false +} + +// SetUpdatedAt gets a reference to the given string and assigns it to the UpdatedAt field. +func (o *Box) SetUpdatedAt(v string) { + o.UpdatedAt = &v +} + +// GetClass returns the Class field value if set, zero value otherwise. +// Deprecated +func (o *Box) GetClass() string { + if o == nil || IsNil(o.Class) { + var ret string + return ret + } + return *o.Class +} + +// GetClassOk returns a tuple with the Class field value if set, nil otherwise +// and a boolean to check if the value has been set. +// Deprecated +func (o *Box) GetClassOk() (*string, bool) { + if o == nil || IsNil(o.Class) { + return nil, false + } + return o.Class, true +} + +// HasClass returns a boolean if a field has been set. +func (o *Box) HasClass() bool { + if o != nil && !IsNil(o.Class) { + return true + } + + return false +} + +// SetClass gets a reference to the given string and assigns it to the Class field. +// Deprecated +func (o *Box) SetClass(v string) { + o.Class = &v +} + +// GetDaemonVersion returns the DaemonVersion field value if set, zero value otherwise. +func (o *Box) GetDaemonVersion() string { + if o == nil || IsNil(o.DaemonVersion) { + var ret string + return ret + } + return *o.DaemonVersion +} + +// GetDaemonVersionOk returns a tuple with the DaemonVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetDaemonVersionOk() (*string, bool) { + if o == nil || IsNil(o.DaemonVersion) { + return nil, false + } + return o.DaemonVersion, true +} + +// HasDaemonVersion returns a boolean if a field has been set. +func (o *Box) HasDaemonVersion() bool { + if o != nil && !IsNil(o.DaemonVersion) { + return true + } + + return false +} + +// SetDaemonVersion gets a reference to the given string and assigns it to the DaemonVersion field. +func (o *Box) SetDaemonVersion(v string) { + o.DaemonVersion = &v +} + +// GetRunnerId returns the RunnerId field value if set, zero value otherwise. +func (o *Box) GetRunnerId() string { + if o == nil || IsNil(o.RunnerId) { + var ret string + return ret + } + return *o.RunnerId +} + +// GetRunnerIdOk returns a tuple with the RunnerId field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Box) GetRunnerIdOk() (*string, bool) { + if o == nil || IsNil(o.RunnerId) { + return nil, false + } + return o.RunnerId, true +} + +// HasRunnerId returns a boolean if a field has been set. +func (o *Box) HasRunnerId() bool { + if o != nil && !IsNil(o.RunnerId) { + return true + } + + return false +} + +// SetRunnerId gets a reference to the given string and assigns it to the RunnerId field. +func (o *Box) SetRunnerId(v string) { + o.RunnerId = &v +} + +// GetToolboxProxyUrl returns the ToolboxProxyUrl field value +func (o *Box) GetToolboxProxyUrl() string { + if o == nil { + var ret string + return ret + } + + return o.ToolboxProxyUrl +} + +// GetToolboxProxyUrlOk returns a tuple with the ToolboxProxyUrl field value +// and a boolean to check if the value has been set. +func (o *Box) GetToolboxProxyUrlOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ToolboxProxyUrl, true +} + +// SetToolboxProxyUrl sets field value +func (o *Box) SetToolboxProxyUrl(v string) { + o.ToolboxProxyUrl = v +} + +func (o Box) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o Box) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["id"] = o.Id + toSerialize["organizationId"] = o.OrganizationId + toSerialize["name"] = o.Name + toSerialize["user"] = o.User + toSerialize["env"] = o.Env + toSerialize["labels"] = o.Labels + toSerialize["public"] = o.Public + toSerialize["networkBlockAll"] = o.NetworkBlockAll + if !IsNil(o.NetworkAllowList) { + toSerialize["networkAllowList"] = o.NetworkAllowList + } + toSerialize["target"] = o.Target + if !IsNil(o.Image) { + toSerialize["image"] = o.Image + } + toSerialize["cpu"] = o.Cpu + toSerialize["gpu"] = o.Gpu + toSerialize["memory"] = o.Memory + toSerialize["disk"] = o.Disk + if !IsNil(o.State) { + toSerialize["state"] = o.State + } + if !IsNil(o.DesiredState) { + toSerialize["desiredState"] = o.DesiredState + } + if !IsNil(o.ErrorReason) { + toSerialize["errorReason"] = o.ErrorReason + } + if !IsNil(o.Recoverable) { + toSerialize["recoverable"] = o.Recoverable + } + if !IsNil(o.AutoStopInterval) { + toSerialize["autoStopInterval"] = o.AutoStopInterval + } + if !IsNil(o.AutoDeleteInterval) { + toSerialize["autoDeleteInterval"] = o.AutoDeleteInterval + } + if !IsNil(o.Volumes) { + toSerialize["volumes"] = o.Volumes + } + if !IsNil(o.CreatedAt) { + toSerialize["createdAt"] = o.CreatedAt + } + if !IsNil(o.UpdatedAt) { + toSerialize["updatedAt"] = o.UpdatedAt + } + if !IsNil(o.Class) { + toSerialize["class"] = o.Class + } + if !IsNil(o.DaemonVersion) { + toSerialize["daemonVersion"] = o.DaemonVersion + } + if !IsNil(o.RunnerId) { + toSerialize["runnerId"] = o.RunnerId + } + toSerialize["toolboxProxyUrl"] = o.ToolboxProxyUrl + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *Box) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "id", + "organizationId", + "name", + "user", + "env", + "labels", + "public", + "networkBlockAll", + "target", + "cpu", + "gpu", + "memory", + "disk", + "toolboxProxyUrl", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBox := _Box{} + + err = json.Unmarshal(data, &varBox) + + if err != nil { + return err + } + + *o = Box(varBox) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "id") + delete(additionalProperties, "organizationId") + delete(additionalProperties, "name") + delete(additionalProperties, "user") + delete(additionalProperties, "env") + delete(additionalProperties, "labels") + delete(additionalProperties, "public") + delete(additionalProperties, "networkBlockAll") + delete(additionalProperties, "networkAllowList") + delete(additionalProperties, "target") + delete(additionalProperties, "image") + delete(additionalProperties, "cpu") + delete(additionalProperties, "gpu") + delete(additionalProperties, "memory") + delete(additionalProperties, "disk") + delete(additionalProperties, "state") + delete(additionalProperties, "desiredState") + delete(additionalProperties, "errorReason") + delete(additionalProperties, "recoverable") + delete(additionalProperties, "autoStopInterval") + delete(additionalProperties, "autoDeleteInterval") + delete(additionalProperties, "volumes") + delete(additionalProperties, "createdAt") + delete(additionalProperties, "updatedAt") + delete(additionalProperties, "class") + delete(additionalProperties, "daemonVersion") + delete(additionalProperties, "runnerId") + delete(additionalProperties, "toolboxProxyUrl") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableBox struct { + value *Box + isSet bool +} + +func (v NullableBox) Get() *Box { + return v.value +} + +func (v *NullableBox) Set(val *Box) { + v.value = val + v.isSet = true +} + +func (v NullableBox) IsSet() bool { + return v.isSet +} + +func (v *NullableBox) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBox(val *Box) *NullableBox { + return &NullableBox{value: val, isSet: true} +} + +func (v NullableBox) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBox) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_box_class.go b/apps/api-client-go/model_box_class.go new file mode 100644 index 000000000..96a69a7e7 --- /dev/null +++ b/apps/api-client-go/model_box_class.go @@ -0,0 +1,117 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" +) + +// BoxClass The class of the runner +type BoxClass string + +// List of BoxClass +const ( + BOXCLASS_SMALL BoxClass = "small" + BOXCLASS_MEDIUM BoxClass = "medium" + BOXCLASS_LARGE BoxClass = "large" + BOXCLASS_UNKNOWN_DEFAULT_OPEN_API BoxClass = "unknown_default_open_api" +) + +// All allowed values of BoxClass enum +var AllowedBoxClassEnumValues = []BoxClass{ + "small", + "medium", + "large", + "unknown_default_open_api", +} + +func (v *BoxClass) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := BoxClass(value) + for _, existing := range AllowedBoxClassEnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + *v = BOXCLASS_UNKNOWN_DEFAULT_OPEN_API + return nil +} + +// NewBoxClassFromValue returns a pointer to a valid BoxClass +// for the value passed as argument, or an error if the value passed is not allowed by the enum +func NewBoxClassFromValue(v string) (*BoxClass, error) { + ev := BoxClass(v) + if ev.IsValid() { + return &ev, nil + } else { + enumValue := BOXCLASS_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil + } +} + +// IsValid return true if the value is valid for the enum, false otherwise +func (v BoxClass) IsValid() bool { + for _, existing := range AllowedBoxClassEnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to BoxClass value +func (v BoxClass) Ptr() *BoxClass { + return &v +} + +type NullableBoxClass struct { + value *BoxClass + isSet bool +} + +func (v NullableBoxClass) Get() *BoxClass { + return v.value +} + +func (v *NullableBoxClass) Set(val *BoxClass) { + v.value = val + v.isSet = true +} + +func (v NullableBoxClass) IsSet() bool { + return v.isSet +} + +func (v *NullableBoxClass) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBoxClass(val *BoxClass) *NullableBoxClass { + return &NullableBoxClass{value: val, isSet: true} +} + +func (v NullableBoxClass) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBoxClass) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + diff --git a/apps/api-client-go/model_box_desired_state.go b/apps/api-client-go/model_box_desired_state.go new file mode 100644 index 000000000..e780783ae --- /dev/null +++ b/apps/api-client-go/model_box_desired_state.go @@ -0,0 +1,119 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" +) + +// BoxDesiredState The desired state of the box +type BoxDesiredState string + +// List of BoxDesiredState +const ( + BOXDESIREDSTATE_DESTROYED BoxDesiredState = "destroyed" + BOXDESIREDSTATE_STARTED BoxDesiredState = "started" + BOXDESIREDSTATE_STOPPED BoxDesiredState = "stopped" + BOXDESIREDSTATE_RESIZED BoxDesiredState = "resized" + BOXDESIREDSTATE_UNKNOWN_DEFAULT_OPEN_API BoxDesiredState = "unknown_default_open_api" +) + +// All allowed values of BoxDesiredState enum +var AllowedBoxDesiredStateEnumValues = []BoxDesiredState{ + "destroyed", + "started", + "stopped", + "resized", + "unknown_default_open_api", +} + +func (v *BoxDesiredState) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := BoxDesiredState(value) + for _, existing := range AllowedBoxDesiredStateEnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + *v = BOXDESIREDSTATE_UNKNOWN_DEFAULT_OPEN_API + return nil +} + +// NewBoxDesiredStateFromValue returns a pointer to a valid BoxDesiredState +// for the value passed as argument, or an error if the value passed is not allowed by the enum +func NewBoxDesiredStateFromValue(v string) (*BoxDesiredState, error) { + ev := BoxDesiredState(v) + if ev.IsValid() { + return &ev, nil + } else { + enumValue := BOXDESIREDSTATE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil + } +} + +// IsValid return true if the value is valid for the enum, false otherwise +func (v BoxDesiredState) IsValid() bool { + for _, existing := range AllowedBoxDesiredStateEnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to BoxDesiredState value +func (v BoxDesiredState) Ptr() *BoxDesiredState { + return &v +} + +type NullableBoxDesiredState struct { + value *BoxDesiredState + isSet bool +} + +func (v NullableBoxDesiredState) Get() *BoxDesiredState { + return v.value +} + +func (v *NullableBoxDesiredState) Set(val *BoxDesiredState) { + v.value = val + v.isSet = true +} + +func (v NullableBoxDesiredState) IsSet() bool { + return v.isSet +} + +func (v *NullableBoxDesiredState) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBoxDesiredState(val *BoxDesiredState) *NullableBoxDesiredState { + return &NullableBoxDesiredState{value: val, isSet: true} +} + +func (v NullableBoxDesiredState) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBoxDesiredState) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + diff --git a/apps/api-client-go/model_box_labels.go b/apps/api-client-go/model_box_labels.go new file mode 100644 index 000000000..99342753f --- /dev/null +++ b/apps/api-client-go/model_box_labels.go @@ -0,0 +1,170 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the BoxLabels type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BoxLabels{} + +// BoxLabels struct for BoxLabels +type BoxLabels struct { + // Key-value pairs of labels + Labels map[string]string `json:"labels"` + AdditionalProperties map[string]interface{} +} + +type _BoxLabels BoxLabels + +// NewBoxLabels instantiates a new BoxLabels object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBoxLabels(labels map[string]string) *BoxLabels { + this := BoxLabels{} + this.Labels = labels + return &this +} + +// NewBoxLabelsWithDefaults instantiates a new BoxLabels object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBoxLabelsWithDefaults() *BoxLabels { + this := BoxLabels{} + return &this +} + +// GetLabels returns the Labels field value +func (o *BoxLabels) GetLabels() map[string]string { + if o == nil { + var ret map[string]string + return ret + } + + return o.Labels +} + +// GetLabelsOk returns a tuple with the Labels field value +// and a boolean to check if the value has been set. +func (o *BoxLabels) GetLabelsOk() (*map[string]string, bool) { + if o == nil { + return nil, false + } + return &o.Labels, true +} + +// SetLabels sets field value +func (o *BoxLabels) SetLabels(v map[string]string) { + o.Labels = v +} + +func (o BoxLabels) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BoxLabels) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["labels"] = o.Labels + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *BoxLabels) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "labels", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBoxLabels := _BoxLabels{} + + err = json.Unmarshal(data, &varBoxLabels) + + if err != nil { + return err + } + + *o = BoxLabels(varBoxLabels) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "labels") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableBoxLabels struct { + value *BoxLabels + isSet bool +} + +func (v NullableBoxLabels) Get() *BoxLabels { + return v.value +} + +func (v *NullableBoxLabels) Set(val *BoxLabels) { + v.value = val + v.isSet = true +} + +func (v NullableBoxLabels) IsSet() bool { + return v.isSet +} + +func (v *NullableBoxLabels) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBoxLabels(val *BoxLabels) *NullableBoxLabels { + return &NullableBoxLabels{value: val, isSet: true} +} + +func (v NullableBoxLabels) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBoxLabels) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_box_state.go b/apps/api-client-go/model_box_state.go new file mode 100644 index 000000000..02b547d2a --- /dev/null +++ b/apps/api-client-go/model_box_state.go @@ -0,0 +1,137 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" +) + +// BoxState The state of the box +type BoxState string + +// List of BoxState +const ( + BOXSTATE_CREATING BoxState = "creating" + BOXSTATE_RESTORING BoxState = "restoring" + BOXSTATE_DESTROYED BoxState = "destroyed" + BOXSTATE_DESTROYING BoxState = "destroying" + BOXSTATE_STARTED BoxState = "started" + BOXSTATE_STOPPED BoxState = "stopped" + BOXSTATE_STARTING BoxState = "starting" + BOXSTATE_STOPPING BoxState = "stopping" + BOXSTATE_ERROR BoxState = "error" + BOXSTATE_UNKNOWN BoxState = "unknown" + BOXSTATE_ARCHIVED BoxState = "archived" + BOXSTATE_ARCHIVING BoxState = "archiving" + BOXSTATE_RESIZING BoxState = "resizing" + BOXSTATE_UNKNOWN_DEFAULT_OPEN_API BoxState = "unknown_default_open_api" +) + +// All allowed values of BoxState enum +var AllowedBoxStateEnumValues = []BoxState{ + "creating", + "restoring", + "destroyed", + "destroying", + "started", + "stopped", + "starting", + "stopping", + "error", + "unknown", + "archived", + "archiving", + "resizing", + "unknown_default_open_api", +} + +func (v *BoxState) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := BoxState(value) + for _, existing := range AllowedBoxStateEnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + *v = BOXSTATE_UNKNOWN_DEFAULT_OPEN_API + return nil +} + +// NewBoxStateFromValue returns a pointer to a valid BoxState +// for the value passed as argument, or an error if the value passed is not allowed by the enum +func NewBoxStateFromValue(v string) (*BoxState, error) { + ev := BoxState(v) + if ev.IsValid() { + return &ev, nil + } else { + enumValue := BOXSTATE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil + } +} + +// IsValid return true if the value is valid for the enum, false otherwise +func (v BoxState) IsValid() bool { + for _, existing := range AllowedBoxStateEnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to BoxState value +func (v BoxState) Ptr() *BoxState { + return &v +} + +type NullableBoxState struct { + value *BoxState + isSet bool +} + +func (v NullableBoxState) Get() *BoxState { + return v.value +} + +func (v *NullableBoxState) Set(val *BoxState) { + v.value = val + v.isSet = true +} + +func (v NullableBoxState) IsSet() bool { + return v.isSet +} + +func (v *NullableBoxState) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBoxState(val *BoxState) *NullableBoxState { + return &NullableBoxState{value: val, isSet: true} +} + +func (v NullableBoxState) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBoxState) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + diff --git a/apps/api-client-go/model_box_volume.go b/apps/api-client-go/model_box_volume.go new file mode 100644 index 000000000..e6614751d --- /dev/null +++ b/apps/api-client-go/model_box_volume.go @@ -0,0 +1,238 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the BoxVolume type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &BoxVolume{} + +// BoxVolume struct for BoxVolume +type BoxVolume struct { + // The ID of the volume + VolumeId string `json:"volumeId"` + // The mount path for the volume + MountPath string `json:"mountPath"` + // Optional subpath within the volume to mount. When specified, only this S3 prefix will be accessible. When omitted, the entire volume is mounted. + Subpath *string `json:"subpath,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _BoxVolume BoxVolume + +// NewBoxVolume instantiates a new BoxVolume object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewBoxVolume(volumeId string, mountPath string) *BoxVolume { + this := BoxVolume{} + this.VolumeId = volumeId + this.MountPath = mountPath + return &this +} + +// NewBoxVolumeWithDefaults instantiates a new BoxVolume object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewBoxVolumeWithDefaults() *BoxVolume { + this := BoxVolume{} + return &this +} + +// GetVolumeId returns the VolumeId field value +func (o *BoxVolume) GetVolumeId() string { + if o == nil { + var ret string + return ret + } + + return o.VolumeId +} + +// GetVolumeIdOk returns a tuple with the VolumeId field value +// and a boolean to check if the value has been set. +func (o *BoxVolume) GetVolumeIdOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.VolumeId, true +} + +// SetVolumeId sets field value +func (o *BoxVolume) SetVolumeId(v string) { + o.VolumeId = v +} + +// GetMountPath returns the MountPath field value +func (o *BoxVolume) GetMountPath() string { + if o == nil { + var ret string + return ret + } + + return o.MountPath +} + +// GetMountPathOk returns a tuple with the MountPath field value +// and a boolean to check if the value has been set. +func (o *BoxVolume) GetMountPathOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.MountPath, true +} + +// SetMountPath sets field value +func (o *BoxVolume) SetMountPath(v string) { + o.MountPath = v +} + +// GetSubpath returns the Subpath field value if set, zero value otherwise. +func (o *BoxVolume) GetSubpath() string { + if o == nil || IsNil(o.Subpath) { + var ret string + return ret + } + return *o.Subpath +} + +// GetSubpathOk returns a tuple with the Subpath field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *BoxVolume) GetSubpathOk() (*string, bool) { + if o == nil || IsNil(o.Subpath) { + return nil, false + } + return o.Subpath, true +} + +// HasSubpath returns a boolean if a field has been set. +func (o *BoxVolume) HasSubpath() bool { + if o != nil && !IsNil(o.Subpath) { + return true + } + + return false +} + +// SetSubpath gets a reference to the given string and assigns it to the Subpath field. +func (o *BoxVolume) SetSubpath(v string) { + o.Subpath = &v +} + +func (o BoxVolume) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o BoxVolume) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["volumeId"] = o.VolumeId + toSerialize["mountPath"] = o.MountPath + if !IsNil(o.Subpath) { + toSerialize["subpath"] = o.Subpath + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *BoxVolume) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "volumeId", + "mountPath", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varBoxVolume := _BoxVolume{} + + err = json.Unmarshal(data, &varBoxVolume) + + if err != nil { + return err + } + + *o = BoxVolume(varBoxVolume) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "volumeId") + delete(additionalProperties, "mountPath") + delete(additionalProperties, "subpath") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableBoxVolume struct { + value *BoxVolume + isSet bool +} + +func (v NullableBoxVolume) Get() *BoxVolume { + return v.value +} + +func (v *NullableBoxVolume) Set(val *BoxVolume) { + v.value = val + v.isSet = true +} + +func (v NullableBoxVolume) IsSet() bool { + return v.isSet +} + +func (v *NullableBoxVolume) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBoxVolume(val *BoxVolume) *NullableBoxVolume { + return &NullableBoxVolume{value: val, isSet: true} +} + +func (v NullableBoxVolume) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBoxVolume) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_boxlite_configuration.go b/apps/api-client-go/model_boxlite_configuration.go index 9379ecbcb..debcc5a5d 100644 --- a/apps/api-client-go/model_boxlite_configuration.go +++ b/apps/api-client-go/model_boxlite_configuration.go @@ -37,12 +37,8 @@ type BoxliteConfiguration struct { ProxyTemplateUrl string `json:"proxyTemplateUrl"` // Toolbox template URL ProxyToolboxUrl string `json:"proxyToolboxUrl"` - // Default snapshot for sandboxes - DefaultSnapshot string `json:"defaultSnapshot"` // Dashboard URL DashboardUrl string `json:"dashboardUrl"` - // Maximum auto-archive interval in minutes - MaxAutoArchiveInterval float32 `json:"maxAutoArchiveInterval"` // Whether maintenance mode is enabled MaintananceMode bool `json:"maintananceMode"` // Current environment @@ -66,7 +62,7 @@ type _BoxliteConfiguration BoxliteConfiguration // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewBoxliteConfiguration(version string, oidc OidcConfig, linkedAccountsEnabled bool, announcements map[string]Announcement, proxyTemplateUrl string, proxyToolboxUrl string, defaultSnapshot string, dashboardUrl string, maxAutoArchiveInterval float32, maintananceMode bool, environment string) *BoxliteConfiguration { +func NewBoxliteConfiguration(version string, oidc OidcConfig, linkedAccountsEnabled bool, announcements map[string]Announcement, proxyTemplateUrl string, proxyToolboxUrl string, dashboardUrl string, maintananceMode bool, environment string) *BoxliteConfiguration { this := BoxliteConfiguration{} this.Version = version this.Oidc = oidc @@ -74,9 +70,7 @@ func NewBoxliteConfiguration(version string, oidc OidcConfig, linkedAccountsEnab this.Announcements = announcements this.ProxyTemplateUrl = proxyTemplateUrl this.ProxyToolboxUrl = proxyToolboxUrl - this.DefaultSnapshot = defaultSnapshot this.DashboardUrl = dashboardUrl - this.MaxAutoArchiveInterval = maxAutoArchiveInterval this.MaintananceMode = maintananceMode this.Environment = environment return &this @@ -298,30 +292,6 @@ func (o *BoxliteConfiguration) SetProxyToolboxUrl(v string) { o.ProxyToolboxUrl = v } -// GetDefaultSnapshot returns the DefaultSnapshot field value -func (o *BoxliteConfiguration) GetDefaultSnapshot() string { - if o == nil { - var ret string - return ret - } - - return o.DefaultSnapshot -} - -// GetDefaultSnapshotOk returns a tuple with the DefaultSnapshot field value -// and a boolean to check if the value has been set. -func (o *BoxliteConfiguration) GetDefaultSnapshotOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.DefaultSnapshot, true -} - -// SetDefaultSnapshot sets field value -func (o *BoxliteConfiguration) SetDefaultSnapshot(v string) { - o.DefaultSnapshot = v -} - // GetDashboardUrl returns the DashboardUrl field value func (o *BoxliteConfiguration) GetDashboardUrl() string { if o == nil { @@ -346,30 +316,6 @@ func (o *BoxliteConfiguration) SetDashboardUrl(v string) { o.DashboardUrl = v } -// GetMaxAutoArchiveInterval returns the MaxAutoArchiveInterval field value -func (o *BoxliteConfiguration) GetMaxAutoArchiveInterval() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.MaxAutoArchiveInterval -} - -// GetMaxAutoArchiveIntervalOk returns a tuple with the MaxAutoArchiveInterval field value -// and a boolean to check if the value has been set. -func (o *BoxliteConfiguration) GetMaxAutoArchiveIntervalOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.MaxAutoArchiveInterval, true -} - -// SetMaxAutoArchiveInterval sets field value -func (o *BoxliteConfiguration) SetMaxAutoArchiveInterval(v float32) { - o.MaxAutoArchiveInterval = v -} - // GetMaintananceMode returns the MaintananceMode field value func (o *BoxliteConfiguration) GetMaintananceMode() bool { if o == nil { @@ -600,9 +546,7 @@ func (o BoxliteConfiguration) ToMap() (map[string]interface{}, error) { } toSerialize["proxyTemplateUrl"] = o.ProxyTemplateUrl toSerialize["proxyToolboxUrl"] = o.ProxyToolboxUrl - toSerialize["defaultSnapshot"] = o.DefaultSnapshot toSerialize["dashboardUrl"] = o.DashboardUrl - toSerialize["maxAutoArchiveInterval"] = o.MaxAutoArchiveInterval toSerialize["maintananceMode"] = o.MaintananceMode toSerialize["environment"] = o.Environment if !IsNil(o.BillingApiUrl) { @@ -639,9 +583,7 @@ func (o *BoxliteConfiguration) UnmarshalJSON(data []byte) (err error) { "announcements", "proxyTemplateUrl", "proxyToolboxUrl", - "defaultSnapshot", "dashboardUrl", - "maxAutoArchiveInterval", "maintananceMode", "environment", } @@ -681,9 +623,7 @@ func (o *BoxliteConfiguration) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "pylonAppId") delete(additionalProperties, "proxyTemplateUrl") delete(additionalProperties, "proxyToolboxUrl") - delete(additionalProperties, "defaultSnapshot") delete(additionalProperties, "dashboardUrl") - delete(additionalProperties, "maxAutoArchiveInterval") delete(additionalProperties, "maintananceMode") delete(additionalProperties, "environment") delete(additionalProperties, "billingApiUrl") @@ -732,3 +672,5 @@ func (v *NullableBoxliteConfiguration) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_build_info.go b/apps/api-client-go/model_build_info.go deleted file mode 100644 index 77dcf8474..000000000 --- a/apps/api-client-go/model_build_info.go +++ /dev/null @@ -1,305 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "time" - "fmt" -) - -// checks if the BuildInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &BuildInfo{} - -// BuildInfo struct for BuildInfo -type BuildInfo struct { - // The Dockerfile content used for the build - DockerfileContent *string `json:"dockerfileContent,omitempty"` - // The context hashes used for the build - ContextHashes []string `json:"contextHashes,omitempty"` - // The creation timestamp - CreatedAt time.Time `json:"createdAt"` - // The last update timestamp - UpdatedAt time.Time `json:"updatedAt"` - // The snapshot reference - SnapshotRef string `json:"snapshotRef"` - AdditionalProperties map[string]interface{} -} - -type _BuildInfo BuildInfo - -// NewBuildInfo instantiates a new BuildInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewBuildInfo(createdAt time.Time, updatedAt time.Time, snapshotRef string) *BuildInfo { - this := BuildInfo{} - this.CreatedAt = createdAt - this.UpdatedAt = updatedAt - this.SnapshotRef = snapshotRef - return &this -} - -// NewBuildInfoWithDefaults instantiates a new BuildInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewBuildInfoWithDefaults() *BuildInfo { - this := BuildInfo{} - return &this -} - -// GetDockerfileContent returns the DockerfileContent field value if set, zero value otherwise. -func (o *BuildInfo) GetDockerfileContent() string { - if o == nil || IsNil(o.DockerfileContent) { - var ret string - return ret - } - return *o.DockerfileContent -} - -// GetDockerfileContentOk returns a tuple with the DockerfileContent field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *BuildInfo) GetDockerfileContentOk() (*string, bool) { - if o == nil || IsNil(o.DockerfileContent) { - return nil, false - } - return o.DockerfileContent, true -} - -// HasDockerfileContent returns a boolean if a field has been set. -func (o *BuildInfo) HasDockerfileContent() bool { - if o != nil && !IsNil(o.DockerfileContent) { - return true - } - - return false -} - -// SetDockerfileContent gets a reference to the given string and assigns it to the DockerfileContent field. -func (o *BuildInfo) SetDockerfileContent(v string) { - o.DockerfileContent = &v -} - -// GetContextHashes returns the ContextHashes field value if set, zero value otherwise. -func (o *BuildInfo) GetContextHashes() []string { - if o == nil || IsNil(o.ContextHashes) { - var ret []string - return ret - } - return o.ContextHashes -} - -// GetContextHashesOk returns a tuple with the ContextHashes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *BuildInfo) GetContextHashesOk() ([]string, bool) { - if o == nil || IsNil(o.ContextHashes) { - return nil, false - } - return o.ContextHashes, true -} - -// HasContextHashes returns a boolean if a field has been set. -func (o *BuildInfo) HasContextHashes() bool { - if o != nil && !IsNil(o.ContextHashes) { - return true - } - - return false -} - -// SetContextHashes gets a reference to the given []string and assigns it to the ContextHashes field. -func (o *BuildInfo) SetContextHashes(v []string) { - o.ContextHashes = v -} - -// GetCreatedAt returns the CreatedAt field value -func (o *BuildInfo) GetCreatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value -// and a boolean to check if the value has been set. -func (o *BuildInfo) GetCreatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.CreatedAt, true -} - -// SetCreatedAt sets field value -func (o *BuildInfo) SetCreatedAt(v time.Time) { - o.CreatedAt = v -} - -// GetUpdatedAt returns the UpdatedAt field value -func (o *BuildInfo) GetUpdatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value -// and a boolean to check if the value has been set. -func (o *BuildInfo) GetUpdatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.UpdatedAt, true -} - -// SetUpdatedAt sets field value -func (o *BuildInfo) SetUpdatedAt(v time.Time) { - o.UpdatedAt = v -} - -// GetSnapshotRef returns the SnapshotRef field value -func (o *BuildInfo) GetSnapshotRef() string { - if o == nil { - var ret string - return ret - } - - return o.SnapshotRef -} - -// GetSnapshotRefOk returns a tuple with the SnapshotRef field value -// and a boolean to check if the value has been set. -func (o *BuildInfo) GetSnapshotRefOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.SnapshotRef, true -} - -// SetSnapshotRef sets field value -func (o *BuildInfo) SetSnapshotRef(v string) { - o.SnapshotRef = v -} - -func (o BuildInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o BuildInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.DockerfileContent) { - toSerialize["dockerfileContent"] = o.DockerfileContent - } - if !IsNil(o.ContextHashes) { - toSerialize["contextHashes"] = o.ContextHashes - } - toSerialize["createdAt"] = o.CreatedAt - toSerialize["updatedAt"] = o.UpdatedAt - toSerialize["snapshotRef"] = o.SnapshotRef - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *BuildInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "createdAt", - "updatedAt", - "snapshotRef", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varBuildInfo := _BuildInfo{} - - err = json.Unmarshal(data, &varBuildInfo) - - if err != nil { - return err - } - - *o = BuildInfo(varBuildInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "dockerfileContent") - delete(additionalProperties, "contextHashes") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "updatedAt") - delete(additionalProperties, "snapshotRef") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableBuildInfo struct { - value *BuildInfo - isSet bool -} - -func (v NullableBuildInfo) Get() *BuildInfo { - return v.value -} - -func (v *NullableBuildInfo) Set(val *BuildInfo) { - v.value = val - v.isSet = true -} - -func (v NullableBuildInfo) IsSet() bool { - return v.isSet -} - -func (v *NullableBuildInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableBuildInfo(val *BuildInfo) *NullableBuildInfo { - return &NullableBuildInfo{value: val, isSet: true} -} - -func (v NullableBuildInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableBuildInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_command.go b/apps/api-client-go/model_command.go deleted file mode 100644 index c5e53a2bb..000000000 --- a/apps/api-client-go/model_command.go +++ /dev/null @@ -1,236 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Command type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Command{} - -// Command struct for Command -type Command struct { - // The ID of the command - Id string `json:"id"` - // The command that was executed - Command string `json:"command"` - // The exit code of the command - ExitCode *float32 `json:"exitCode,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _Command Command - -// NewCommand instantiates a new Command object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCommand(id string, command string) *Command { - this := Command{} - this.Id = id - this.Command = command - return &this -} - -// NewCommandWithDefaults instantiates a new Command object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCommandWithDefaults() *Command { - this := Command{} - return &this -} - -// GetId returns the Id field value -func (o *Command) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *Command) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *Command) SetId(v string) { - o.Id = v -} - -// GetCommand returns the Command field value -func (o *Command) GetCommand() string { - if o == nil { - var ret string - return ret - } - - return o.Command -} - -// GetCommandOk returns a tuple with the Command field value -// and a boolean to check if the value has been set. -func (o *Command) GetCommandOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Command, true -} - -// SetCommand sets field value -func (o *Command) SetCommand(v string) { - o.Command = v -} - -// GetExitCode returns the ExitCode field value if set, zero value otherwise. -func (o *Command) GetExitCode() float32 { - if o == nil || IsNil(o.ExitCode) { - var ret float32 - return ret - } - return *o.ExitCode -} - -// GetExitCodeOk returns a tuple with the ExitCode field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Command) GetExitCodeOk() (*float32, bool) { - if o == nil || IsNil(o.ExitCode) { - return nil, false - } - return o.ExitCode, true -} - -// HasExitCode returns a boolean if a field has been set. -func (o *Command) HasExitCode() bool { - if o != nil && !IsNil(o.ExitCode) { - return true - } - - return false -} - -// SetExitCode gets a reference to the given float32 and assigns it to the ExitCode field. -func (o *Command) SetExitCode(v float32) { - o.ExitCode = &v -} - -func (o Command) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Command) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - toSerialize["command"] = o.Command - if !IsNil(o.ExitCode) { - toSerialize["exitCode"] = o.ExitCode - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Command) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "command", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCommand := _Command{} - - err = json.Unmarshal(data, &varCommand) - - if err != nil { - return err - } - - *o = Command(varCommand) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "command") - delete(additionalProperties, "exitCode") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCommand struct { - value *Command - isSet bool -} - -func (v NullableCommand) Get() *Command { - return v.value -} - -func (v *NullableCommand) Set(val *Command) { - v.value = val - v.isSet = true -} - -func (v NullableCommand) IsSet() bool { - return v.isSet -} - -func (v *NullableCommand) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCommand(val *Command) *NullableCommand { - return &NullableCommand{value: val, isSet: true} -} - -func (v NullableCommand) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCommand) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_completion_context.go b/apps/api-client-go/model_completion_context.go deleted file mode 100644 index cf4cb62af..000000000 --- a/apps/api-client-go/model_completion_context.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CompletionContext type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CompletionContext{} - -// CompletionContext struct for CompletionContext -type CompletionContext struct { - TriggerKind float32 `json:"triggerKind"` - TriggerCharacter *string `json:"triggerCharacter,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CompletionContext CompletionContext - -// NewCompletionContext instantiates a new CompletionContext object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCompletionContext(triggerKind float32) *CompletionContext { - this := CompletionContext{} - this.TriggerKind = triggerKind - return &this -} - -// NewCompletionContextWithDefaults instantiates a new CompletionContext object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCompletionContextWithDefaults() *CompletionContext { - this := CompletionContext{} - return &this -} - -// GetTriggerKind returns the TriggerKind field value -func (o *CompletionContext) GetTriggerKind() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TriggerKind -} - -// GetTriggerKindOk returns a tuple with the TriggerKind field value -// and a boolean to check if the value has been set. -func (o *CompletionContext) GetTriggerKindOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TriggerKind, true -} - -// SetTriggerKind sets field value -func (o *CompletionContext) SetTriggerKind(v float32) { - o.TriggerKind = v -} - -// GetTriggerCharacter returns the TriggerCharacter field value if set, zero value otherwise. -func (o *CompletionContext) GetTriggerCharacter() string { - if o == nil || IsNil(o.TriggerCharacter) { - var ret string - return ret - } - return *o.TriggerCharacter -} - -// GetTriggerCharacterOk returns a tuple with the TriggerCharacter field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionContext) GetTriggerCharacterOk() (*string, bool) { - if o == nil || IsNil(o.TriggerCharacter) { - return nil, false - } - return o.TriggerCharacter, true -} - -// HasTriggerCharacter returns a boolean if a field has been set. -func (o *CompletionContext) HasTriggerCharacter() bool { - if o != nil && !IsNil(o.TriggerCharacter) { - return true - } - - return false -} - -// SetTriggerCharacter gets a reference to the given string and assigns it to the TriggerCharacter field. -func (o *CompletionContext) SetTriggerCharacter(v string) { - o.TriggerCharacter = &v -} - -func (o CompletionContext) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CompletionContext) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["triggerKind"] = o.TriggerKind - if !IsNil(o.TriggerCharacter) { - toSerialize["triggerCharacter"] = o.TriggerCharacter - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CompletionContext) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "triggerKind", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCompletionContext := _CompletionContext{} - - err = json.Unmarshal(data, &varCompletionContext) - - if err != nil { - return err - } - - *o = CompletionContext(varCompletionContext) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "triggerKind") - delete(additionalProperties, "triggerCharacter") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCompletionContext struct { - value *CompletionContext - isSet bool -} - -func (v NullableCompletionContext) Get() *CompletionContext { - return v.value -} - -func (v *NullableCompletionContext) Set(val *CompletionContext) { - v.value = val - v.isSet = true -} - -func (v NullableCompletionContext) IsSet() bool { - return v.isSet -} - -func (v *NullableCompletionContext) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCompletionContext(val *CompletionContext) *NullableCompletionContext { - return &NullableCompletionContext{value: val, isSet: true} -} - -func (v NullableCompletionContext) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCompletionContext) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_completion_item.go b/apps/api-client-go/model_completion_item.go deleted file mode 100644 index b0a61a49b..000000000 --- a/apps/api-client-go/model_completion_item.go +++ /dev/null @@ -1,389 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CompletionItem type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CompletionItem{} - -// CompletionItem struct for CompletionItem -type CompletionItem struct { - Label string `json:"label"` - Kind *float32 `json:"kind,omitempty"` - Detail *string `json:"detail,omitempty"` - Documentation map[string]interface{} `json:"documentation,omitempty"` - SortText *string `json:"sortText,omitempty"` - FilterText *string `json:"filterText,omitempty"` - InsertText *string `json:"insertText,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CompletionItem CompletionItem - -// NewCompletionItem instantiates a new CompletionItem object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCompletionItem(label string) *CompletionItem { - this := CompletionItem{} - this.Label = label - return &this -} - -// NewCompletionItemWithDefaults instantiates a new CompletionItem object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCompletionItemWithDefaults() *CompletionItem { - this := CompletionItem{} - return &this -} - -// GetLabel returns the Label field value -func (o *CompletionItem) GetLabel() string { - if o == nil { - var ret string - return ret - } - - return o.Label -} - -// GetLabelOk returns a tuple with the Label field value -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetLabelOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Label, true -} - -// SetLabel sets field value -func (o *CompletionItem) SetLabel(v string) { - o.Label = v -} - -// GetKind returns the Kind field value if set, zero value otherwise. -func (o *CompletionItem) GetKind() float32 { - if o == nil || IsNil(o.Kind) { - var ret float32 - return ret - } - return *o.Kind -} - -// GetKindOk returns a tuple with the Kind field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetKindOk() (*float32, bool) { - if o == nil || IsNil(o.Kind) { - return nil, false - } - return o.Kind, true -} - -// HasKind returns a boolean if a field has been set. -func (o *CompletionItem) HasKind() bool { - if o != nil && !IsNil(o.Kind) { - return true - } - - return false -} - -// SetKind gets a reference to the given float32 and assigns it to the Kind field. -func (o *CompletionItem) SetKind(v float32) { - o.Kind = &v -} - -// GetDetail returns the Detail field value if set, zero value otherwise. -func (o *CompletionItem) GetDetail() string { - if o == nil || IsNil(o.Detail) { - var ret string - return ret - } - return *o.Detail -} - -// GetDetailOk returns a tuple with the Detail field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetDetailOk() (*string, bool) { - if o == nil || IsNil(o.Detail) { - return nil, false - } - return o.Detail, true -} - -// HasDetail returns a boolean if a field has been set. -func (o *CompletionItem) HasDetail() bool { - if o != nil && !IsNil(o.Detail) { - return true - } - - return false -} - -// SetDetail gets a reference to the given string and assigns it to the Detail field. -func (o *CompletionItem) SetDetail(v string) { - o.Detail = &v -} - -// GetDocumentation returns the Documentation field value if set, zero value otherwise. -func (o *CompletionItem) GetDocumentation() map[string]interface{} { - if o == nil || IsNil(o.Documentation) { - var ret map[string]interface{} - return ret - } - return o.Documentation -} - -// GetDocumentationOk returns a tuple with the Documentation field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetDocumentationOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Documentation) { - return map[string]interface{}{}, false - } - return o.Documentation, true -} - -// HasDocumentation returns a boolean if a field has been set. -func (o *CompletionItem) HasDocumentation() bool { - if o != nil && !IsNil(o.Documentation) { - return true - } - - return false -} - -// SetDocumentation gets a reference to the given map[string]interface{} and assigns it to the Documentation field. -func (o *CompletionItem) SetDocumentation(v map[string]interface{}) { - o.Documentation = v -} - -// GetSortText returns the SortText field value if set, zero value otherwise. -func (o *CompletionItem) GetSortText() string { - if o == nil || IsNil(o.SortText) { - var ret string - return ret - } - return *o.SortText -} - -// GetSortTextOk returns a tuple with the SortText field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetSortTextOk() (*string, bool) { - if o == nil || IsNil(o.SortText) { - return nil, false - } - return o.SortText, true -} - -// HasSortText returns a boolean if a field has been set. -func (o *CompletionItem) HasSortText() bool { - if o != nil && !IsNil(o.SortText) { - return true - } - - return false -} - -// SetSortText gets a reference to the given string and assigns it to the SortText field. -func (o *CompletionItem) SetSortText(v string) { - o.SortText = &v -} - -// GetFilterText returns the FilterText field value if set, zero value otherwise. -func (o *CompletionItem) GetFilterText() string { - if o == nil || IsNil(o.FilterText) { - var ret string - return ret - } - return *o.FilterText -} - -// GetFilterTextOk returns a tuple with the FilterText field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetFilterTextOk() (*string, bool) { - if o == nil || IsNil(o.FilterText) { - return nil, false - } - return o.FilterText, true -} - -// HasFilterText returns a boolean if a field has been set. -func (o *CompletionItem) HasFilterText() bool { - if o != nil && !IsNil(o.FilterText) { - return true - } - - return false -} - -// SetFilterText gets a reference to the given string and assigns it to the FilterText field. -func (o *CompletionItem) SetFilterText(v string) { - o.FilterText = &v -} - -// GetInsertText returns the InsertText field value if set, zero value otherwise. -func (o *CompletionItem) GetInsertText() string { - if o == nil || IsNil(o.InsertText) { - var ret string - return ret - } - return *o.InsertText -} - -// GetInsertTextOk returns a tuple with the InsertText field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompletionItem) GetInsertTextOk() (*string, bool) { - if o == nil || IsNil(o.InsertText) { - return nil, false - } - return o.InsertText, true -} - -// HasInsertText returns a boolean if a field has been set. -func (o *CompletionItem) HasInsertText() bool { - if o != nil && !IsNil(o.InsertText) { - return true - } - - return false -} - -// SetInsertText gets a reference to the given string and assigns it to the InsertText field. -func (o *CompletionItem) SetInsertText(v string) { - o.InsertText = &v -} - -func (o CompletionItem) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CompletionItem) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["label"] = o.Label - if !IsNil(o.Kind) { - toSerialize["kind"] = o.Kind - } - if !IsNil(o.Detail) { - toSerialize["detail"] = o.Detail - } - if !IsNil(o.Documentation) { - toSerialize["documentation"] = o.Documentation - } - if !IsNil(o.SortText) { - toSerialize["sortText"] = o.SortText - } - if !IsNil(o.FilterText) { - toSerialize["filterText"] = o.FilterText - } - if !IsNil(o.InsertText) { - toSerialize["insertText"] = o.InsertText - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CompletionItem) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "label", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCompletionItem := _CompletionItem{} - - err = json.Unmarshal(data, &varCompletionItem) - - if err != nil { - return err - } - - *o = CompletionItem(varCompletionItem) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "label") - delete(additionalProperties, "kind") - delete(additionalProperties, "detail") - delete(additionalProperties, "documentation") - delete(additionalProperties, "sortText") - delete(additionalProperties, "filterText") - delete(additionalProperties, "insertText") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCompletionItem struct { - value *CompletionItem - isSet bool -} - -func (v NullableCompletionItem) Get() *CompletionItem { - return v.value -} - -func (v *NullableCompletionItem) Set(val *CompletionItem) { - v.value = val - v.isSet = true -} - -func (v NullableCompletionItem) IsSet() bool { - return v.isSet -} - -func (v *NullableCompletionItem) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCompletionItem(val *CompletionItem) *NullableCompletionItem { - return &NullableCompletionItem{value: val, isSet: true} -} - -func (v NullableCompletionItem) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCompletionItem) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_completion_list.go b/apps/api-client-go/model_completion_list.go deleted file mode 100644 index 18873e893..000000000 --- a/apps/api-client-go/model_completion_list.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CompletionList type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CompletionList{} - -// CompletionList struct for CompletionList -type CompletionList struct { - IsIncomplete bool `json:"isIncomplete"` - Items []CompletionItem `json:"items"` - AdditionalProperties map[string]interface{} -} - -type _CompletionList CompletionList - -// NewCompletionList instantiates a new CompletionList object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCompletionList(isIncomplete bool, items []CompletionItem) *CompletionList { - this := CompletionList{} - this.IsIncomplete = isIncomplete - this.Items = items - return &this -} - -// NewCompletionListWithDefaults instantiates a new CompletionList object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCompletionListWithDefaults() *CompletionList { - this := CompletionList{} - return &this -} - -// GetIsIncomplete returns the IsIncomplete field value -func (o *CompletionList) GetIsIncomplete() bool { - if o == nil { - var ret bool - return ret - } - - return o.IsIncomplete -} - -// GetIsIncompleteOk returns a tuple with the IsIncomplete field value -// and a boolean to check if the value has been set. -func (o *CompletionList) GetIsIncompleteOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.IsIncomplete, true -} - -// SetIsIncomplete sets field value -func (o *CompletionList) SetIsIncomplete(v bool) { - o.IsIncomplete = v -} - -// GetItems returns the Items field value -func (o *CompletionList) GetItems() []CompletionItem { - if o == nil { - var ret []CompletionItem - return ret - } - - return o.Items -} - -// GetItemsOk returns a tuple with the Items field value -// and a boolean to check if the value has been set. -func (o *CompletionList) GetItemsOk() ([]CompletionItem, bool) { - if o == nil { - return nil, false - } - return o.Items, true -} - -// SetItems sets field value -func (o *CompletionList) SetItems(v []CompletionItem) { - o.Items = v -} - -func (o CompletionList) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CompletionList) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["isIncomplete"] = o.IsIncomplete - toSerialize["items"] = o.Items - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CompletionList) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "isIncomplete", - "items", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCompletionList := _CompletionList{} - - err = json.Unmarshal(data, &varCompletionList) - - if err != nil { - return err - } - - *o = CompletionList(varCompletionList) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "isIncomplete") - delete(additionalProperties, "items") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCompletionList struct { - value *CompletionList - isSet bool -} - -func (v NullableCompletionList) Get() *CompletionList { - return v.value -} - -func (v *NullableCompletionList) Set(val *CompletionList) { - v.value = val - v.isSet = true -} - -func (v NullableCompletionList) IsSet() bool { - return v.isSet -} - -func (v *NullableCompletionList) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCompletionList(val *CompletionList) *NullableCompletionList { - return &NullableCompletionList{value: val, isSet: true} -} - -func (v NullableCompletionList) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCompletionList) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_compressed_screenshot_response.go b/apps/api-client-go/model_compressed_screenshot_response.go deleted file mode 100644 index 0eb461533..000000000 --- a/apps/api-client-go/model_compressed_screenshot_response.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CompressedScreenshotResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CompressedScreenshotResponse{} - -// CompressedScreenshotResponse struct for CompressedScreenshotResponse -type CompressedScreenshotResponse struct { - // Base64 encoded compressed screenshot image data - Screenshot string `json:"screenshot"` - // The current cursor position when the compressed screenshot was taken - CursorPosition map[string]interface{} `json:"cursorPosition,omitempty"` - // The size of the compressed screenshot data in bytes - SizeBytes *float32 `json:"sizeBytes,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CompressedScreenshotResponse CompressedScreenshotResponse - -// NewCompressedScreenshotResponse instantiates a new CompressedScreenshotResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCompressedScreenshotResponse(screenshot string) *CompressedScreenshotResponse { - this := CompressedScreenshotResponse{} - this.Screenshot = screenshot - return &this -} - -// NewCompressedScreenshotResponseWithDefaults instantiates a new CompressedScreenshotResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCompressedScreenshotResponseWithDefaults() *CompressedScreenshotResponse { - this := CompressedScreenshotResponse{} - return &this -} - -// GetScreenshot returns the Screenshot field value -func (o *CompressedScreenshotResponse) GetScreenshot() string { - if o == nil { - var ret string - return ret - } - - return o.Screenshot -} - -// GetScreenshotOk returns a tuple with the Screenshot field value -// and a boolean to check if the value has been set. -func (o *CompressedScreenshotResponse) GetScreenshotOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Screenshot, true -} - -// SetScreenshot sets field value -func (o *CompressedScreenshotResponse) SetScreenshot(v string) { - o.Screenshot = v -} - -// GetCursorPosition returns the CursorPosition field value if set, zero value otherwise. -func (o *CompressedScreenshotResponse) GetCursorPosition() map[string]interface{} { - if o == nil || IsNil(o.CursorPosition) { - var ret map[string]interface{} - return ret - } - return o.CursorPosition -} - -// GetCursorPositionOk returns a tuple with the CursorPosition field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompressedScreenshotResponse) GetCursorPositionOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.CursorPosition) { - return map[string]interface{}{}, false - } - return o.CursorPosition, true -} - -// HasCursorPosition returns a boolean if a field has been set. -func (o *CompressedScreenshotResponse) HasCursorPosition() bool { - if o != nil && !IsNil(o.CursorPosition) { - return true - } - - return false -} - -// SetCursorPosition gets a reference to the given map[string]interface{} and assigns it to the CursorPosition field. -func (o *CompressedScreenshotResponse) SetCursorPosition(v map[string]interface{}) { - o.CursorPosition = v -} - -// GetSizeBytes returns the SizeBytes field value if set, zero value otherwise. -func (o *CompressedScreenshotResponse) GetSizeBytes() float32 { - if o == nil || IsNil(o.SizeBytes) { - var ret float32 - return ret - } - return *o.SizeBytes -} - -// GetSizeBytesOk returns a tuple with the SizeBytes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CompressedScreenshotResponse) GetSizeBytesOk() (*float32, bool) { - if o == nil || IsNil(o.SizeBytes) { - return nil, false - } - return o.SizeBytes, true -} - -// HasSizeBytes returns a boolean if a field has been set. -func (o *CompressedScreenshotResponse) HasSizeBytes() bool { - if o != nil && !IsNil(o.SizeBytes) { - return true - } - - return false -} - -// SetSizeBytes gets a reference to the given float32 and assigns it to the SizeBytes field. -func (o *CompressedScreenshotResponse) SetSizeBytes(v float32) { - o.SizeBytes = &v -} - -func (o CompressedScreenshotResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CompressedScreenshotResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["screenshot"] = o.Screenshot - if !IsNil(o.CursorPosition) { - toSerialize["cursorPosition"] = o.CursorPosition - } - if !IsNil(o.SizeBytes) { - toSerialize["sizeBytes"] = o.SizeBytes - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CompressedScreenshotResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "screenshot", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCompressedScreenshotResponse := _CompressedScreenshotResponse{} - - err = json.Unmarshal(data, &varCompressedScreenshotResponse) - - if err != nil { - return err - } - - *o = CompressedScreenshotResponse(varCompressedScreenshotResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "screenshot") - delete(additionalProperties, "cursorPosition") - delete(additionalProperties, "sizeBytes") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCompressedScreenshotResponse struct { - value *CompressedScreenshotResponse - isSet bool -} - -func (v NullableCompressedScreenshotResponse) Get() *CompressedScreenshotResponse { - return v.value -} - -func (v *NullableCompressedScreenshotResponse) Set(val *CompressedScreenshotResponse) { - v.value = val - v.isSet = true -} - -func (v NullableCompressedScreenshotResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableCompressedScreenshotResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCompressedScreenshotResponse(val *CompressedScreenshotResponse) *NullableCompressedScreenshotResponse { - return &NullableCompressedScreenshotResponse{value: val, isSet: true} -} - -func (v NullableCompressedScreenshotResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCompressedScreenshotResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_computer_use_start_response.go b/apps/api-client-go/model_computer_use_start_response.go deleted file mode 100644 index 454f551d3..000000000 --- a/apps/api-client-go/model_computer_use_start_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ComputerUseStartResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ComputerUseStartResponse{} - -// ComputerUseStartResponse struct for ComputerUseStartResponse -type ComputerUseStartResponse struct { - // A message indicating the result of starting computer use processes - Message string `json:"message"` - // Status information about all VNC desktop processes after starting - Status map[string]interface{} `json:"status"` - AdditionalProperties map[string]interface{} -} - -type _ComputerUseStartResponse ComputerUseStartResponse - -// NewComputerUseStartResponse instantiates a new ComputerUseStartResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewComputerUseStartResponse(message string, status map[string]interface{}) *ComputerUseStartResponse { - this := ComputerUseStartResponse{} - this.Message = message - this.Status = status - return &this -} - -// NewComputerUseStartResponseWithDefaults instantiates a new ComputerUseStartResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewComputerUseStartResponseWithDefaults() *ComputerUseStartResponse { - this := ComputerUseStartResponse{} - return &this -} - -// GetMessage returns the Message field value -func (o *ComputerUseStartResponse) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *ComputerUseStartResponse) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *ComputerUseStartResponse) SetMessage(v string) { - o.Message = v -} - -// GetStatus returns the Status field value -func (o *ComputerUseStartResponse) GetStatus() map[string]interface{} { - if o == nil { - var ret map[string]interface{} - return ret - } - - return o.Status -} - -// GetStatusOk returns a tuple with the Status field value -// and a boolean to check if the value has been set. -func (o *ComputerUseStartResponse) GetStatusOk() (map[string]interface{}, bool) { - if o == nil { - return map[string]interface{}{}, false - } - return o.Status, true -} - -// SetStatus sets field value -func (o *ComputerUseStartResponse) SetStatus(v map[string]interface{}) { - o.Status = v -} - -func (o ComputerUseStartResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ComputerUseStartResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["message"] = o.Message - toSerialize["status"] = o.Status - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ComputerUseStartResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "message", - "status", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varComputerUseStartResponse := _ComputerUseStartResponse{} - - err = json.Unmarshal(data, &varComputerUseStartResponse) - - if err != nil { - return err - } - - *o = ComputerUseStartResponse(varComputerUseStartResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "message") - delete(additionalProperties, "status") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableComputerUseStartResponse struct { - value *ComputerUseStartResponse - isSet bool -} - -func (v NullableComputerUseStartResponse) Get() *ComputerUseStartResponse { - return v.value -} - -func (v *NullableComputerUseStartResponse) Set(val *ComputerUseStartResponse) { - v.value = val - v.isSet = true -} - -func (v NullableComputerUseStartResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableComputerUseStartResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableComputerUseStartResponse(val *ComputerUseStartResponse) *NullableComputerUseStartResponse { - return &NullableComputerUseStartResponse{value: val, isSet: true} -} - -func (v NullableComputerUseStartResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableComputerUseStartResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_computer_use_status_response.go b/apps/api-client-go/model_computer_use_status_response.go deleted file mode 100644 index 0c16a4390..000000000 --- a/apps/api-client-go/model_computer_use_status_response.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ComputerUseStatusResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ComputerUseStatusResponse{} - -// ComputerUseStatusResponse struct for ComputerUseStatusResponse -type ComputerUseStatusResponse struct { - // Status of computer use services (active, partial, inactive, error) - Status string `json:"status"` - AdditionalProperties map[string]interface{} -} - -type _ComputerUseStatusResponse ComputerUseStatusResponse - -// NewComputerUseStatusResponse instantiates a new ComputerUseStatusResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewComputerUseStatusResponse(status string) *ComputerUseStatusResponse { - this := ComputerUseStatusResponse{} - this.Status = status - return &this -} - -// NewComputerUseStatusResponseWithDefaults instantiates a new ComputerUseStatusResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewComputerUseStatusResponseWithDefaults() *ComputerUseStatusResponse { - this := ComputerUseStatusResponse{} - return &this -} - -// GetStatus returns the Status field value -func (o *ComputerUseStatusResponse) GetStatus() string { - if o == nil { - var ret string - return ret - } - - return o.Status -} - -// GetStatusOk returns a tuple with the Status field value -// and a boolean to check if the value has been set. -func (o *ComputerUseStatusResponse) GetStatusOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Status, true -} - -// SetStatus sets field value -func (o *ComputerUseStatusResponse) SetStatus(v string) { - o.Status = v -} - -func (o ComputerUseStatusResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ComputerUseStatusResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["status"] = o.Status - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ComputerUseStatusResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "status", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varComputerUseStatusResponse := _ComputerUseStatusResponse{} - - err = json.Unmarshal(data, &varComputerUseStatusResponse) - - if err != nil { - return err - } - - *o = ComputerUseStatusResponse(varComputerUseStatusResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "status") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableComputerUseStatusResponse struct { - value *ComputerUseStatusResponse - isSet bool -} - -func (v NullableComputerUseStatusResponse) Get() *ComputerUseStatusResponse { - return v.value -} - -func (v *NullableComputerUseStatusResponse) Set(val *ComputerUseStatusResponse) { - v.value = val - v.isSet = true -} - -func (v NullableComputerUseStatusResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableComputerUseStatusResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableComputerUseStatusResponse(val *ComputerUseStatusResponse) *NullableComputerUseStatusResponse { - return &NullableComputerUseStatusResponse{value: val, isSet: true} -} - -func (v NullableComputerUseStatusResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableComputerUseStatusResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_computer_use_stop_response.go b/apps/api-client-go/model_computer_use_stop_response.go deleted file mode 100644 index c78d3137d..000000000 --- a/apps/api-client-go/model_computer_use_stop_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ComputerUseStopResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ComputerUseStopResponse{} - -// ComputerUseStopResponse struct for ComputerUseStopResponse -type ComputerUseStopResponse struct { - // A message indicating the result of stopping computer use processes - Message string `json:"message"` - // Status information about all VNC desktop processes after stopping - Status map[string]interface{} `json:"status"` - AdditionalProperties map[string]interface{} -} - -type _ComputerUseStopResponse ComputerUseStopResponse - -// NewComputerUseStopResponse instantiates a new ComputerUseStopResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewComputerUseStopResponse(message string, status map[string]interface{}) *ComputerUseStopResponse { - this := ComputerUseStopResponse{} - this.Message = message - this.Status = status - return &this -} - -// NewComputerUseStopResponseWithDefaults instantiates a new ComputerUseStopResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewComputerUseStopResponseWithDefaults() *ComputerUseStopResponse { - this := ComputerUseStopResponse{} - return &this -} - -// GetMessage returns the Message field value -func (o *ComputerUseStopResponse) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *ComputerUseStopResponse) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *ComputerUseStopResponse) SetMessage(v string) { - o.Message = v -} - -// GetStatus returns the Status field value -func (o *ComputerUseStopResponse) GetStatus() map[string]interface{} { - if o == nil { - var ret map[string]interface{} - return ret - } - - return o.Status -} - -// GetStatusOk returns a tuple with the Status field value -// and a boolean to check if the value has been set. -func (o *ComputerUseStopResponse) GetStatusOk() (map[string]interface{}, bool) { - if o == nil { - return map[string]interface{}{}, false - } - return o.Status, true -} - -// SetStatus sets field value -func (o *ComputerUseStopResponse) SetStatus(v map[string]interface{}) { - o.Status = v -} - -func (o ComputerUseStopResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ComputerUseStopResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["message"] = o.Message - toSerialize["status"] = o.Status - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ComputerUseStopResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "message", - "status", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varComputerUseStopResponse := _ComputerUseStopResponse{} - - err = json.Unmarshal(data, &varComputerUseStopResponse) - - if err != nil { - return err - } - - *o = ComputerUseStopResponse(varComputerUseStopResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "message") - delete(additionalProperties, "status") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableComputerUseStopResponse struct { - value *ComputerUseStopResponse - isSet bool -} - -func (v NullableComputerUseStopResponse) Get() *ComputerUseStopResponse { - return v.value -} - -func (v *NullableComputerUseStopResponse) Set(val *ComputerUseStopResponse) { - v.value = val - v.isSet = true -} - -func (v NullableComputerUseStopResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableComputerUseStopResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableComputerUseStopResponse(val *ComputerUseStopResponse) *NullableComputerUseStopResponse { - return &NullableComputerUseStopResponse{value: val, isSet: true} -} - -func (v NullableComputerUseStopResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableComputerUseStopResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_api_key.go b/apps/api-client-go/model_create_api_key.go index a21c0214a..5eb4673f7 100644 --- a/apps/api-client-go/model_create_api_key.go +++ b/apps/api-client-go/model_create_api_key.go @@ -245,3 +245,5 @@ func (v *NullableCreateApiKey) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_build_info.go b/apps/api-client-go/model_create_build_info.go deleted file mode 100644 index 3ed961a02..000000000 --- a/apps/api-client-go/model_create_build_info.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CreateBuildInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateBuildInfo{} - -// CreateBuildInfo struct for CreateBuildInfo -type CreateBuildInfo struct { - // The Dockerfile content used for the build - DockerfileContent string `json:"dockerfileContent"` - // The context hashes used for the build - ContextHashes []string `json:"contextHashes,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateBuildInfo CreateBuildInfo - -// NewCreateBuildInfo instantiates a new CreateBuildInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateBuildInfo(dockerfileContent string) *CreateBuildInfo { - this := CreateBuildInfo{} - this.DockerfileContent = dockerfileContent - return &this -} - -// NewCreateBuildInfoWithDefaults instantiates a new CreateBuildInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateBuildInfoWithDefaults() *CreateBuildInfo { - this := CreateBuildInfo{} - return &this -} - -// GetDockerfileContent returns the DockerfileContent field value -func (o *CreateBuildInfo) GetDockerfileContent() string { - if o == nil { - var ret string - return ret - } - - return o.DockerfileContent -} - -// GetDockerfileContentOk returns a tuple with the DockerfileContent field value -// and a boolean to check if the value has been set. -func (o *CreateBuildInfo) GetDockerfileContentOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.DockerfileContent, true -} - -// SetDockerfileContent sets field value -func (o *CreateBuildInfo) SetDockerfileContent(v string) { - o.DockerfileContent = v -} - -// GetContextHashes returns the ContextHashes field value if set, zero value otherwise. -func (o *CreateBuildInfo) GetContextHashes() []string { - if o == nil || IsNil(o.ContextHashes) { - var ret []string - return ret - } - return o.ContextHashes -} - -// GetContextHashesOk returns a tuple with the ContextHashes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateBuildInfo) GetContextHashesOk() ([]string, bool) { - if o == nil || IsNil(o.ContextHashes) { - return nil, false - } - return o.ContextHashes, true -} - -// HasContextHashes returns a boolean if a field has been set. -func (o *CreateBuildInfo) HasContextHashes() bool { - if o != nil && !IsNil(o.ContextHashes) { - return true - } - - return false -} - -// SetContextHashes gets a reference to the given []string and assigns it to the ContextHashes field. -func (o *CreateBuildInfo) SetContextHashes(v []string) { - o.ContextHashes = v -} - -func (o CreateBuildInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateBuildInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["dockerfileContent"] = o.DockerfileContent - if !IsNil(o.ContextHashes) { - toSerialize["contextHashes"] = o.ContextHashes - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateBuildInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "dockerfileContent", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCreateBuildInfo := _CreateBuildInfo{} - - err = json.Unmarshal(data, &varCreateBuildInfo) - - if err != nil { - return err - } - - *o = CreateBuildInfo(varCreateBuildInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "dockerfileContent") - delete(additionalProperties, "contextHashes") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateBuildInfo struct { - value *CreateBuildInfo - isSet bool -} - -func (v NullableCreateBuildInfo) Get() *CreateBuildInfo { - return v.value -} - -func (v *NullableCreateBuildInfo) Set(val *CreateBuildInfo) { - v.value = val - v.isSet = true -} - -func (v NullableCreateBuildInfo) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateBuildInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateBuildInfo(val *CreateBuildInfo) *NullableCreateBuildInfo { - return &NullableCreateBuildInfo{value: val, isSet: true} -} - -func (v NullableCreateBuildInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateBuildInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_docker_registry.go b/apps/api-client-go/model_create_docker_registry.go deleted file mode 100644 index 903436a36..000000000 --- a/apps/api-client-go/model_create_docker_registry.go +++ /dev/null @@ -1,366 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CreateDockerRegistry type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateDockerRegistry{} - -// CreateDockerRegistry struct for CreateDockerRegistry -type CreateDockerRegistry struct { - // Registry name - Name string `json:"name"` - // Registry URL - Url string `json:"url"` - // Registry username - Username string `json:"username"` - // Registry password - Password string `json:"password"` - // Registry project - Project *string `json:"project,omitempty"` - // Registry type - RegistryType string `json:"registryType"` - // Set as default registry - IsDefault *bool `json:"isDefault,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateDockerRegistry CreateDockerRegistry - -// NewCreateDockerRegistry instantiates a new CreateDockerRegistry object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateDockerRegistry(name string, url string, username string, password string, registryType string) *CreateDockerRegistry { - this := CreateDockerRegistry{} - this.Name = name - this.Url = url - this.Username = username - this.Password = password - this.RegistryType = registryType - return &this -} - -// NewCreateDockerRegistryWithDefaults instantiates a new CreateDockerRegistry object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateDockerRegistryWithDefaults() *CreateDockerRegistry { - this := CreateDockerRegistry{} - var registryType string = "organization" - this.RegistryType = registryType - return &this -} - -// GetName returns the Name field value -func (o *CreateDockerRegistry) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *CreateDockerRegistry) SetName(v string) { - o.Name = v -} - -// GetUrl returns the Url field value -func (o *CreateDockerRegistry) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *CreateDockerRegistry) SetUrl(v string) { - o.Url = v -} - -// GetUsername returns the Username field value -func (o *CreateDockerRegistry) GetUsername() string { - if o == nil { - var ret string - return ret - } - - return o.Username -} - -// GetUsernameOk returns a tuple with the Username field value -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Username, true -} - -// SetUsername sets field value -func (o *CreateDockerRegistry) SetUsername(v string) { - o.Username = v -} - -// GetPassword returns the Password field value -func (o *CreateDockerRegistry) GetPassword() string { - if o == nil { - var ret string - return ret - } - - return o.Password -} - -// GetPasswordOk returns a tuple with the Password field value -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetPasswordOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Password, true -} - -// SetPassword sets field value -func (o *CreateDockerRegistry) SetPassword(v string) { - o.Password = v -} - -// GetProject returns the Project field value if set, zero value otherwise. -func (o *CreateDockerRegistry) GetProject() string { - if o == nil || IsNil(o.Project) { - var ret string - return ret - } - return *o.Project -} - -// GetProjectOk returns a tuple with the Project field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetProjectOk() (*string, bool) { - if o == nil || IsNil(o.Project) { - return nil, false - } - return o.Project, true -} - -// HasProject returns a boolean if a field has been set. -func (o *CreateDockerRegistry) HasProject() bool { - if o != nil && !IsNil(o.Project) { - return true - } - - return false -} - -// SetProject gets a reference to the given string and assigns it to the Project field. -func (o *CreateDockerRegistry) SetProject(v string) { - o.Project = &v -} - -// GetRegistryType returns the RegistryType field value -func (o *CreateDockerRegistry) GetRegistryType() string { - if o == nil { - var ret string - return ret - } - - return o.RegistryType -} - -// GetRegistryTypeOk returns a tuple with the RegistryType field value -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetRegistryTypeOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegistryType, true -} - -// SetRegistryType sets field value -func (o *CreateDockerRegistry) SetRegistryType(v string) { - o.RegistryType = v -} - -// GetIsDefault returns the IsDefault field value if set, zero value otherwise. -func (o *CreateDockerRegistry) GetIsDefault() bool { - if o == nil || IsNil(o.IsDefault) { - var ret bool - return ret - } - return *o.IsDefault -} - -// GetIsDefaultOk returns a tuple with the IsDefault field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateDockerRegistry) GetIsDefaultOk() (*bool, bool) { - if o == nil || IsNil(o.IsDefault) { - return nil, false - } - return o.IsDefault, true -} - -// HasIsDefault returns a boolean if a field has been set. -func (o *CreateDockerRegistry) HasIsDefault() bool { - if o != nil && !IsNil(o.IsDefault) { - return true - } - - return false -} - -// SetIsDefault gets a reference to the given bool and assigns it to the IsDefault field. -func (o *CreateDockerRegistry) SetIsDefault(v bool) { - o.IsDefault = &v -} - -func (o CreateDockerRegistry) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateDockerRegistry) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["name"] = o.Name - toSerialize["url"] = o.Url - toSerialize["username"] = o.Username - toSerialize["password"] = o.Password - if !IsNil(o.Project) { - toSerialize["project"] = o.Project - } - toSerialize["registryType"] = o.RegistryType - if !IsNil(o.IsDefault) { - toSerialize["isDefault"] = o.IsDefault - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateDockerRegistry) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "name", - "url", - "username", - "password", - "registryType", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCreateDockerRegistry := _CreateDockerRegistry{} - - err = json.Unmarshal(data, &varCreateDockerRegistry) - - if err != nil { - return err - } - - *o = CreateDockerRegistry(varCreateDockerRegistry) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "url") - delete(additionalProperties, "username") - delete(additionalProperties, "password") - delete(additionalProperties, "project") - delete(additionalProperties, "registryType") - delete(additionalProperties, "isDefault") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateDockerRegistry struct { - value *CreateDockerRegistry - isSet bool -} - -func (v NullableCreateDockerRegistry) Get() *CreateDockerRegistry { - return v.value -} - -func (v *NullableCreateDockerRegistry) Set(val *CreateDockerRegistry) { - v.value = val - v.isSet = true -} - -func (v NullableCreateDockerRegistry) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateDockerRegistry) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateDockerRegistry(val *CreateDockerRegistry) *NullableCreateDockerRegistry { - return &NullableCreateDockerRegistry{value: val, isSet: true} -} - -func (v NullableCreateDockerRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateDockerRegistry) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_linked_account.go b/apps/api-client-go/model_create_linked_account.go index 95f0da86a..a1c423fca 100644 --- a/apps/api-client-go/model_create_linked_account.go +++ b/apps/api-client-go/model_create_linked_account.go @@ -196,3 +196,5 @@ func (v *NullableCreateLinkedAccount) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_organization.go b/apps/api-client-go/model_create_organization.go index 5fed16baa..77bb00974 100644 --- a/apps/api-client-go/model_create_organization.go +++ b/apps/api-client-go/model_create_organization.go @@ -196,3 +196,5 @@ func (v *NullableCreateOrganization) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_organization_invitation.go b/apps/api-client-go/model_create_organization_invitation.go index 4ac0cf5ce..9f58ecd0d 100644 --- a/apps/api-client-go/model_create_organization_invitation.go +++ b/apps/api-client-go/model_create_organization_invitation.go @@ -267,3 +267,5 @@ func (v *NullableCreateOrganizationInvitation) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_organization_quota.go b/apps/api-client-go/model_create_organization_quota.go deleted file mode 100644 index f84226a59..000000000 --- a/apps/api-client-go/model_create_organization_quota.go +++ /dev/null @@ -1,450 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the CreateOrganizationQuota type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateOrganizationQuota{} - -// CreateOrganizationQuota struct for CreateOrganizationQuota -type CreateOrganizationQuota struct { - TotalCpuQuota *float32 `json:"totalCpuQuota,omitempty"` - TotalMemoryQuota *float32 `json:"totalMemoryQuota,omitempty"` - TotalDiskQuota *float32 `json:"totalDiskQuota,omitempty"` - MaxCpuPerSandbox *float32 `json:"maxCpuPerSandbox,omitempty"` - MaxMemoryPerSandbox *float32 `json:"maxMemoryPerSandbox,omitempty"` - MaxDiskPerSandbox *float32 `json:"maxDiskPerSandbox,omitempty"` - SnapshotQuota *float32 `json:"snapshotQuota,omitempty"` - MaxSnapshotSize *float32 `json:"maxSnapshotSize,omitempty"` - VolumeQuota *float32 `json:"volumeQuota,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateOrganizationQuota CreateOrganizationQuota - -// NewCreateOrganizationQuota instantiates a new CreateOrganizationQuota object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateOrganizationQuota() *CreateOrganizationQuota { - this := CreateOrganizationQuota{} - return &this -} - -// NewCreateOrganizationQuotaWithDefaults instantiates a new CreateOrganizationQuota object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateOrganizationQuotaWithDefaults() *CreateOrganizationQuota { - this := CreateOrganizationQuota{} - return &this -} - -// GetTotalCpuQuota returns the TotalCpuQuota field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetTotalCpuQuota() float32 { - if o == nil || IsNil(o.TotalCpuQuota) { - var ret float32 - return ret - } - return *o.TotalCpuQuota -} - -// GetTotalCpuQuotaOk returns a tuple with the TotalCpuQuota field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetTotalCpuQuotaOk() (*float32, bool) { - if o == nil || IsNil(o.TotalCpuQuota) { - return nil, false - } - return o.TotalCpuQuota, true -} - -// HasTotalCpuQuota returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasTotalCpuQuota() bool { - if o != nil && !IsNil(o.TotalCpuQuota) { - return true - } - - return false -} - -// SetTotalCpuQuota gets a reference to the given float32 and assigns it to the TotalCpuQuota field. -func (o *CreateOrganizationQuota) SetTotalCpuQuota(v float32) { - o.TotalCpuQuota = &v -} - -// GetTotalMemoryQuota returns the TotalMemoryQuota field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetTotalMemoryQuota() float32 { - if o == nil || IsNil(o.TotalMemoryQuota) { - var ret float32 - return ret - } - return *o.TotalMemoryQuota -} - -// GetTotalMemoryQuotaOk returns a tuple with the TotalMemoryQuota field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetTotalMemoryQuotaOk() (*float32, bool) { - if o == nil || IsNil(o.TotalMemoryQuota) { - return nil, false - } - return o.TotalMemoryQuota, true -} - -// HasTotalMemoryQuota returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasTotalMemoryQuota() bool { - if o != nil && !IsNil(o.TotalMemoryQuota) { - return true - } - - return false -} - -// SetTotalMemoryQuota gets a reference to the given float32 and assigns it to the TotalMemoryQuota field. -func (o *CreateOrganizationQuota) SetTotalMemoryQuota(v float32) { - o.TotalMemoryQuota = &v -} - -// GetTotalDiskQuota returns the TotalDiskQuota field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetTotalDiskQuota() float32 { - if o == nil || IsNil(o.TotalDiskQuota) { - var ret float32 - return ret - } - return *o.TotalDiskQuota -} - -// GetTotalDiskQuotaOk returns a tuple with the TotalDiskQuota field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetTotalDiskQuotaOk() (*float32, bool) { - if o == nil || IsNil(o.TotalDiskQuota) { - return nil, false - } - return o.TotalDiskQuota, true -} - -// HasTotalDiskQuota returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasTotalDiskQuota() bool { - if o != nil && !IsNil(o.TotalDiskQuota) { - return true - } - - return false -} - -// SetTotalDiskQuota gets a reference to the given float32 and assigns it to the TotalDiskQuota field. -func (o *CreateOrganizationQuota) SetTotalDiskQuota(v float32) { - o.TotalDiskQuota = &v -} - -// GetMaxCpuPerSandbox returns the MaxCpuPerSandbox field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetMaxCpuPerSandbox() float32 { - if o == nil || IsNil(o.MaxCpuPerSandbox) { - var ret float32 - return ret - } - return *o.MaxCpuPerSandbox -} - -// GetMaxCpuPerSandboxOk returns a tuple with the MaxCpuPerSandbox field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetMaxCpuPerSandboxOk() (*float32, bool) { - if o == nil || IsNil(o.MaxCpuPerSandbox) { - return nil, false - } - return o.MaxCpuPerSandbox, true -} - -// HasMaxCpuPerSandbox returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasMaxCpuPerSandbox() bool { - if o != nil && !IsNil(o.MaxCpuPerSandbox) { - return true - } - - return false -} - -// SetMaxCpuPerSandbox gets a reference to the given float32 and assigns it to the MaxCpuPerSandbox field. -func (o *CreateOrganizationQuota) SetMaxCpuPerSandbox(v float32) { - o.MaxCpuPerSandbox = &v -} - -// GetMaxMemoryPerSandbox returns the MaxMemoryPerSandbox field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetMaxMemoryPerSandbox() float32 { - if o == nil || IsNil(o.MaxMemoryPerSandbox) { - var ret float32 - return ret - } - return *o.MaxMemoryPerSandbox -} - -// GetMaxMemoryPerSandboxOk returns a tuple with the MaxMemoryPerSandbox field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetMaxMemoryPerSandboxOk() (*float32, bool) { - if o == nil || IsNil(o.MaxMemoryPerSandbox) { - return nil, false - } - return o.MaxMemoryPerSandbox, true -} - -// HasMaxMemoryPerSandbox returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasMaxMemoryPerSandbox() bool { - if o != nil && !IsNil(o.MaxMemoryPerSandbox) { - return true - } - - return false -} - -// SetMaxMemoryPerSandbox gets a reference to the given float32 and assigns it to the MaxMemoryPerSandbox field. -func (o *CreateOrganizationQuota) SetMaxMemoryPerSandbox(v float32) { - o.MaxMemoryPerSandbox = &v -} - -// GetMaxDiskPerSandbox returns the MaxDiskPerSandbox field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetMaxDiskPerSandbox() float32 { - if o == nil || IsNil(o.MaxDiskPerSandbox) { - var ret float32 - return ret - } - return *o.MaxDiskPerSandbox -} - -// GetMaxDiskPerSandboxOk returns a tuple with the MaxDiskPerSandbox field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetMaxDiskPerSandboxOk() (*float32, bool) { - if o == nil || IsNil(o.MaxDiskPerSandbox) { - return nil, false - } - return o.MaxDiskPerSandbox, true -} - -// HasMaxDiskPerSandbox returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasMaxDiskPerSandbox() bool { - if o != nil && !IsNil(o.MaxDiskPerSandbox) { - return true - } - - return false -} - -// SetMaxDiskPerSandbox gets a reference to the given float32 and assigns it to the MaxDiskPerSandbox field. -func (o *CreateOrganizationQuota) SetMaxDiskPerSandbox(v float32) { - o.MaxDiskPerSandbox = &v -} - -// GetSnapshotQuota returns the SnapshotQuota field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetSnapshotQuota() float32 { - if o == nil || IsNil(o.SnapshotQuota) { - var ret float32 - return ret - } - return *o.SnapshotQuota -} - -// GetSnapshotQuotaOk returns a tuple with the SnapshotQuota field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetSnapshotQuotaOk() (*float32, bool) { - if o == nil || IsNil(o.SnapshotQuota) { - return nil, false - } - return o.SnapshotQuota, true -} - -// HasSnapshotQuota returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasSnapshotQuota() bool { - if o != nil && !IsNil(o.SnapshotQuota) { - return true - } - - return false -} - -// SetSnapshotQuota gets a reference to the given float32 and assigns it to the SnapshotQuota field. -func (o *CreateOrganizationQuota) SetSnapshotQuota(v float32) { - o.SnapshotQuota = &v -} - -// GetMaxSnapshotSize returns the MaxSnapshotSize field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetMaxSnapshotSize() float32 { - if o == nil || IsNil(o.MaxSnapshotSize) { - var ret float32 - return ret - } - return *o.MaxSnapshotSize -} - -// GetMaxSnapshotSizeOk returns a tuple with the MaxSnapshotSize field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetMaxSnapshotSizeOk() (*float32, bool) { - if o == nil || IsNil(o.MaxSnapshotSize) { - return nil, false - } - return o.MaxSnapshotSize, true -} - -// HasMaxSnapshotSize returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasMaxSnapshotSize() bool { - if o != nil && !IsNil(o.MaxSnapshotSize) { - return true - } - - return false -} - -// SetMaxSnapshotSize gets a reference to the given float32 and assigns it to the MaxSnapshotSize field. -func (o *CreateOrganizationQuota) SetMaxSnapshotSize(v float32) { - o.MaxSnapshotSize = &v -} - -// GetVolumeQuota returns the VolumeQuota field value if set, zero value otherwise. -func (o *CreateOrganizationQuota) GetVolumeQuota() float32 { - if o == nil || IsNil(o.VolumeQuota) { - var ret float32 - return ret - } - return *o.VolumeQuota -} - -// GetVolumeQuotaOk returns a tuple with the VolumeQuota field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateOrganizationQuota) GetVolumeQuotaOk() (*float32, bool) { - if o == nil || IsNil(o.VolumeQuota) { - return nil, false - } - return o.VolumeQuota, true -} - -// HasVolumeQuota returns a boolean if a field has been set. -func (o *CreateOrganizationQuota) HasVolumeQuota() bool { - if o != nil && !IsNil(o.VolumeQuota) { - return true - } - - return false -} - -// SetVolumeQuota gets a reference to the given float32 and assigns it to the VolumeQuota field. -func (o *CreateOrganizationQuota) SetVolumeQuota(v float32) { - o.VolumeQuota = &v -} - -func (o CreateOrganizationQuota) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateOrganizationQuota) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.TotalCpuQuota) { - toSerialize["totalCpuQuota"] = o.TotalCpuQuota - } - if !IsNil(o.TotalMemoryQuota) { - toSerialize["totalMemoryQuota"] = o.TotalMemoryQuota - } - if !IsNil(o.TotalDiskQuota) { - toSerialize["totalDiskQuota"] = o.TotalDiskQuota - } - if !IsNil(o.MaxCpuPerSandbox) { - toSerialize["maxCpuPerSandbox"] = o.MaxCpuPerSandbox - } - if !IsNil(o.MaxMemoryPerSandbox) { - toSerialize["maxMemoryPerSandbox"] = o.MaxMemoryPerSandbox - } - if !IsNil(o.MaxDiskPerSandbox) { - toSerialize["maxDiskPerSandbox"] = o.MaxDiskPerSandbox - } - if !IsNil(o.SnapshotQuota) { - toSerialize["snapshotQuota"] = o.SnapshotQuota - } - if !IsNil(o.MaxSnapshotSize) { - toSerialize["maxSnapshotSize"] = o.MaxSnapshotSize - } - if !IsNil(o.VolumeQuota) { - toSerialize["volumeQuota"] = o.VolumeQuota - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateOrganizationQuota) UnmarshalJSON(data []byte) (err error) { - varCreateOrganizationQuota := _CreateOrganizationQuota{} - - err = json.Unmarshal(data, &varCreateOrganizationQuota) - - if err != nil { - return err - } - - *o = CreateOrganizationQuota(varCreateOrganizationQuota) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "totalCpuQuota") - delete(additionalProperties, "totalMemoryQuota") - delete(additionalProperties, "totalDiskQuota") - delete(additionalProperties, "maxCpuPerSandbox") - delete(additionalProperties, "maxMemoryPerSandbox") - delete(additionalProperties, "maxDiskPerSandbox") - delete(additionalProperties, "snapshotQuota") - delete(additionalProperties, "maxSnapshotSize") - delete(additionalProperties, "volumeQuota") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateOrganizationQuota struct { - value *CreateOrganizationQuota - isSet bool -} - -func (v NullableCreateOrganizationQuota) Get() *CreateOrganizationQuota { - return v.value -} - -func (v *NullableCreateOrganizationQuota) Set(val *CreateOrganizationQuota) { - v.value = val - v.isSet = true -} - -func (v NullableCreateOrganizationQuota) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateOrganizationQuota) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateOrganizationQuota(val *CreateOrganizationQuota) *NullableCreateOrganizationQuota { - return &NullableCreateOrganizationQuota{value: val, isSet: true} -} - -func (v NullableCreateOrganizationQuota) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateOrganizationQuota) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_organization_role.go b/apps/api-client-go/model_create_organization_role.go index 30899eebc..385ef3d0e 100644 --- a/apps/api-client-go/model_create_organization_role.go +++ b/apps/api-client-go/model_create_organization_role.go @@ -226,3 +226,5 @@ func (v *NullableCreateOrganizationRole) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_region.go b/apps/api-client-go/model_create_region.go index 9bbc0acf7..663956ad6 100644 --- a/apps/api-client-go/model_create_region.go +++ b/apps/api-client-go/model_create_region.go @@ -27,8 +27,6 @@ type CreateRegion struct { ProxyUrl NullableString `json:"proxyUrl,omitempty"` // SSH Gateway URL for the region SshGatewayUrl NullableString `json:"sshGatewayUrl,omitempty"` - // Snapshot Manager URL for the region - SnapshotManagerUrl NullableString `json:"snapshotManagerUrl,omitempty"` AdditionalProperties map[string]interface{} } @@ -160,48 +158,6 @@ func (o *CreateRegion) UnsetSshGatewayUrl() { o.SshGatewayUrl.Unset() } -// GetSnapshotManagerUrl returns the SnapshotManagerUrl field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *CreateRegion) GetSnapshotManagerUrl() string { - if o == nil || IsNil(o.SnapshotManagerUrl.Get()) { - var ret string - return ret - } - return *o.SnapshotManagerUrl.Get() -} - -// GetSnapshotManagerUrlOk returns a tuple with the SnapshotManagerUrl field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *CreateRegion) GetSnapshotManagerUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.SnapshotManagerUrl.Get(), o.SnapshotManagerUrl.IsSet() -} - -// HasSnapshotManagerUrl returns a boolean if a field has been set. -func (o *CreateRegion) HasSnapshotManagerUrl() bool { - if o != nil && o.SnapshotManagerUrl.IsSet() { - return true - } - - return false -} - -// SetSnapshotManagerUrl gets a reference to the given NullableString and assigns it to the SnapshotManagerUrl field. -func (o *CreateRegion) SetSnapshotManagerUrl(v string) { - o.SnapshotManagerUrl.Set(&v) -} -// SetSnapshotManagerUrlNil sets the value for SnapshotManagerUrl to be an explicit nil -func (o *CreateRegion) SetSnapshotManagerUrlNil() { - o.SnapshotManagerUrl.Set(nil) -} - -// UnsetSnapshotManagerUrl ensures that no value is present for SnapshotManagerUrl, not even an explicit nil -func (o *CreateRegion) UnsetSnapshotManagerUrl() { - o.SnapshotManagerUrl.Unset() -} - func (o CreateRegion) MarshalJSON() ([]byte, error) { toSerialize,err := o.ToMap() if err != nil { @@ -219,9 +175,6 @@ func (o CreateRegion) ToMap() (map[string]interface{}, error) { if o.SshGatewayUrl.IsSet() { toSerialize["sshGatewayUrl"] = o.SshGatewayUrl.Get() } - if o.SnapshotManagerUrl.IsSet() { - toSerialize["snapshotManagerUrl"] = o.SnapshotManagerUrl.Get() - } for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -268,7 +221,6 @@ func (o *CreateRegion) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "name") delete(additionalProperties, "proxyUrl") delete(additionalProperties, "sshGatewayUrl") - delete(additionalProperties, "snapshotManagerUrl") o.AdditionalProperties = additionalProperties } @@ -310,3 +262,5 @@ func (v *NullableCreateRegion) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_region_response.go b/apps/api-client-go/model_create_region_response.go index 40c8131f1..c134c55b2 100644 --- a/apps/api-client-go/model_create_region_response.go +++ b/apps/api-client-go/model_create_region_response.go @@ -27,10 +27,6 @@ type CreateRegionResponse struct { ProxyApiKey NullableString `json:"proxyApiKey,omitempty"` // SSH Gateway API key for the region SshGatewayApiKey NullableString `json:"sshGatewayApiKey,omitempty"` - // Snapshot Manager username for the region - SnapshotManagerUsername NullableString `json:"snapshotManagerUsername,omitempty"` - // Snapshot Manager password for the region - SnapshotManagerPassword NullableString `json:"snapshotManagerPassword,omitempty"` AdditionalProperties map[string]interface{} } @@ -162,90 +158,6 @@ func (o *CreateRegionResponse) UnsetSshGatewayApiKey() { o.SshGatewayApiKey.Unset() } -// GetSnapshotManagerUsername returns the SnapshotManagerUsername field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *CreateRegionResponse) GetSnapshotManagerUsername() string { - if o == nil || IsNil(o.SnapshotManagerUsername.Get()) { - var ret string - return ret - } - return *o.SnapshotManagerUsername.Get() -} - -// GetSnapshotManagerUsernameOk returns a tuple with the SnapshotManagerUsername field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *CreateRegionResponse) GetSnapshotManagerUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.SnapshotManagerUsername.Get(), o.SnapshotManagerUsername.IsSet() -} - -// HasSnapshotManagerUsername returns a boolean if a field has been set. -func (o *CreateRegionResponse) HasSnapshotManagerUsername() bool { - if o != nil && o.SnapshotManagerUsername.IsSet() { - return true - } - - return false -} - -// SetSnapshotManagerUsername gets a reference to the given NullableString and assigns it to the SnapshotManagerUsername field. -func (o *CreateRegionResponse) SetSnapshotManagerUsername(v string) { - o.SnapshotManagerUsername.Set(&v) -} -// SetSnapshotManagerUsernameNil sets the value for SnapshotManagerUsername to be an explicit nil -func (o *CreateRegionResponse) SetSnapshotManagerUsernameNil() { - o.SnapshotManagerUsername.Set(nil) -} - -// UnsetSnapshotManagerUsername ensures that no value is present for SnapshotManagerUsername, not even an explicit nil -func (o *CreateRegionResponse) UnsetSnapshotManagerUsername() { - o.SnapshotManagerUsername.Unset() -} - -// GetSnapshotManagerPassword returns the SnapshotManagerPassword field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *CreateRegionResponse) GetSnapshotManagerPassword() string { - if o == nil || IsNil(o.SnapshotManagerPassword.Get()) { - var ret string - return ret - } - return *o.SnapshotManagerPassword.Get() -} - -// GetSnapshotManagerPasswordOk returns a tuple with the SnapshotManagerPassword field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *CreateRegionResponse) GetSnapshotManagerPasswordOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.SnapshotManagerPassword.Get(), o.SnapshotManagerPassword.IsSet() -} - -// HasSnapshotManagerPassword returns a boolean if a field has been set. -func (o *CreateRegionResponse) HasSnapshotManagerPassword() bool { - if o != nil && o.SnapshotManagerPassword.IsSet() { - return true - } - - return false -} - -// SetSnapshotManagerPassword gets a reference to the given NullableString and assigns it to the SnapshotManagerPassword field. -func (o *CreateRegionResponse) SetSnapshotManagerPassword(v string) { - o.SnapshotManagerPassword.Set(&v) -} -// SetSnapshotManagerPasswordNil sets the value for SnapshotManagerPassword to be an explicit nil -func (o *CreateRegionResponse) SetSnapshotManagerPasswordNil() { - o.SnapshotManagerPassword.Set(nil) -} - -// UnsetSnapshotManagerPassword ensures that no value is present for SnapshotManagerPassword, not even an explicit nil -func (o *CreateRegionResponse) UnsetSnapshotManagerPassword() { - o.SnapshotManagerPassword.Unset() -} - func (o CreateRegionResponse) MarshalJSON() ([]byte, error) { toSerialize,err := o.ToMap() if err != nil { @@ -263,12 +175,6 @@ func (o CreateRegionResponse) ToMap() (map[string]interface{}, error) { if o.SshGatewayApiKey.IsSet() { toSerialize["sshGatewayApiKey"] = o.SshGatewayApiKey.Get() } - if o.SnapshotManagerUsername.IsSet() { - toSerialize["snapshotManagerUsername"] = o.SnapshotManagerUsername.Get() - } - if o.SnapshotManagerPassword.IsSet() { - toSerialize["snapshotManagerPassword"] = o.SnapshotManagerPassword.Get() - } for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -315,8 +221,6 @@ func (o *CreateRegionResponse) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "id") delete(additionalProperties, "proxyApiKey") delete(additionalProperties, "sshGatewayApiKey") - delete(additionalProperties, "snapshotManagerUsername") - delete(additionalProperties, "snapshotManagerPassword") o.AdditionalProperties = additionalProperties } @@ -358,3 +262,5 @@ func (v *NullableCreateRegionResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_runner.go b/apps/api-client-go/model_create_runner.go index 821a10e45..540ba9284 100644 --- a/apps/api-client-go/model_create_runner.go +++ b/apps/api-client-go/model_create_runner.go @@ -194,3 +194,5 @@ func (v *NullableCreateRunner) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_runner_response.go b/apps/api-client-go/model_create_runner_response.go index b04b7b0c8..f0e540ad1 100644 --- a/apps/api-client-go/model_create_runner_response.go +++ b/apps/api-client-go/model_create_runner_response.go @@ -196,3 +196,5 @@ func (v *NullableCreateRunnerResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_sandbox.go b/apps/api-client-go/model_create_sandbox.go deleted file mode 100644 index 20408e776..000000000 --- a/apps/api-client-go/model_create_sandbox.go +++ /dev/null @@ -1,839 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the CreateSandbox type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateSandbox{} - -// CreateSandbox struct for CreateSandbox -type CreateSandbox struct { - // The name of the sandbox. If not provided, the sandbox ID will be used as the name - Name *string `json:"name,omitempty"` - // The ID or name of the snapshot used for the sandbox - Snapshot *string `json:"snapshot,omitempty"` - // The user associated with the project - User *string `json:"user,omitempty"` - // Environment variables for the sandbox - Env *map[string]string `json:"env,omitempty"` - // Labels for the sandbox - Labels *map[string]string `json:"labels,omitempty"` - // Whether the sandbox http preview is publicly accessible - Public *bool `json:"public,omitempty"` - // Whether to block all network access for the sandbox - NetworkBlockAll *bool `json:"networkBlockAll,omitempty"` - // Comma-separated list of allowed CIDR network addresses for the sandbox - NetworkAllowList *string `json:"networkAllowList,omitempty"` - // The sandbox class type - Class *string `json:"class,omitempty"` - // The target (region) where the sandbox will be created - Target *string `json:"target,omitempty"` - // CPU cores allocated to the sandbox - Cpu *int32 `json:"cpu,omitempty"` - // GPU units allocated to the sandbox - Gpu *int32 `json:"gpu,omitempty"` - // Memory allocated to the sandbox in GB - Memory *int32 `json:"memory,omitempty"` - // Disk space allocated to the sandbox in GB - Disk *int32 `json:"disk,omitempty"` - // Auto-stop interval in minutes (0 means disabled) - AutoStopInterval *int32 `json:"autoStopInterval,omitempty"` - // Auto-archive interval in minutes (0 means the maximum interval will be used) - AutoArchiveInterval *int32 `json:"autoArchiveInterval,omitempty"` - // Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - AutoDeleteInterval *int32 `json:"autoDeleteInterval,omitempty"` - // Array of volumes to attach to the sandbox - Volumes []SandboxVolume `json:"volumes,omitempty"` - // Build information for the sandbox - BuildInfo *CreateBuildInfo `json:"buildInfo,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateSandbox CreateSandbox - -// NewCreateSandbox instantiates a new CreateSandbox object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateSandbox() *CreateSandbox { - this := CreateSandbox{} - return &this -} - -// NewCreateSandboxWithDefaults instantiates a new CreateSandbox object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateSandboxWithDefaults() *CreateSandbox { - this := CreateSandbox{} - return &this -} - -// GetName returns the Name field value if set, zero value otherwise. -func (o *CreateSandbox) GetName() string { - if o == nil || IsNil(o.Name) { - var ret string - return ret - } - return *o.Name -} - -// GetNameOk returns a tuple with the Name field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetNameOk() (*string, bool) { - if o == nil || IsNil(o.Name) { - return nil, false - } - return o.Name, true -} - -// HasName returns a boolean if a field has been set. -func (o *CreateSandbox) HasName() bool { - if o != nil && !IsNil(o.Name) { - return true - } - - return false -} - -// SetName gets a reference to the given string and assigns it to the Name field. -func (o *CreateSandbox) SetName(v string) { - o.Name = &v -} - -// GetSnapshot returns the Snapshot field value if set, zero value otherwise. -func (o *CreateSandbox) GetSnapshot() string { - if o == nil || IsNil(o.Snapshot) { - var ret string - return ret - } - return *o.Snapshot -} - -// GetSnapshotOk returns a tuple with the Snapshot field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetSnapshotOk() (*string, bool) { - if o == nil || IsNil(o.Snapshot) { - return nil, false - } - return o.Snapshot, true -} - -// HasSnapshot returns a boolean if a field has been set. -func (o *CreateSandbox) HasSnapshot() bool { - if o != nil && !IsNil(o.Snapshot) { - return true - } - - return false -} - -// SetSnapshot gets a reference to the given string and assigns it to the Snapshot field. -func (o *CreateSandbox) SetSnapshot(v string) { - o.Snapshot = &v -} - -// GetUser returns the User field value if set, zero value otherwise. -func (o *CreateSandbox) GetUser() string { - if o == nil || IsNil(o.User) { - var ret string - return ret - } - return *o.User -} - -// GetUserOk returns a tuple with the User field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetUserOk() (*string, bool) { - if o == nil || IsNil(o.User) { - return nil, false - } - return o.User, true -} - -// HasUser returns a boolean if a field has been set. -func (o *CreateSandbox) HasUser() bool { - if o != nil && !IsNil(o.User) { - return true - } - - return false -} - -// SetUser gets a reference to the given string and assigns it to the User field. -func (o *CreateSandbox) SetUser(v string) { - o.User = &v -} - -// GetEnv returns the Env field value if set, zero value otherwise. -func (o *CreateSandbox) GetEnv() map[string]string { - if o == nil || IsNil(o.Env) { - var ret map[string]string - return ret - } - return *o.Env -} - -// GetEnvOk returns a tuple with the Env field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetEnvOk() (*map[string]string, bool) { - if o == nil || IsNil(o.Env) { - return nil, false - } - return o.Env, true -} - -// HasEnv returns a boolean if a field has been set. -func (o *CreateSandbox) HasEnv() bool { - if o != nil && !IsNil(o.Env) { - return true - } - - return false -} - -// SetEnv gets a reference to the given map[string]string and assigns it to the Env field. -func (o *CreateSandbox) SetEnv(v map[string]string) { - o.Env = &v -} - -// GetLabels returns the Labels field value if set, zero value otherwise. -func (o *CreateSandbox) GetLabels() map[string]string { - if o == nil || IsNil(o.Labels) { - var ret map[string]string - return ret - } - return *o.Labels -} - -// GetLabelsOk returns a tuple with the Labels field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetLabelsOk() (*map[string]string, bool) { - if o == nil || IsNil(o.Labels) { - return nil, false - } - return o.Labels, true -} - -// HasLabels returns a boolean if a field has been set. -func (o *CreateSandbox) HasLabels() bool { - if o != nil && !IsNil(o.Labels) { - return true - } - - return false -} - -// SetLabels gets a reference to the given map[string]string and assigns it to the Labels field. -func (o *CreateSandbox) SetLabels(v map[string]string) { - o.Labels = &v -} - -// GetPublic returns the Public field value if set, zero value otherwise. -func (o *CreateSandbox) GetPublic() bool { - if o == nil || IsNil(o.Public) { - var ret bool - return ret - } - return *o.Public -} - -// GetPublicOk returns a tuple with the Public field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetPublicOk() (*bool, bool) { - if o == nil || IsNil(o.Public) { - return nil, false - } - return o.Public, true -} - -// HasPublic returns a boolean if a field has been set. -func (o *CreateSandbox) HasPublic() bool { - if o != nil && !IsNil(o.Public) { - return true - } - - return false -} - -// SetPublic gets a reference to the given bool and assigns it to the Public field. -func (o *CreateSandbox) SetPublic(v bool) { - o.Public = &v -} - -// GetNetworkBlockAll returns the NetworkBlockAll field value if set, zero value otherwise. -func (o *CreateSandbox) GetNetworkBlockAll() bool { - if o == nil || IsNil(o.NetworkBlockAll) { - var ret bool - return ret - } - return *o.NetworkBlockAll -} - -// GetNetworkBlockAllOk returns a tuple with the NetworkBlockAll field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetNetworkBlockAllOk() (*bool, bool) { - if o == nil || IsNil(o.NetworkBlockAll) { - return nil, false - } - return o.NetworkBlockAll, true -} - -// HasNetworkBlockAll returns a boolean if a field has been set. -func (o *CreateSandbox) HasNetworkBlockAll() bool { - if o != nil && !IsNil(o.NetworkBlockAll) { - return true - } - - return false -} - -// SetNetworkBlockAll gets a reference to the given bool and assigns it to the NetworkBlockAll field. -func (o *CreateSandbox) SetNetworkBlockAll(v bool) { - o.NetworkBlockAll = &v -} - -// GetNetworkAllowList returns the NetworkAllowList field value if set, zero value otherwise. -func (o *CreateSandbox) GetNetworkAllowList() string { - if o == nil || IsNil(o.NetworkAllowList) { - var ret string - return ret - } - return *o.NetworkAllowList -} - -// GetNetworkAllowListOk returns a tuple with the NetworkAllowList field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetNetworkAllowListOk() (*string, bool) { - if o == nil || IsNil(o.NetworkAllowList) { - return nil, false - } - return o.NetworkAllowList, true -} - -// HasNetworkAllowList returns a boolean if a field has been set. -func (o *CreateSandbox) HasNetworkAllowList() bool { - if o != nil && !IsNil(o.NetworkAllowList) { - return true - } - - return false -} - -// SetNetworkAllowList gets a reference to the given string and assigns it to the NetworkAllowList field. -func (o *CreateSandbox) SetNetworkAllowList(v string) { - o.NetworkAllowList = &v -} - -// GetClass returns the Class field value if set, zero value otherwise. -func (o *CreateSandbox) GetClass() string { - if o == nil || IsNil(o.Class) { - var ret string - return ret - } - return *o.Class -} - -// GetClassOk returns a tuple with the Class field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetClassOk() (*string, bool) { - if o == nil || IsNil(o.Class) { - return nil, false - } - return o.Class, true -} - -// HasClass returns a boolean if a field has been set. -func (o *CreateSandbox) HasClass() bool { - if o != nil && !IsNil(o.Class) { - return true - } - - return false -} - -// SetClass gets a reference to the given string and assigns it to the Class field. -func (o *CreateSandbox) SetClass(v string) { - o.Class = &v -} - -// GetTarget returns the Target field value if set, zero value otherwise. -func (o *CreateSandbox) GetTarget() string { - if o == nil || IsNil(o.Target) { - var ret string - return ret - } - return *o.Target -} - -// GetTargetOk returns a tuple with the Target field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetTargetOk() (*string, bool) { - if o == nil || IsNil(o.Target) { - return nil, false - } - return o.Target, true -} - -// HasTarget returns a boolean if a field has been set. -func (o *CreateSandbox) HasTarget() bool { - if o != nil && !IsNil(o.Target) { - return true - } - - return false -} - -// SetTarget gets a reference to the given string and assigns it to the Target field. -func (o *CreateSandbox) SetTarget(v string) { - o.Target = &v -} - -// GetCpu returns the Cpu field value if set, zero value otherwise. -func (o *CreateSandbox) GetCpu() int32 { - if o == nil || IsNil(o.Cpu) { - var ret int32 - return ret - } - return *o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetCpuOk() (*int32, bool) { - if o == nil || IsNil(o.Cpu) { - return nil, false - } - return o.Cpu, true -} - -// HasCpu returns a boolean if a field has been set. -func (o *CreateSandbox) HasCpu() bool { - if o != nil && !IsNil(o.Cpu) { - return true - } - - return false -} - -// SetCpu gets a reference to the given int32 and assigns it to the Cpu field. -func (o *CreateSandbox) SetCpu(v int32) { - o.Cpu = &v -} - -// GetGpu returns the Gpu field value if set, zero value otherwise. -func (o *CreateSandbox) GetGpu() int32 { - if o == nil || IsNil(o.Gpu) { - var ret int32 - return ret - } - return *o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetGpuOk() (*int32, bool) { - if o == nil || IsNil(o.Gpu) { - return nil, false - } - return o.Gpu, true -} - -// HasGpu returns a boolean if a field has been set. -func (o *CreateSandbox) HasGpu() bool { - if o != nil && !IsNil(o.Gpu) { - return true - } - - return false -} - -// SetGpu gets a reference to the given int32 and assigns it to the Gpu field. -func (o *CreateSandbox) SetGpu(v int32) { - o.Gpu = &v -} - -// GetMemory returns the Memory field value if set, zero value otherwise. -func (o *CreateSandbox) GetMemory() int32 { - if o == nil || IsNil(o.Memory) { - var ret int32 - return ret - } - return *o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetMemoryOk() (*int32, bool) { - if o == nil || IsNil(o.Memory) { - return nil, false - } - return o.Memory, true -} - -// HasMemory returns a boolean if a field has been set. -func (o *CreateSandbox) HasMemory() bool { - if o != nil && !IsNil(o.Memory) { - return true - } - - return false -} - -// SetMemory gets a reference to the given int32 and assigns it to the Memory field. -func (o *CreateSandbox) SetMemory(v int32) { - o.Memory = &v -} - -// GetDisk returns the Disk field value if set, zero value otherwise. -func (o *CreateSandbox) GetDisk() int32 { - if o == nil || IsNil(o.Disk) { - var ret int32 - return ret - } - return *o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetDiskOk() (*int32, bool) { - if o == nil || IsNil(o.Disk) { - return nil, false - } - return o.Disk, true -} - -// HasDisk returns a boolean if a field has been set. -func (o *CreateSandbox) HasDisk() bool { - if o != nil && !IsNil(o.Disk) { - return true - } - - return false -} - -// SetDisk gets a reference to the given int32 and assigns it to the Disk field. -func (o *CreateSandbox) SetDisk(v int32) { - o.Disk = &v -} - -// GetAutoStopInterval returns the AutoStopInterval field value if set, zero value otherwise. -func (o *CreateSandbox) GetAutoStopInterval() int32 { - if o == nil || IsNil(o.AutoStopInterval) { - var ret int32 - return ret - } - return *o.AutoStopInterval -} - -// GetAutoStopIntervalOk returns a tuple with the AutoStopInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetAutoStopIntervalOk() (*int32, bool) { - if o == nil || IsNil(o.AutoStopInterval) { - return nil, false - } - return o.AutoStopInterval, true -} - -// HasAutoStopInterval returns a boolean if a field has been set. -func (o *CreateSandbox) HasAutoStopInterval() bool { - if o != nil && !IsNil(o.AutoStopInterval) { - return true - } - - return false -} - -// SetAutoStopInterval gets a reference to the given int32 and assigns it to the AutoStopInterval field. -func (o *CreateSandbox) SetAutoStopInterval(v int32) { - o.AutoStopInterval = &v -} - -// GetAutoArchiveInterval returns the AutoArchiveInterval field value if set, zero value otherwise. -func (o *CreateSandbox) GetAutoArchiveInterval() int32 { - if o == nil || IsNil(o.AutoArchiveInterval) { - var ret int32 - return ret - } - return *o.AutoArchiveInterval -} - -// GetAutoArchiveIntervalOk returns a tuple with the AutoArchiveInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetAutoArchiveIntervalOk() (*int32, bool) { - if o == nil || IsNil(o.AutoArchiveInterval) { - return nil, false - } - return o.AutoArchiveInterval, true -} - -// HasAutoArchiveInterval returns a boolean if a field has been set. -func (o *CreateSandbox) HasAutoArchiveInterval() bool { - if o != nil && !IsNil(o.AutoArchiveInterval) { - return true - } - - return false -} - -// SetAutoArchiveInterval gets a reference to the given int32 and assigns it to the AutoArchiveInterval field. -func (o *CreateSandbox) SetAutoArchiveInterval(v int32) { - o.AutoArchiveInterval = &v -} - -// GetAutoDeleteInterval returns the AutoDeleteInterval field value if set, zero value otherwise. -func (o *CreateSandbox) GetAutoDeleteInterval() int32 { - if o == nil || IsNil(o.AutoDeleteInterval) { - var ret int32 - return ret - } - return *o.AutoDeleteInterval -} - -// GetAutoDeleteIntervalOk returns a tuple with the AutoDeleteInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetAutoDeleteIntervalOk() (*int32, bool) { - if o == nil || IsNil(o.AutoDeleteInterval) { - return nil, false - } - return o.AutoDeleteInterval, true -} - -// HasAutoDeleteInterval returns a boolean if a field has been set. -func (o *CreateSandbox) HasAutoDeleteInterval() bool { - if o != nil && !IsNil(o.AutoDeleteInterval) { - return true - } - - return false -} - -// SetAutoDeleteInterval gets a reference to the given int32 and assigns it to the AutoDeleteInterval field. -func (o *CreateSandbox) SetAutoDeleteInterval(v int32) { - o.AutoDeleteInterval = &v -} - -// GetVolumes returns the Volumes field value if set, zero value otherwise. -func (o *CreateSandbox) GetVolumes() []SandboxVolume { - if o == nil || IsNil(o.Volumes) { - var ret []SandboxVolume - return ret - } - return o.Volumes -} - -// GetVolumesOk returns a tuple with the Volumes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetVolumesOk() ([]SandboxVolume, bool) { - if o == nil || IsNil(o.Volumes) { - return nil, false - } - return o.Volumes, true -} - -// HasVolumes returns a boolean if a field has been set. -func (o *CreateSandbox) HasVolumes() bool { - if o != nil && !IsNil(o.Volumes) { - return true - } - - return false -} - -// SetVolumes gets a reference to the given []SandboxVolume and assigns it to the Volumes field. -func (o *CreateSandbox) SetVolumes(v []SandboxVolume) { - o.Volumes = v -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *CreateSandbox) GetBuildInfo() CreateBuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret CreateBuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSandbox) GetBuildInfoOk() (*CreateBuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *CreateSandbox) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given CreateBuildInfo and assigns it to the BuildInfo field. -func (o *CreateSandbox) SetBuildInfo(v CreateBuildInfo) { - o.BuildInfo = &v -} - -func (o CreateSandbox) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateSandbox) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Name) { - toSerialize["name"] = o.Name - } - if !IsNil(o.Snapshot) { - toSerialize["snapshot"] = o.Snapshot - } - if !IsNil(o.User) { - toSerialize["user"] = o.User - } - if !IsNil(o.Env) { - toSerialize["env"] = o.Env - } - if !IsNil(o.Labels) { - toSerialize["labels"] = o.Labels - } - if !IsNil(o.Public) { - toSerialize["public"] = o.Public - } - if !IsNil(o.NetworkBlockAll) { - toSerialize["networkBlockAll"] = o.NetworkBlockAll - } - if !IsNil(o.NetworkAllowList) { - toSerialize["networkAllowList"] = o.NetworkAllowList - } - if !IsNil(o.Class) { - toSerialize["class"] = o.Class - } - if !IsNil(o.Target) { - toSerialize["target"] = o.Target - } - if !IsNil(o.Cpu) { - toSerialize["cpu"] = o.Cpu - } - if !IsNil(o.Gpu) { - toSerialize["gpu"] = o.Gpu - } - if !IsNil(o.Memory) { - toSerialize["memory"] = o.Memory - } - if !IsNil(o.Disk) { - toSerialize["disk"] = o.Disk - } - if !IsNil(o.AutoStopInterval) { - toSerialize["autoStopInterval"] = o.AutoStopInterval - } - if !IsNil(o.AutoArchiveInterval) { - toSerialize["autoArchiveInterval"] = o.AutoArchiveInterval - } - if !IsNil(o.AutoDeleteInterval) { - toSerialize["autoDeleteInterval"] = o.AutoDeleteInterval - } - if !IsNil(o.Volumes) { - toSerialize["volumes"] = o.Volumes - } - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateSandbox) UnmarshalJSON(data []byte) (err error) { - varCreateSandbox := _CreateSandbox{} - - err = json.Unmarshal(data, &varCreateSandbox) - - if err != nil { - return err - } - - *o = CreateSandbox(varCreateSandbox) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "snapshot") - delete(additionalProperties, "user") - delete(additionalProperties, "env") - delete(additionalProperties, "labels") - delete(additionalProperties, "public") - delete(additionalProperties, "networkBlockAll") - delete(additionalProperties, "networkAllowList") - delete(additionalProperties, "class") - delete(additionalProperties, "target") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - delete(additionalProperties, "autoStopInterval") - delete(additionalProperties, "autoArchiveInterval") - delete(additionalProperties, "autoDeleteInterval") - delete(additionalProperties, "volumes") - delete(additionalProperties, "buildInfo") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateSandbox struct { - value *CreateSandbox - isSet bool -} - -func (v NullableCreateSandbox) Get() *CreateSandbox { - return v.value -} - -func (v *NullableCreateSandbox) Set(val *CreateSandbox) { - v.value = val - v.isSet = true -} - -func (v NullableCreateSandbox) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateSandbox) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateSandbox(val *CreateSandbox) *NullableCreateSandbox { - return &NullableCreateSandbox{value: val, isSet: true} -} - -func (v NullableCreateSandbox) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateSandbox) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_session_request.go b/apps/api-client-go/model_create_session_request.go deleted file mode 100644 index 85483de4e..000000000 --- a/apps/api-client-go/model_create_session_request.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CreateSessionRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateSessionRequest{} - -// CreateSessionRequest struct for CreateSessionRequest -type CreateSessionRequest struct { - // The ID of the session - SessionId string `json:"sessionId"` - AdditionalProperties map[string]interface{} -} - -type _CreateSessionRequest CreateSessionRequest - -// NewCreateSessionRequest instantiates a new CreateSessionRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateSessionRequest(sessionId string) *CreateSessionRequest { - this := CreateSessionRequest{} - this.SessionId = sessionId - return &this -} - -// NewCreateSessionRequestWithDefaults instantiates a new CreateSessionRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateSessionRequestWithDefaults() *CreateSessionRequest { - this := CreateSessionRequest{} - return &this -} - -// GetSessionId returns the SessionId field value -func (o *CreateSessionRequest) GetSessionId() string { - if o == nil { - var ret string - return ret - } - - return o.SessionId -} - -// GetSessionIdOk returns a tuple with the SessionId field value -// and a boolean to check if the value has been set. -func (o *CreateSessionRequest) GetSessionIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.SessionId, true -} - -// SetSessionId sets field value -func (o *CreateSessionRequest) SetSessionId(v string) { - o.SessionId = v -} - -func (o CreateSessionRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateSessionRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["sessionId"] = o.SessionId - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateSessionRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "sessionId", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCreateSessionRequest := _CreateSessionRequest{} - - err = json.Unmarshal(data, &varCreateSessionRequest) - - if err != nil { - return err - } - - *o = CreateSessionRequest(varCreateSessionRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sessionId") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateSessionRequest struct { - value *CreateSessionRequest - isSet bool -} - -func (v NullableCreateSessionRequest) Get() *CreateSessionRequest { - return v.value -} - -func (v *NullableCreateSessionRequest) Set(val *CreateSessionRequest) { - v.value = val - v.isSet = true -} - -func (v NullableCreateSessionRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateSessionRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateSessionRequest(val *CreateSessionRequest) *NullableCreateSessionRequest { - return &NullableCreateSessionRequest{value: val, isSet: true} -} - -func (v NullableCreateSessionRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateSessionRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_snapshot.go b/apps/api-client-go/model_create_snapshot.go deleted file mode 100644 index 8dfa6ea37..000000000 --- a/apps/api-client-go/model_create_snapshot.go +++ /dev/null @@ -1,510 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the CreateSnapshot type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateSnapshot{} - -// CreateSnapshot struct for CreateSnapshot -type CreateSnapshot struct { - // The name of the snapshot - Name string `json:"name"` - // The image name of the snapshot - ImageName *string `json:"imageName,omitempty"` - // The entrypoint command for the snapshot - Entrypoint []string `json:"entrypoint,omitempty"` - // Whether the snapshot is general - General *bool `json:"general,omitempty"` - // CPU cores allocated to the resulting sandbox - Cpu *int32 `json:"cpu,omitempty"` - // GPU units allocated to the resulting sandbox - Gpu *int32 `json:"gpu,omitempty"` - // Memory allocated to the resulting sandbox in GB - Memory *int32 `json:"memory,omitempty"` - // Disk space allocated to the sandbox in GB - Disk *int32 `json:"disk,omitempty"` - // Build information for the snapshot - BuildInfo *CreateBuildInfo `json:"buildInfo,omitempty"` - // ID of the region where the snapshot will be available. Defaults to organization default region if not specified. - RegionId *string `json:"regionId,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateSnapshot CreateSnapshot - -// NewCreateSnapshot instantiates a new CreateSnapshot object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateSnapshot(name string) *CreateSnapshot { - this := CreateSnapshot{} - this.Name = name - return &this -} - -// NewCreateSnapshotWithDefaults instantiates a new CreateSnapshot object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateSnapshotWithDefaults() *CreateSnapshot { - this := CreateSnapshot{} - return &this -} - -// GetName returns the Name field value -func (o *CreateSnapshot) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *CreateSnapshot) SetName(v string) { - o.Name = v -} - -// GetImageName returns the ImageName field value if set, zero value otherwise. -func (o *CreateSnapshot) GetImageName() string { - if o == nil || IsNil(o.ImageName) { - var ret string - return ret - } - return *o.ImageName -} - -// GetImageNameOk returns a tuple with the ImageName field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetImageNameOk() (*string, bool) { - if o == nil || IsNil(o.ImageName) { - return nil, false - } - return o.ImageName, true -} - -// HasImageName returns a boolean if a field has been set. -func (o *CreateSnapshot) HasImageName() bool { - if o != nil && !IsNil(o.ImageName) { - return true - } - - return false -} - -// SetImageName gets a reference to the given string and assigns it to the ImageName field. -func (o *CreateSnapshot) SetImageName(v string) { - o.ImageName = &v -} - -// GetEntrypoint returns the Entrypoint field value if set, zero value otherwise. -func (o *CreateSnapshot) GetEntrypoint() []string { - if o == nil || IsNil(o.Entrypoint) { - var ret []string - return ret - } - return o.Entrypoint -} - -// GetEntrypointOk returns a tuple with the Entrypoint field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetEntrypointOk() ([]string, bool) { - if o == nil || IsNil(o.Entrypoint) { - return nil, false - } - return o.Entrypoint, true -} - -// HasEntrypoint returns a boolean if a field has been set. -func (o *CreateSnapshot) HasEntrypoint() bool { - if o != nil && !IsNil(o.Entrypoint) { - return true - } - - return false -} - -// SetEntrypoint gets a reference to the given []string and assigns it to the Entrypoint field. -func (o *CreateSnapshot) SetEntrypoint(v []string) { - o.Entrypoint = v -} - -// GetGeneral returns the General field value if set, zero value otherwise. -func (o *CreateSnapshot) GetGeneral() bool { - if o == nil || IsNil(o.General) { - var ret bool - return ret - } - return *o.General -} - -// GetGeneralOk returns a tuple with the General field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetGeneralOk() (*bool, bool) { - if o == nil || IsNil(o.General) { - return nil, false - } - return o.General, true -} - -// HasGeneral returns a boolean if a field has been set. -func (o *CreateSnapshot) HasGeneral() bool { - if o != nil && !IsNil(o.General) { - return true - } - - return false -} - -// SetGeneral gets a reference to the given bool and assigns it to the General field. -func (o *CreateSnapshot) SetGeneral(v bool) { - o.General = &v -} - -// GetCpu returns the Cpu field value if set, zero value otherwise. -func (o *CreateSnapshot) GetCpu() int32 { - if o == nil || IsNil(o.Cpu) { - var ret int32 - return ret - } - return *o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetCpuOk() (*int32, bool) { - if o == nil || IsNil(o.Cpu) { - return nil, false - } - return o.Cpu, true -} - -// HasCpu returns a boolean if a field has been set. -func (o *CreateSnapshot) HasCpu() bool { - if o != nil && !IsNil(o.Cpu) { - return true - } - - return false -} - -// SetCpu gets a reference to the given int32 and assigns it to the Cpu field. -func (o *CreateSnapshot) SetCpu(v int32) { - o.Cpu = &v -} - -// GetGpu returns the Gpu field value if set, zero value otherwise. -func (o *CreateSnapshot) GetGpu() int32 { - if o == nil || IsNil(o.Gpu) { - var ret int32 - return ret - } - return *o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetGpuOk() (*int32, bool) { - if o == nil || IsNil(o.Gpu) { - return nil, false - } - return o.Gpu, true -} - -// HasGpu returns a boolean if a field has been set. -func (o *CreateSnapshot) HasGpu() bool { - if o != nil && !IsNil(o.Gpu) { - return true - } - - return false -} - -// SetGpu gets a reference to the given int32 and assigns it to the Gpu field. -func (o *CreateSnapshot) SetGpu(v int32) { - o.Gpu = &v -} - -// GetMemory returns the Memory field value if set, zero value otherwise. -func (o *CreateSnapshot) GetMemory() int32 { - if o == nil || IsNil(o.Memory) { - var ret int32 - return ret - } - return *o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetMemoryOk() (*int32, bool) { - if o == nil || IsNil(o.Memory) { - return nil, false - } - return o.Memory, true -} - -// HasMemory returns a boolean if a field has been set. -func (o *CreateSnapshot) HasMemory() bool { - if o != nil && !IsNil(o.Memory) { - return true - } - - return false -} - -// SetMemory gets a reference to the given int32 and assigns it to the Memory field. -func (o *CreateSnapshot) SetMemory(v int32) { - o.Memory = &v -} - -// GetDisk returns the Disk field value if set, zero value otherwise. -func (o *CreateSnapshot) GetDisk() int32 { - if o == nil || IsNil(o.Disk) { - var ret int32 - return ret - } - return *o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetDiskOk() (*int32, bool) { - if o == nil || IsNil(o.Disk) { - return nil, false - } - return o.Disk, true -} - -// HasDisk returns a boolean if a field has been set. -func (o *CreateSnapshot) HasDisk() bool { - if o != nil && !IsNil(o.Disk) { - return true - } - - return false -} - -// SetDisk gets a reference to the given int32 and assigns it to the Disk field. -func (o *CreateSnapshot) SetDisk(v int32) { - o.Disk = &v -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *CreateSnapshot) GetBuildInfo() CreateBuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret CreateBuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetBuildInfoOk() (*CreateBuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *CreateSnapshot) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given CreateBuildInfo and assigns it to the BuildInfo field. -func (o *CreateSnapshot) SetBuildInfo(v CreateBuildInfo) { - o.BuildInfo = &v -} - -// GetRegionId returns the RegionId field value if set, zero value otherwise. -func (o *CreateSnapshot) GetRegionId() string { - if o == nil || IsNil(o.RegionId) { - var ret string - return ret - } - return *o.RegionId -} - -// GetRegionIdOk returns a tuple with the RegionId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateSnapshot) GetRegionIdOk() (*string, bool) { - if o == nil || IsNil(o.RegionId) { - return nil, false - } - return o.RegionId, true -} - -// HasRegionId returns a boolean if a field has been set. -func (o *CreateSnapshot) HasRegionId() bool { - if o != nil && !IsNil(o.RegionId) { - return true - } - - return false -} - -// SetRegionId gets a reference to the given string and assigns it to the RegionId field. -func (o *CreateSnapshot) SetRegionId(v string) { - o.RegionId = &v -} - -func (o CreateSnapshot) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateSnapshot) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["name"] = o.Name - if !IsNil(o.ImageName) { - toSerialize["imageName"] = o.ImageName - } - if !IsNil(o.Entrypoint) { - toSerialize["entrypoint"] = o.Entrypoint - } - if !IsNil(o.General) { - toSerialize["general"] = o.General - } - if !IsNil(o.Cpu) { - toSerialize["cpu"] = o.Cpu - } - if !IsNil(o.Gpu) { - toSerialize["gpu"] = o.Gpu - } - if !IsNil(o.Memory) { - toSerialize["memory"] = o.Memory - } - if !IsNil(o.Disk) { - toSerialize["disk"] = o.Disk - } - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - if !IsNil(o.RegionId) { - toSerialize["regionId"] = o.RegionId - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateSnapshot) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "name", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varCreateSnapshot := _CreateSnapshot{} - - err = json.Unmarshal(data, &varCreateSnapshot) - - if err != nil { - return err - } - - *o = CreateSnapshot(varCreateSnapshot) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "imageName") - delete(additionalProperties, "entrypoint") - delete(additionalProperties, "general") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - delete(additionalProperties, "buildInfo") - delete(additionalProperties, "regionId") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateSnapshot struct { - value *CreateSnapshot - isSet bool -} - -func (v NullableCreateSnapshot) Get() *CreateSnapshot { - return v.value -} - -func (v *NullableCreateSnapshot) Set(val *CreateSnapshot) { - v.value = val - v.isSet = true -} - -func (v NullableCreateSnapshot) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateSnapshot) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateSnapshot(val *CreateSnapshot) *NullableCreateSnapshot { - return &NullableCreateSnapshot{value: val, isSet: true} -} - -func (v NullableCreateSnapshot) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateSnapshot) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_create_user.go b/apps/api-client-go/model_create_user.go index dbca2c8b2..f481c030a 100644 --- a/apps/api-client-go/model_create_user.go +++ b/apps/api-client-go/model_create_user.go @@ -24,7 +24,9 @@ type CreateUser struct { Id string `json:"id"` Name string `json:"name"` Email *string `json:"email,omitempty"` - PersonalOrganizationQuota *CreateOrganizationQuota `json:"personalOrganizationQuota,omitempty"` + DefaultOrganizationDefaultRegionId *string `json:"defaultOrganizationDefaultRegionId,omitempty"` + // Deprecated alias for defaultOrganizationDefaultRegionId. + // Deprecated PersonalOrganizationDefaultRegionId *string `json:"personalOrganizationDefaultRegionId,omitempty"` Role *string `json:"role,omitempty"` EmailVerified *bool `json:"emailVerified,omitempty"` @@ -132,39 +134,40 @@ func (o *CreateUser) SetEmail(v string) { o.Email = &v } -// GetPersonalOrganizationQuota returns the PersonalOrganizationQuota field value if set, zero value otherwise. -func (o *CreateUser) GetPersonalOrganizationQuota() CreateOrganizationQuota { - if o == nil || IsNil(o.PersonalOrganizationQuota) { - var ret CreateOrganizationQuota +// GetDefaultOrganizationDefaultRegionId returns the DefaultOrganizationDefaultRegionId field value if set, zero value otherwise. +func (o *CreateUser) GetDefaultOrganizationDefaultRegionId() string { + if o == nil || IsNil(o.DefaultOrganizationDefaultRegionId) { + var ret string return ret } - return *o.PersonalOrganizationQuota + return *o.DefaultOrganizationDefaultRegionId } -// GetPersonalOrganizationQuotaOk returns a tuple with the PersonalOrganizationQuota field value if set, nil otherwise +// GetDefaultOrganizationDefaultRegionIdOk returns a tuple with the DefaultOrganizationDefaultRegionId field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *CreateUser) GetPersonalOrganizationQuotaOk() (*CreateOrganizationQuota, bool) { - if o == nil || IsNil(o.PersonalOrganizationQuota) { +func (o *CreateUser) GetDefaultOrganizationDefaultRegionIdOk() (*string, bool) { + if o == nil || IsNil(o.DefaultOrganizationDefaultRegionId) { return nil, false } - return o.PersonalOrganizationQuota, true + return o.DefaultOrganizationDefaultRegionId, true } -// HasPersonalOrganizationQuota returns a boolean if a field has been set. -func (o *CreateUser) HasPersonalOrganizationQuota() bool { - if o != nil && !IsNil(o.PersonalOrganizationQuota) { +// HasDefaultOrganizationDefaultRegionId returns a boolean if a field has been set. +func (o *CreateUser) HasDefaultOrganizationDefaultRegionId() bool { + if o != nil && !IsNil(o.DefaultOrganizationDefaultRegionId) { return true } return false } -// SetPersonalOrganizationQuota gets a reference to the given CreateOrganizationQuota and assigns it to the PersonalOrganizationQuota field. -func (o *CreateUser) SetPersonalOrganizationQuota(v CreateOrganizationQuota) { - o.PersonalOrganizationQuota = &v +// SetDefaultOrganizationDefaultRegionId gets a reference to the given string and assigns it to the DefaultOrganizationDefaultRegionId field. +func (o *CreateUser) SetDefaultOrganizationDefaultRegionId(v string) { + o.DefaultOrganizationDefaultRegionId = &v } // GetPersonalOrganizationDefaultRegionId returns the PersonalOrganizationDefaultRegionId field value if set, zero value otherwise. +// Deprecated func (o *CreateUser) GetPersonalOrganizationDefaultRegionId() string { if o == nil || IsNil(o.PersonalOrganizationDefaultRegionId) { var ret string @@ -175,6 +178,7 @@ func (o *CreateUser) GetPersonalOrganizationDefaultRegionId() string { // GetPersonalOrganizationDefaultRegionIdOk returns a tuple with the PersonalOrganizationDefaultRegionId field value if set, nil otherwise // and a boolean to check if the value has been set. +// Deprecated func (o *CreateUser) GetPersonalOrganizationDefaultRegionIdOk() (*string, bool) { if o == nil || IsNil(o.PersonalOrganizationDefaultRegionId) { return nil, false @@ -192,6 +196,7 @@ func (o *CreateUser) HasPersonalOrganizationDefaultRegionId() bool { } // SetPersonalOrganizationDefaultRegionId gets a reference to the given string and assigns it to the PersonalOrganizationDefaultRegionId field. +// Deprecated func (o *CreateUser) SetPersonalOrganizationDefaultRegionId(v string) { o.PersonalOrganizationDefaultRegionId = &v } @@ -275,8 +280,8 @@ func (o CreateUser) ToMap() (map[string]interface{}, error) { if !IsNil(o.Email) { toSerialize["email"] = o.Email } - if !IsNil(o.PersonalOrganizationQuota) { - toSerialize["personalOrganizationQuota"] = o.PersonalOrganizationQuota + if !IsNil(o.DefaultOrganizationDefaultRegionId) { + toSerialize["defaultOrganizationDefaultRegionId"] = o.DefaultOrganizationDefaultRegionId } if !IsNil(o.PersonalOrganizationDefaultRegionId) { toSerialize["personalOrganizationDefaultRegionId"] = o.PersonalOrganizationDefaultRegionId @@ -334,7 +339,7 @@ func (o *CreateUser) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "id") delete(additionalProperties, "name") delete(additionalProperties, "email") - delete(additionalProperties, "personalOrganizationQuota") + delete(additionalProperties, "defaultOrganizationDefaultRegionId") delete(additionalProperties, "personalOrganizationDefaultRegionId") delete(additionalProperties, "role") delete(additionalProperties, "emailVerified") @@ -379,3 +384,5 @@ func (v *NullableCreateUser) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_volume.go b/apps/api-client-go/model_create_volume.go index 26ca3f898..83276f0aa 100644 --- a/apps/api-client-go/model_create_volume.go +++ b/apps/api-client-go/model_create_volume.go @@ -165,3 +165,5 @@ func (v *NullableCreateVolume) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_create_workspace.go b/apps/api-client-go/model_create_workspace.go deleted file mode 100644 index b0d91d51e..000000000 --- a/apps/api-client-go/model_create_workspace.go +++ /dev/null @@ -1,687 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the CreateWorkspace type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &CreateWorkspace{} - -// CreateWorkspace struct for CreateWorkspace -type CreateWorkspace struct { - // The image used for the workspace - Image *string `json:"image,omitempty"` - // The user associated with the project - User *string `json:"user,omitempty"` - // Environment variables for the workspace - Env *map[string]string `json:"env,omitempty"` - // Labels for the workspace - Labels *map[string]string `json:"labels,omitempty"` - // Whether the workspace http preview is publicly accessible - Public *bool `json:"public,omitempty"` - // The workspace class type - Class *string `json:"class,omitempty"` - // The target (region) where the workspace will be created - Target *string `json:"target,omitempty"` - // CPU cores allocated to the workspace - Cpu *int32 `json:"cpu,omitempty"` - // GPU units allocated to the workspace - Gpu *int32 `json:"gpu,omitempty"` - // Memory allocated to the workspace in GB - Memory *int32 `json:"memory,omitempty"` - // Disk space allocated to the workspace in GB - Disk *int32 `json:"disk,omitempty"` - // Auto-stop interval in minutes (0 means disabled) - AutoStopInterval *int32 `json:"autoStopInterval,omitempty"` - // Auto-archive interval in minutes (0 means the maximum interval will be used) - AutoArchiveInterval *int32 `json:"autoArchiveInterval,omitempty"` - // Array of volumes to attach to the workspace - Volumes []SandboxVolume `json:"volumes,omitempty"` - // Build information for the workspace - BuildInfo *CreateBuildInfo `json:"buildInfo,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _CreateWorkspace CreateWorkspace - -// NewCreateWorkspace instantiates a new CreateWorkspace object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewCreateWorkspace() *CreateWorkspace { - this := CreateWorkspace{} - return &this -} - -// NewCreateWorkspaceWithDefaults instantiates a new CreateWorkspace object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewCreateWorkspaceWithDefaults() *CreateWorkspace { - this := CreateWorkspace{} - return &this -} - -// GetImage returns the Image field value if set, zero value otherwise. -func (o *CreateWorkspace) GetImage() string { - if o == nil || IsNil(o.Image) { - var ret string - return ret - } - return *o.Image -} - -// GetImageOk returns a tuple with the Image field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetImageOk() (*string, bool) { - if o == nil || IsNil(o.Image) { - return nil, false - } - return o.Image, true -} - -// HasImage returns a boolean if a field has been set. -func (o *CreateWorkspace) HasImage() bool { - if o != nil && !IsNil(o.Image) { - return true - } - - return false -} - -// SetImage gets a reference to the given string and assigns it to the Image field. -func (o *CreateWorkspace) SetImage(v string) { - o.Image = &v -} - -// GetUser returns the User field value if set, zero value otherwise. -func (o *CreateWorkspace) GetUser() string { - if o == nil || IsNil(o.User) { - var ret string - return ret - } - return *o.User -} - -// GetUserOk returns a tuple with the User field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetUserOk() (*string, bool) { - if o == nil || IsNil(o.User) { - return nil, false - } - return o.User, true -} - -// HasUser returns a boolean if a field has been set. -func (o *CreateWorkspace) HasUser() bool { - if o != nil && !IsNil(o.User) { - return true - } - - return false -} - -// SetUser gets a reference to the given string and assigns it to the User field. -func (o *CreateWorkspace) SetUser(v string) { - o.User = &v -} - -// GetEnv returns the Env field value if set, zero value otherwise. -func (o *CreateWorkspace) GetEnv() map[string]string { - if o == nil || IsNil(o.Env) { - var ret map[string]string - return ret - } - return *o.Env -} - -// GetEnvOk returns a tuple with the Env field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetEnvOk() (*map[string]string, bool) { - if o == nil || IsNil(o.Env) { - return nil, false - } - return o.Env, true -} - -// HasEnv returns a boolean if a field has been set. -func (o *CreateWorkspace) HasEnv() bool { - if o != nil && !IsNil(o.Env) { - return true - } - - return false -} - -// SetEnv gets a reference to the given map[string]string and assigns it to the Env field. -func (o *CreateWorkspace) SetEnv(v map[string]string) { - o.Env = &v -} - -// GetLabels returns the Labels field value if set, zero value otherwise. -func (o *CreateWorkspace) GetLabels() map[string]string { - if o == nil || IsNil(o.Labels) { - var ret map[string]string - return ret - } - return *o.Labels -} - -// GetLabelsOk returns a tuple with the Labels field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetLabelsOk() (*map[string]string, bool) { - if o == nil || IsNil(o.Labels) { - return nil, false - } - return o.Labels, true -} - -// HasLabels returns a boolean if a field has been set. -func (o *CreateWorkspace) HasLabels() bool { - if o != nil && !IsNil(o.Labels) { - return true - } - - return false -} - -// SetLabels gets a reference to the given map[string]string and assigns it to the Labels field. -func (o *CreateWorkspace) SetLabels(v map[string]string) { - o.Labels = &v -} - -// GetPublic returns the Public field value if set, zero value otherwise. -func (o *CreateWorkspace) GetPublic() bool { - if o == nil || IsNil(o.Public) { - var ret bool - return ret - } - return *o.Public -} - -// GetPublicOk returns a tuple with the Public field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetPublicOk() (*bool, bool) { - if o == nil || IsNil(o.Public) { - return nil, false - } - return o.Public, true -} - -// HasPublic returns a boolean if a field has been set. -func (o *CreateWorkspace) HasPublic() bool { - if o != nil && !IsNil(o.Public) { - return true - } - - return false -} - -// SetPublic gets a reference to the given bool and assigns it to the Public field. -func (o *CreateWorkspace) SetPublic(v bool) { - o.Public = &v -} - -// GetClass returns the Class field value if set, zero value otherwise. -func (o *CreateWorkspace) GetClass() string { - if o == nil || IsNil(o.Class) { - var ret string - return ret - } - return *o.Class -} - -// GetClassOk returns a tuple with the Class field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetClassOk() (*string, bool) { - if o == nil || IsNil(o.Class) { - return nil, false - } - return o.Class, true -} - -// HasClass returns a boolean if a field has been set. -func (o *CreateWorkspace) HasClass() bool { - if o != nil && !IsNil(o.Class) { - return true - } - - return false -} - -// SetClass gets a reference to the given string and assigns it to the Class field. -func (o *CreateWorkspace) SetClass(v string) { - o.Class = &v -} - -// GetTarget returns the Target field value if set, zero value otherwise. -func (o *CreateWorkspace) GetTarget() string { - if o == nil || IsNil(o.Target) { - var ret string - return ret - } - return *o.Target -} - -// GetTargetOk returns a tuple with the Target field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetTargetOk() (*string, bool) { - if o == nil || IsNil(o.Target) { - return nil, false - } - return o.Target, true -} - -// HasTarget returns a boolean if a field has been set. -func (o *CreateWorkspace) HasTarget() bool { - if o != nil && !IsNil(o.Target) { - return true - } - - return false -} - -// SetTarget gets a reference to the given string and assigns it to the Target field. -func (o *CreateWorkspace) SetTarget(v string) { - o.Target = &v -} - -// GetCpu returns the Cpu field value if set, zero value otherwise. -func (o *CreateWorkspace) GetCpu() int32 { - if o == nil || IsNil(o.Cpu) { - var ret int32 - return ret - } - return *o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetCpuOk() (*int32, bool) { - if o == nil || IsNil(o.Cpu) { - return nil, false - } - return o.Cpu, true -} - -// HasCpu returns a boolean if a field has been set. -func (o *CreateWorkspace) HasCpu() bool { - if o != nil && !IsNil(o.Cpu) { - return true - } - - return false -} - -// SetCpu gets a reference to the given int32 and assigns it to the Cpu field. -func (o *CreateWorkspace) SetCpu(v int32) { - o.Cpu = &v -} - -// GetGpu returns the Gpu field value if set, zero value otherwise. -func (o *CreateWorkspace) GetGpu() int32 { - if o == nil || IsNil(o.Gpu) { - var ret int32 - return ret - } - return *o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetGpuOk() (*int32, bool) { - if o == nil || IsNil(o.Gpu) { - return nil, false - } - return o.Gpu, true -} - -// HasGpu returns a boolean if a field has been set. -func (o *CreateWorkspace) HasGpu() bool { - if o != nil && !IsNil(o.Gpu) { - return true - } - - return false -} - -// SetGpu gets a reference to the given int32 and assigns it to the Gpu field. -func (o *CreateWorkspace) SetGpu(v int32) { - o.Gpu = &v -} - -// GetMemory returns the Memory field value if set, zero value otherwise. -func (o *CreateWorkspace) GetMemory() int32 { - if o == nil || IsNil(o.Memory) { - var ret int32 - return ret - } - return *o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetMemoryOk() (*int32, bool) { - if o == nil || IsNil(o.Memory) { - return nil, false - } - return o.Memory, true -} - -// HasMemory returns a boolean if a field has been set. -func (o *CreateWorkspace) HasMemory() bool { - if o != nil && !IsNil(o.Memory) { - return true - } - - return false -} - -// SetMemory gets a reference to the given int32 and assigns it to the Memory field. -func (o *CreateWorkspace) SetMemory(v int32) { - o.Memory = &v -} - -// GetDisk returns the Disk field value if set, zero value otherwise. -func (o *CreateWorkspace) GetDisk() int32 { - if o == nil || IsNil(o.Disk) { - var ret int32 - return ret - } - return *o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetDiskOk() (*int32, bool) { - if o == nil || IsNil(o.Disk) { - return nil, false - } - return o.Disk, true -} - -// HasDisk returns a boolean if a field has been set. -func (o *CreateWorkspace) HasDisk() bool { - if o != nil && !IsNil(o.Disk) { - return true - } - - return false -} - -// SetDisk gets a reference to the given int32 and assigns it to the Disk field. -func (o *CreateWorkspace) SetDisk(v int32) { - o.Disk = &v -} - -// GetAutoStopInterval returns the AutoStopInterval field value if set, zero value otherwise. -func (o *CreateWorkspace) GetAutoStopInterval() int32 { - if o == nil || IsNil(o.AutoStopInterval) { - var ret int32 - return ret - } - return *o.AutoStopInterval -} - -// GetAutoStopIntervalOk returns a tuple with the AutoStopInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetAutoStopIntervalOk() (*int32, bool) { - if o == nil || IsNil(o.AutoStopInterval) { - return nil, false - } - return o.AutoStopInterval, true -} - -// HasAutoStopInterval returns a boolean if a field has been set. -func (o *CreateWorkspace) HasAutoStopInterval() bool { - if o != nil && !IsNil(o.AutoStopInterval) { - return true - } - - return false -} - -// SetAutoStopInterval gets a reference to the given int32 and assigns it to the AutoStopInterval field. -func (o *CreateWorkspace) SetAutoStopInterval(v int32) { - o.AutoStopInterval = &v -} - -// GetAutoArchiveInterval returns the AutoArchiveInterval field value if set, zero value otherwise. -func (o *CreateWorkspace) GetAutoArchiveInterval() int32 { - if o == nil || IsNil(o.AutoArchiveInterval) { - var ret int32 - return ret - } - return *o.AutoArchiveInterval -} - -// GetAutoArchiveIntervalOk returns a tuple with the AutoArchiveInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetAutoArchiveIntervalOk() (*int32, bool) { - if o == nil || IsNil(o.AutoArchiveInterval) { - return nil, false - } - return o.AutoArchiveInterval, true -} - -// HasAutoArchiveInterval returns a boolean if a field has been set. -func (o *CreateWorkspace) HasAutoArchiveInterval() bool { - if o != nil && !IsNil(o.AutoArchiveInterval) { - return true - } - - return false -} - -// SetAutoArchiveInterval gets a reference to the given int32 and assigns it to the AutoArchiveInterval field. -func (o *CreateWorkspace) SetAutoArchiveInterval(v int32) { - o.AutoArchiveInterval = &v -} - -// GetVolumes returns the Volumes field value if set, zero value otherwise. -func (o *CreateWorkspace) GetVolumes() []SandboxVolume { - if o == nil || IsNil(o.Volumes) { - var ret []SandboxVolume - return ret - } - return o.Volumes -} - -// GetVolumesOk returns a tuple with the Volumes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetVolumesOk() ([]SandboxVolume, bool) { - if o == nil || IsNil(o.Volumes) { - return nil, false - } - return o.Volumes, true -} - -// HasVolumes returns a boolean if a field has been set. -func (o *CreateWorkspace) HasVolumes() bool { - if o != nil && !IsNil(o.Volumes) { - return true - } - - return false -} - -// SetVolumes gets a reference to the given []SandboxVolume and assigns it to the Volumes field. -func (o *CreateWorkspace) SetVolumes(v []SandboxVolume) { - o.Volumes = v -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *CreateWorkspace) GetBuildInfo() CreateBuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret CreateBuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *CreateWorkspace) GetBuildInfoOk() (*CreateBuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *CreateWorkspace) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given CreateBuildInfo and assigns it to the BuildInfo field. -func (o *CreateWorkspace) SetBuildInfo(v CreateBuildInfo) { - o.BuildInfo = &v -} - -func (o CreateWorkspace) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o CreateWorkspace) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Image) { - toSerialize["image"] = o.Image - } - if !IsNil(o.User) { - toSerialize["user"] = o.User - } - if !IsNil(o.Env) { - toSerialize["env"] = o.Env - } - if !IsNil(o.Labels) { - toSerialize["labels"] = o.Labels - } - if !IsNil(o.Public) { - toSerialize["public"] = o.Public - } - if !IsNil(o.Class) { - toSerialize["class"] = o.Class - } - if !IsNil(o.Target) { - toSerialize["target"] = o.Target - } - if !IsNil(o.Cpu) { - toSerialize["cpu"] = o.Cpu - } - if !IsNil(o.Gpu) { - toSerialize["gpu"] = o.Gpu - } - if !IsNil(o.Memory) { - toSerialize["memory"] = o.Memory - } - if !IsNil(o.Disk) { - toSerialize["disk"] = o.Disk - } - if !IsNil(o.AutoStopInterval) { - toSerialize["autoStopInterval"] = o.AutoStopInterval - } - if !IsNil(o.AutoArchiveInterval) { - toSerialize["autoArchiveInterval"] = o.AutoArchiveInterval - } - if !IsNil(o.Volumes) { - toSerialize["volumes"] = o.Volumes - } - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *CreateWorkspace) UnmarshalJSON(data []byte) (err error) { - varCreateWorkspace := _CreateWorkspace{} - - err = json.Unmarshal(data, &varCreateWorkspace) - - if err != nil { - return err - } - - *o = CreateWorkspace(varCreateWorkspace) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "image") - delete(additionalProperties, "user") - delete(additionalProperties, "env") - delete(additionalProperties, "labels") - delete(additionalProperties, "public") - delete(additionalProperties, "class") - delete(additionalProperties, "target") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - delete(additionalProperties, "autoStopInterval") - delete(additionalProperties, "autoArchiveInterval") - delete(additionalProperties, "volumes") - delete(additionalProperties, "buildInfo") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableCreateWorkspace struct { - value *CreateWorkspace - isSet bool -} - -func (v NullableCreateWorkspace) Get() *CreateWorkspace { - return v.value -} - -func (v *NullableCreateWorkspace) Set(val *CreateWorkspace) { - v.value = val - v.isSet = true -} - -func (v NullableCreateWorkspace) IsSet() bool { - return v.isSet -} - -func (v *NullableCreateWorkspace) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableCreateWorkspace(val *CreateWorkspace) *NullableCreateWorkspace { - return &NullableCreateWorkspace{value: val, isSet: true} -} - -func (v NullableCreateWorkspace) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableCreateWorkspace) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_display_info_response.go b/apps/api-client-go/model_display_info_response.go deleted file mode 100644 index de8292276..000000000 --- a/apps/api-client-go/model_display_info_response.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the DisplayInfoResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &DisplayInfoResponse{} - -// DisplayInfoResponse struct for DisplayInfoResponse -type DisplayInfoResponse struct { - // Array of display information for all connected displays - Displays []map[string]interface{} `json:"displays"` - AdditionalProperties map[string]interface{} -} - -type _DisplayInfoResponse DisplayInfoResponse - -// NewDisplayInfoResponse instantiates a new DisplayInfoResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewDisplayInfoResponse(displays []map[string]interface{}) *DisplayInfoResponse { - this := DisplayInfoResponse{} - this.Displays = displays - return &this -} - -// NewDisplayInfoResponseWithDefaults instantiates a new DisplayInfoResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewDisplayInfoResponseWithDefaults() *DisplayInfoResponse { - this := DisplayInfoResponse{} - return &this -} - -// GetDisplays returns the Displays field value -func (o *DisplayInfoResponse) GetDisplays() []map[string]interface{} { - if o == nil { - var ret []map[string]interface{} - return ret - } - - return o.Displays -} - -// GetDisplaysOk returns a tuple with the Displays field value -// and a boolean to check if the value has been set. -func (o *DisplayInfoResponse) GetDisplaysOk() ([]map[string]interface{}, bool) { - if o == nil { - return nil, false - } - return o.Displays, true -} - -// SetDisplays sets field value -func (o *DisplayInfoResponse) SetDisplays(v []map[string]interface{}) { - o.Displays = v -} - -func (o DisplayInfoResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o DisplayInfoResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["displays"] = o.Displays - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *DisplayInfoResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "displays", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varDisplayInfoResponse := _DisplayInfoResponse{} - - err = json.Unmarshal(data, &varDisplayInfoResponse) - - if err != nil { - return err - } - - *o = DisplayInfoResponse(varDisplayInfoResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "displays") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableDisplayInfoResponse struct { - value *DisplayInfoResponse - isSet bool -} - -func (v NullableDisplayInfoResponse) Get() *DisplayInfoResponse { - return v.value -} - -func (v *NullableDisplayInfoResponse) Set(val *DisplayInfoResponse) { - v.value = val - v.isSet = true -} - -func (v NullableDisplayInfoResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableDisplayInfoResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableDisplayInfoResponse(val *DisplayInfoResponse) *NullableDisplayInfoResponse { - return &NullableDisplayInfoResponse{value: val, isSet: true} -} - -func (v NullableDisplayInfoResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableDisplayInfoResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_docker_registry.go b/apps/api-client-go/model_docker_registry.go deleted file mode 100644 index 67e8a4751..000000000 --- a/apps/api-client-go/model_docker_registry.go +++ /dev/null @@ -1,379 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "time" - "fmt" -) - -// checks if the DockerRegistry type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &DockerRegistry{} - -// DockerRegistry struct for DockerRegistry -type DockerRegistry struct { - // Registry ID - Id string `json:"id"` - // Registry name - Name string `json:"name"` - // Registry URL - Url string `json:"url"` - // Registry username - Username string `json:"username"` - // Registry project - Project string `json:"project"` - // Registry type - RegistryType string `json:"registryType"` - // Creation timestamp - CreatedAt time.Time `json:"createdAt"` - // Last update timestamp - UpdatedAt time.Time `json:"updatedAt"` - AdditionalProperties map[string]interface{} -} - -type _DockerRegistry DockerRegistry - -// NewDockerRegistry instantiates a new DockerRegistry object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewDockerRegistry(id string, name string, url string, username string, project string, registryType string, createdAt time.Time, updatedAt time.Time) *DockerRegistry { - this := DockerRegistry{} - this.Id = id - this.Name = name - this.Url = url - this.Username = username - this.Project = project - this.RegistryType = registryType - this.CreatedAt = createdAt - this.UpdatedAt = updatedAt - return &this -} - -// NewDockerRegistryWithDefaults instantiates a new DockerRegistry object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewDockerRegistryWithDefaults() *DockerRegistry { - this := DockerRegistry{} - return &this -} - -// GetId returns the Id field value -func (o *DockerRegistry) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *DockerRegistry) SetId(v string) { - o.Id = v -} - -// GetName returns the Name field value -func (o *DockerRegistry) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *DockerRegistry) SetName(v string) { - o.Name = v -} - -// GetUrl returns the Url field value -func (o *DockerRegistry) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *DockerRegistry) SetUrl(v string) { - o.Url = v -} - -// GetUsername returns the Username field value -func (o *DockerRegistry) GetUsername() string { - if o == nil { - var ret string - return ret - } - - return o.Username -} - -// GetUsernameOk returns a tuple with the Username field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Username, true -} - -// SetUsername sets field value -func (o *DockerRegistry) SetUsername(v string) { - o.Username = v -} - -// GetProject returns the Project field value -func (o *DockerRegistry) GetProject() string { - if o == nil { - var ret string - return ret - } - - return o.Project -} - -// GetProjectOk returns a tuple with the Project field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetProjectOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Project, true -} - -// SetProject sets field value -func (o *DockerRegistry) SetProject(v string) { - o.Project = v -} - -// GetRegistryType returns the RegistryType field value -func (o *DockerRegistry) GetRegistryType() string { - if o == nil { - var ret string - return ret - } - - return o.RegistryType -} - -// GetRegistryTypeOk returns a tuple with the RegistryType field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetRegistryTypeOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegistryType, true -} - -// SetRegistryType sets field value -func (o *DockerRegistry) SetRegistryType(v string) { - o.RegistryType = v -} - -// GetCreatedAt returns the CreatedAt field value -func (o *DockerRegistry) GetCreatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetCreatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.CreatedAt, true -} - -// SetCreatedAt sets field value -func (o *DockerRegistry) SetCreatedAt(v time.Time) { - o.CreatedAt = v -} - -// GetUpdatedAt returns the UpdatedAt field value -func (o *DockerRegistry) GetUpdatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value -// and a boolean to check if the value has been set. -func (o *DockerRegistry) GetUpdatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.UpdatedAt, true -} - -// SetUpdatedAt sets field value -func (o *DockerRegistry) SetUpdatedAt(v time.Time) { - o.UpdatedAt = v -} - -func (o DockerRegistry) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o DockerRegistry) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - toSerialize["name"] = o.Name - toSerialize["url"] = o.Url - toSerialize["username"] = o.Username - toSerialize["project"] = o.Project - toSerialize["registryType"] = o.RegistryType - toSerialize["createdAt"] = o.CreatedAt - toSerialize["updatedAt"] = o.UpdatedAt - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *DockerRegistry) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "name", - "url", - "username", - "project", - "registryType", - "createdAt", - "updatedAt", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varDockerRegistry := _DockerRegistry{} - - err = json.Unmarshal(data, &varDockerRegistry) - - if err != nil { - return err - } - - *o = DockerRegistry(varDockerRegistry) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "name") - delete(additionalProperties, "url") - delete(additionalProperties, "username") - delete(additionalProperties, "project") - delete(additionalProperties, "registryType") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "updatedAt") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableDockerRegistry struct { - value *DockerRegistry - isSet bool -} - -func (v NullableDockerRegistry) Get() *DockerRegistry { - return v.value -} - -func (v *NullableDockerRegistry) Set(val *DockerRegistry) { - v.value = val - v.isSet = true -} - -func (v NullableDockerRegistry) IsSet() bool { - return v.isSet -} - -func (v *NullableDockerRegistry) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableDockerRegistry(val *DockerRegistry) *NullableDockerRegistry { - return &NullableDockerRegistry{value: val, isSet: true} -} - -func (v NullableDockerRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableDockerRegistry) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_download_files.go b/apps/api-client-go/model_download_files.go deleted file mode 100644 index 83e2fb200..000000000 --- a/apps/api-client-go/model_download_files.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the DownloadFiles type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &DownloadFiles{} - -// DownloadFiles struct for DownloadFiles -type DownloadFiles struct { - // List of remote file paths to download - Paths []string `json:"paths"` - AdditionalProperties map[string]interface{} -} - -type _DownloadFiles DownloadFiles - -// NewDownloadFiles instantiates a new DownloadFiles object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewDownloadFiles(paths []string) *DownloadFiles { - this := DownloadFiles{} - this.Paths = paths - return &this -} - -// NewDownloadFilesWithDefaults instantiates a new DownloadFiles object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewDownloadFilesWithDefaults() *DownloadFiles { - this := DownloadFiles{} - return &this -} - -// GetPaths returns the Paths field value -func (o *DownloadFiles) GetPaths() []string { - if o == nil { - var ret []string - return ret - } - - return o.Paths -} - -// GetPathsOk returns a tuple with the Paths field value -// and a boolean to check if the value has been set. -func (o *DownloadFiles) GetPathsOk() ([]string, bool) { - if o == nil { - return nil, false - } - return o.Paths, true -} - -// SetPaths sets field value -func (o *DownloadFiles) SetPaths(v []string) { - o.Paths = v -} - -func (o DownloadFiles) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o DownloadFiles) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["paths"] = o.Paths - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *DownloadFiles) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "paths", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varDownloadFiles := _DownloadFiles{} - - err = json.Unmarshal(data, &varDownloadFiles) - - if err != nil { - return err - } - - *o = DownloadFiles(varDownloadFiles) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "paths") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableDownloadFiles struct { - value *DownloadFiles - isSet bool -} - -func (v NullableDownloadFiles) Get() *DownloadFiles { - return v.value -} - -func (v *NullableDownloadFiles) Set(val *DownloadFiles) { - v.value = val - v.isSet = true -} - -func (v NullableDownloadFiles) IsSet() bool { - return v.isSet -} - -func (v *NullableDownloadFiles) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableDownloadFiles(val *DownloadFiles) *NullableDownloadFiles { - return &NullableDownloadFiles{value: val, isSet: true} -} - -func (v NullableDownloadFiles) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableDownloadFiles) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_execute_request.go b/apps/api-client-go/model_execute_request.go deleted file mode 100644 index e24df6c6a..000000000 --- a/apps/api-client-go/model_execute_request.go +++ /dev/null @@ -1,243 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ExecuteRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ExecuteRequest{} - -// ExecuteRequest struct for ExecuteRequest -type ExecuteRequest struct { - Command string `json:"command"` - // Current working directory - Cwd *string `json:"cwd,omitempty"` - // Timeout in seconds, defaults to 10 seconds - Timeout *float32 `json:"timeout,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _ExecuteRequest ExecuteRequest - -// NewExecuteRequest instantiates a new ExecuteRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewExecuteRequest(command string) *ExecuteRequest { - this := ExecuteRequest{} - this.Command = command - return &this -} - -// NewExecuteRequestWithDefaults instantiates a new ExecuteRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewExecuteRequestWithDefaults() *ExecuteRequest { - this := ExecuteRequest{} - return &this -} - -// GetCommand returns the Command field value -func (o *ExecuteRequest) GetCommand() string { - if o == nil { - var ret string - return ret - } - - return o.Command -} - -// GetCommandOk returns a tuple with the Command field value -// and a boolean to check if the value has been set. -func (o *ExecuteRequest) GetCommandOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Command, true -} - -// SetCommand sets field value -func (o *ExecuteRequest) SetCommand(v string) { - o.Command = v -} - -// GetCwd returns the Cwd field value if set, zero value otherwise. -func (o *ExecuteRequest) GetCwd() string { - if o == nil || IsNil(o.Cwd) { - var ret string - return ret - } - return *o.Cwd -} - -// GetCwdOk returns a tuple with the Cwd field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ExecuteRequest) GetCwdOk() (*string, bool) { - if o == nil || IsNil(o.Cwd) { - return nil, false - } - return o.Cwd, true -} - -// HasCwd returns a boolean if a field has been set. -func (o *ExecuteRequest) HasCwd() bool { - if o != nil && !IsNil(o.Cwd) { - return true - } - - return false -} - -// SetCwd gets a reference to the given string and assigns it to the Cwd field. -func (o *ExecuteRequest) SetCwd(v string) { - o.Cwd = &v -} - -// GetTimeout returns the Timeout field value if set, zero value otherwise. -func (o *ExecuteRequest) GetTimeout() float32 { - if o == nil || IsNil(o.Timeout) { - var ret float32 - return ret - } - return *o.Timeout -} - -// GetTimeoutOk returns a tuple with the Timeout field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ExecuteRequest) GetTimeoutOk() (*float32, bool) { - if o == nil || IsNil(o.Timeout) { - return nil, false - } - return o.Timeout, true -} - -// HasTimeout returns a boolean if a field has been set. -func (o *ExecuteRequest) HasTimeout() bool { - if o != nil && !IsNil(o.Timeout) { - return true - } - - return false -} - -// SetTimeout gets a reference to the given float32 and assigns it to the Timeout field. -func (o *ExecuteRequest) SetTimeout(v float32) { - o.Timeout = &v -} - -func (o ExecuteRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ExecuteRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["command"] = o.Command - if !IsNil(o.Cwd) { - toSerialize["cwd"] = o.Cwd - } - if !IsNil(o.Timeout) { - toSerialize["timeout"] = o.Timeout - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ExecuteRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "command", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varExecuteRequest := _ExecuteRequest{} - - err = json.Unmarshal(data, &varExecuteRequest) - - if err != nil { - return err - } - - *o = ExecuteRequest(varExecuteRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "command") - delete(additionalProperties, "cwd") - delete(additionalProperties, "timeout") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableExecuteRequest struct { - value *ExecuteRequest - isSet bool -} - -func (v NullableExecuteRequest) Get() *ExecuteRequest { - return v.value -} - -func (v *NullableExecuteRequest) Set(val *ExecuteRequest) { - v.value = val - v.isSet = true -} - -func (v NullableExecuteRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableExecuteRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableExecuteRequest(val *ExecuteRequest) *NullableExecuteRequest { - return &NullableExecuteRequest{value: val, isSet: true} -} - -func (v NullableExecuteRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableExecuteRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_execute_response.go b/apps/api-client-go/model_execute_response.go deleted file mode 100644 index c8788f8fb..000000000 --- a/apps/api-client-go/model_execute_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ExecuteResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ExecuteResponse{} - -// ExecuteResponse struct for ExecuteResponse -type ExecuteResponse struct { - // Exit code - ExitCode float32 `json:"exitCode"` - // Command output - Result string `json:"result"` - AdditionalProperties map[string]interface{} -} - -type _ExecuteResponse ExecuteResponse - -// NewExecuteResponse instantiates a new ExecuteResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewExecuteResponse(exitCode float32, result string) *ExecuteResponse { - this := ExecuteResponse{} - this.ExitCode = exitCode - this.Result = result - return &this -} - -// NewExecuteResponseWithDefaults instantiates a new ExecuteResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewExecuteResponseWithDefaults() *ExecuteResponse { - this := ExecuteResponse{} - return &this -} - -// GetExitCode returns the ExitCode field value -func (o *ExecuteResponse) GetExitCode() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.ExitCode -} - -// GetExitCodeOk returns a tuple with the ExitCode field value -// and a boolean to check if the value has been set. -func (o *ExecuteResponse) GetExitCodeOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.ExitCode, true -} - -// SetExitCode sets field value -func (o *ExecuteResponse) SetExitCode(v float32) { - o.ExitCode = v -} - -// GetResult returns the Result field value -func (o *ExecuteResponse) GetResult() string { - if o == nil { - var ret string - return ret - } - - return o.Result -} - -// GetResultOk returns a tuple with the Result field value -// and a boolean to check if the value has been set. -func (o *ExecuteResponse) GetResultOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Result, true -} - -// SetResult sets field value -func (o *ExecuteResponse) SetResult(v string) { - o.Result = v -} - -func (o ExecuteResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ExecuteResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["exitCode"] = o.ExitCode - toSerialize["result"] = o.Result - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ExecuteResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "exitCode", - "result", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varExecuteResponse := _ExecuteResponse{} - - err = json.Unmarshal(data, &varExecuteResponse) - - if err != nil { - return err - } - - *o = ExecuteResponse(varExecuteResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "exitCode") - delete(additionalProperties, "result") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableExecuteResponse struct { - value *ExecuteResponse - isSet bool -} - -func (v NullableExecuteResponse) Get() *ExecuteResponse { - return v.value -} - -func (v *NullableExecuteResponse) Set(val *ExecuteResponse) { - v.value = val - v.isSet = true -} - -func (v NullableExecuteResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableExecuteResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableExecuteResponse(val *ExecuteResponse) *NullableExecuteResponse { - return &NullableExecuteResponse{value: val, isSet: true} -} - -func (v NullableExecuteResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableExecuteResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_file_info.go b/apps/api-client-go/model_file_info.go deleted file mode 100644 index eff3b3692..000000000 --- a/apps/api-client-go/model_file_info.go +++ /dev/null @@ -1,370 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the FileInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &FileInfo{} - -// FileInfo struct for FileInfo -type FileInfo struct { - Name string `json:"name"` - IsDir bool `json:"isDir"` - Size float32 `json:"size"` - ModTime string `json:"modTime"` - Mode string `json:"mode"` - Permissions string `json:"permissions"` - Owner string `json:"owner"` - Group string `json:"group"` - AdditionalProperties map[string]interface{} -} - -type _FileInfo FileInfo - -// NewFileInfo instantiates a new FileInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewFileInfo(name string, isDir bool, size float32, modTime string, mode string, permissions string, owner string, group string) *FileInfo { - this := FileInfo{} - this.Name = name - this.IsDir = isDir - this.Size = size - this.ModTime = modTime - this.Mode = mode - this.Permissions = permissions - this.Owner = owner - this.Group = group - return &this -} - -// NewFileInfoWithDefaults instantiates a new FileInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewFileInfoWithDefaults() *FileInfo { - this := FileInfo{} - return &this -} - -// GetName returns the Name field value -func (o *FileInfo) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *FileInfo) SetName(v string) { - o.Name = v -} - -// GetIsDir returns the IsDir field value -func (o *FileInfo) GetIsDir() bool { - if o == nil { - var ret bool - return ret - } - - return o.IsDir -} - -// GetIsDirOk returns a tuple with the IsDir field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetIsDirOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.IsDir, true -} - -// SetIsDir sets field value -func (o *FileInfo) SetIsDir(v bool) { - o.IsDir = v -} - -// GetSize returns the Size field value -func (o *FileInfo) GetSize() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Size -} - -// GetSizeOk returns a tuple with the Size field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetSizeOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Size, true -} - -// SetSize sets field value -func (o *FileInfo) SetSize(v float32) { - o.Size = v -} - -// GetModTime returns the ModTime field value -func (o *FileInfo) GetModTime() string { - if o == nil { - var ret string - return ret - } - - return o.ModTime -} - -// GetModTimeOk returns a tuple with the ModTime field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetModTimeOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ModTime, true -} - -// SetModTime sets field value -func (o *FileInfo) SetModTime(v string) { - o.ModTime = v -} - -// GetMode returns the Mode field value -func (o *FileInfo) GetMode() string { - if o == nil { - var ret string - return ret - } - - return o.Mode -} - -// GetModeOk returns a tuple with the Mode field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetModeOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Mode, true -} - -// SetMode sets field value -func (o *FileInfo) SetMode(v string) { - o.Mode = v -} - -// GetPermissions returns the Permissions field value -func (o *FileInfo) GetPermissions() string { - if o == nil { - var ret string - return ret - } - - return o.Permissions -} - -// GetPermissionsOk returns a tuple with the Permissions field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetPermissionsOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Permissions, true -} - -// SetPermissions sets field value -func (o *FileInfo) SetPermissions(v string) { - o.Permissions = v -} - -// GetOwner returns the Owner field value -func (o *FileInfo) GetOwner() string { - if o == nil { - var ret string - return ret - } - - return o.Owner -} - -// GetOwnerOk returns a tuple with the Owner field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetOwnerOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Owner, true -} - -// SetOwner sets field value -func (o *FileInfo) SetOwner(v string) { - o.Owner = v -} - -// GetGroup returns the Group field value -func (o *FileInfo) GetGroup() string { - if o == nil { - var ret string - return ret - } - - return o.Group -} - -// GetGroupOk returns a tuple with the Group field value -// and a boolean to check if the value has been set. -func (o *FileInfo) GetGroupOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Group, true -} - -// SetGroup sets field value -func (o *FileInfo) SetGroup(v string) { - o.Group = v -} - -func (o FileInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o FileInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["name"] = o.Name - toSerialize["isDir"] = o.IsDir - toSerialize["size"] = o.Size - toSerialize["modTime"] = o.ModTime - toSerialize["mode"] = o.Mode - toSerialize["permissions"] = o.Permissions - toSerialize["owner"] = o.Owner - toSerialize["group"] = o.Group - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *FileInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "name", - "isDir", - "size", - "modTime", - "mode", - "permissions", - "owner", - "group", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varFileInfo := _FileInfo{} - - err = json.Unmarshal(data, &varFileInfo) - - if err != nil { - return err - } - - *o = FileInfo(varFileInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "isDir") - delete(additionalProperties, "size") - delete(additionalProperties, "modTime") - delete(additionalProperties, "mode") - delete(additionalProperties, "permissions") - delete(additionalProperties, "owner") - delete(additionalProperties, "group") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableFileInfo struct { - value *FileInfo - isSet bool -} - -func (v NullableFileInfo) Get() *FileInfo { - return v.value -} - -func (v *NullableFileInfo) Set(val *FileInfo) { - v.value = val - v.isSet = true -} - -func (v NullableFileInfo) IsSet() bool { - return v.isSet -} - -func (v *NullableFileInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableFileInfo(val *FileInfo) *NullableFileInfo { - return &NullableFileInfo{value: val, isSet: true} -} - -func (v NullableFileInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableFileInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_file_status.go b/apps/api-client-go/model_file_status.go deleted file mode 100644 index 9a0e9c6c9..000000000 --- a/apps/api-client-go/model_file_status.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the FileStatus type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &FileStatus{} - -// FileStatus struct for FileStatus -type FileStatus struct { - Name string `json:"name"` - Staging string `json:"staging"` - Worktree string `json:"worktree"` - Extra string `json:"extra"` - AdditionalProperties map[string]interface{} -} - -type _FileStatus FileStatus - -// NewFileStatus instantiates a new FileStatus object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewFileStatus(name string, staging string, worktree string, extra string) *FileStatus { - this := FileStatus{} - this.Name = name - this.Staging = staging - this.Worktree = worktree - this.Extra = extra - return &this -} - -// NewFileStatusWithDefaults instantiates a new FileStatus object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewFileStatusWithDefaults() *FileStatus { - this := FileStatus{} - return &this -} - -// GetName returns the Name field value -func (o *FileStatus) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *FileStatus) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *FileStatus) SetName(v string) { - o.Name = v -} - -// GetStaging returns the Staging field value -func (o *FileStatus) GetStaging() string { - if o == nil { - var ret string - return ret - } - - return o.Staging -} - -// GetStagingOk returns a tuple with the Staging field value -// and a boolean to check if the value has been set. -func (o *FileStatus) GetStagingOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Staging, true -} - -// SetStaging sets field value -func (o *FileStatus) SetStaging(v string) { - o.Staging = v -} - -// GetWorktree returns the Worktree field value -func (o *FileStatus) GetWorktree() string { - if o == nil { - var ret string - return ret - } - - return o.Worktree -} - -// GetWorktreeOk returns a tuple with the Worktree field value -// and a boolean to check if the value has been set. -func (o *FileStatus) GetWorktreeOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Worktree, true -} - -// SetWorktree sets field value -func (o *FileStatus) SetWorktree(v string) { - o.Worktree = v -} - -// GetExtra returns the Extra field value -func (o *FileStatus) GetExtra() string { - if o == nil { - var ret string - return ret - } - - return o.Extra -} - -// GetExtraOk returns a tuple with the Extra field value -// and a boolean to check if the value has been set. -func (o *FileStatus) GetExtraOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Extra, true -} - -// SetExtra sets field value -func (o *FileStatus) SetExtra(v string) { - o.Extra = v -} - -func (o FileStatus) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o FileStatus) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["name"] = o.Name - toSerialize["staging"] = o.Staging - toSerialize["worktree"] = o.Worktree - toSerialize["extra"] = o.Extra - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *FileStatus) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "name", - "staging", - "worktree", - "extra", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varFileStatus := _FileStatus{} - - err = json.Unmarshal(data, &varFileStatus) - - if err != nil { - return err - } - - *o = FileStatus(varFileStatus) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "staging") - delete(additionalProperties, "worktree") - delete(additionalProperties, "extra") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableFileStatus struct { - value *FileStatus - isSet bool -} - -func (v NullableFileStatus) Get() *FileStatus { - return v.value -} - -func (v *NullableFileStatus) Set(val *FileStatus) { - v.value = val - v.isSet = true -} - -func (v NullableFileStatus) IsSet() bool { - return v.isSet -} - -func (v *NullableFileStatus) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableFileStatus(val *FileStatus) *NullableFileStatus { - return &NullableFileStatus{value: val, isSet: true} -} - -func (v NullableFileStatus) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableFileStatus) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_add_request.go b/apps/api-client-go/model_git_add_request.go deleted file mode 100644 index 836b85def..000000000 --- a/apps/api-client-go/model_git_add_request.go +++ /dev/null @@ -1,197 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitAddRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitAddRequest{} - -// GitAddRequest struct for GitAddRequest -type GitAddRequest struct { - Path string `json:"path"` - // files to add (use . for all files) - Files []string `json:"files"` - AdditionalProperties map[string]interface{} -} - -type _GitAddRequest GitAddRequest - -// NewGitAddRequest instantiates a new GitAddRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitAddRequest(path string, files []string) *GitAddRequest { - this := GitAddRequest{} - this.Path = path - this.Files = files - return &this -} - -// NewGitAddRequestWithDefaults instantiates a new GitAddRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitAddRequestWithDefaults() *GitAddRequest { - this := GitAddRequest{} - return &this -} - -// GetPath returns the Path field value -func (o *GitAddRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitAddRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitAddRequest) SetPath(v string) { - o.Path = v -} - -// GetFiles returns the Files field value -func (o *GitAddRequest) GetFiles() []string { - if o == nil { - var ret []string - return ret - } - - return o.Files -} - -// GetFilesOk returns a tuple with the Files field value -// and a boolean to check if the value has been set. -func (o *GitAddRequest) GetFilesOk() ([]string, bool) { - if o == nil { - return nil, false - } - return o.Files, true -} - -// SetFiles sets field value -func (o *GitAddRequest) SetFiles(v []string) { - o.Files = v -} - -func (o GitAddRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitAddRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - toSerialize["files"] = o.Files - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitAddRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - "files", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitAddRequest := _GitAddRequest{} - - err = json.Unmarshal(data, &varGitAddRequest) - - if err != nil { - return err - } - - *o = GitAddRequest(varGitAddRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "files") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitAddRequest struct { - value *GitAddRequest - isSet bool -} - -func (v NullableGitAddRequest) Get() *GitAddRequest { - return v.value -} - -func (v *NullableGitAddRequest) Set(val *GitAddRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitAddRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitAddRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitAddRequest(val *GitAddRequest) *NullableGitAddRequest { - return &NullableGitAddRequest{value: val, isSet: true} -} - -func (v NullableGitAddRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitAddRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_branch_request.go b/apps/api-client-go/model_git_branch_request.go deleted file mode 100644 index 9899ae885..000000000 --- a/apps/api-client-go/model_git_branch_request.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitBranchRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitBranchRequest{} - -// GitBranchRequest struct for GitBranchRequest -type GitBranchRequest struct { - Path string `json:"path"` - Name string `json:"name"` - AdditionalProperties map[string]interface{} -} - -type _GitBranchRequest GitBranchRequest - -// NewGitBranchRequest instantiates a new GitBranchRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitBranchRequest(path string, name string) *GitBranchRequest { - this := GitBranchRequest{} - this.Path = path - this.Name = name - return &this -} - -// NewGitBranchRequestWithDefaults instantiates a new GitBranchRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitBranchRequestWithDefaults() *GitBranchRequest { - this := GitBranchRequest{} - return &this -} - -// GetPath returns the Path field value -func (o *GitBranchRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitBranchRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitBranchRequest) SetPath(v string) { - o.Path = v -} - -// GetName returns the Name field value -func (o *GitBranchRequest) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *GitBranchRequest) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *GitBranchRequest) SetName(v string) { - o.Name = v -} - -func (o GitBranchRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitBranchRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - toSerialize["name"] = o.Name - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitBranchRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - "name", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitBranchRequest := _GitBranchRequest{} - - err = json.Unmarshal(data, &varGitBranchRequest) - - if err != nil { - return err - } - - *o = GitBranchRequest(varGitBranchRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "name") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitBranchRequest struct { - value *GitBranchRequest - isSet bool -} - -func (v NullableGitBranchRequest) Get() *GitBranchRequest { - return v.value -} - -func (v *NullableGitBranchRequest) Set(val *GitBranchRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitBranchRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitBranchRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitBranchRequest(val *GitBranchRequest) *NullableGitBranchRequest { - return &NullableGitBranchRequest{value: val, isSet: true} -} - -func (v NullableGitBranchRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitBranchRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_checkout_request.go b/apps/api-client-go/model_git_checkout_request.go deleted file mode 100644 index ffb0242fa..000000000 --- a/apps/api-client-go/model_git_checkout_request.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitCheckoutRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitCheckoutRequest{} - -// GitCheckoutRequest struct for GitCheckoutRequest -type GitCheckoutRequest struct { - Path string `json:"path"` - Branch string `json:"branch"` - AdditionalProperties map[string]interface{} -} - -type _GitCheckoutRequest GitCheckoutRequest - -// NewGitCheckoutRequest instantiates a new GitCheckoutRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitCheckoutRequest(path string, branch string) *GitCheckoutRequest { - this := GitCheckoutRequest{} - this.Path = path - this.Branch = branch - return &this -} - -// NewGitCheckoutRequestWithDefaults instantiates a new GitCheckoutRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitCheckoutRequestWithDefaults() *GitCheckoutRequest { - this := GitCheckoutRequest{} - return &this -} - -// GetPath returns the Path field value -func (o *GitCheckoutRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitCheckoutRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitCheckoutRequest) SetPath(v string) { - o.Path = v -} - -// GetBranch returns the Branch field value -func (o *GitCheckoutRequest) GetBranch() string { - if o == nil { - var ret string - return ret - } - - return o.Branch -} - -// GetBranchOk returns a tuple with the Branch field value -// and a boolean to check if the value has been set. -func (o *GitCheckoutRequest) GetBranchOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Branch, true -} - -// SetBranch sets field value -func (o *GitCheckoutRequest) SetBranch(v string) { - o.Branch = v -} - -func (o GitCheckoutRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitCheckoutRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - toSerialize["branch"] = o.Branch - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitCheckoutRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - "branch", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitCheckoutRequest := _GitCheckoutRequest{} - - err = json.Unmarshal(data, &varGitCheckoutRequest) - - if err != nil { - return err - } - - *o = GitCheckoutRequest(varGitCheckoutRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "branch") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitCheckoutRequest struct { - value *GitCheckoutRequest - isSet bool -} - -func (v NullableGitCheckoutRequest) Get() *GitCheckoutRequest { - return v.value -} - -func (v *NullableGitCheckoutRequest) Set(val *GitCheckoutRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitCheckoutRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitCheckoutRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitCheckoutRequest(val *GitCheckoutRequest) *NullableGitCheckoutRequest { - return &NullableGitCheckoutRequest{value: val, isSet: true} -} - -func (v NullableGitCheckoutRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitCheckoutRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_clone_request.go b/apps/api-client-go/model_git_clone_request.go deleted file mode 100644 index 94bd13c67..000000000 --- a/apps/api-client-go/model_git_clone_request.go +++ /dev/null @@ -1,344 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitCloneRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitCloneRequest{} - -// GitCloneRequest struct for GitCloneRequest -type GitCloneRequest struct { - Url string `json:"url"` - Path string `json:"path"` - Username *string `json:"username,omitempty"` - Password *string `json:"password,omitempty"` - Branch *string `json:"branch,omitempty"` - CommitId *string `json:"commit_id,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _GitCloneRequest GitCloneRequest - -// NewGitCloneRequest instantiates a new GitCloneRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitCloneRequest(url string, path string) *GitCloneRequest { - this := GitCloneRequest{} - this.Url = url - this.Path = path - return &this -} - -// NewGitCloneRequestWithDefaults instantiates a new GitCloneRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitCloneRequestWithDefaults() *GitCloneRequest { - this := GitCloneRequest{} - return &this -} - -// GetUrl returns the Url field value -func (o *GitCloneRequest) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *GitCloneRequest) SetUrl(v string) { - o.Url = v -} - -// GetPath returns the Path field value -func (o *GitCloneRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitCloneRequest) SetPath(v string) { - o.Path = v -} - -// GetUsername returns the Username field value if set, zero value otherwise. -func (o *GitCloneRequest) GetUsername() string { - if o == nil || IsNil(o.Username) { - var ret string - return ret - } - return *o.Username -} - -// GetUsernameOk returns a tuple with the Username field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetUsernameOk() (*string, bool) { - if o == nil || IsNil(o.Username) { - return nil, false - } - return o.Username, true -} - -// HasUsername returns a boolean if a field has been set. -func (o *GitCloneRequest) HasUsername() bool { - if o != nil && !IsNil(o.Username) { - return true - } - - return false -} - -// SetUsername gets a reference to the given string and assigns it to the Username field. -func (o *GitCloneRequest) SetUsername(v string) { - o.Username = &v -} - -// GetPassword returns the Password field value if set, zero value otherwise. -func (o *GitCloneRequest) GetPassword() string { - if o == nil || IsNil(o.Password) { - var ret string - return ret - } - return *o.Password -} - -// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetPasswordOk() (*string, bool) { - if o == nil || IsNil(o.Password) { - return nil, false - } - return o.Password, true -} - -// HasPassword returns a boolean if a field has been set. -func (o *GitCloneRequest) HasPassword() bool { - if o != nil && !IsNil(o.Password) { - return true - } - - return false -} - -// SetPassword gets a reference to the given string and assigns it to the Password field. -func (o *GitCloneRequest) SetPassword(v string) { - o.Password = &v -} - -// GetBranch returns the Branch field value if set, zero value otherwise. -func (o *GitCloneRequest) GetBranch() string { - if o == nil || IsNil(o.Branch) { - var ret string - return ret - } - return *o.Branch -} - -// GetBranchOk returns a tuple with the Branch field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetBranchOk() (*string, bool) { - if o == nil || IsNil(o.Branch) { - return nil, false - } - return o.Branch, true -} - -// HasBranch returns a boolean if a field has been set. -func (o *GitCloneRequest) HasBranch() bool { - if o != nil && !IsNil(o.Branch) { - return true - } - - return false -} - -// SetBranch gets a reference to the given string and assigns it to the Branch field. -func (o *GitCloneRequest) SetBranch(v string) { - o.Branch = &v -} - -// GetCommitId returns the CommitId field value if set, zero value otherwise. -func (o *GitCloneRequest) GetCommitId() string { - if o == nil || IsNil(o.CommitId) { - var ret string - return ret - } - return *o.CommitId -} - -// GetCommitIdOk returns a tuple with the CommitId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitCloneRequest) GetCommitIdOk() (*string, bool) { - if o == nil || IsNil(o.CommitId) { - return nil, false - } - return o.CommitId, true -} - -// HasCommitId returns a boolean if a field has been set. -func (o *GitCloneRequest) HasCommitId() bool { - if o != nil && !IsNil(o.CommitId) { - return true - } - - return false -} - -// SetCommitId gets a reference to the given string and assigns it to the CommitId field. -func (o *GitCloneRequest) SetCommitId(v string) { - o.CommitId = &v -} - -func (o GitCloneRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitCloneRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["url"] = o.Url - toSerialize["path"] = o.Path - if !IsNil(o.Username) { - toSerialize["username"] = o.Username - } - if !IsNil(o.Password) { - toSerialize["password"] = o.Password - } - if !IsNil(o.Branch) { - toSerialize["branch"] = o.Branch - } - if !IsNil(o.CommitId) { - toSerialize["commit_id"] = o.CommitId - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitCloneRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "url", - "path", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitCloneRequest := _GitCloneRequest{} - - err = json.Unmarshal(data, &varGitCloneRequest) - - if err != nil { - return err - } - - *o = GitCloneRequest(varGitCloneRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "url") - delete(additionalProperties, "path") - delete(additionalProperties, "username") - delete(additionalProperties, "password") - delete(additionalProperties, "branch") - delete(additionalProperties, "commit_id") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitCloneRequest struct { - value *GitCloneRequest - isSet bool -} - -func (v NullableGitCloneRequest) Get() *GitCloneRequest { - return v.value -} - -func (v *NullableGitCloneRequest) Set(val *GitCloneRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitCloneRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitCloneRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitCloneRequest(val *GitCloneRequest) *NullableGitCloneRequest { - return &NullableGitCloneRequest{value: val, isSet: true} -} - -func (v NullableGitCloneRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitCloneRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_commit_info.go b/apps/api-client-go/model_git_commit_info.go deleted file mode 100644 index c4d24ba2c..000000000 --- a/apps/api-client-go/model_git_commit_info.go +++ /dev/null @@ -1,283 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitCommitInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitCommitInfo{} - -// GitCommitInfo struct for GitCommitInfo -type GitCommitInfo struct { - Hash string `json:"hash"` - Message string `json:"message"` - Author string `json:"author"` - Email string `json:"email"` - Timestamp string `json:"timestamp"` - AdditionalProperties map[string]interface{} -} - -type _GitCommitInfo GitCommitInfo - -// NewGitCommitInfo instantiates a new GitCommitInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitCommitInfo(hash string, message string, author string, email string, timestamp string) *GitCommitInfo { - this := GitCommitInfo{} - this.Hash = hash - this.Message = message - this.Author = author - this.Email = email - this.Timestamp = timestamp - return &this -} - -// NewGitCommitInfoWithDefaults instantiates a new GitCommitInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitCommitInfoWithDefaults() *GitCommitInfo { - this := GitCommitInfo{} - return &this -} - -// GetHash returns the Hash field value -func (o *GitCommitInfo) GetHash() string { - if o == nil { - var ret string - return ret - } - - return o.Hash -} - -// GetHashOk returns a tuple with the Hash field value -// and a boolean to check if the value has been set. -func (o *GitCommitInfo) GetHashOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Hash, true -} - -// SetHash sets field value -func (o *GitCommitInfo) SetHash(v string) { - o.Hash = v -} - -// GetMessage returns the Message field value -func (o *GitCommitInfo) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *GitCommitInfo) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *GitCommitInfo) SetMessage(v string) { - o.Message = v -} - -// GetAuthor returns the Author field value -func (o *GitCommitInfo) GetAuthor() string { - if o == nil { - var ret string - return ret - } - - return o.Author -} - -// GetAuthorOk returns a tuple with the Author field value -// and a boolean to check if the value has been set. -func (o *GitCommitInfo) GetAuthorOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Author, true -} - -// SetAuthor sets field value -func (o *GitCommitInfo) SetAuthor(v string) { - o.Author = v -} - -// GetEmail returns the Email field value -func (o *GitCommitInfo) GetEmail() string { - if o == nil { - var ret string - return ret - } - - return o.Email -} - -// GetEmailOk returns a tuple with the Email field value -// and a boolean to check if the value has been set. -func (o *GitCommitInfo) GetEmailOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Email, true -} - -// SetEmail sets field value -func (o *GitCommitInfo) SetEmail(v string) { - o.Email = v -} - -// GetTimestamp returns the Timestamp field value -func (o *GitCommitInfo) GetTimestamp() string { - if o == nil { - var ret string - return ret - } - - return o.Timestamp -} - -// GetTimestampOk returns a tuple with the Timestamp field value -// and a boolean to check if the value has been set. -func (o *GitCommitInfo) GetTimestampOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Timestamp, true -} - -// SetTimestamp sets field value -func (o *GitCommitInfo) SetTimestamp(v string) { - o.Timestamp = v -} - -func (o GitCommitInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitCommitInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["hash"] = o.Hash - toSerialize["message"] = o.Message - toSerialize["author"] = o.Author - toSerialize["email"] = o.Email - toSerialize["timestamp"] = o.Timestamp - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitCommitInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "hash", - "message", - "author", - "email", - "timestamp", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitCommitInfo := _GitCommitInfo{} - - err = json.Unmarshal(data, &varGitCommitInfo) - - if err != nil { - return err - } - - *o = GitCommitInfo(varGitCommitInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "hash") - delete(additionalProperties, "message") - delete(additionalProperties, "author") - delete(additionalProperties, "email") - delete(additionalProperties, "timestamp") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitCommitInfo struct { - value *GitCommitInfo - isSet bool -} - -func (v NullableGitCommitInfo) Get() *GitCommitInfo { - return v.value -} - -func (v *NullableGitCommitInfo) Set(val *GitCommitInfo) { - v.value = val - v.isSet = true -} - -func (v NullableGitCommitInfo) IsSet() bool { - return v.isSet -} - -func (v *NullableGitCommitInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitCommitInfo(val *GitCommitInfo) *NullableGitCommitInfo { - return &NullableGitCommitInfo{value: val, isSet: true} -} - -func (v NullableGitCommitInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitCommitInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_commit_request.go b/apps/api-client-go/model_git_commit_request.go deleted file mode 100644 index b3816cddc..000000000 --- a/apps/api-client-go/model_git_commit_request.go +++ /dev/null @@ -1,296 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitCommitRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitCommitRequest{} - -// GitCommitRequest struct for GitCommitRequest -type GitCommitRequest struct { - Path string `json:"path"` - Message string `json:"message"` - Author string `json:"author"` - Email string `json:"email"` - // Allow creating an empty commit when no changes are staged - AllowEmpty *bool `json:"allow_empty,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _GitCommitRequest GitCommitRequest - -// NewGitCommitRequest instantiates a new GitCommitRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitCommitRequest(path string, message string, author string, email string) *GitCommitRequest { - this := GitCommitRequest{} - this.Path = path - this.Message = message - this.Author = author - this.Email = email - var allowEmpty bool = false - this.AllowEmpty = &allowEmpty - return &this -} - -// NewGitCommitRequestWithDefaults instantiates a new GitCommitRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitCommitRequestWithDefaults() *GitCommitRequest { - this := GitCommitRequest{} - var allowEmpty bool = false - this.AllowEmpty = &allowEmpty - return &this -} - -// GetPath returns the Path field value -func (o *GitCommitRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitCommitRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitCommitRequest) SetPath(v string) { - o.Path = v -} - -// GetMessage returns the Message field value -func (o *GitCommitRequest) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *GitCommitRequest) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *GitCommitRequest) SetMessage(v string) { - o.Message = v -} - -// GetAuthor returns the Author field value -func (o *GitCommitRequest) GetAuthor() string { - if o == nil { - var ret string - return ret - } - - return o.Author -} - -// GetAuthorOk returns a tuple with the Author field value -// and a boolean to check if the value has been set. -func (o *GitCommitRequest) GetAuthorOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Author, true -} - -// SetAuthor sets field value -func (o *GitCommitRequest) SetAuthor(v string) { - o.Author = v -} - -// GetEmail returns the Email field value -func (o *GitCommitRequest) GetEmail() string { - if o == nil { - var ret string - return ret - } - - return o.Email -} - -// GetEmailOk returns a tuple with the Email field value -// and a boolean to check if the value has been set. -func (o *GitCommitRequest) GetEmailOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Email, true -} - -// SetEmail sets field value -func (o *GitCommitRequest) SetEmail(v string) { - o.Email = v -} - -// GetAllowEmpty returns the AllowEmpty field value if set, zero value otherwise. -func (o *GitCommitRequest) GetAllowEmpty() bool { - if o == nil || IsNil(o.AllowEmpty) { - var ret bool - return ret - } - return *o.AllowEmpty -} - -// GetAllowEmptyOk returns a tuple with the AllowEmpty field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitCommitRequest) GetAllowEmptyOk() (*bool, bool) { - if o == nil || IsNil(o.AllowEmpty) { - return nil, false - } - return o.AllowEmpty, true -} - -// HasAllowEmpty returns a boolean if a field has been set. -func (o *GitCommitRequest) HasAllowEmpty() bool { - if o != nil && !IsNil(o.AllowEmpty) { - return true - } - - return false -} - -// SetAllowEmpty gets a reference to the given bool and assigns it to the AllowEmpty field. -func (o *GitCommitRequest) SetAllowEmpty(v bool) { - o.AllowEmpty = &v -} - -func (o GitCommitRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitCommitRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - toSerialize["message"] = o.Message - toSerialize["author"] = o.Author - toSerialize["email"] = o.Email - if !IsNil(o.AllowEmpty) { - toSerialize["allow_empty"] = o.AllowEmpty - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitCommitRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - "message", - "author", - "email", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitCommitRequest := _GitCommitRequest{} - - err = json.Unmarshal(data, &varGitCommitRequest) - - if err != nil { - return err - } - - *o = GitCommitRequest(varGitCommitRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "message") - delete(additionalProperties, "author") - delete(additionalProperties, "email") - delete(additionalProperties, "allow_empty") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitCommitRequest struct { - value *GitCommitRequest - isSet bool -} - -func (v NullableGitCommitRequest) Get() *GitCommitRequest { - return v.value -} - -func (v *NullableGitCommitRequest) Set(val *GitCommitRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitCommitRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitCommitRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitCommitRequest(val *GitCommitRequest) *NullableGitCommitRequest { - return &NullableGitCommitRequest{value: val, isSet: true} -} - -func (v NullableGitCommitRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitCommitRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_commit_response.go b/apps/api-client-go/model_git_commit_response.go deleted file mode 100644 index 131e7024a..000000000 --- a/apps/api-client-go/model_git_commit_response.go +++ /dev/null @@ -1,167 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitCommitResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitCommitResponse{} - -// GitCommitResponse struct for GitCommitResponse -type GitCommitResponse struct { - Hash string `json:"hash"` - AdditionalProperties map[string]interface{} -} - -type _GitCommitResponse GitCommitResponse - -// NewGitCommitResponse instantiates a new GitCommitResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitCommitResponse(hash string) *GitCommitResponse { - this := GitCommitResponse{} - this.Hash = hash - return &this -} - -// NewGitCommitResponseWithDefaults instantiates a new GitCommitResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitCommitResponseWithDefaults() *GitCommitResponse { - this := GitCommitResponse{} - return &this -} - -// GetHash returns the Hash field value -func (o *GitCommitResponse) GetHash() string { - if o == nil { - var ret string - return ret - } - - return o.Hash -} - -// GetHashOk returns a tuple with the Hash field value -// and a boolean to check if the value has been set. -func (o *GitCommitResponse) GetHashOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Hash, true -} - -// SetHash sets field value -func (o *GitCommitResponse) SetHash(v string) { - o.Hash = v -} - -func (o GitCommitResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitCommitResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["hash"] = o.Hash - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitCommitResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "hash", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitCommitResponse := _GitCommitResponse{} - - err = json.Unmarshal(data, &varGitCommitResponse) - - if err != nil { - return err - } - - *o = GitCommitResponse(varGitCommitResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "hash") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitCommitResponse struct { - value *GitCommitResponse - isSet bool -} - -func (v NullableGitCommitResponse) Get() *GitCommitResponse { - return v.value -} - -func (v *NullableGitCommitResponse) Set(val *GitCommitResponse) { - v.value = val - v.isSet = true -} - -func (v NullableGitCommitResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableGitCommitResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitCommitResponse(val *GitCommitResponse) *NullableGitCommitResponse { - return &NullableGitCommitResponse{value: val, isSet: true} -} - -func (v NullableGitCommitResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitCommitResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_delete_branch_request.go b/apps/api-client-go/model_git_delete_branch_request.go deleted file mode 100644 index a302206df..000000000 --- a/apps/api-client-go/model_git_delete_branch_request.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitDeleteBranchRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitDeleteBranchRequest{} - -// GitDeleteBranchRequest struct for GitDeleteBranchRequest -type GitDeleteBranchRequest struct { - Path string `json:"path"` - Name string `json:"name"` - AdditionalProperties map[string]interface{} -} - -type _GitDeleteBranchRequest GitDeleteBranchRequest - -// NewGitDeleteBranchRequest instantiates a new GitDeleteBranchRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitDeleteBranchRequest(path string, name string) *GitDeleteBranchRequest { - this := GitDeleteBranchRequest{} - this.Path = path - this.Name = name - return &this -} - -// NewGitDeleteBranchRequestWithDefaults instantiates a new GitDeleteBranchRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitDeleteBranchRequestWithDefaults() *GitDeleteBranchRequest { - this := GitDeleteBranchRequest{} - return &this -} - -// GetPath returns the Path field value -func (o *GitDeleteBranchRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitDeleteBranchRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitDeleteBranchRequest) SetPath(v string) { - o.Path = v -} - -// GetName returns the Name field value -func (o *GitDeleteBranchRequest) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *GitDeleteBranchRequest) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *GitDeleteBranchRequest) SetName(v string) { - o.Name = v -} - -func (o GitDeleteBranchRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitDeleteBranchRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - toSerialize["name"] = o.Name - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitDeleteBranchRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - "name", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitDeleteBranchRequest := _GitDeleteBranchRequest{} - - err = json.Unmarshal(data, &varGitDeleteBranchRequest) - - if err != nil { - return err - } - - *o = GitDeleteBranchRequest(varGitDeleteBranchRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "name") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitDeleteBranchRequest struct { - value *GitDeleteBranchRequest - isSet bool -} - -func (v NullableGitDeleteBranchRequest) Get() *GitDeleteBranchRequest { - return v.value -} - -func (v *NullableGitDeleteBranchRequest) Set(val *GitDeleteBranchRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitDeleteBranchRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitDeleteBranchRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitDeleteBranchRequest(val *GitDeleteBranchRequest) *NullableGitDeleteBranchRequest { - return &NullableGitDeleteBranchRequest{value: val, isSet: true} -} - -func (v NullableGitDeleteBranchRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitDeleteBranchRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_repo_request.go b/apps/api-client-go/model_git_repo_request.go deleted file mode 100644 index 5ddd91a0d..000000000 --- a/apps/api-client-go/model_git_repo_request.go +++ /dev/null @@ -1,241 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitRepoRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitRepoRequest{} - -// GitRepoRequest struct for GitRepoRequest -type GitRepoRequest struct { - Path string `json:"path"` - Username *string `json:"username,omitempty"` - Password *string `json:"password,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _GitRepoRequest GitRepoRequest - -// NewGitRepoRequest instantiates a new GitRepoRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitRepoRequest(path string) *GitRepoRequest { - this := GitRepoRequest{} - this.Path = path - return &this -} - -// NewGitRepoRequestWithDefaults instantiates a new GitRepoRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitRepoRequestWithDefaults() *GitRepoRequest { - this := GitRepoRequest{} - return &this -} - -// GetPath returns the Path field value -func (o *GitRepoRequest) GetPath() string { - if o == nil { - var ret string - return ret - } - - return o.Path -} - -// GetPathOk returns a tuple with the Path field value -// and a boolean to check if the value has been set. -func (o *GitRepoRequest) GetPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Path, true -} - -// SetPath sets field value -func (o *GitRepoRequest) SetPath(v string) { - o.Path = v -} - -// GetUsername returns the Username field value if set, zero value otherwise. -func (o *GitRepoRequest) GetUsername() string { - if o == nil || IsNil(o.Username) { - var ret string - return ret - } - return *o.Username -} - -// GetUsernameOk returns a tuple with the Username field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitRepoRequest) GetUsernameOk() (*string, bool) { - if o == nil || IsNil(o.Username) { - return nil, false - } - return o.Username, true -} - -// HasUsername returns a boolean if a field has been set. -func (o *GitRepoRequest) HasUsername() bool { - if o != nil && !IsNil(o.Username) { - return true - } - - return false -} - -// SetUsername gets a reference to the given string and assigns it to the Username field. -func (o *GitRepoRequest) SetUsername(v string) { - o.Username = &v -} - -// GetPassword returns the Password field value if set, zero value otherwise. -func (o *GitRepoRequest) GetPassword() string { - if o == nil || IsNil(o.Password) { - var ret string - return ret - } - return *o.Password -} - -// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitRepoRequest) GetPasswordOk() (*string, bool) { - if o == nil || IsNil(o.Password) { - return nil, false - } - return o.Password, true -} - -// HasPassword returns a boolean if a field has been set. -func (o *GitRepoRequest) HasPassword() bool { - if o != nil && !IsNil(o.Password) { - return true - } - - return false -} - -// SetPassword gets a reference to the given string and assigns it to the Password field. -func (o *GitRepoRequest) SetPassword(v string) { - o.Password = &v -} - -func (o GitRepoRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitRepoRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["path"] = o.Path - if !IsNil(o.Username) { - toSerialize["username"] = o.Username - } - if !IsNil(o.Password) { - toSerialize["password"] = o.Password - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitRepoRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "path", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitRepoRequest := _GitRepoRequest{} - - err = json.Unmarshal(data, &varGitRepoRequest) - - if err != nil { - return err - } - - *o = GitRepoRequest(varGitRepoRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "path") - delete(additionalProperties, "username") - delete(additionalProperties, "password") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitRepoRequest struct { - value *GitRepoRequest - isSet bool -} - -func (v NullableGitRepoRequest) Get() *GitRepoRequest { - return v.value -} - -func (v *NullableGitRepoRequest) Set(val *GitRepoRequest) { - v.value = val - v.isSet = true -} - -func (v NullableGitRepoRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableGitRepoRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitRepoRequest(val *GitRepoRequest) *NullableGitRepoRequest { - return &NullableGitRepoRequest{value: val, isSet: true} -} - -func (v NullableGitRepoRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitRepoRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_git_status.go b/apps/api-client-go/model_git_status.go deleted file mode 100644 index bc3b61f7f..000000000 --- a/apps/api-client-go/model_git_status.go +++ /dev/null @@ -1,307 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the GitStatus type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &GitStatus{} - -// GitStatus struct for GitStatus -type GitStatus struct { - CurrentBranch string `json:"currentBranch"` - FileStatus []FileStatus `json:"fileStatus"` - Ahead *float32 `json:"ahead,omitempty"` - Behind *float32 `json:"behind,omitempty"` - BranchPublished *bool `json:"branchPublished,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _GitStatus GitStatus - -// NewGitStatus instantiates a new GitStatus object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewGitStatus(currentBranch string, fileStatus []FileStatus) *GitStatus { - this := GitStatus{} - this.CurrentBranch = currentBranch - this.FileStatus = fileStatus - return &this -} - -// NewGitStatusWithDefaults instantiates a new GitStatus object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewGitStatusWithDefaults() *GitStatus { - this := GitStatus{} - return &this -} - -// GetCurrentBranch returns the CurrentBranch field value -func (o *GitStatus) GetCurrentBranch() string { - if o == nil { - var ret string - return ret - } - - return o.CurrentBranch -} - -// GetCurrentBranchOk returns a tuple with the CurrentBranch field value -// and a boolean to check if the value has been set. -func (o *GitStatus) GetCurrentBranchOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.CurrentBranch, true -} - -// SetCurrentBranch sets field value -func (o *GitStatus) SetCurrentBranch(v string) { - o.CurrentBranch = v -} - -// GetFileStatus returns the FileStatus field value -func (o *GitStatus) GetFileStatus() []FileStatus { - if o == nil { - var ret []FileStatus - return ret - } - - return o.FileStatus -} - -// GetFileStatusOk returns a tuple with the FileStatus field value -// and a boolean to check if the value has been set. -func (o *GitStatus) GetFileStatusOk() ([]FileStatus, bool) { - if o == nil { - return nil, false - } - return o.FileStatus, true -} - -// SetFileStatus sets field value -func (o *GitStatus) SetFileStatus(v []FileStatus) { - o.FileStatus = v -} - -// GetAhead returns the Ahead field value if set, zero value otherwise. -func (o *GitStatus) GetAhead() float32 { - if o == nil || IsNil(o.Ahead) { - var ret float32 - return ret - } - return *o.Ahead -} - -// GetAheadOk returns a tuple with the Ahead field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitStatus) GetAheadOk() (*float32, bool) { - if o == nil || IsNil(o.Ahead) { - return nil, false - } - return o.Ahead, true -} - -// HasAhead returns a boolean if a field has been set. -func (o *GitStatus) HasAhead() bool { - if o != nil && !IsNil(o.Ahead) { - return true - } - - return false -} - -// SetAhead gets a reference to the given float32 and assigns it to the Ahead field. -func (o *GitStatus) SetAhead(v float32) { - o.Ahead = &v -} - -// GetBehind returns the Behind field value if set, zero value otherwise. -func (o *GitStatus) GetBehind() float32 { - if o == nil || IsNil(o.Behind) { - var ret float32 - return ret - } - return *o.Behind -} - -// GetBehindOk returns a tuple with the Behind field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitStatus) GetBehindOk() (*float32, bool) { - if o == nil || IsNil(o.Behind) { - return nil, false - } - return o.Behind, true -} - -// HasBehind returns a boolean if a field has been set. -func (o *GitStatus) HasBehind() bool { - if o != nil && !IsNil(o.Behind) { - return true - } - - return false -} - -// SetBehind gets a reference to the given float32 and assigns it to the Behind field. -func (o *GitStatus) SetBehind(v float32) { - o.Behind = &v -} - -// GetBranchPublished returns the BranchPublished field value if set, zero value otherwise. -func (o *GitStatus) GetBranchPublished() bool { - if o == nil || IsNil(o.BranchPublished) { - var ret bool - return ret - } - return *o.BranchPublished -} - -// GetBranchPublishedOk returns a tuple with the BranchPublished field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *GitStatus) GetBranchPublishedOk() (*bool, bool) { - if o == nil || IsNil(o.BranchPublished) { - return nil, false - } - return o.BranchPublished, true -} - -// HasBranchPublished returns a boolean if a field has been set. -func (o *GitStatus) HasBranchPublished() bool { - if o != nil && !IsNil(o.BranchPublished) { - return true - } - - return false -} - -// SetBranchPublished gets a reference to the given bool and assigns it to the BranchPublished field. -func (o *GitStatus) SetBranchPublished(v bool) { - o.BranchPublished = &v -} - -func (o GitStatus) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o GitStatus) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["currentBranch"] = o.CurrentBranch - toSerialize["fileStatus"] = o.FileStatus - if !IsNil(o.Ahead) { - toSerialize["ahead"] = o.Ahead - } - if !IsNil(o.Behind) { - toSerialize["behind"] = o.Behind - } - if !IsNil(o.BranchPublished) { - toSerialize["branchPublished"] = o.BranchPublished - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *GitStatus) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "currentBranch", - "fileStatus", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varGitStatus := _GitStatus{} - - err = json.Unmarshal(data, &varGitStatus) - - if err != nil { - return err - } - - *o = GitStatus(varGitStatus) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "currentBranch") - delete(additionalProperties, "fileStatus") - delete(additionalProperties, "ahead") - delete(additionalProperties, "behind") - delete(additionalProperties, "branchPublished") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableGitStatus struct { - value *GitStatus - isSet bool -} - -func (v NullableGitStatus) Get() *GitStatus { - return v.value -} - -func (v *NullableGitStatus) Set(val *GitStatus) { - v.value = val - v.isSet = true -} - -func (v NullableGitStatus) IsSet() bool { - return v.isSet -} - -func (v *NullableGitStatus) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableGitStatus(val *GitStatus) *NullableGitStatus { - return &NullableGitStatus{value: val, isSet: true} -} - -func (v NullableGitStatus) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableGitStatus) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_health_controller_check_200_response.go b/apps/api-client-go/model_health_controller_check_200_response.go index f354bbb8f..cf6d1e7e7 100644 --- a/apps/api-client-go/model_health_controller_check_200_response.go +++ b/apps/api-client-go/model_health_controller_check_200_response.go @@ -265,3 +265,5 @@ func (v *NullableHealthControllerCheck200Response) UnmarshalJSON(src []byte) err v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_health_controller_check_200_response_info_value.go b/apps/api-client-go/model_health_controller_check_200_response_info_value.go index 2f1301e5b..474046729 100644 --- a/apps/api-client-go/model_health_controller_check_200_response_info_value.go +++ b/apps/api-client-go/model_health_controller_check_200_response_info_value.go @@ -165,3 +165,5 @@ func (v *NullableHealthControllerCheck200ResponseInfoValue) UnmarshalJSON(src [] v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_health_controller_check_503_response.go b/apps/api-client-go/model_health_controller_check_503_response.go deleted file mode 100644 index f7892960f..000000000 --- a/apps/api-client-go/model_health_controller_check_503_response.go +++ /dev/null @@ -1,267 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the HealthControllerCheck503Response type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &HealthControllerCheck503Response{} - -// HealthControllerCheck503Response struct for HealthControllerCheck503Response -type HealthControllerCheck503Response struct { - Status *string `json:"status,omitempty"` - Info map[string]HealthControllerCheck200ResponseInfoValue `json:"info,omitempty"` - Error map[string]HealthControllerCheck200ResponseInfoValue `json:"error,omitempty"` - Details *map[string]HealthControllerCheck200ResponseInfoValue `json:"details,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _HealthControllerCheck503Response HealthControllerCheck503Response - -// NewHealthControllerCheck503Response instantiates a new HealthControllerCheck503Response object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewHealthControllerCheck503Response() *HealthControllerCheck503Response { - this := HealthControllerCheck503Response{} - return &this -} - -// NewHealthControllerCheck503ResponseWithDefaults instantiates a new HealthControllerCheck503Response object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewHealthControllerCheck503ResponseWithDefaults() *HealthControllerCheck503Response { - this := HealthControllerCheck503Response{} - return &this -} - -// GetStatus returns the Status field value if set, zero value otherwise. -func (o *HealthControllerCheck503Response) GetStatus() string { - if o == nil || IsNil(o.Status) { - var ret string - return ret - } - return *o.Status -} - -// GetStatusOk returns a tuple with the Status field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *HealthControllerCheck503Response) GetStatusOk() (*string, bool) { - if o == nil || IsNil(o.Status) { - return nil, false - } - return o.Status, true -} - -// HasStatus returns a boolean if a field has been set. -func (o *HealthControllerCheck503Response) HasStatus() bool { - if o != nil && !IsNil(o.Status) { - return true - } - - return false -} - -// SetStatus gets a reference to the given string and assigns it to the Status field. -func (o *HealthControllerCheck503Response) SetStatus(v string) { - o.Status = &v -} - -// GetInfo returns the Info field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *HealthControllerCheck503Response) GetInfo() map[string]HealthControllerCheck200ResponseInfoValue { - if o == nil { - var ret map[string]HealthControllerCheck200ResponseInfoValue - return ret - } - return o.Info -} - -// GetInfoOk returns a tuple with the Info field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *HealthControllerCheck503Response) GetInfoOk() (*map[string]HealthControllerCheck200ResponseInfoValue, bool) { - if o == nil || IsNil(o.Info) { - return nil, false - } - return &o.Info, true -} - -// HasInfo returns a boolean if a field has been set. -func (o *HealthControllerCheck503Response) HasInfo() bool { - if o != nil && !IsNil(o.Info) { - return true - } - - return false -} - -// SetInfo gets a reference to the given map[string]HealthControllerCheck200ResponseInfoValue and assigns it to the Info field. -func (o *HealthControllerCheck503Response) SetInfo(v map[string]HealthControllerCheck200ResponseInfoValue) { - o.Info = v -} - -// GetError returns the Error field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *HealthControllerCheck503Response) GetError() map[string]HealthControllerCheck200ResponseInfoValue { - if o == nil { - var ret map[string]HealthControllerCheck200ResponseInfoValue - return ret - } - return o.Error -} - -// GetErrorOk returns a tuple with the Error field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *HealthControllerCheck503Response) GetErrorOk() (*map[string]HealthControllerCheck200ResponseInfoValue, bool) { - if o == nil || IsNil(o.Error) { - return nil, false - } - return &o.Error, true -} - -// HasError returns a boolean if a field has been set. -func (o *HealthControllerCheck503Response) HasError() bool { - if o != nil && !IsNil(o.Error) { - return true - } - - return false -} - -// SetError gets a reference to the given map[string]HealthControllerCheck200ResponseInfoValue and assigns it to the Error field. -func (o *HealthControllerCheck503Response) SetError(v map[string]HealthControllerCheck200ResponseInfoValue) { - o.Error = v -} - -// GetDetails returns the Details field value if set, zero value otherwise. -func (o *HealthControllerCheck503Response) GetDetails() map[string]HealthControllerCheck200ResponseInfoValue { - if o == nil || IsNil(o.Details) { - var ret map[string]HealthControllerCheck200ResponseInfoValue - return ret - } - return *o.Details -} - -// GetDetailsOk returns a tuple with the Details field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *HealthControllerCheck503Response) GetDetailsOk() (*map[string]HealthControllerCheck200ResponseInfoValue, bool) { - if o == nil || IsNil(o.Details) { - return nil, false - } - return o.Details, true -} - -// HasDetails returns a boolean if a field has been set. -func (o *HealthControllerCheck503Response) HasDetails() bool { - if o != nil && !IsNil(o.Details) { - return true - } - - return false -} - -// SetDetails gets a reference to the given map[string]HealthControllerCheck200ResponseInfoValue and assigns it to the Details field. -func (o *HealthControllerCheck503Response) SetDetails(v map[string]HealthControllerCheck200ResponseInfoValue) { - o.Details = &v -} - -func (o HealthControllerCheck503Response) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o HealthControllerCheck503Response) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Status) { - toSerialize["status"] = o.Status - } - if o.Info != nil { - toSerialize["info"] = o.Info - } - if o.Error != nil { - toSerialize["error"] = o.Error - } - if !IsNil(o.Details) { - toSerialize["details"] = o.Details - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *HealthControllerCheck503Response) UnmarshalJSON(data []byte) (err error) { - varHealthControllerCheck503Response := _HealthControllerCheck503Response{} - - err = json.Unmarshal(data, &varHealthControllerCheck503Response) - - if err != nil { - return err - } - - *o = HealthControllerCheck503Response(varHealthControllerCheck503Response) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "status") - delete(additionalProperties, "info") - delete(additionalProperties, "error") - delete(additionalProperties, "details") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableHealthControllerCheck503Response struct { - value *HealthControllerCheck503Response - isSet bool -} - -func (v NullableHealthControllerCheck503Response) Get() *HealthControllerCheck503Response { - return v.value -} - -func (v *NullableHealthControllerCheck503Response) Set(val *HealthControllerCheck503Response) { - v.value = val - v.isSet = true -} - -func (v NullableHealthControllerCheck503Response) IsSet() bool { - return v.isSet -} - -func (v *NullableHealthControllerCheck503Response) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableHealthControllerCheck503Response(val *HealthControllerCheck503Response) *NullableHealthControllerCheck503Response { - return &NullableHealthControllerCheck503Response{value: val, isSet: true} -} - -func (v NullableHealthControllerCheck503Response) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableHealthControllerCheck503Response) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_job.go b/apps/api-client-go/model_job.go index 4abb0a7b0..0a9ee4554 100644 --- a/apps/api-client-go/model_job.go +++ b/apps/api-client-go/model_job.go @@ -29,7 +29,7 @@ type Job struct { Status JobStatus `json:"status"` // The type of resource this job operates on ResourceType string `json:"resourceType"` - // The ID of the resource this job operates on (sandboxId, snapshotRef, etc.) + // The ID of the resource this job operates on (boxId, etc.) ResourceId string `json:"resourceId"` // Job-specific JSON-encoded payload data (operational metadata) Payload *string `json:"payload,omitempty"` @@ -468,3 +468,5 @@ func (v *NullableJob) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_job_status.go b/apps/api-client-go/model_job_status.go index a9603de4a..f366c889c 100644 --- a/apps/api-client-go/model_job_status.go +++ b/apps/api-client-go/model_job_status.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // JobStatus the model 'JobStatus' @@ -25,6 +24,7 @@ const ( JOBSTATUS_IN_PROGRESS JobStatus = "IN_PROGRESS" JOBSTATUS_COMPLETED JobStatus = "COMPLETED" JOBSTATUS_FAILED JobStatus = "FAILED" + JOBSTATUS_UNKNOWN_DEFAULT_OPEN_API JobStatus = "unknown_default_open_api" ) // All allowed values of JobStatus enum @@ -33,6 +33,7 @@ var AllowedJobStatusEnumValues = []JobStatus{ "IN_PROGRESS", "COMPLETED", "FAILED", + "unknown_default_open_api", } func (v *JobStatus) UnmarshalJSON(src []byte) error { @@ -49,7 +50,8 @@ func (v *JobStatus) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid JobStatus", value) + *v = JOBSTATUS_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewJobStatusFromValue returns a pointer to a valid JobStatus @@ -59,7 +61,8 @@ func NewJobStatusFromValue(v string) (*JobStatus, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for JobStatus: valid values are %v", v, AllowedJobStatusEnumValues) + enumValue := JOBSTATUS_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -113,3 +116,4 @@ func (v *NullableJobStatus) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_job_type.go b/apps/api-client-go/model_job_type.go index fbd5b1ae1..530dccd43 100644 --- a/apps/api-client-go/model_job_type.go +++ b/apps/api-client-go/model_job_type.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // JobType The type of the job @@ -21,34 +20,34 @@ type JobType string // List of JobType const ( - JOBTYPE_CREATE_SANDBOX JobType = "CREATE_SANDBOX" - JOBTYPE_START_SANDBOX JobType = "START_SANDBOX" - JOBTYPE_STOP_SANDBOX JobType = "STOP_SANDBOX" - JOBTYPE_DESTROY_SANDBOX JobType = "DESTROY_SANDBOX" - JOBTYPE_RESIZE_SANDBOX JobType = "RESIZE_SANDBOX" + JOBTYPE_CREATE_BOX JobType = "CREATE_BOX" + JOBTYPE_START_BOX JobType = "START_BOX" + JOBTYPE_STOP_BOX JobType = "STOP_BOX" + JOBTYPE_DESTROY_BOX JobType = "DESTROY_BOX" + JOBTYPE_RESIZE_BOX JobType = "RESIZE_BOX" JOBTYPE_CREATE_BACKUP JobType = "CREATE_BACKUP" - JOBTYPE_BUILD_SNAPSHOT JobType = "BUILD_SNAPSHOT" - JOBTYPE_PULL_SNAPSHOT JobType = "PULL_SNAPSHOT" - JOBTYPE_RECOVER_SANDBOX JobType = "RECOVER_SANDBOX" - JOBTYPE_INSPECT_SNAPSHOT_IN_REGISTRY JobType = "INSPECT_SNAPSHOT_IN_REGISTRY" - JOBTYPE_REMOVE_SNAPSHOT JobType = "REMOVE_SNAPSHOT" - JOBTYPE_UPDATE_SANDBOX_NETWORK_SETTINGS JobType = "UPDATE_SANDBOX_NETWORK_SETTINGS" + JOBTYPE_PULL_ARTIFACT JobType = "PULL_ARTIFACT" + JOBTYPE_RECOVER_BOX JobType = "RECOVER_BOX" + JOBTYPE_INSPECT_ARTIFACT_IN_REGISTRY JobType = "INSPECT_ARTIFACT_IN_REGISTRY" + JOBTYPE_REMOVE_ARTIFACT JobType = "REMOVE_ARTIFACT" + JOBTYPE_UPDATE_BOX_NETWORK_SETTINGS JobType = "UPDATE_BOX_NETWORK_SETTINGS" + JOBTYPE_UNKNOWN_DEFAULT_OPEN_API JobType = "unknown_default_open_api" ) // All allowed values of JobType enum var AllowedJobTypeEnumValues = []JobType{ - "CREATE_SANDBOX", - "START_SANDBOX", - "STOP_SANDBOX", - "DESTROY_SANDBOX", - "RESIZE_SANDBOX", + "CREATE_BOX", + "START_BOX", + "STOP_BOX", + "DESTROY_BOX", + "RESIZE_BOX", "CREATE_BACKUP", - "BUILD_SNAPSHOT", - "PULL_SNAPSHOT", - "RECOVER_SANDBOX", - "INSPECT_SNAPSHOT_IN_REGISTRY", - "REMOVE_SNAPSHOT", - "UPDATE_SANDBOX_NETWORK_SETTINGS", + "PULL_ARTIFACT", + "RECOVER_BOX", + "INSPECT_ARTIFACT_IN_REGISTRY", + "REMOVE_ARTIFACT", + "UPDATE_BOX_NETWORK_SETTINGS", + "unknown_default_open_api", } func (v *JobType) UnmarshalJSON(src []byte) error { @@ -65,7 +64,8 @@ func (v *JobType) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid JobType", value) + *v = JOBTYPE_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewJobTypeFromValue returns a pointer to a valid JobType @@ -75,7 +75,8 @@ func NewJobTypeFromValue(v string) (*JobType, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for JobType: valid values are %v", v, AllowedJobTypeEnumValues) + enumValue := JOBTYPE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -129,3 +130,4 @@ func (v *NullableJobType) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_keyboard_hotkey_request.go b/apps/api-client-go/model_keyboard_hotkey_request.go deleted file mode 100644 index e7a9f1ba4..000000000 --- a/apps/api-client-go/model_keyboard_hotkey_request.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the KeyboardHotkeyRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &KeyboardHotkeyRequest{} - -// KeyboardHotkeyRequest struct for KeyboardHotkeyRequest -type KeyboardHotkeyRequest struct { - // The hotkey combination to press (e.g., \"ctrl+c\", \"cmd+v\", \"alt+tab\") - Keys string `json:"keys"` - AdditionalProperties map[string]interface{} -} - -type _KeyboardHotkeyRequest KeyboardHotkeyRequest - -// NewKeyboardHotkeyRequest instantiates a new KeyboardHotkeyRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewKeyboardHotkeyRequest(keys string) *KeyboardHotkeyRequest { - this := KeyboardHotkeyRequest{} - this.Keys = keys - return &this -} - -// NewKeyboardHotkeyRequestWithDefaults instantiates a new KeyboardHotkeyRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewKeyboardHotkeyRequestWithDefaults() *KeyboardHotkeyRequest { - this := KeyboardHotkeyRequest{} - return &this -} - -// GetKeys returns the Keys field value -func (o *KeyboardHotkeyRequest) GetKeys() string { - if o == nil { - var ret string - return ret - } - - return o.Keys -} - -// GetKeysOk returns a tuple with the Keys field value -// and a boolean to check if the value has been set. -func (o *KeyboardHotkeyRequest) GetKeysOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Keys, true -} - -// SetKeys sets field value -func (o *KeyboardHotkeyRequest) SetKeys(v string) { - o.Keys = v -} - -func (o KeyboardHotkeyRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o KeyboardHotkeyRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["keys"] = o.Keys - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *KeyboardHotkeyRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "keys", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varKeyboardHotkeyRequest := _KeyboardHotkeyRequest{} - - err = json.Unmarshal(data, &varKeyboardHotkeyRequest) - - if err != nil { - return err - } - - *o = KeyboardHotkeyRequest(varKeyboardHotkeyRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "keys") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableKeyboardHotkeyRequest struct { - value *KeyboardHotkeyRequest - isSet bool -} - -func (v NullableKeyboardHotkeyRequest) Get() *KeyboardHotkeyRequest { - return v.value -} - -func (v *NullableKeyboardHotkeyRequest) Set(val *KeyboardHotkeyRequest) { - v.value = val - v.isSet = true -} - -func (v NullableKeyboardHotkeyRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableKeyboardHotkeyRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableKeyboardHotkeyRequest(val *KeyboardHotkeyRequest) *NullableKeyboardHotkeyRequest { - return &NullableKeyboardHotkeyRequest{value: val, isSet: true} -} - -func (v NullableKeyboardHotkeyRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableKeyboardHotkeyRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_keyboard_press_request.go b/apps/api-client-go/model_keyboard_press_request.go deleted file mode 100644 index 0a7540d91..000000000 --- a/apps/api-client-go/model_keyboard_press_request.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the KeyboardPressRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &KeyboardPressRequest{} - -// KeyboardPressRequest struct for KeyboardPressRequest -type KeyboardPressRequest struct { - // The key to press (e.g., a, b, c, enter, space, etc.) - Key string `json:"key"` - // Array of modifier keys to press along with the main key (ctrl, alt, shift, cmd) - Modifiers []string `json:"modifiers,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _KeyboardPressRequest KeyboardPressRequest - -// NewKeyboardPressRequest instantiates a new KeyboardPressRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewKeyboardPressRequest(key string) *KeyboardPressRequest { - this := KeyboardPressRequest{} - this.Key = key - return &this -} - -// NewKeyboardPressRequestWithDefaults instantiates a new KeyboardPressRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewKeyboardPressRequestWithDefaults() *KeyboardPressRequest { - this := KeyboardPressRequest{} - return &this -} - -// GetKey returns the Key field value -func (o *KeyboardPressRequest) GetKey() string { - if o == nil { - var ret string - return ret - } - - return o.Key -} - -// GetKeyOk returns a tuple with the Key field value -// and a boolean to check if the value has been set. -func (o *KeyboardPressRequest) GetKeyOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Key, true -} - -// SetKey sets field value -func (o *KeyboardPressRequest) SetKey(v string) { - o.Key = v -} - -// GetModifiers returns the Modifiers field value if set, zero value otherwise. -func (o *KeyboardPressRequest) GetModifiers() []string { - if o == nil || IsNil(o.Modifiers) { - var ret []string - return ret - } - return o.Modifiers -} - -// GetModifiersOk returns a tuple with the Modifiers field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *KeyboardPressRequest) GetModifiersOk() ([]string, bool) { - if o == nil || IsNil(o.Modifiers) { - return nil, false - } - return o.Modifiers, true -} - -// HasModifiers returns a boolean if a field has been set. -func (o *KeyboardPressRequest) HasModifiers() bool { - if o != nil && !IsNil(o.Modifiers) { - return true - } - - return false -} - -// SetModifiers gets a reference to the given []string and assigns it to the Modifiers field. -func (o *KeyboardPressRequest) SetModifiers(v []string) { - o.Modifiers = v -} - -func (o KeyboardPressRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o KeyboardPressRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["key"] = o.Key - if !IsNil(o.Modifiers) { - toSerialize["modifiers"] = o.Modifiers - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *KeyboardPressRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "key", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varKeyboardPressRequest := _KeyboardPressRequest{} - - err = json.Unmarshal(data, &varKeyboardPressRequest) - - if err != nil { - return err - } - - *o = KeyboardPressRequest(varKeyboardPressRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "key") - delete(additionalProperties, "modifiers") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableKeyboardPressRequest struct { - value *KeyboardPressRequest - isSet bool -} - -func (v NullableKeyboardPressRequest) Get() *KeyboardPressRequest { - return v.value -} - -func (v *NullableKeyboardPressRequest) Set(val *KeyboardPressRequest) { - v.value = val - v.isSet = true -} - -func (v NullableKeyboardPressRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableKeyboardPressRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableKeyboardPressRequest(val *KeyboardPressRequest) *NullableKeyboardPressRequest { - return &NullableKeyboardPressRequest{value: val, isSet: true} -} - -func (v NullableKeyboardPressRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableKeyboardPressRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_keyboard_type_request.go b/apps/api-client-go/model_keyboard_type_request.go deleted file mode 100644 index b936e1cdf..000000000 --- a/apps/api-client-go/model_keyboard_type_request.go +++ /dev/null @@ -1,206 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the KeyboardTypeRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &KeyboardTypeRequest{} - -// KeyboardTypeRequest struct for KeyboardTypeRequest -type KeyboardTypeRequest struct { - // The text to type using the keyboard - Text string `json:"text"` - // Delay in milliseconds between keystrokes. Defaults to 0 - Delay *float32 `json:"delay,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _KeyboardTypeRequest KeyboardTypeRequest - -// NewKeyboardTypeRequest instantiates a new KeyboardTypeRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewKeyboardTypeRequest(text string) *KeyboardTypeRequest { - this := KeyboardTypeRequest{} - this.Text = text - return &this -} - -// NewKeyboardTypeRequestWithDefaults instantiates a new KeyboardTypeRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewKeyboardTypeRequestWithDefaults() *KeyboardTypeRequest { - this := KeyboardTypeRequest{} - return &this -} - -// GetText returns the Text field value -func (o *KeyboardTypeRequest) GetText() string { - if o == nil { - var ret string - return ret - } - - return o.Text -} - -// GetTextOk returns a tuple with the Text field value -// and a boolean to check if the value has been set. -func (o *KeyboardTypeRequest) GetTextOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Text, true -} - -// SetText sets field value -func (o *KeyboardTypeRequest) SetText(v string) { - o.Text = v -} - -// GetDelay returns the Delay field value if set, zero value otherwise. -func (o *KeyboardTypeRequest) GetDelay() float32 { - if o == nil || IsNil(o.Delay) { - var ret float32 - return ret - } - return *o.Delay -} - -// GetDelayOk returns a tuple with the Delay field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *KeyboardTypeRequest) GetDelayOk() (*float32, bool) { - if o == nil || IsNil(o.Delay) { - return nil, false - } - return o.Delay, true -} - -// HasDelay returns a boolean if a field has been set. -func (o *KeyboardTypeRequest) HasDelay() bool { - if o != nil && !IsNil(o.Delay) { - return true - } - - return false -} - -// SetDelay gets a reference to the given float32 and assigns it to the Delay field. -func (o *KeyboardTypeRequest) SetDelay(v float32) { - o.Delay = &v -} - -func (o KeyboardTypeRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o KeyboardTypeRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["text"] = o.Text - if !IsNil(o.Delay) { - toSerialize["delay"] = o.Delay - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *KeyboardTypeRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "text", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varKeyboardTypeRequest := _KeyboardTypeRequest{} - - err = json.Unmarshal(data, &varKeyboardTypeRequest) - - if err != nil { - return err - } - - *o = KeyboardTypeRequest(varKeyboardTypeRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "text") - delete(additionalProperties, "delay") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableKeyboardTypeRequest struct { - value *KeyboardTypeRequest - isSet bool -} - -func (v NullableKeyboardTypeRequest) Get() *KeyboardTypeRequest { - return v.value -} - -func (v *NullableKeyboardTypeRequest) Set(val *KeyboardTypeRequest) { - v.value = val - v.isSet = true -} - -func (v NullableKeyboardTypeRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableKeyboardTypeRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableKeyboardTypeRequest(val *KeyboardTypeRequest) *NullableKeyboardTypeRequest { - return &NullableKeyboardTypeRequest{value: val, isSet: true} -} - -func (v NullableKeyboardTypeRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableKeyboardTypeRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_list_branch_response.go b/apps/api-client-go/model_list_branch_response.go deleted file mode 100644 index 3ea89639e..000000000 --- a/apps/api-client-go/model_list_branch_response.go +++ /dev/null @@ -1,167 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ListBranchResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ListBranchResponse{} - -// ListBranchResponse struct for ListBranchResponse -type ListBranchResponse struct { - Branches []string `json:"branches"` - AdditionalProperties map[string]interface{} -} - -type _ListBranchResponse ListBranchResponse - -// NewListBranchResponse instantiates a new ListBranchResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewListBranchResponse(branches []string) *ListBranchResponse { - this := ListBranchResponse{} - this.Branches = branches - return &this -} - -// NewListBranchResponseWithDefaults instantiates a new ListBranchResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewListBranchResponseWithDefaults() *ListBranchResponse { - this := ListBranchResponse{} - return &this -} - -// GetBranches returns the Branches field value -func (o *ListBranchResponse) GetBranches() []string { - if o == nil { - var ret []string - return ret - } - - return o.Branches -} - -// GetBranchesOk returns a tuple with the Branches field value -// and a boolean to check if the value has been set. -func (o *ListBranchResponse) GetBranchesOk() ([]string, bool) { - if o == nil { - return nil, false - } - return o.Branches, true -} - -// SetBranches sets field value -func (o *ListBranchResponse) SetBranches(v []string) { - o.Branches = v -} - -func (o ListBranchResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ListBranchResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["branches"] = o.Branches - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ListBranchResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "branches", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varListBranchResponse := _ListBranchResponse{} - - err = json.Unmarshal(data, &varListBranchResponse) - - if err != nil { - return err - } - - *o = ListBranchResponse(varListBranchResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "branches") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableListBranchResponse struct { - value *ListBranchResponse - isSet bool -} - -func (v NullableListBranchResponse) Get() *ListBranchResponse { - return v.value -} - -func (v *NullableListBranchResponse) Set(val *ListBranchResponse) { - v.value = val - v.isSet = true -} - -func (v NullableListBranchResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableListBranchResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableListBranchResponse(val *ListBranchResponse) *NullableListBranchResponse { - return &NullableListBranchResponse{value: val, isSet: true} -} - -func (v NullableListBranchResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableListBranchResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_log_entry.go b/apps/api-client-go/model_log_entry.go index f86f58e5b..f845a1f42 100644 --- a/apps/api-client-go/model_log_entry.go +++ b/apps/api-client-go/model_log_entry.go @@ -430,3 +430,5 @@ func (v *NullableLogEntry) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_lsp_completion_params.go b/apps/api-client-go/model_lsp_completion_params.go deleted file mode 100644 index 7e6388c42..000000000 --- a/apps/api-client-go/model_lsp_completion_params.go +++ /dev/null @@ -1,294 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the LspCompletionParams type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &LspCompletionParams{} - -// LspCompletionParams struct for LspCompletionParams -type LspCompletionParams struct { - // Language identifier - LanguageId string `json:"languageId"` - // Path to the project - PathToProject string `json:"pathToProject"` - // Document URI - Uri string `json:"uri"` - Position Position `json:"position"` - Context *CompletionContext `json:"context,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _LspCompletionParams LspCompletionParams - -// NewLspCompletionParams instantiates a new LspCompletionParams object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewLspCompletionParams(languageId string, pathToProject string, uri string, position Position) *LspCompletionParams { - this := LspCompletionParams{} - this.LanguageId = languageId - this.PathToProject = pathToProject - this.Uri = uri - this.Position = position - return &this -} - -// NewLspCompletionParamsWithDefaults instantiates a new LspCompletionParams object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewLspCompletionParamsWithDefaults() *LspCompletionParams { - this := LspCompletionParams{} - return &this -} - -// GetLanguageId returns the LanguageId field value -func (o *LspCompletionParams) GetLanguageId() string { - if o == nil { - var ret string - return ret - } - - return o.LanguageId -} - -// GetLanguageIdOk returns a tuple with the LanguageId field value -// and a boolean to check if the value has been set. -func (o *LspCompletionParams) GetLanguageIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.LanguageId, true -} - -// SetLanguageId sets field value -func (o *LspCompletionParams) SetLanguageId(v string) { - o.LanguageId = v -} - -// GetPathToProject returns the PathToProject field value -func (o *LspCompletionParams) GetPathToProject() string { - if o == nil { - var ret string - return ret - } - - return o.PathToProject -} - -// GetPathToProjectOk returns a tuple with the PathToProject field value -// and a boolean to check if the value has been set. -func (o *LspCompletionParams) GetPathToProjectOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.PathToProject, true -} - -// SetPathToProject sets field value -func (o *LspCompletionParams) SetPathToProject(v string) { - o.PathToProject = v -} - -// GetUri returns the Uri field value -func (o *LspCompletionParams) GetUri() string { - if o == nil { - var ret string - return ret - } - - return o.Uri -} - -// GetUriOk returns a tuple with the Uri field value -// and a boolean to check if the value has been set. -func (o *LspCompletionParams) GetUriOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Uri, true -} - -// SetUri sets field value -func (o *LspCompletionParams) SetUri(v string) { - o.Uri = v -} - -// GetPosition returns the Position field value -func (o *LspCompletionParams) GetPosition() Position { - if o == nil { - var ret Position - return ret - } - - return o.Position -} - -// GetPositionOk returns a tuple with the Position field value -// and a boolean to check if the value has been set. -func (o *LspCompletionParams) GetPositionOk() (*Position, bool) { - if o == nil { - return nil, false - } - return &o.Position, true -} - -// SetPosition sets field value -func (o *LspCompletionParams) SetPosition(v Position) { - o.Position = v -} - -// GetContext returns the Context field value if set, zero value otherwise. -func (o *LspCompletionParams) GetContext() CompletionContext { - if o == nil || IsNil(o.Context) { - var ret CompletionContext - return ret - } - return *o.Context -} - -// GetContextOk returns a tuple with the Context field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *LspCompletionParams) GetContextOk() (*CompletionContext, bool) { - if o == nil || IsNil(o.Context) { - return nil, false - } - return o.Context, true -} - -// HasContext returns a boolean if a field has been set. -func (o *LspCompletionParams) HasContext() bool { - if o != nil && !IsNil(o.Context) { - return true - } - - return false -} - -// SetContext gets a reference to the given CompletionContext and assigns it to the Context field. -func (o *LspCompletionParams) SetContext(v CompletionContext) { - o.Context = &v -} - -func (o LspCompletionParams) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o LspCompletionParams) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["languageId"] = o.LanguageId - toSerialize["pathToProject"] = o.PathToProject - toSerialize["uri"] = o.Uri - toSerialize["position"] = o.Position - if !IsNil(o.Context) { - toSerialize["context"] = o.Context - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *LspCompletionParams) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "languageId", - "pathToProject", - "uri", - "position", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varLspCompletionParams := _LspCompletionParams{} - - err = json.Unmarshal(data, &varLspCompletionParams) - - if err != nil { - return err - } - - *o = LspCompletionParams(varLspCompletionParams) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "languageId") - delete(additionalProperties, "pathToProject") - delete(additionalProperties, "uri") - delete(additionalProperties, "position") - delete(additionalProperties, "context") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableLspCompletionParams struct { - value *LspCompletionParams - isSet bool -} - -func (v NullableLspCompletionParams) Get() *LspCompletionParams { - return v.value -} - -func (v *NullableLspCompletionParams) Set(val *LspCompletionParams) { - v.value = val - v.isSet = true -} - -func (v NullableLspCompletionParams) IsSet() bool { - return v.isSet -} - -func (v *NullableLspCompletionParams) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableLspCompletionParams(val *LspCompletionParams) *NullableLspCompletionParams { - return &NullableLspCompletionParams{value: val, isSet: true} -} - -func (v NullableLspCompletionParams) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableLspCompletionParams) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_lsp_document_request.go b/apps/api-client-go/model_lsp_document_request.go deleted file mode 100644 index 4c478d2fa..000000000 --- a/apps/api-client-go/model_lsp_document_request.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the LspDocumentRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &LspDocumentRequest{} - -// LspDocumentRequest struct for LspDocumentRequest -type LspDocumentRequest struct { - // Language identifier - LanguageId string `json:"languageId"` - // Path to the project - PathToProject string `json:"pathToProject"` - // Document URI - Uri string `json:"uri"` - AdditionalProperties map[string]interface{} -} - -type _LspDocumentRequest LspDocumentRequest - -// NewLspDocumentRequest instantiates a new LspDocumentRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewLspDocumentRequest(languageId string, pathToProject string, uri string) *LspDocumentRequest { - this := LspDocumentRequest{} - this.LanguageId = languageId - this.PathToProject = pathToProject - this.Uri = uri - return &this -} - -// NewLspDocumentRequestWithDefaults instantiates a new LspDocumentRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewLspDocumentRequestWithDefaults() *LspDocumentRequest { - this := LspDocumentRequest{} - return &this -} - -// GetLanguageId returns the LanguageId field value -func (o *LspDocumentRequest) GetLanguageId() string { - if o == nil { - var ret string - return ret - } - - return o.LanguageId -} - -// GetLanguageIdOk returns a tuple with the LanguageId field value -// and a boolean to check if the value has been set. -func (o *LspDocumentRequest) GetLanguageIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.LanguageId, true -} - -// SetLanguageId sets field value -func (o *LspDocumentRequest) SetLanguageId(v string) { - o.LanguageId = v -} - -// GetPathToProject returns the PathToProject field value -func (o *LspDocumentRequest) GetPathToProject() string { - if o == nil { - var ret string - return ret - } - - return o.PathToProject -} - -// GetPathToProjectOk returns a tuple with the PathToProject field value -// and a boolean to check if the value has been set. -func (o *LspDocumentRequest) GetPathToProjectOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.PathToProject, true -} - -// SetPathToProject sets field value -func (o *LspDocumentRequest) SetPathToProject(v string) { - o.PathToProject = v -} - -// GetUri returns the Uri field value -func (o *LspDocumentRequest) GetUri() string { - if o == nil { - var ret string - return ret - } - - return o.Uri -} - -// GetUriOk returns a tuple with the Uri field value -// and a boolean to check if the value has been set. -func (o *LspDocumentRequest) GetUriOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Uri, true -} - -// SetUri sets field value -func (o *LspDocumentRequest) SetUri(v string) { - o.Uri = v -} - -func (o LspDocumentRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o LspDocumentRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["languageId"] = o.LanguageId - toSerialize["pathToProject"] = o.PathToProject - toSerialize["uri"] = o.Uri - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *LspDocumentRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "languageId", - "pathToProject", - "uri", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varLspDocumentRequest := _LspDocumentRequest{} - - err = json.Unmarshal(data, &varLspDocumentRequest) - - if err != nil { - return err - } - - *o = LspDocumentRequest(varLspDocumentRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "languageId") - delete(additionalProperties, "pathToProject") - delete(additionalProperties, "uri") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableLspDocumentRequest struct { - value *LspDocumentRequest - isSet bool -} - -func (v NullableLspDocumentRequest) Get() *LspDocumentRequest { - return v.value -} - -func (v *NullableLspDocumentRequest) Set(val *LspDocumentRequest) { - v.value = val - v.isSet = true -} - -func (v NullableLspDocumentRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableLspDocumentRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableLspDocumentRequest(val *LspDocumentRequest) *NullableLspDocumentRequest { - return &NullableLspDocumentRequest{value: val, isSet: true} -} - -func (v NullableLspDocumentRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableLspDocumentRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_lsp_location.go b/apps/api-client-go/model_lsp_location.go deleted file mode 100644 index ae5a3759d..000000000 --- a/apps/api-client-go/model_lsp_location.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the LspLocation type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &LspLocation{} - -// LspLocation struct for LspLocation -type LspLocation struct { - Range Range `json:"range"` - Uri string `json:"uri"` - AdditionalProperties map[string]interface{} -} - -type _LspLocation LspLocation - -// NewLspLocation instantiates a new LspLocation object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewLspLocation(range_ Range, uri string) *LspLocation { - this := LspLocation{} - this.Range = range_ - this.Uri = uri - return &this -} - -// NewLspLocationWithDefaults instantiates a new LspLocation object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewLspLocationWithDefaults() *LspLocation { - this := LspLocation{} - return &this -} - -// GetRange returns the Range field value -func (o *LspLocation) GetRange() Range { - if o == nil { - var ret Range - return ret - } - - return o.Range -} - -// GetRangeOk returns a tuple with the Range field value -// and a boolean to check if the value has been set. -func (o *LspLocation) GetRangeOk() (*Range, bool) { - if o == nil { - return nil, false - } - return &o.Range, true -} - -// SetRange sets field value -func (o *LspLocation) SetRange(v Range) { - o.Range = v -} - -// GetUri returns the Uri field value -func (o *LspLocation) GetUri() string { - if o == nil { - var ret string - return ret - } - - return o.Uri -} - -// GetUriOk returns a tuple with the Uri field value -// and a boolean to check if the value has been set. -func (o *LspLocation) GetUriOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Uri, true -} - -// SetUri sets field value -func (o *LspLocation) SetUri(v string) { - o.Uri = v -} - -func (o LspLocation) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o LspLocation) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["range"] = o.Range - toSerialize["uri"] = o.Uri - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *LspLocation) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "range", - "uri", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varLspLocation := _LspLocation{} - - err = json.Unmarshal(data, &varLspLocation) - - if err != nil { - return err - } - - *o = LspLocation(varLspLocation) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "range") - delete(additionalProperties, "uri") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableLspLocation struct { - value *LspLocation - isSet bool -} - -func (v NullableLspLocation) Get() *LspLocation { - return v.value -} - -func (v *NullableLspLocation) Set(val *LspLocation) { - v.value = val - v.isSet = true -} - -func (v NullableLspLocation) IsSet() bool { - return v.isSet -} - -func (v *NullableLspLocation) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableLspLocation(val *LspLocation) *NullableLspLocation { - return &NullableLspLocation{value: val, isSet: true} -} - -func (v NullableLspLocation) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableLspLocation) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_lsp_server_request.go b/apps/api-client-go/model_lsp_server_request.go deleted file mode 100644 index 4d144d23c..000000000 --- a/apps/api-client-go/model_lsp_server_request.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the LspServerRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &LspServerRequest{} - -// LspServerRequest struct for LspServerRequest -type LspServerRequest struct { - // Language identifier - LanguageId string `json:"languageId"` - // Path to the project - PathToProject string `json:"pathToProject"` - AdditionalProperties map[string]interface{} -} - -type _LspServerRequest LspServerRequest - -// NewLspServerRequest instantiates a new LspServerRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewLspServerRequest(languageId string, pathToProject string) *LspServerRequest { - this := LspServerRequest{} - this.LanguageId = languageId - this.PathToProject = pathToProject - return &this -} - -// NewLspServerRequestWithDefaults instantiates a new LspServerRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewLspServerRequestWithDefaults() *LspServerRequest { - this := LspServerRequest{} - return &this -} - -// GetLanguageId returns the LanguageId field value -func (o *LspServerRequest) GetLanguageId() string { - if o == nil { - var ret string - return ret - } - - return o.LanguageId -} - -// GetLanguageIdOk returns a tuple with the LanguageId field value -// and a boolean to check if the value has been set. -func (o *LspServerRequest) GetLanguageIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.LanguageId, true -} - -// SetLanguageId sets field value -func (o *LspServerRequest) SetLanguageId(v string) { - o.LanguageId = v -} - -// GetPathToProject returns the PathToProject field value -func (o *LspServerRequest) GetPathToProject() string { - if o == nil { - var ret string - return ret - } - - return o.PathToProject -} - -// GetPathToProjectOk returns a tuple with the PathToProject field value -// and a boolean to check if the value has been set. -func (o *LspServerRequest) GetPathToProjectOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.PathToProject, true -} - -// SetPathToProject sets field value -func (o *LspServerRequest) SetPathToProject(v string) { - o.PathToProject = v -} - -func (o LspServerRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o LspServerRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["languageId"] = o.LanguageId - toSerialize["pathToProject"] = o.PathToProject - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *LspServerRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "languageId", - "pathToProject", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varLspServerRequest := _LspServerRequest{} - - err = json.Unmarshal(data, &varLspServerRequest) - - if err != nil { - return err - } - - *o = LspServerRequest(varLspServerRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "languageId") - delete(additionalProperties, "pathToProject") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableLspServerRequest struct { - value *LspServerRequest - isSet bool -} - -func (v NullableLspServerRequest) Get() *LspServerRequest { - return v.value -} - -func (v *NullableLspServerRequest) Set(val *LspServerRequest) { - v.value = val - v.isSet = true -} - -func (v NullableLspServerRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableLspServerRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableLspServerRequest(val *LspServerRequest) *NullableLspServerRequest { - return &NullableLspServerRequest{value: val, isSet: true} -} - -func (v NullableLspServerRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableLspServerRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_lsp_symbol.go b/apps/api-client-go/model_lsp_symbol.go deleted file mode 100644 index 3b1a09576..000000000 --- a/apps/api-client-go/model_lsp_symbol.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the LspSymbol type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &LspSymbol{} - -// LspSymbol struct for LspSymbol -type LspSymbol struct { - Kind float32 `json:"kind"` - Location LspLocation `json:"location"` - Name string `json:"name"` - AdditionalProperties map[string]interface{} -} - -type _LspSymbol LspSymbol - -// NewLspSymbol instantiates a new LspSymbol object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewLspSymbol(kind float32, location LspLocation, name string) *LspSymbol { - this := LspSymbol{} - this.Kind = kind - this.Location = location - this.Name = name - return &this -} - -// NewLspSymbolWithDefaults instantiates a new LspSymbol object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewLspSymbolWithDefaults() *LspSymbol { - this := LspSymbol{} - return &this -} - -// GetKind returns the Kind field value -func (o *LspSymbol) GetKind() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Kind -} - -// GetKindOk returns a tuple with the Kind field value -// and a boolean to check if the value has been set. -func (o *LspSymbol) GetKindOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Kind, true -} - -// SetKind sets field value -func (o *LspSymbol) SetKind(v float32) { - o.Kind = v -} - -// GetLocation returns the Location field value -func (o *LspSymbol) GetLocation() LspLocation { - if o == nil { - var ret LspLocation - return ret - } - - return o.Location -} - -// GetLocationOk returns a tuple with the Location field value -// and a boolean to check if the value has been set. -func (o *LspSymbol) GetLocationOk() (*LspLocation, bool) { - if o == nil { - return nil, false - } - return &o.Location, true -} - -// SetLocation sets field value -func (o *LspSymbol) SetLocation(v LspLocation) { - o.Location = v -} - -// GetName returns the Name field value -func (o *LspSymbol) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *LspSymbol) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *LspSymbol) SetName(v string) { - o.Name = v -} - -func (o LspSymbol) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o LspSymbol) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["kind"] = o.Kind - toSerialize["location"] = o.Location - toSerialize["name"] = o.Name - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *LspSymbol) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "kind", - "location", - "name", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varLspSymbol := _LspSymbol{} - - err = json.Unmarshal(data, &varLspSymbol) - - if err != nil { - return err - } - - *o = LspSymbol(varLspSymbol) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "kind") - delete(additionalProperties, "location") - delete(additionalProperties, "name") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableLspSymbol struct { - value *LspSymbol - isSet bool -} - -func (v NullableLspSymbol) Get() *LspSymbol { - return v.value -} - -func (v *NullableLspSymbol) Set(val *LspSymbol) { - v.value = val - v.isSet = true -} - -func (v NullableLspSymbol) IsSet() bool { - return v.isSet -} - -func (v *NullableLspSymbol) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableLspSymbol(val *LspSymbol) *NullableLspSymbol { - return &NullableLspSymbol{value: val, isSet: true} -} - -func (v NullableLspSymbol) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableLspSymbol) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_match.go b/apps/api-client-go/model_match.go deleted file mode 100644 index bcd172380..000000000 --- a/apps/api-client-go/model_match.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Match type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Match{} - -// Match struct for Match -type Match struct { - File string `json:"file"` - Line float32 `json:"line"` - Content string `json:"content"` - AdditionalProperties map[string]interface{} -} - -type _Match Match - -// NewMatch instantiates a new Match object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMatch(file string, line float32, content string) *Match { - this := Match{} - this.File = file - this.Line = line - this.Content = content - return &this -} - -// NewMatchWithDefaults instantiates a new Match object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMatchWithDefaults() *Match { - this := Match{} - return &this -} - -// GetFile returns the File field value -func (o *Match) GetFile() string { - if o == nil { - var ret string - return ret - } - - return o.File -} - -// GetFileOk returns a tuple with the File field value -// and a boolean to check if the value has been set. -func (o *Match) GetFileOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.File, true -} - -// SetFile sets field value -func (o *Match) SetFile(v string) { - o.File = v -} - -// GetLine returns the Line field value -func (o *Match) GetLine() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Line -} - -// GetLineOk returns a tuple with the Line field value -// and a boolean to check if the value has been set. -func (o *Match) GetLineOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Line, true -} - -// SetLine sets field value -func (o *Match) SetLine(v float32) { - o.Line = v -} - -// GetContent returns the Content field value -func (o *Match) GetContent() string { - if o == nil { - var ret string - return ret - } - - return o.Content -} - -// GetContentOk returns a tuple with the Content field value -// and a boolean to check if the value has been set. -func (o *Match) GetContentOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Content, true -} - -// SetContent sets field value -func (o *Match) SetContent(v string) { - o.Content = v -} - -func (o Match) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Match) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["file"] = o.File - toSerialize["line"] = o.Line - toSerialize["content"] = o.Content - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Match) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "file", - "line", - "content", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMatch := _Match{} - - err = json.Unmarshal(data, &varMatch) - - if err != nil { - return err - } - - *o = Match(varMatch) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "file") - delete(additionalProperties, "line") - delete(additionalProperties, "content") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMatch struct { - value *Match - isSet bool -} - -func (v NullableMatch) Get() *Match { - return v.value -} - -func (v *NullableMatch) Set(val *Match) { - v.value = val - v.isSet = true -} - -func (v NullableMatch) IsSet() bool { - return v.isSet -} - -func (v *NullableMatch) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMatch(val *Match) *NullableMatch { - return &NullableMatch{value: val, isSet: true} -} - -func (v NullableMatch) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMatch) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_metric_data_point.go b/apps/api-client-go/model_metric_data_point.go index ad3354af0..44e3f045d 100644 --- a/apps/api-client-go/model_metric_data_point.go +++ b/apps/api-client-go/model_metric_data_point.go @@ -196,3 +196,5 @@ func (v *NullableMetricDataPoint) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_metric_series.go b/apps/api-client-go/model_metric_series.go index d6af4f967..e5e8d46a5 100644 --- a/apps/api-client-go/model_metric_series.go +++ b/apps/api-client-go/model_metric_series.go @@ -196,3 +196,5 @@ func (v *NullableMetricSeries) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_metrics_response.go b/apps/api-client-go/model_metrics_response.go index 2f72a1ef4..3fcba8415 100644 --- a/apps/api-client-go/model_metrics_response.go +++ b/apps/api-client-go/model_metrics_response.go @@ -166,3 +166,5 @@ func (v *NullableMetricsResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_mouse_click_request.go b/apps/api-client-go/model_mouse_click_request.go deleted file mode 100644 index a292c4323..000000000 --- a/apps/api-client-go/model_mouse_click_request.go +++ /dev/null @@ -1,274 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseClickRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseClickRequest{} - -// MouseClickRequest struct for MouseClickRequest -type MouseClickRequest struct { - // The X coordinate where to perform the mouse click - X float32 `json:"x"` - // The Y coordinate where to perform the mouse click - Y float32 `json:"y"` - // The mouse button to click (left, right, middle). Defaults to left - Button *string `json:"button,omitempty"` - // Whether to perform a double-click instead of a single click - Double *bool `json:"double,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _MouseClickRequest MouseClickRequest - -// NewMouseClickRequest instantiates a new MouseClickRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseClickRequest(x float32, y float32) *MouseClickRequest { - this := MouseClickRequest{} - this.X = x - this.Y = y - return &this -} - -// NewMouseClickRequestWithDefaults instantiates a new MouseClickRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseClickRequestWithDefaults() *MouseClickRequest { - this := MouseClickRequest{} - return &this -} - -// GetX returns the X field value -func (o *MouseClickRequest) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseClickRequest) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseClickRequest) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseClickRequest) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseClickRequest) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseClickRequest) SetY(v float32) { - o.Y = v -} - -// GetButton returns the Button field value if set, zero value otherwise. -func (o *MouseClickRequest) GetButton() string { - if o == nil || IsNil(o.Button) { - var ret string - return ret - } - return *o.Button -} - -// GetButtonOk returns a tuple with the Button field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *MouseClickRequest) GetButtonOk() (*string, bool) { - if o == nil || IsNil(o.Button) { - return nil, false - } - return o.Button, true -} - -// HasButton returns a boolean if a field has been set. -func (o *MouseClickRequest) HasButton() bool { - if o != nil && !IsNil(o.Button) { - return true - } - - return false -} - -// SetButton gets a reference to the given string and assigns it to the Button field. -func (o *MouseClickRequest) SetButton(v string) { - o.Button = &v -} - -// GetDouble returns the Double field value if set, zero value otherwise. -func (o *MouseClickRequest) GetDouble() bool { - if o == nil || IsNil(o.Double) { - var ret bool - return ret - } - return *o.Double -} - -// GetDoubleOk returns a tuple with the Double field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *MouseClickRequest) GetDoubleOk() (*bool, bool) { - if o == nil || IsNil(o.Double) { - return nil, false - } - return o.Double, true -} - -// HasDouble returns a boolean if a field has been set. -func (o *MouseClickRequest) HasDouble() bool { - if o != nil && !IsNil(o.Double) { - return true - } - - return false -} - -// SetDouble gets a reference to the given bool and assigns it to the Double field. -func (o *MouseClickRequest) SetDouble(v bool) { - o.Double = &v -} - -func (o MouseClickRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseClickRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - if !IsNil(o.Button) { - toSerialize["button"] = o.Button - } - if !IsNil(o.Double) { - toSerialize["double"] = o.Double - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseClickRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseClickRequest := _MouseClickRequest{} - - err = json.Unmarshal(data, &varMouseClickRequest) - - if err != nil { - return err - } - - *o = MouseClickRequest(varMouseClickRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - delete(additionalProperties, "button") - delete(additionalProperties, "double") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseClickRequest struct { - value *MouseClickRequest - isSet bool -} - -func (v NullableMouseClickRequest) Get() *MouseClickRequest { - return v.value -} - -func (v *NullableMouseClickRequest) Set(val *MouseClickRequest) { - v.value = val - v.isSet = true -} - -func (v NullableMouseClickRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseClickRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseClickRequest(val *MouseClickRequest) *NullableMouseClickRequest { - return &NullableMouseClickRequest{value: val, isSet: true} -} - -func (v NullableMouseClickRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseClickRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_click_response.go b/apps/api-client-go/model_mouse_click_response.go deleted file mode 100644 index e17be26b7..000000000 --- a/apps/api-client-go/model_mouse_click_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseClickResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseClickResponse{} - -// MouseClickResponse struct for MouseClickResponse -type MouseClickResponse struct { - // The actual X coordinate where the click occurred - X float32 `json:"x"` - // The actual Y coordinate where the click occurred - Y float32 `json:"y"` - AdditionalProperties map[string]interface{} -} - -type _MouseClickResponse MouseClickResponse - -// NewMouseClickResponse instantiates a new MouseClickResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseClickResponse(x float32, y float32) *MouseClickResponse { - this := MouseClickResponse{} - this.X = x - this.Y = y - return &this -} - -// NewMouseClickResponseWithDefaults instantiates a new MouseClickResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseClickResponseWithDefaults() *MouseClickResponse { - this := MouseClickResponse{} - return &this -} - -// GetX returns the X field value -func (o *MouseClickResponse) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseClickResponse) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseClickResponse) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseClickResponse) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseClickResponse) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseClickResponse) SetY(v float32) { - o.Y = v -} - -func (o MouseClickResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseClickResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseClickResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseClickResponse := _MouseClickResponse{} - - err = json.Unmarshal(data, &varMouseClickResponse) - - if err != nil { - return err - } - - *o = MouseClickResponse(varMouseClickResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseClickResponse struct { - value *MouseClickResponse - isSet bool -} - -func (v NullableMouseClickResponse) Get() *MouseClickResponse { - return v.value -} - -func (v *NullableMouseClickResponse) Set(val *MouseClickResponse) { - v.value = val - v.isSet = true -} - -func (v NullableMouseClickResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseClickResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseClickResponse(val *MouseClickResponse) *NullableMouseClickResponse { - return &NullableMouseClickResponse{value: val, isSet: true} -} - -func (v NullableMouseClickResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseClickResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_drag_request.go b/apps/api-client-go/model_mouse_drag_request.go deleted file mode 100644 index 0b8dc423c..000000000 --- a/apps/api-client-go/model_mouse_drag_request.go +++ /dev/null @@ -1,296 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseDragRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseDragRequest{} - -// MouseDragRequest struct for MouseDragRequest -type MouseDragRequest struct { - // The starting X coordinate for the drag operation - StartX float32 `json:"startX"` - // The starting Y coordinate for the drag operation - StartY float32 `json:"startY"` - // The ending X coordinate for the drag operation - EndX float32 `json:"endX"` - // The ending Y coordinate for the drag operation - EndY float32 `json:"endY"` - // The mouse button to use for dragging (left, right, middle). Defaults to left - Button *string `json:"button,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _MouseDragRequest MouseDragRequest - -// NewMouseDragRequest instantiates a new MouseDragRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseDragRequest(startX float32, startY float32, endX float32, endY float32) *MouseDragRequest { - this := MouseDragRequest{} - this.StartX = startX - this.StartY = startY - this.EndX = endX - this.EndY = endY - return &this -} - -// NewMouseDragRequestWithDefaults instantiates a new MouseDragRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseDragRequestWithDefaults() *MouseDragRequest { - this := MouseDragRequest{} - return &this -} - -// GetStartX returns the StartX field value -func (o *MouseDragRequest) GetStartX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.StartX -} - -// GetStartXOk returns a tuple with the StartX field value -// and a boolean to check if the value has been set. -func (o *MouseDragRequest) GetStartXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.StartX, true -} - -// SetStartX sets field value -func (o *MouseDragRequest) SetStartX(v float32) { - o.StartX = v -} - -// GetStartY returns the StartY field value -func (o *MouseDragRequest) GetStartY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.StartY -} - -// GetStartYOk returns a tuple with the StartY field value -// and a boolean to check if the value has been set. -func (o *MouseDragRequest) GetStartYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.StartY, true -} - -// SetStartY sets field value -func (o *MouseDragRequest) SetStartY(v float32) { - o.StartY = v -} - -// GetEndX returns the EndX field value -func (o *MouseDragRequest) GetEndX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.EndX -} - -// GetEndXOk returns a tuple with the EndX field value -// and a boolean to check if the value has been set. -func (o *MouseDragRequest) GetEndXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.EndX, true -} - -// SetEndX sets field value -func (o *MouseDragRequest) SetEndX(v float32) { - o.EndX = v -} - -// GetEndY returns the EndY field value -func (o *MouseDragRequest) GetEndY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.EndY -} - -// GetEndYOk returns a tuple with the EndY field value -// and a boolean to check if the value has been set. -func (o *MouseDragRequest) GetEndYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.EndY, true -} - -// SetEndY sets field value -func (o *MouseDragRequest) SetEndY(v float32) { - o.EndY = v -} - -// GetButton returns the Button field value if set, zero value otherwise. -func (o *MouseDragRequest) GetButton() string { - if o == nil || IsNil(o.Button) { - var ret string - return ret - } - return *o.Button -} - -// GetButtonOk returns a tuple with the Button field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *MouseDragRequest) GetButtonOk() (*string, bool) { - if o == nil || IsNil(o.Button) { - return nil, false - } - return o.Button, true -} - -// HasButton returns a boolean if a field has been set. -func (o *MouseDragRequest) HasButton() bool { - if o != nil && !IsNil(o.Button) { - return true - } - - return false -} - -// SetButton gets a reference to the given string and assigns it to the Button field. -func (o *MouseDragRequest) SetButton(v string) { - o.Button = &v -} - -func (o MouseDragRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseDragRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["startX"] = o.StartX - toSerialize["startY"] = o.StartY - toSerialize["endX"] = o.EndX - toSerialize["endY"] = o.EndY - if !IsNil(o.Button) { - toSerialize["button"] = o.Button - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseDragRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "startX", - "startY", - "endX", - "endY", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseDragRequest := _MouseDragRequest{} - - err = json.Unmarshal(data, &varMouseDragRequest) - - if err != nil { - return err - } - - *o = MouseDragRequest(varMouseDragRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "startX") - delete(additionalProperties, "startY") - delete(additionalProperties, "endX") - delete(additionalProperties, "endY") - delete(additionalProperties, "button") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseDragRequest struct { - value *MouseDragRequest - isSet bool -} - -func (v NullableMouseDragRequest) Get() *MouseDragRequest { - return v.value -} - -func (v *NullableMouseDragRequest) Set(val *MouseDragRequest) { - v.value = val - v.isSet = true -} - -func (v NullableMouseDragRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseDragRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseDragRequest(val *MouseDragRequest) *NullableMouseDragRequest { - return &NullableMouseDragRequest{value: val, isSet: true} -} - -func (v NullableMouseDragRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseDragRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_drag_response.go b/apps/api-client-go/model_mouse_drag_response.go deleted file mode 100644 index b368f816c..000000000 --- a/apps/api-client-go/model_mouse_drag_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseDragResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseDragResponse{} - -// MouseDragResponse struct for MouseDragResponse -type MouseDragResponse struct { - // The actual X coordinate where the drag ended - X float32 `json:"x"` - // The actual Y coordinate where the drag ended - Y float32 `json:"y"` - AdditionalProperties map[string]interface{} -} - -type _MouseDragResponse MouseDragResponse - -// NewMouseDragResponse instantiates a new MouseDragResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseDragResponse(x float32, y float32) *MouseDragResponse { - this := MouseDragResponse{} - this.X = x - this.Y = y - return &this -} - -// NewMouseDragResponseWithDefaults instantiates a new MouseDragResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseDragResponseWithDefaults() *MouseDragResponse { - this := MouseDragResponse{} - return &this -} - -// GetX returns the X field value -func (o *MouseDragResponse) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseDragResponse) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseDragResponse) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseDragResponse) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseDragResponse) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseDragResponse) SetY(v float32) { - o.Y = v -} - -func (o MouseDragResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseDragResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseDragResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseDragResponse := _MouseDragResponse{} - - err = json.Unmarshal(data, &varMouseDragResponse) - - if err != nil { - return err - } - - *o = MouseDragResponse(varMouseDragResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseDragResponse struct { - value *MouseDragResponse - isSet bool -} - -func (v NullableMouseDragResponse) Get() *MouseDragResponse { - return v.value -} - -func (v *NullableMouseDragResponse) Set(val *MouseDragResponse) { - v.value = val - v.isSet = true -} - -func (v NullableMouseDragResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseDragResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseDragResponse(val *MouseDragResponse) *NullableMouseDragResponse { - return &NullableMouseDragResponse{value: val, isSet: true} -} - -func (v NullableMouseDragResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseDragResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_move_request.go b/apps/api-client-go/model_mouse_move_request.go deleted file mode 100644 index ec4f69439..000000000 --- a/apps/api-client-go/model_mouse_move_request.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseMoveRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseMoveRequest{} - -// MouseMoveRequest struct for MouseMoveRequest -type MouseMoveRequest struct { - // The target X coordinate to move the mouse cursor to - X float32 `json:"x"` - // The target Y coordinate to move the mouse cursor to - Y float32 `json:"y"` - AdditionalProperties map[string]interface{} -} - -type _MouseMoveRequest MouseMoveRequest - -// NewMouseMoveRequest instantiates a new MouseMoveRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseMoveRequest(x float32, y float32) *MouseMoveRequest { - this := MouseMoveRequest{} - this.X = x - this.Y = y - return &this -} - -// NewMouseMoveRequestWithDefaults instantiates a new MouseMoveRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseMoveRequestWithDefaults() *MouseMoveRequest { - this := MouseMoveRequest{} - return &this -} - -// GetX returns the X field value -func (o *MouseMoveRequest) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseMoveRequest) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseMoveRequest) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseMoveRequest) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseMoveRequest) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseMoveRequest) SetY(v float32) { - o.Y = v -} - -func (o MouseMoveRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseMoveRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseMoveRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseMoveRequest := _MouseMoveRequest{} - - err = json.Unmarshal(data, &varMouseMoveRequest) - - if err != nil { - return err - } - - *o = MouseMoveRequest(varMouseMoveRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseMoveRequest struct { - value *MouseMoveRequest - isSet bool -} - -func (v NullableMouseMoveRequest) Get() *MouseMoveRequest { - return v.value -} - -func (v *NullableMouseMoveRequest) Set(val *MouseMoveRequest) { - v.value = val - v.isSet = true -} - -func (v NullableMouseMoveRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseMoveRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseMoveRequest(val *MouseMoveRequest) *NullableMouseMoveRequest { - return &NullableMouseMoveRequest{value: val, isSet: true} -} - -func (v NullableMouseMoveRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseMoveRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_move_response.go b/apps/api-client-go/model_mouse_move_response.go deleted file mode 100644 index 27d8d3d76..000000000 --- a/apps/api-client-go/model_mouse_move_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseMoveResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseMoveResponse{} - -// MouseMoveResponse struct for MouseMoveResponse -type MouseMoveResponse struct { - // The actual X coordinate where the mouse cursor ended up - X float32 `json:"x"` - // The actual Y coordinate where the mouse cursor ended up - Y float32 `json:"y"` - AdditionalProperties map[string]interface{} -} - -type _MouseMoveResponse MouseMoveResponse - -// NewMouseMoveResponse instantiates a new MouseMoveResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseMoveResponse(x float32, y float32) *MouseMoveResponse { - this := MouseMoveResponse{} - this.X = x - this.Y = y - return &this -} - -// NewMouseMoveResponseWithDefaults instantiates a new MouseMoveResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseMoveResponseWithDefaults() *MouseMoveResponse { - this := MouseMoveResponse{} - return &this -} - -// GetX returns the X field value -func (o *MouseMoveResponse) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseMoveResponse) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseMoveResponse) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseMoveResponse) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseMoveResponse) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseMoveResponse) SetY(v float32) { - o.Y = v -} - -func (o MouseMoveResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseMoveResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseMoveResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseMoveResponse := _MouseMoveResponse{} - - err = json.Unmarshal(data, &varMouseMoveResponse) - - if err != nil { - return err - } - - *o = MouseMoveResponse(varMouseMoveResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseMoveResponse struct { - value *MouseMoveResponse - isSet bool -} - -func (v NullableMouseMoveResponse) Get() *MouseMoveResponse { - return v.value -} - -func (v *NullableMouseMoveResponse) Set(val *MouseMoveResponse) { - v.value = val - v.isSet = true -} - -func (v NullableMouseMoveResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseMoveResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseMoveResponse(val *MouseMoveResponse) *NullableMouseMoveResponse { - return &NullableMouseMoveResponse{value: val, isSet: true} -} - -func (v NullableMouseMoveResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseMoveResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_position.go b/apps/api-client-go/model_mouse_position.go deleted file mode 100644 index 606cbffab..000000000 --- a/apps/api-client-go/model_mouse_position.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MousePosition type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MousePosition{} - -// MousePosition struct for MousePosition -type MousePosition struct { - // The X coordinate of the mouse cursor position - X float32 `json:"x"` - // The Y coordinate of the mouse cursor position - Y float32 `json:"y"` - AdditionalProperties map[string]interface{} -} - -type _MousePosition MousePosition - -// NewMousePosition instantiates a new MousePosition object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMousePosition(x float32, y float32) *MousePosition { - this := MousePosition{} - this.X = x - this.Y = y - return &this -} - -// NewMousePositionWithDefaults instantiates a new MousePosition object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMousePositionWithDefaults() *MousePosition { - this := MousePosition{} - return &this -} - -// GetX returns the X field value -func (o *MousePosition) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MousePosition) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MousePosition) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MousePosition) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MousePosition) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MousePosition) SetY(v float32) { - o.Y = v -} - -func (o MousePosition) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MousePosition) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MousePosition) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMousePosition := _MousePosition{} - - err = json.Unmarshal(data, &varMousePosition) - - if err != nil { - return err - } - - *o = MousePosition(varMousePosition) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMousePosition struct { - value *MousePosition - isSet bool -} - -func (v NullableMousePosition) Get() *MousePosition { - return v.value -} - -func (v *NullableMousePosition) Set(val *MousePosition) { - v.value = val - v.isSet = true -} - -func (v NullableMousePosition) IsSet() bool { - return v.isSet -} - -func (v *NullableMousePosition) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMousePosition(val *MousePosition) *NullableMousePosition { - return &NullableMousePosition{value: val, isSet: true} -} - -func (v NullableMousePosition) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMousePosition) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_scroll_request.go b/apps/api-client-go/model_mouse_scroll_request.go deleted file mode 100644 index 18d406385..000000000 --- a/apps/api-client-go/model_mouse_scroll_request.go +++ /dev/null @@ -1,266 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseScrollRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseScrollRequest{} - -// MouseScrollRequest struct for MouseScrollRequest -type MouseScrollRequest struct { - // The X coordinate where to perform the scroll operation - X float32 `json:"x"` - // The Y coordinate where to perform the scroll operation - Y float32 `json:"y"` - // The scroll direction (up, down) - Direction string `json:"direction"` - // The number of scroll units to scroll. Defaults to 1 - Amount *float32 `json:"amount,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _MouseScrollRequest MouseScrollRequest - -// NewMouseScrollRequest instantiates a new MouseScrollRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseScrollRequest(x float32, y float32, direction string) *MouseScrollRequest { - this := MouseScrollRequest{} - this.X = x - this.Y = y - this.Direction = direction - return &this -} - -// NewMouseScrollRequestWithDefaults instantiates a new MouseScrollRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseScrollRequestWithDefaults() *MouseScrollRequest { - this := MouseScrollRequest{} - return &this -} - -// GetX returns the X field value -func (o *MouseScrollRequest) GetX() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.X -} - -// GetXOk returns a tuple with the X field value -// and a boolean to check if the value has been set. -func (o *MouseScrollRequest) GetXOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.X, true -} - -// SetX sets field value -func (o *MouseScrollRequest) SetX(v float32) { - o.X = v -} - -// GetY returns the Y field value -func (o *MouseScrollRequest) GetY() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Y -} - -// GetYOk returns a tuple with the Y field value -// and a boolean to check if the value has been set. -func (o *MouseScrollRequest) GetYOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Y, true -} - -// SetY sets field value -func (o *MouseScrollRequest) SetY(v float32) { - o.Y = v -} - -// GetDirection returns the Direction field value -func (o *MouseScrollRequest) GetDirection() string { - if o == nil { - var ret string - return ret - } - - return o.Direction -} - -// GetDirectionOk returns a tuple with the Direction field value -// and a boolean to check if the value has been set. -func (o *MouseScrollRequest) GetDirectionOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Direction, true -} - -// SetDirection sets field value -func (o *MouseScrollRequest) SetDirection(v string) { - o.Direction = v -} - -// GetAmount returns the Amount field value if set, zero value otherwise. -func (o *MouseScrollRequest) GetAmount() float32 { - if o == nil || IsNil(o.Amount) { - var ret float32 - return ret - } - return *o.Amount -} - -// GetAmountOk returns a tuple with the Amount field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *MouseScrollRequest) GetAmountOk() (*float32, bool) { - if o == nil || IsNil(o.Amount) { - return nil, false - } - return o.Amount, true -} - -// HasAmount returns a boolean if a field has been set. -func (o *MouseScrollRequest) HasAmount() bool { - if o != nil && !IsNil(o.Amount) { - return true - } - - return false -} - -// SetAmount gets a reference to the given float32 and assigns it to the Amount field. -func (o *MouseScrollRequest) SetAmount(v float32) { - o.Amount = &v -} - -func (o MouseScrollRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseScrollRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["x"] = o.X - toSerialize["y"] = o.Y - toSerialize["direction"] = o.Direction - if !IsNil(o.Amount) { - toSerialize["amount"] = o.Amount - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseScrollRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "x", - "y", - "direction", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseScrollRequest := _MouseScrollRequest{} - - err = json.Unmarshal(data, &varMouseScrollRequest) - - if err != nil { - return err - } - - *o = MouseScrollRequest(varMouseScrollRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "x") - delete(additionalProperties, "y") - delete(additionalProperties, "direction") - delete(additionalProperties, "amount") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseScrollRequest struct { - value *MouseScrollRequest - isSet bool -} - -func (v NullableMouseScrollRequest) Get() *MouseScrollRequest { - return v.value -} - -func (v *NullableMouseScrollRequest) Set(val *MouseScrollRequest) { - v.value = val - v.isSet = true -} - -func (v NullableMouseScrollRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseScrollRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseScrollRequest(val *MouseScrollRequest) *NullableMouseScrollRequest { - return &NullableMouseScrollRequest{value: val, isSet: true} -} - -func (v NullableMouseScrollRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseScrollRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_mouse_scroll_response.go b/apps/api-client-go/model_mouse_scroll_response.go deleted file mode 100644 index f11dbb5ce..000000000 --- a/apps/api-client-go/model_mouse_scroll_response.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the MouseScrollResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &MouseScrollResponse{} - -// MouseScrollResponse struct for MouseScrollResponse -type MouseScrollResponse struct { - // Whether the mouse scroll operation was successful - Success bool `json:"success"` - AdditionalProperties map[string]interface{} -} - -type _MouseScrollResponse MouseScrollResponse - -// NewMouseScrollResponse instantiates a new MouseScrollResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewMouseScrollResponse(success bool) *MouseScrollResponse { - this := MouseScrollResponse{} - this.Success = success - return &this -} - -// NewMouseScrollResponseWithDefaults instantiates a new MouseScrollResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewMouseScrollResponseWithDefaults() *MouseScrollResponse { - this := MouseScrollResponse{} - return &this -} - -// GetSuccess returns the Success field value -func (o *MouseScrollResponse) GetSuccess() bool { - if o == nil { - var ret bool - return ret - } - - return o.Success -} - -// GetSuccessOk returns a tuple with the Success field value -// and a boolean to check if the value has been set. -func (o *MouseScrollResponse) GetSuccessOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.Success, true -} - -// SetSuccess sets field value -func (o *MouseScrollResponse) SetSuccess(v bool) { - o.Success = v -} - -func (o MouseScrollResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o MouseScrollResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["success"] = o.Success - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *MouseScrollResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "success", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varMouseScrollResponse := _MouseScrollResponse{} - - err = json.Unmarshal(data, &varMouseScrollResponse) - - if err != nil { - return err - } - - *o = MouseScrollResponse(varMouseScrollResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "success") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableMouseScrollResponse struct { - value *MouseScrollResponse - isSet bool -} - -func (v NullableMouseScrollResponse) Get() *MouseScrollResponse { - return v.value -} - -func (v *NullableMouseScrollResponse) Set(val *MouseScrollResponse) { - v.value = val - v.isSet = true -} - -func (v NullableMouseScrollResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableMouseScrollResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableMouseScrollResponse(val *MouseScrollResponse) *NullableMouseScrollResponse { - return &NullableMouseScrollResponse{value: val, isSet: true} -} - -func (v NullableMouseScrollResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableMouseScrollResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_oidc_config.go b/apps/api-client-go/model_oidc_config.go index df5db8034..c0f53ea22 100644 --- a/apps/api-client-go/model_oidc_config.go +++ b/apps/api-client-go/model_oidc_config.go @@ -27,6 +27,8 @@ type OidcConfig struct { ClientId string `json:"clientId"` // OIDC audience Audience string `json:"audience"` + // OIDC end-session endpoint. Set when the IdP does not advertise one via discovery (e.g. Dex) and BoxLite hosts a compatible logout endpoint. + EndSessionEndpoint *string `json:"endSessionEndpoint,omitempty"` AdditionalProperties map[string]interface{} } @@ -124,6 +126,38 @@ func (o *OidcConfig) SetAudience(v string) { o.Audience = v } +// GetEndSessionEndpoint returns the EndSessionEndpoint field value if set, zero value otherwise. +func (o *OidcConfig) GetEndSessionEndpoint() string { + if o == nil || IsNil(o.EndSessionEndpoint) { + var ret string + return ret + } + return *o.EndSessionEndpoint +} + +// GetEndSessionEndpointOk returns a tuple with the EndSessionEndpoint field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *OidcConfig) GetEndSessionEndpointOk() (*string, bool) { + if o == nil || IsNil(o.EndSessionEndpoint) { + return nil, false + } + return o.EndSessionEndpoint, true +} + +// HasEndSessionEndpoint returns a boolean if a field has been set. +func (o *OidcConfig) HasEndSessionEndpoint() bool { + if o != nil && !IsNil(o.EndSessionEndpoint) { + return true + } + + return false +} + +// SetEndSessionEndpoint gets a reference to the given string and assigns it to the EndSessionEndpoint field. +func (o *OidcConfig) SetEndSessionEndpoint(v string) { + o.EndSessionEndpoint = &v +} + func (o OidcConfig) MarshalJSON() ([]byte, error) { toSerialize,err := o.ToMap() if err != nil { @@ -137,6 +171,9 @@ func (o OidcConfig) ToMap() (map[string]interface{}, error) { toSerialize["issuer"] = o.Issuer toSerialize["clientId"] = o.ClientId toSerialize["audience"] = o.Audience + if !IsNil(o.EndSessionEndpoint) { + toSerialize["endSessionEndpoint"] = o.EndSessionEndpoint + } for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -185,6 +222,7 @@ func (o *OidcConfig) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "issuer") delete(additionalProperties, "clientId") delete(additionalProperties, "audience") + delete(additionalProperties, "endSessionEndpoint") o.AdditionalProperties = additionalProperties } @@ -226,3 +264,5 @@ func (v *NullableOidcConfig) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_organization.go b/apps/api-client-go/model_organization.go index dfcc8f161..b38f58d71 100644 --- a/apps/api-client-go/model_organization.go +++ b/apps/api-client-go/model_organization.go @@ -28,7 +28,10 @@ type Organization struct { Name string `json:"name"` // User ID of the organization creator CreatedBy string `json:"createdBy"` - // Personal organization flag + // Whether this organization is the authenticated user default organization + IsDefaultForAuthenticatedUser bool `json:"isDefaultForAuthenticatedUser"` + // Deprecated alias for isDefaultForAuthenticatedUser. Kept for backward compatibility with older REST clients. + // Deprecated Personal bool `json:"personal"` // Creation timestamp CreatedAt time.Time `json:"createdAt"` @@ -44,32 +47,32 @@ type Organization struct { SuspendedUntil time.Time `json:"suspendedUntil"` // Suspension cleanup grace period hours SuspensionCleanupGracePeriodHours float32 `json:"suspensionCleanupGracePeriodHours"` - // Max CPU per sandbox - MaxCpuPerSandbox float32 `json:"maxCpuPerSandbox"` - // Max memory per sandbox - MaxMemoryPerSandbox float32 `json:"maxMemoryPerSandbox"` - // Max disk per sandbox - MaxDiskPerSandbox float32 `json:"maxDiskPerSandbox"` - // Time in minutes before an unused snapshot is deactivated - SnapshotDeactivationTimeoutMinutes float32 `json:"snapshotDeactivationTimeoutMinutes"` - // Sandbox default network block all - SandboxLimitedNetworkEgress bool `json:"sandboxLimitedNetworkEgress"` + // Max CPU per box + MaxCpuPerBox float32 `json:"maxCpuPerBox"` + // Max memory per box + MaxMemoryPerBox float32 `json:"maxMemoryPerBox"` + // Max disk per box + MaxDiskPerBox float32 `json:"maxDiskPerBox"` + // Time in minutes before an unused template is deactivated + TemplateDeactivationTimeoutMinutes float32 `json:"templateDeactivationTimeoutMinutes"` + // Box default network block all + BoxLimitedNetworkEgress bool `json:"boxLimitedNetworkEgress"` // Default region ID DefaultRegionId *string `json:"defaultRegionId,omitempty"` // Authenticated rate limit per minute AuthenticatedRateLimit NullableFloat32 `json:"authenticatedRateLimit"` - // Sandbox create rate limit per minute - SandboxCreateRateLimit NullableFloat32 `json:"sandboxCreateRateLimit"` - // Sandbox lifecycle rate limit per minute - SandboxLifecycleRateLimit NullableFloat32 `json:"sandboxLifecycleRateLimit"` + // Box create rate limit per minute + BoxCreateRateLimit NullableFloat32 `json:"boxCreateRateLimit"` + // Box lifecycle rate limit per minute + BoxLifecycleRateLimit NullableFloat32 `json:"boxLifecycleRateLimit"` // Experimental configuration ExperimentalConfig map[string]interface{} `json:"experimentalConfig"` // Authenticated rate limit TTL in seconds AuthenticatedRateLimitTtlSeconds NullableFloat32 `json:"authenticatedRateLimitTtlSeconds"` - // Sandbox create rate limit TTL in seconds - SandboxCreateRateLimitTtlSeconds NullableFloat32 `json:"sandboxCreateRateLimitTtlSeconds"` - // Sandbox lifecycle rate limit TTL in seconds - SandboxLifecycleRateLimitTtlSeconds NullableFloat32 `json:"sandboxLifecycleRateLimitTtlSeconds"` + // Box create rate limit TTL in seconds + BoxCreateRateLimitTtlSeconds NullableFloat32 `json:"boxCreateRateLimitTtlSeconds"` + // Box lifecycle rate limit TTL in seconds + BoxLifecycleRateLimitTtlSeconds NullableFloat32 `json:"boxLifecycleRateLimitTtlSeconds"` AdditionalProperties map[string]interface{} } @@ -79,11 +82,12 @@ type _Organization Organization // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewOrganization(id string, name string, createdBy string, personal bool, createdAt time.Time, updatedAt time.Time, suspended bool, suspendedAt time.Time, suspensionReason string, suspendedUntil time.Time, suspensionCleanupGracePeriodHours float32, maxCpuPerSandbox float32, maxMemoryPerSandbox float32, maxDiskPerSandbox float32, snapshotDeactivationTimeoutMinutes float32, sandboxLimitedNetworkEgress bool, authenticatedRateLimit NullableFloat32, sandboxCreateRateLimit NullableFloat32, sandboxLifecycleRateLimit NullableFloat32, experimentalConfig map[string]interface{}, authenticatedRateLimitTtlSeconds NullableFloat32, sandboxCreateRateLimitTtlSeconds NullableFloat32, sandboxLifecycleRateLimitTtlSeconds NullableFloat32) *Organization { +func NewOrganization(id string, name string, createdBy string, isDefaultForAuthenticatedUser bool, personal bool, createdAt time.Time, updatedAt time.Time, suspended bool, suspendedAt time.Time, suspensionReason string, suspendedUntil time.Time, suspensionCleanupGracePeriodHours float32, maxCpuPerBox float32, maxMemoryPerBox float32, maxDiskPerBox float32, templateDeactivationTimeoutMinutes float32, boxLimitedNetworkEgress bool, authenticatedRateLimit NullableFloat32, boxCreateRateLimit NullableFloat32, boxLifecycleRateLimit NullableFloat32, experimentalConfig map[string]interface{}, authenticatedRateLimitTtlSeconds NullableFloat32, boxCreateRateLimitTtlSeconds NullableFloat32, boxLifecycleRateLimitTtlSeconds NullableFloat32) *Organization { this := Organization{} this.Id = id this.Name = name this.CreatedBy = createdBy + this.IsDefaultForAuthenticatedUser = isDefaultForAuthenticatedUser this.Personal = personal this.CreatedAt = createdAt this.UpdatedAt = updatedAt @@ -92,18 +96,18 @@ func NewOrganization(id string, name string, createdBy string, personal bool, cr this.SuspensionReason = suspensionReason this.SuspendedUntil = suspendedUntil this.SuspensionCleanupGracePeriodHours = suspensionCleanupGracePeriodHours - this.MaxCpuPerSandbox = maxCpuPerSandbox - this.MaxMemoryPerSandbox = maxMemoryPerSandbox - this.MaxDiskPerSandbox = maxDiskPerSandbox - this.SnapshotDeactivationTimeoutMinutes = snapshotDeactivationTimeoutMinutes - this.SandboxLimitedNetworkEgress = sandboxLimitedNetworkEgress + this.MaxCpuPerBox = maxCpuPerBox + this.MaxMemoryPerBox = maxMemoryPerBox + this.MaxDiskPerBox = maxDiskPerBox + this.TemplateDeactivationTimeoutMinutes = templateDeactivationTimeoutMinutes + this.BoxLimitedNetworkEgress = boxLimitedNetworkEgress this.AuthenticatedRateLimit = authenticatedRateLimit - this.SandboxCreateRateLimit = sandboxCreateRateLimit - this.SandboxLifecycleRateLimit = sandboxLifecycleRateLimit + this.BoxCreateRateLimit = boxCreateRateLimit + this.BoxLifecycleRateLimit = boxLifecycleRateLimit this.ExperimentalConfig = experimentalConfig this.AuthenticatedRateLimitTtlSeconds = authenticatedRateLimitTtlSeconds - this.SandboxCreateRateLimitTtlSeconds = sandboxCreateRateLimitTtlSeconds - this.SandboxLifecycleRateLimitTtlSeconds = sandboxLifecycleRateLimitTtlSeconds + this.BoxCreateRateLimitTtlSeconds = boxCreateRateLimitTtlSeconds + this.BoxLifecycleRateLimitTtlSeconds = boxLifecycleRateLimitTtlSeconds return &this } @@ -112,8 +116,8 @@ func NewOrganization(id string, name string, createdBy string, personal bool, cr // but it doesn't guarantee that properties required by API are set func NewOrganizationWithDefaults() *Organization { this := Organization{} - var snapshotDeactivationTimeoutMinutes float32 = 20160 - this.SnapshotDeactivationTimeoutMinutes = snapshotDeactivationTimeoutMinutes + var templateDeactivationTimeoutMinutes float32 = 20160 + this.TemplateDeactivationTimeoutMinutes = templateDeactivationTimeoutMinutes return &this } @@ -189,7 +193,32 @@ func (o *Organization) SetCreatedBy(v string) { o.CreatedBy = v } +// GetIsDefaultForAuthenticatedUser returns the IsDefaultForAuthenticatedUser field value +func (o *Organization) GetIsDefaultForAuthenticatedUser() bool { + if o == nil { + var ret bool + return ret + } + + return o.IsDefaultForAuthenticatedUser +} + +// GetIsDefaultForAuthenticatedUserOk returns a tuple with the IsDefaultForAuthenticatedUser field value +// and a boolean to check if the value has been set. +func (o *Organization) GetIsDefaultForAuthenticatedUserOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.IsDefaultForAuthenticatedUser, true +} + +// SetIsDefaultForAuthenticatedUser sets field value +func (o *Organization) SetIsDefaultForAuthenticatedUser(v bool) { + o.IsDefaultForAuthenticatedUser = v +} + // GetPersonal returns the Personal field value +// Deprecated func (o *Organization) GetPersonal() bool { if o == nil { var ret bool @@ -201,6 +230,7 @@ func (o *Organization) GetPersonal() bool { // GetPersonalOk returns a tuple with the Personal field value // and a boolean to check if the value has been set. +// Deprecated func (o *Organization) GetPersonalOk() (*bool, bool) { if o == nil { return nil, false @@ -209,6 +239,7 @@ func (o *Organization) GetPersonalOk() (*bool, bool) { } // SetPersonal sets field value +// Deprecated func (o *Organization) SetPersonal(v bool) { o.Personal = v } @@ -381,124 +412,124 @@ func (o *Organization) SetSuspensionCleanupGracePeriodHours(v float32) { o.SuspensionCleanupGracePeriodHours = v } -// GetMaxCpuPerSandbox returns the MaxCpuPerSandbox field value -func (o *Organization) GetMaxCpuPerSandbox() float32 { +// GetMaxCpuPerBox returns the MaxCpuPerBox field value +func (o *Organization) GetMaxCpuPerBox() float32 { if o == nil { var ret float32 return ret } - return o.MaxCpuPerSandbox + return o.MaxCpuPerBox } -// GetMaxCpuPerSandboxOk returns a tuple with the MaxCpuPerSandbox field value +// GetMaxCpuPerBoxOk returns a tuple with the MaxCpuPerBox field value // and a boolean to check if the value has been set. -func (o *Organization) GetMaxCpuPerSandboxOk() (*float32, bool) { +func (o *Organization) GetMaxCpuPerBoxOk() (*float32, bool) { if o == nil { return nil, false } - return &o.MaxCpuPerSandbox, true + return &o.MaxCpuPerBox, true } -// SetMaxCpuPerSandbox sets field value -func (o *Organization) SetMaxCpuPerSandbox(v float32) { - o.MaxCpuPerSandbox = v +// SetMaxCpuPerBox sets field value +func (o *Organization) SetMaxCpuPerBox(v float32) { + o.MaxCpuPerBox = v } -// GetMaxMemoryPerSandbox returns the MaxMemoryPerSandbox field value -func (o *Organization) GetMaxMemoryPerSandbox() float32 { +// GetMaxMemoryPerBox returns the MaxMemoryPerBox field value +func (o *Organization) GetMaxMemoryPerBox() float32 { if o == nil { var ret float32 return ret } - return o.MaxMemoryPerSandbox + return o.MaxMemoryPerBox } -// GetMaxMemoryPerSandboxOk returns a tuple with the MaxMemoryPerSandbox field value +// GetMaxMemoryPerBoxOk returns a tuple with the MaxMemoryPerBox field value // and a boolean to check if the value has been set. -func (o *Organization) GetMaxMemoryPerSandboxOk() (*float32, bool) { +func (o *Organization) GetMaxMemoryPerBoxOk() (*float32, bool) { if o == nil { return nil, false } - return &o.MaxMemoryPerSandbox, true + return &o.MaxMemoryPerBox, true } -// SetMaxMemoryPerSandbox sets field value -func (o *Organization) SetMaxMemoryPerSandbox(v float32) { - o.MaxMemoryPerSandbox = v +// SetMaxMemoryPerBox sets field value +func (o *Organization) SetMaxMemoryPerBox(v float32) { + o.MaxMemoryPerBox = v } -// GetMaxDiskPerSandbox returns the MaxDiskPerSandbox field value -func (o *Organization) GetMaxDiskPerSandbox() float32 { +// GetMaxDiskPerBox returns the MaxDiskPerBox field value +func (o *Organization) GetMaxDiskPerBox() float32 { if o == nil { var ret float32 return ret } - return o.MaxDiskPerSandbox + return o.MaxDiskPerBox } -// GetMaxDiskPerSandboxOk returns a tuple with the MaxDiskPerSandbox field value +// GetMaxDiskPerBoxOk returns a tuple with the MaxDiskPerBox field value // and a boolean to check if the value has been set. -func (o *Organization) GetMaxDiskPerSandboxOk() (*float32, bool) { +func (o *Organization) GetMaxDiskPerBoxOk() (*float32, bool) { if o == nil { return nil, false } - return &o.MaxDiskPerSandbox, true + return &o.MaxDiskPerBox, true } -// SetMaxDiskPerSandbox sets field value -func (o *Organization) SetMaxDiskPerSandbox(v float32) { - o.MaxDiskPerSandbox = v +// SetMaxDiskPerBox sets field value +func (o *Organization) SetMaxDiskPerBox(v float32) { + o.MaxDiskPerBox = v } -// GetSnapshotDeactivationTimeoutMinutes returns the SnapshotDeactivationTimeoutMinutes field value -func (o *Organization) GetSnapshotDeactivationTimeoutMinutes() float32 { +// GetTemplateDeactivationTimeoutMinutes returns the TemplateDeactivationTimeoutMinutes field value +func (o *Organization) GetTemplateDeactivationTimeoutMinutes() float32 { if o == nil { var ret float32 return ret } - return o.SnapshotDeactivationTimeoutMinutes + return o.TemplateDeactivationTimeoutMinutes } -// GetSnapshotDeactivationTimeoutMinutesOk returns a tuple with the SnapshotDeactivationTimeoutMinutes field value +// GetTemplateDeactivationTimeoutMinutesOk returns a tuple with the TemplateDeactivationTimeoutMinutes field value // and a boolean to check if the value has been set. -func (o *Organization) GetSnapshotDeactivationTimeoutMinutesOk() (*float32, bool) { +func (o *Organization) GetTemplateDeactivationTimeoutMinutesOk() (*float32, bool) { if o == nil { return nil, false } - return &o.SnapshotDeactivationTimeoutMinutes, true + return &o.TemplateDeactivationTimeoutMinutes, true } -// SetSnapshotDeactivationTimeoutMinutes sets field value -func (o *Organization) SetSnapshotDeactivationTimeoutMinutes(v float32) { - o.SnapshotDeactivationTimeoutMinutes = v +// SetTemplateDeactivationTimeoutMinutes sets field value +func (o *Organization) SetTemplateDeactivationTimeoutMinutes(v float32) { + o.TemplateDeactivationTimeoutMinutes = v } -// GetSandboxLimitedNetworkEgress returns the SandboxLimitedNetworkEgress field value -func (o *Organization) GetSandboxLimitedNetworkEgress() bool { +// GetBoxLimitedNetworkEgress returns the BoxLimitedNetworkEgress field value +func (o *Organization) GetBoxLimitedNetworkEgress() bool { if o == nil { var ret bool return ret } - return o.SandboxLimitedNetworkEgress + return o.BoxLimitedNetworkEgress } -// GetSandboxLimitedNetworkEgressOk returns a tuple with the SandboxLimitedNetworkEgress field value +// GetBoxLimitedNetworkEgressOk returns a tuple with the BoxLimitedNetworkEgress field value // and a boolean to check if the value has been set. -func (o *Organization) GetSandboxLimitedNetworkEgressOk() (*bool, bool) { +func (o *Organization) GetBoxLimitedNetworkEgressOk() (*bool, bool) { if o == nil { return nil, false } - return &o.SandboxLimitedNetworkEgress, true + return &o.BoxLimitedNetworkEgress, true } -// SetSandboxLimitedNetworkEgress sets field value -func (o *Organization) SetSandboxLimitedNetworkEgress(v bool) { - o.SandboxLimitedNetworkEgress = v +// SetBoxLimitedNetworkEgress sets field value +func (o *Organization) SetBoxLimitedNetworkEgress(v bool) { + o.BoxLimitedNetworkEgress = v } // GetDefaultRegionId returns the DefaultRegionId field value if set, zero value otherwise. @@ -559,56 +590,56 @@ func (o *Organization) SetAuthenticatedRateLimit(v float32) { o.AuthenticatedRateLimit.Set(&v) } -// GetSandboxCreateRateLimit returns the SandboxCreateRateLimit field value +// GetBoxCreateRateLimit returns the BoxCreateRateLimit field value // If the value is explicit nil, the zero value for float32 will be returned -func (o *Organization) GetSandboxCreateRateLimit() float32 { - if o == nil || o.SandboxCreateRateLimit.Get() == nil { +func (o *Organization) GetBoxCreateRateLimit() float32 { + if o == nil || o.BoxCreateRateLimit.Get() == nil { var ret float32 return ret } - return *o.SandboxCreateRateLimit.Get() + return *o.BoxCreateRateLimit.Get() } -// GetSandboxCreateRateLimitOk returns a tuple with the SandboxCreateRateLimit field value +// GetBoxCreateRateLimitOk returns a tuple with the BoxCreateRateLimit field value // and a boolean to check if the value has been set. // NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Organization) GetSandboxCreateRateLimitOk() (*float32, bool) { +func (o *Organization) GetBoxCreateRateLimitOk() (*float32, bool) { if o == nil { return nil, false } - return o.SandboxCreateRateLimit.Get(), o.SandboxCreateRateLimit.IsSet() + return o.BoxCreateRateLimit.Get(), o.BoxCreateRateLimit.IsSet() } -// SetSandboxCreateRateLimit sets field value -func (o *Organization) SetSandboxCreateRateLimit(v float32) { - o.SandboxCreateRateLimit.Set(&v) +// SetBoxCreateRateLimit sets field value +func (o *Organization) SetBoxCreateRateLimit(v float32) { + o.BoxCreateRateLimit.Set(&v) } -// GetSandboxLifecycleRateLimit returns the SandboxLifecycleRateLimit field value +// GetBoxLifecycleRateLimit returns the BoxLifecycleRateLimit field value // If the value is explicit nil, the zero value for float32 will be returned -func (o *Organization) GetSandboxLifecycleRateLimit() float32 { - if o == nil || o.SandboxLifecycleRateLimit.Get() == nil { +func (o *Organization) GetBoxLifecycleRateLimit() float32 { + if o == nil || o.BoxLifecycleRateLimit.Get() == nil { var ret float32 return ret } - return *o.SandboxLifecycleRateLimit.Get() + return *o.BoxLifecycleRateLimit.Get() } -// GetSandboxLifecycleRateLimitOk returns a tuple with the SandboxLifecycleRateLimit field value +// GetBoxLifecycleRateLimitOk returns a tuple with the BoxLifecycleRateLimit field value // and a boolean to check if the value has been set. // NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Organization) GetSandboxLifecycleRateLimitOk() (*float32, bool) { +func (o *Organization) GetBoxLifecycleRateLimitOk() (*float32, bool) { if o == nil { return nil, false } - return o.SandboxLifecycleRateLimit.Get(), o.SandboxLifecycleRateLimit.IsSet() + return o.BoxLifecycleRateLimit.Get(), o.BoxLifecycleRateLimit.IsSet() } -// SetSandboxLifecycleRateLimit sets field value -func (o *Organization) SetSandboxLifecycleRateLimit(v float32) { - o.SandboxLifecycleRateLimit.Set(&v) +// SetBoxLifecycleRateLimit sets field value +func (o *Organization) SetBoxLifecycleRateLimit(v float32) { + o.BoxLifecycleRateLimit.Set(&v) } // GetExperimentalConfig returns the ExperimentalConfig field value @@ -661,56 +692,56 @@ func (o *Organization) SetAuthenticatedRateLimitTtlSeconds(v float32) { o.AuthenticatedRateLimitTtlSeconds.Set(&v) } -// GetSandboxCreateRateLimitTtlSeconds returns the SandboxCreateRateLimitTtlSeconds field value +// GetBoxCreateRateLimitTtlSeconds returns the BoxCreateRateLimitTtlSeconds field value // If the value is explicit nil, the zero value for float32 will be returned -func (o *Organization) GetSandboxCreateRateLimitTtlSeconds() float32 { - if o == nil || o.SandboxCreateRateLimitTtlSeconds.Get() == nil { +func (o *Organization) GetBoxCreateRateLimitTtlSeconds() float32 { + if o == nil || o.BoxCreateRateLimitTtlSeconds.Get() == nil { var ret float32 return ret } - return *o.SandboxCreateRateLimitTtlSeconds.Get() + return *o.BoxCreateRateLimitTtlSeconds.Get() } -// GetSandboxCreateRateLimitTtlSecondsOk returns a tuple with the SandboxCreateRateLimitTtlSeconds field value +// GetBoxCreateRateLimitTtlSecondsOk returns a tuple with the BoxCreateRateLimitTtlSeconds field value // and a boolean to check if the value has been set. // NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Organization) GetSandboxCreateRateLimitTtlSecondsOk() (*float32, bool) { +func (o *Organization) GetBoxCreateRateLimitTtlSecondsOk() (*float32, bool) { if o == nil { return nil, false } - return o.SandboxCreateRateLimitTtlSeconds.Get(), o.SandboxCreateRateLimitTtlSeconds.IsSet() + return o.BoxCreateRateLimitTtlSeconds.Get(), o.BoxCreateRateLimitTtlSeconds.IsSet() } -// SetSandboxCreateRateLimitTtlSeconds sets field value -func (o *Organization) SetSandboxCreateRateLimitTtlSeconds(v float32) { - o.SandboxCreateRateLimitTtlSeconds.Set(&v) +// SetBoxCreateRateLimitTtlSeconds sets field value +func (o *Organization) SetBoxCreateRateLimitTtlSeconds(v float32) { + o.BoxCreateRateLimitTtlSeconds.Set(&v) } -// GetSandboxLifecycleRateLimitTtlSeconds returns the SandboxLifecycleRateLimitTtlSeconds field value +// GetBoxLifecycleRateLimitTtlSeconds returns the BoxLifecycleRateLimitTtlSeconds field value // If the value is explicit nil, the zero value for float32 will be returned -func (o *Organization) GetSandboxLifecycleRateLimitTtlSeconds() float32 { - if o == nil || o.SandboxLifecycleRateLimitTtlSeconds.Get() == nil { +func (o *Organization) GetBoxLifecycleRateLimitTtlSeconds() float32 { + if o == nil || o.BoxLifecycleRateLimitTtlSeconds.Get() == nil { var ret float32 return ret } - return *o.SandboxLifecycleRateLimitTtlSeconds.Get() + return *o.BoxLifecycleRateLimitTtlSeconds.Get() } -// GetSandboxLifecycleRateLimitTtlSecondsOk returns a tuple with the SandboxLifecycleRateLimitTtlSeconds field value +// GetBoxLifecycleRateLimitTtlSecondsOk returns a tuple with the BoxLifecycleRateLimitTtlSeconds field value // and a boolean to check if the value has been set. // NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Organization) GetSandboxLifecycleRateLimitTtlSecondsOk() (*float32, bool) { +func (o *Organization) GetBoxLifecycleRateLimitTtlSecondsOk() (*float32, bool) { if o == nil { return nil, false } - return o.SandboxLifecycleRateLimitTtlSeconds.Get(), o.SandboxLifecycleRateLimitTtlSeconds.IsSet() + return o.BoxLifecycleRateLimitTtlSeconds.Get(), o.BoxLifecycleRateLimitTtlSeconds.IsSet() } -// SetSandboxLifecycleRateLimitTtlSeconds sets field value -func (o *Organization) SetSandboxLifecycleRateLimitTtlSeconds(v float32) { - o.SandboxLifecycleRateLimitTtlSeconds.Set(&v) +// SetBoxLifecycleRateLimitTtlSeconds sets field value +func (o *Organization) SetBoxLifecycleRateLimitTtlSeconds(v float32) { + o.BoxLifecycleRateLimitTtlSeconds.Set(&v) } func (o Organization) MarshalJSON() ([]byte, error) { @@ -726,6 +757,7 @@ func (o Organization) ToMap() (map[string]interface{}, error) { toSerialize["id"] = o.Id toSerialize["name"] = o.Name toSerialize["createdBy"] = o.CreatedBy + toSerialize["isDefaultForAuthenticatedUser"] = o.IsDefaultForAuthenticatedUser toSerialize["personal"] = o.Personal toSerialize["createdAt"] = o.CreatedAt toSerialize["updatedAt"] = o.UpdatedAt @@ -734,21 +766,21 @@ func (o Organization) ToMap() (map[string]interface{}, error) { toSerialize["suspensionReason"] = o.SuspensionReason toSerialize["suspendedUntil"] = o.SuspendedUntil toSerialize["suspensionCleanupGracePeriodHours"] = o.SuspensionCleanupGracePeriodHours - toSerialize["maxCpuPerSandbox"] = o.MaxCpuPerSandbox - toSerialize["maxMemoryPerSandbox"] = o.MaxMemoryPerSandbox - toSerialize["maxDiskPerSandbox"] = o.MaxDiskPerSandbox - toSerialize["snapshotDeactivationTimeoutMinutes"] = o.SnapshotDeactivationTimeoutMinutes - toSerialize["sandboxLimitedNetworkEgress"] = o.SandboxLimitedNetworkEgress + toSerialize["maxCpuPerBox"] = o.MaxCpuPerBox + toSerialize["maxMemoryPerBox"] = o.MaxMemoryPerBox + toSerialize["maxDiskPerBox"] = o.MaxDiskPerBox + toSerialize["templateDeactivationTimeoutMinutes"] = o.TemplateDeactivationTimeoutMinutes + toSerialize["boxLimitedNetworkEgress"] = o.BoxLimitedNetworkEgress if !IsNil(o.DefaultRegionId) { toSerialize["defaultRegionId"] = o.DefaultRegionId } toSerialize["authenticatedRateLimit"] = o.AuthenticatedRateLimit.Get() - toSerialize["sandboxCreateRateLimit"] = o.SandboxCreateRateLimit.Get() - toSerialize["sandboxLifecycleRateLimit"] = o.SandboxLifecycleRateLimit.Get() + toSerialize["boxCreateRateLimit"] = o.BoxCreateRateLimit.Get() + toSerialize["boxLifecycleRateLimit"] = o.BoxLifecycleRateLimit.Get() toSerialize["experimentalConfig"] = o.ExperimentalConfig toSerialize["authenticatedRateLimitTtlSeconds"] = o.AuthenticatedRateLimitTtlSeconds.Get() - toSerialize["sandboxCreateRateLimitTtlSeconds"] = o.SandboxCreateRateLimitTtlSeconds.Get() - toSerialize["sandboxLifecycleRateLimitTtlSeconds"] = o.SandboxLifecycleRateLimitTtlSeconds.Get() + toSerialize["boxCreateRateLimitTtlSeconds"] = o.BoxCreateRateLimitTtlSeconds.Get() + toSerialize["boxLifecycleRateLimitTtlSeconds"] = o.BoxLifecycleRateLimitTtlSeconds.Get() for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -765,6 +797,7 @@ func (o *Organization) UnmarshalJSON(data []byte) (err error) { "id", "name", "createdBy", + "isDefaultForAuthenticatedUser", "personal", "createdAt", "updatedAt", @@ -773,18 +806,18 @@ func (o *Organization) UnmarshalJSON(data []byte) (err error) { "suspensionReason", "suspendedUntil", "suspensionCleanupGracePeriodHours", - "maxCpuPerSandbox", - "maxMemoryPerSandbox", - "maxDiskPerSandbox", - "snapshotDeactivationTimeoutMinutes", - "sandboxLimitedNetworkEgress", + "maxCpuPerBox", + "maxMemoryPerBox", + "maxDiskPerBox", + "templateDeactivationTimeoutMinutes", + "boxLimitedNetworkEgress", "authenticatedRateLimit", - "sandboxCreateRateLimit", - "sandboxLifecycleRateLimit", + "boxCreateRateLimit", + "boxLifecycleRateLimit", "experimentalConfig", "authenticatedRateLimitTtlSeconds", - "sandboxCreateRateLimitTtlSeconds", - "sandboxLifecycleRateLimitTtlSeconds", + "boxCreateRateLimitTtlSeconds", + "boxLifecycleRateLimitTtlSeconds", } allProperties := make(map[string]interface{}) @@ -817,6 +850,7 @@ func (o *Organization) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "id") delete(additionalProperties, "name") delete(additionalProperties, "createdBy") + delete(additionalProperties, "isDefaultForAuthenticatedUser") delete(additionalProperties, "personal") delete(additionalProperties, "createdAt") delete(additionalProperties, "updatedAt") @@ -825,19 +859,19 @@ func (o *Organization) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "suspensionReason") delete(additionalProperties, "suspendedUntil") delete(additionalProperties, "suspensionCleanupGracePeriodHours") - delete(additionalProperties, "maxCpuPerSandbox") - delete(additionalProperties, "maxMemoryPerSandbox") - delete(additionalProperties, "maxDiskPerSandbox") - delete(additionalProperties, "snapshotDeactivationTimeoutMinutes") - delete(additionalProperties, "sandboxLimitedNetworkEgress") + delete(additionalProperties, "maxCpuPerBox") + delete(additionalProperties, "maxMemoryPerBox") + delete(additionalProperties, "maxDiskPerBox") + delete(additionalProperties, "templateDeactivationTimeoutMinutes") + delete(additionalProperties, "boxLimitedNetworkEgress") delete(additionalProperties, "defaultRegionId") delete(additionalProperties, "authenticatedRateLimit") - delete(additionalProperties, "sandboxCreateRateLimit") - delete(additionalProperties, "sandboxLifecycleRateLimit") + delete(additionalProperties, "boxCreateRateLimit") + delete(additionalProperties, "boxLifecycleRateLimit") delete(additionalProperties, "experimentalConfig") delete(additionalProperties, "authenticatedRateLimitTtlSeconds") - delete(additionalProperties, "sandboxCreateRateLimitTtlSeconds") - delete(additionalProperties, "sandboxLifecycleRateLimitTtlSeconds") + delete(additionalProperties, "boxCreateRateLimitTtlSeconds") + delete(additionalProperties, "boxLifecycleRateLimitTtlSeconds") o.AdditionalProperties = additionalProperties } @@ -879,3 +913,5 @@ func (v *NullableOrganization) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_organization_box_default_limited_network_egress.go b/apps/api-client-go/model_organization_box_default_limited_network_egress.go new file mode 100644 index 000000000..ff6717295 --- /dev/null +++ b/apps/api-client-go/model_organization_box_default_limited_network_egress.go @@ -0,0 +1,170 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the OrganizationBoxDefaultLimitedNetworkEgress type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &OrganizationBoxDefaultLimitedNetworkEgress{} + +// OrganizationBoxDefaultLimitedNetworkEgress struct for OrganizationBoxDefaultLimitedNetworkEgress +type OrganizationBoxDefaultLimitedNetworkEgress struct { + // Box default limited network egress + BoxDefaultLimitedNetworkEgress bool `json:"boxDefaultLimitedNetworkEgress"` + AdditionalProperties map[string]interface{} +} + +type _OrganizationBoxDefaultLimitedNetworkEgress OrganizationBoxDefaultLimitedNetworkEgress + +// NewOrganizationBoxDefaultLimitedNetworkEgress instantiates a new OrganizationBoxDefaultLimitedNetworkEgress object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewOrganizationBoxDefaultLimitedNetworkEgress(boxDefaultLimitedNetworkEgress bool) *OrganizationBoxDefaultLimitedNetworkEgress { + this := OrganizationBoxDefaultLimitedNetworkEgress{} + this.BoxDefaultLimitedNetworkEgress = boxDefaultLimitedNetworkEgress + return &this +} + +// NewOrganizationBoxDefaultLimitedNetworkEgressWithDefaults instantiates a new OrganizationBoxDefaultLimitedNetworkEgress object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewOrganizationBoxDefaultLimitedNetworkEgressWithDefaults() *OrganizationBoxDefaultLimitedNetworkEgress { + this := OrganizationBoxDefaultLimitedNetworkEgress{} + return &this +} + +// GetBoxDefaultLimitedNetworkEgress returns the BoxDefaultLimitedNetworkEgress field value +func (o *OrganizationBoxDefaultLimitedNetworkEgress) GetBoxDefaultLimitedNetworkEgress() bool { + if o == nil { + var ret bool + return ret + } + + return o.BoxDefaultLimitedNetworkEgress +} + +// GetBoxDefaultLimitedNetworkEgressOk returns a tuple with the BoxDefaultLimitedNetworkEgress field value +// and a boolean to check if the value has been set. +func (o *OrganizationBoxDefaultLimitedNetworkEgress) GetBoxDefaultLimitedNetworkEgressOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.BoxDefaultLimitedNetworkEgress, true +} + +// SetBoxDefaultLimitedNetworkEgress sets field value +func (o *OrganizationBoxDefaultLimitedNetworkEgress) SetBoxDefaultLimitedNetworkEgress(v bool) { + o.BoxDefaultLimitedNetworkEgress = v +} + +func (o OrganizationBoxDefaultLimitedNetworkEgress) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o OrganizationBoxDefaultLimitedNetworkEgress) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["boxDefaultLimitedNetworkEgress"] = o.BoxDefaultLimitedNetworkEgress + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *OrganizationBoxDefaultLimitedNetworkEgress) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "boxDefaultLimitedNetworkEgress", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varOrganizationBoxDefaultLimitedNetworkEgress := _OrganizationBoxDefaultLimitedNetworkEgress{} + + err = json.Unmarshal(data, &varOrganizationBoxDefaultLimitedNetworkEgress) + + if err != nil { + return err + } + + *o = OrganizationBoxDefaultLimitedNetworkEgress(varOrganizationBoxDefaultLimitedNetworkEgress) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "boxDefaultLimitedNetworkEgress") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableOrganizationBoxDefaultLimitedNetworkEgress struct { + value *OrganizationBoxDefaultLimitedNetworkEgress + isSet bool +} + +func (v NullableOrganizationBoxDefaultLimitedNetworkEgress) Get() *OrganizationBoxDefaultLimitedNetworkEgress { + return v.value +} + +func (v *NullableOrganizationBoxDefaultLimitedNetworkEgress) Set(val *OrganizationBoxDefaultLimitedNetworkEgress) { + v.value = val + v.isSet = true +} + +func (v NullableOrganizationBoxDefaultLimitedNetworkEgress) IsSet() bool { + return v.isSet +} + +func (v *NullableOrganizationBoxDefaultLimitedNetworkEgress) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableOrganizationBoxDefaultLimitedNetworkEgress(val *OrganizationBoxDefaultLimitedNetworkEgress) *NullableOrganizationBoxDefaultLimitedNetworkEgress { + return &NullableOrganizationBoxDefaultLimitedNetworkEgress{value: val, isSet: true} +} + +func (v NullableOrganizationBoxDefaultLimitedNetworkEgress) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableOrganizationBoxDefaultLimitedNetworkEgress) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_organization_invitation.go b/apps/api-client-go/model_organization_invitation.go index db532ef37..18841ca76 100644 --- a/apps/api-client-go/model_organization_invitation.go +++ b/apps/api-client-go/model_organization_invitation.go @@ -467,3 +467,5 @@ func (v *NullableOrganizationInvitation) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_organization_role.go b/apps/api-client-go/model_organization_role.go index 424e8d0f8..ea6469ff1 100644 --- a/apps/api-client-go/model_organization_role.go +++ b/apps/api-client-go/model_organization_role.go @@ -347,3 +347,5 @@ func (v *NullableOrganizationRole) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_organization_sandbox_default_limited_network_egress.go b/apps/api-client-go/model_organization_sandbox_default_limited_network_egress.go deleted file mode 100644 index 3e8090ddf..000000000 --- a/apps/api-client-go/model_organization_sandbox_default_limited_network_egress.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the OrganizationSandboxDefaultLimitedNetworkEgress type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &OrganizationSandboxDefaultLimitedNetworkEgress{} - -// OrganizationSandboxDefaultLimitedNetworkEgress struct for OrganizationSandboxDefaultLimitedNetworkEgress -type OrganizationSandboxDefaultLimitedNetworkEgress struct { - // Sandbox default limited network egress - SandboxDefaultLimitedNetworkEgress bool `json:"sandboxDefaultLimitedNetworkEgress"` - AdditionalProperties map[string]interface{} -} - -type _OrganizationSandboxDefaultLimitedNetworkEgress OrganizationSandboxDefaultLimitedNetworkEgress - -// NewOrganizationSandboxDefaultLimitedNetworkEgress instantiates a new OrganizationSandboxDefaultLimitedNetworkEgress object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewOrganizationSandboxDefaultLimitedNetworkEgress(sandboxDefaultLimitedNetworkEgress bool) *OrganizationSandboxDefaultLimitedNetworkEgress { - this := OrganizationSandboxDefaultLimitedNetworkEgress{} - this.SandboxDefaultLimitedNetworkEgress = sandboxDefaultLimitedNetworkEgress - return &this -} - -// NewOrganizationSandboxDefaultLimitedNetworkEgressWithDefaults instantiates a new OrganizationSandboxDefaultLimitedNetworkEgress object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewOrganizationSandboxDefaultLimitedNetworkEgressWithDefaults() *OrganizationSandboxDefaultLimitedNetworkEgress { - this := OrganizationSandboxDefaultLimitedNetworkEgress{} - return &this -} - -// GetSandboxDefaultLimitedNetworkEgress returns the SandboxDefaultLimitedNetworkEgress field value -func (o *OrganizationSandboxDefaultLimitedNetworkEgress) GetSandboxDefaultLimitedNetworkEgress() bool { - if o == nil { - var ret bool - return ret - } - - return o.SandboxDefaultLimitedNetworkEgress -} - -// GetSandboxDefaultLimitedNetworkEgressOk returns a tuple with the SandboxDefaultLimitedNetworkEgress field value -// and a boolean to check if the value has been set. -func (o *OrganizationSandboxDefaultLimitedNetworkEgress) GetSandboxDefaultLimitedNetworkEgressOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.SandboxDefaultLimitedNetworkEgress, true -} - -// SetSandboxDefaultLimitedNetworkEgress sets field value -func (o *OrganizationSandboxDefaultLimitedNetworkEgress) SetSandboxDefaultLimitedNetworkEgress(v bool) { - o.SandboxDefaultLimitedNetworkEgress = v -} - -func (o OrganizationSandboxDefaultLimitedNetworkEgress) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o OrganizationSandboxDefaultLimitedNetworkEgress) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["sandboxDefaultLimitedNetworkEgress"] = o.SandboxDefaultLimitedNetworkEgress - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *OrganizationSandboxDefaultLimitedNetworkEgress) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "sandboxDefaultLimitedNetworkEgress", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varOrganizationSandboxDefaultLimitedNetworkEgress := _OrganizationSandboxDefaultLimitedNetworkEgress{} - - err = json.Unmarshal(data, &varOrganizationSandboxDefaultLimitedNetworkEgress) - - if err != nil { - return err - } - - *o = OrganizationSandboxDefaultLimitedNetworkEgress(varOrganizationSandboxDefaultLimitedNetworkEgress) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sandboxDefaultLimitedNetworkEgress") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableOrganizationSandboxDefaultLimitedNetworkEgress struct { - value *OrganizationSandboxDefaultLimitedNetworkEgress - isSet bool -} - -func (v NullableOrganizationSandboxDefaultLimitedNetworkEgress) Get() *OrganizationSandboxDefaultLimitedNetworkEgress { - return v.value -} - -func (v *NullableOrganizationSandboxDefaultLimitedNetworkEgress) Set(val *OrganizationSandboxDefaultLimitedNetworkEgress) { - v.value = val - v.isSet = true -} - -func (v NullableOrganizationSandboxDefaultLimitedNetworkEgress) IsSet() bool { - return v.isSet -} - -func (v *NullableOrganizationSandboxDefaultLimitedNetworkEgress) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableOrganizationSandboxDefaultLimitedNetworkEgress(val *OrganizationSandboxDefaultLimitedNetworkEgress) *NullableOrganizationSandboxDefaultLimitedNetworkEgress { - return &NullableOrganizationSandboxDefaultLimitedNetworkEgress{value: val, isSet: true} -} - -func (v NullableOrganizationSandboxDefaultLimitedNetworkEgress) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableOrganizationSandboxDefaultLimitedNetworkEgress) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_organization_suspension.go b/apps/api-client-go/model_organization_suspension.go index 9acb15c3e..d5840e336 100644 --- a/apps/api-client-go/model_organization_suspension.go +++ b/apps/api-client-go/model_organization_suspension.go @@ -235,3 +235,5 @@ func (v *NullableOrganizationSuspension) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_organization_usage_overview.go b/apps/api-client-go/model_organization_usage_overview.go deleted file mode 100644 index 13a1c3598..000000000 --- a/apps/api-client-go/model_organization_usage_overview.go +++ /dev/null @@ -1,283 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the OrganizationUsageOverview type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &OrganizationUsageOverview{} - -// OrganizationUsageOverview struct for OrganizationUsageOverview -type OrganizationUsageOverview struct { - RegionUsage []RegionUsageOverview `json:"regionUsage"` - TotalSnapshotQuota float32 `json:"totalSnapshotQuota"` - CurrentSnapshotUsage float32 `json:"currentSnapshotUsage"` - TotalVolumeQuota float32 `json:"totalVolumeQuota"` - CurrentVolumeUsage float32 `json:"currentVolumeUsage"` - AdditionalProperties map[string]interface{} -} - -type _OrganizationUsageOverview OrganizationUsageOverview - -// NewOrganizationUsageOverview instantiates a new OrganizationUsageOverview object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewOrganizationUsageOverview(regionUsage []RegionUsageOverview, totalSnapshotQuota float32, currentSnapshotUsage float32, totalVolumeQuota float32, currentVolumeUsage float32) *OrganizationUsageOverview { - this := OrganizationUsageOverview{} - this.RegionUsage = regionUsage - this.TotalSnapshotQuota = totalSnapshotQuota - this.CurrentSnapshotUsage = currentSnapshotUsage - this.TotalVolumeQuota = totalVolumeQuota - this.CurrentVolumeUsage = currentVolumeUsage - return &this -} - -// NewOrganizationUsageOverviewWithDefaults instantiates a new OrganizationUsageOverview object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewOrganizationUsageOverviewWithDefaults() *OrganizationUsageOverview { - this := OrganizationUsageOverview{} - return &this -} - -// GetRegionUsage returns the RegionUsage field value -func (o *OrganizationUsageOverview) GetRegionUsage() []RegionUsageOverview { - if o == nil { - var ret []RegionUsageOverview - return ret - } - - return o.RegionUsage -} - -// GetRegionUsageOk returns a tuple with the RegionUsage field value -// and a boolean to check if the value has been set. -func (o *OrganizationUsageOverview) GetRegionUsageOk() ([]RegionUsageOverview, bool) { - if o == nil { - return nil, false - } - return o.RegionUsage, true -} - -// SetRegionUsage sets field value -func (o *OrganizationUsageOverview) SetRegionUsage(v []RegionUsageOverview) { - o.RegionUsage = v -} - -// GetTotalSnapshotQuota returns the TotalSnapshotQuota field value -func (o *OrganizationUsageOverview) GetTotalSnapshotQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalSnapshotQuota -} - -// GetTotalSnapshotQuotaOk returns a tuple with the TotalSnapshotQuota field value -// and a boolean to check if the value has been set. -func (o *OrganizationUsageOverview) GetTotalSnapshotQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalSnapshotQuota, true -} - -// SetTotalSnapshotQuota sets field value -func (o *OrganizationUsageOverview) SetTotalSnapshotQuota(v float32) { - o.TotalSnapshotQuota = v -} - -// GetCurrentSnapshotUsage returns the CurrentSnapshotUsage field value -func (o *OrganizationUsageOverview) GetCurrentSnapshotUsage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentSnapshotUsage -} - -// GetCurrentSnapshotUsageOk returns a tuple with the CurrentSnapshotUsage field value -// and a boolean to check if the value has been set. -func (o *OrganizationUsageOverview) GetCurrentSnapshotUsageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentSnapshotUsage, true -} - -// SetCurrentSnapshotUsage sets field value -func (o *OrganizationUsageOverview) SetCurrentSnapshotUsage(v float32) { - o.CurrentSnapshotUsage = v -} - -// GetTotalVolumeQuota returns the TotalVolumeQuota field value -func (o *OrganizationUsageOverview) GetTotalVolumeQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalVolumeQuota -} - -// GetTotalVolumeQuotaOk returns a tuple with the TotalVolumeQuota field value -// and a boolean to check if the value has been set. -func (o *OrganizationUsageOverview) GetTotalVolumeQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalVolumeQuota, true -} - -// SetTotalVolumeQuota sets field value -func (o *OrganizationUsageOverview) SetTotalVolumeQuota(v float32) { - o.TotalVolumeQuota = v -} - -// GetCurrentVolumeUsage returns the CurrentVolumeUsage field value -func (o *OrganizationUsageOverview) GetCurrentVolumeUsage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentVolumeUsage -} - -// GetCurrentVolumeUsageOk returns a tuple with the CurrentVolumeUsage field value -// and a boolean to check if the value has been set. -func (o *OrganizationUsageOverview) GetCurrentVolumeUsageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentVolumeUsage, true -} - -// SetCurrentVolumeUsage sets field value -func (o *OrganizationUsageOverview) SetCurrentVolumeUsage(v float32) { - o.CurrentVolumeUsage = v -} - -func (o OrganizationUsageOverview) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o OrganizationUsageOverview) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["regionUsage"] = o.RegionUsage - toSerialize["totalSnapshotQuota"] = o.TotalSnapshotQuota - toSerialize["currentSnapshotUsage"] = o.CurrentSnapshotUsage - toSerialize["totalVolumeQuota"] = o.TotalVolumeQuota - toSerialize["currentVolumeUsage"] = o.CurrentVolumeUsage - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *OrganizationUsageOverview) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "regionUsage", - "totalSnapshotQuota", - "currentSnapshotUsage", - "totalVolumeQuota", - "currentVolumeUsage", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varOrganizationUsageOverview := _OrganizationUsageOverview{} - - err = json.Unmarshal(data, &varOrganizationUsageOverview) - - if err != nil { - return err - } - - *o = OrganizationUsageOverview(varOrganizationUsageOverview) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "regionUsage") - delete(additionalProperties, "totalSnapshotQuota") - delete(additionalProperties, "currentSnapshotUsage") - delete(additionalProperties, "totalVolumeQuota") - delete(additionalProperties, "currentVolumeUsage") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableOrganizationUsageOverview struct { - value *OrganizationUsageOverview - isSet bool -} - -func (v NullableOrganizationUsageOverview) Get() *OrganizationUsageOverview { - return v.value -} - -func (v *NullableOrganizationUsageOverview) Set(val *OrganizationUsageOverview) { - v.value = val - v.isSet = true -} - -func (v NullableOrganizationUsageOverview) IsSet() bool { - return v.isSet -} - -func (v *NullableOrganizationUsageOverview) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableOrganizationUsageOverview(val *OrganizationUsageOverview) *NullableOrganizationUsageOverview { - return &NullableOrganizationUsageOverview{value: val, isSet: true} -} - -func (v NullableOrganizationUsageOverview) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableOrganizationUsageOverview) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_organization_user.go b/apps/api-client-go/model_organization_user.go index 77aa1ee2e..b7bcd1dd7 100644 --- a/apps/api-client-go/model_organization_user.go +++ b/apps/api-client-go/model_organization_user.go @@ -32,6 +32,8 @@ type OrganizationUser struct { Email string `json:"email"` // Member role Role string `json:"role"` + // Whether this organization membership is the user default organization + IsDefaultForUser bool `json:"isDefaultForUser"` // Roles assigned to the user AssignedRoles []OrganizationRole `json:"assignedRoles"` // Creation timestamp @@ -47,13 +49,14 @@ type _OrganizationUser OrganizationUser // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewOrganizationUser(userId string, organizationId string, name string, email string, role string, assignedRoles []OrganizationRole, createdAt time.Time, updatedAt time.Time) *OrganizationUser { +func NewOrganizationUser(userId string, organizationId string, name string, email string, role string, isDefaultForUser bool, assignedRoles []OrganizationRole, createdAt time.Time, updatedAt time.Time) *OrganizationUser { this := OrganizationUser{} this.UserId = userId this.OrganizationId = organizationId this.Name = name this.Email = email this.Role = role + this.IsDefaultForUser = isDefaultForUser this.AssignedRoles = assignedRoles this.CreatedAt = createdAt this.UpdatedAt = updatedAt @@ -188,6 +191,30 @@ func (o *OrganizationUser) SetRole(v string) { o.Role = v } +// GetIsDefaultForUser returns the IsDefaultForUser field value +func (o *OrganizationUser) GetIsDefaultForUser() bool { + if o == nil { + var ret bool + return ret + } + + return o.IsDefaultForUser +} + +// GetIsDefaultForUserOk returns a tuple with the IsDefaultForUser field value +// and a boolean to check if the value has been set. +func (o *OrganizationUser) GetIsDefaultForUserOk() (*bool, bool) { + if o == nil { + return nil, false + } + return &o.IsDefaultForUser, true +} + +// SetIsDefaultForUser sets field value +func (o *OrganizationUser) SetIsDefaultForUser(v bool) { + o.IsDefaultForUser = v +} + // GetAssignedRoles returns the AssignedRoles field value func (o *OrganizationUser) GetAssignedRoles() []OrganizationRole { if o == nil { @@ -275,6 +302,7 @@ func (o OrganizationUser) ToMap() (map[string]interface{}, error) { toSerialize["name"] = o.Name toSerialize["email"] = o.Email toSerialize["role"] = o.Role + toSerialize["isDefaultForUser"] = o.IsDefaultForUser toSerialize["assignedRoles"] = o.AssignedRoles toSerialize["createdAt"] = o.CreatedAt toSerialize["updatedAt"] = o.UpdatedAt @@ -296,6 +324,7 @@ func (o *OrganizationUser) UnmarshalJSON(data []byte) (err error) { "name", "email", "role", + "isDefaultForUser", "assignedRoles", "createdAt", "updatedAt", @@ -333,6 +362,7 @@ func (o *OrganizationUser) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "name") delete(additionalProperties, "email") delete(additionalProperties, "role") + delete(additionalProperties, "isDefaultForUser") delete(additionalProperties, "assignedRoles") delete(additionalProperties, "createdAt") delete(additionalProperties, "updatedAt") @@ -377,3 +407,5 @@ func (v *NullableOrganizationUser) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_otel_config.go b/apps/api-client-go/model_otel_config.go index 94ef62bb0..3961e413d 100644 --- a/apps/api-client-go/model_otel_config.go +++ b/apps/api-client-go/model_otel_config.go @@ -205,3 +205,5 @@ func (v *NullableOtelConfig) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_paginated_audit_logs.go b/apps/api-client-go/model_paginated_audit_logs.go index 76c0bd2d1..639af6706 100644 --- a/apps/api-client-go/model_paginated_audit_logs.go +++ b/apps/api-client-go/model_paginated_audit_logs.go @@ -290,3 +290,5 @@ func (v *NullablePaginatedAuditLogs) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_paginated_boxes.go b/apps/api-client-go/model_paginated_boxes.go new file mode 100644 index 000000000..5ffdaf817 --- /dev/null +++ b/apps/api-client-go/model_paginated_boxes.go @@ -0,0 +1,256 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the PaginatedBoxes type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &PaginatedBoxes{} + +// PaginatedBoxes struct for PaginatedBoxes +type PaginatedBoxes struct { + Items []Box `json:"items"` + Total float32 `json:"total"` + Page float32 `json:"page"` + TotalPages float32 `json:"totalPages"` + AdditionalProperties map[string]interface{} +} + +type _PaginatedBoxes PaginatedBoxes + +// NewPaginatedBoxes instantiates a new PaginatedBoxes object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewPaginatedBoxes(items []Box, total float32, page float32, totalPages float32) *PaginatedBoxes { + this := PaginatedBoxes{} + this.Items = items + this.Total = total + this.Page = page + this.TotalPages = totalPages + return &this +} + +// NewPaginatedBoxesWithDefaults instantiates a new PaginatedBoxes object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewPaginatedBoxesWithDefaults() *PaginatedBoxes { + this := PaginatedBoxes{} + return &this +} + +// GetItems returns the Items field value +func (o *PaginatedBoxes) GetItems() []Box { + if o == nil { + var ret []Box + return ret + } + + return o.Items +} + +// GetItemsOk returns a tuple with the Items field value +// and a boolean to check if the value has been set. +func (o *PaginatedBoxes) GetItemsOk() ([]Box, bool) { + if o == nil { + return nil, false + } + return o.Items, true +} + +// SetItems sets field value +func (o *PaginatedBoxes) SetItems(v []Box) { + o.Items = v +} + +// GetTotal returns the Total field value +func (o *PaginatedBoxes) GetTotal() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Total +} + +// GetTotalOk returns a tuple with the Total field value +// and a boolean to check if the value has been set. +func (o *PaginatedBoxes) GetTotalOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Total, true +} + +// SetTotal sets field value +func (o *PaginatedBoxes) SetTotal(v float32) { + o.Total = v +} + +// GetPage returns the Page field value +func (o *PaginatedBoxes) GetPage() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.Page +} + +// GetPageOk returns a tuple with the Page field value +// and a boolean to check if the value has been set. +func (o *PaginatedBoxes) GetPageOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.Page, true +} + +// SetPage sets field value +func (o *PaginatedBoxes) SetPage(v float32) { + o.Page = v +} + +// GetTotalPages returns the TotalPages field value +func (o *PaginatedBoxes) GetTotalPages() float32 { + if o == nil { + var ret float32 + return ret + } + + return o.TotalPages +} + +// GetTotalPagesOk returns a tuple with the TotalPages field value +// and a boolean to check if the value has been set. +func (o *PaginatedBoxes) GetTotalPagesOk() (*float32, bool) { + if o == nil { + return nil, false + } + return &o.TotalPages, true +} + +// SetTotalPages sets field value +func (o *PaginatedBoxes) SetTotalPages(v float32) { + o.TotalPages = v +} + +func (o PaginatedBoxes) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o PaginatedBoxes) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["items"] = o.Items + toSerialize["total"] = o.Total + toSerialize["page"] = o.Page + toSerialize["totalPages"] = o.TotalPages + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *PaginatedBoxes) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "items", + "total", + "page", + "totalPages", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varPaginatedBoxes := _PaginatedBoxes{} + + err = json.Unmarshal(data, &varPaginatedBoxes) + + if err != nil { + return err + } + + *o = PaginatedBoxes(varPaginatedBoxes) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "items") + delete(additionalProperties, "total") + delete(additionalProperties, "page") + delete(additionalProperties, "totalPages") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullablePaginatedBoxes struct { + value *PaginatedBoxes + isSet bool +} + +func (v NullablePaginatedBoxes) Get() *PaginatedBoxes { + return v.value +} + +func (v *NullablePaginatedBoxes) Set(val *PaginatedBoxes) { + v.value = val + v.isSet = true +} + +func (v NullablePaginatedBoxes) IsSet() bool { + return v.isSet +} + +func (v *NullablePaginatedBoxes) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullablePaginatedBoxes(val *PaginatedBoxes) *NullablePaginatedBoxes { + return &NullablePaginatedBoxes{value: val, isSet: true} +} + +func (v NullablePaginatedBoxes) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullablePaginatedBoxes) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_paginated_jobs.go b/apps/api-client-go/model_paginated_jobs.go index eff1e44e4..35ad4778a 100644 --- a/apps/api-client-go/model_paginated_jobs.go +++ b/apps/api-client-go/model_paginated_jobs.go @@ -252,3 +252,5 @@ func (v *NullablePaginatedJobs) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_paginated_logs.go b/apps/api-client-go/model_paginated_logs.go index 0d2d9a6b1..64dda202d 100644 --- a/apps/api-client-go/model_paginated_logs.go +++ b/apps/api-client-go/model_paginated_logs.go @@ -256,3 +256,5 @@ func (v *NullablePaginatedLogs) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_paginated_sandboxes.go b/apps/api-client-go/model_paginated_sandboxes.go deleted file mode 100644 index 792afd6cd..000000000 --- a/apps/api-client-go/model_paginated_sandboxes.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PaginatedSandboxes type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PaginatedSandboxes{} - -// PaginatedSandboxes struct for PaginatedSandboxes -type PaginatedSandboxes struct { - Items []Sandbox `json:"items"` - Total float32 `json:"total"` - Page float32 `json:"page"` - TotalPages float32 `json:"totalPages"` - AdditionalProperties map[string]interface{} -} - -type _PaginatedSandboxes PaginatedSandboxes - -// NewPaginatedSandboxes instantiates a new PaginatedSandboxes object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPaginatedSandboxes(items []Sandbox, total float32, page float32, totalPages float32) *PaginatedSandboxes { - this := PaginatedSandboxes{} - this.Items = items - this.Total = total - this.Page = page - this.TotalPages = totalPages - return &this -} - -// NewPaginatedSandboxesWithDefaults instantiates a new PaginatedSandboxes object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPaginatedSandboxesWithDefaults() *PaginatedSandboxes { - this := PaginatedSandboxes{} - return &this -} - -// GetItems returns the Items field value -func (o *PaginatedSandboxes) GetItems() []Sandbox { - if o == nil { - var ret []Sandbox - return ret - } - - return o.Items -} - -// GetItemsOk returns a tuple with the Items field value -// and a boolean to check if the value has been set. -func (o *PaginatedSandboxes) GetItemsOk() ([]Sandbox, bool) { - if o == nil { - return nil, false - } - return o.Items, true -} - -// SetItems sets field value -func (o *PaginatedSandboxes) SetItems(v []Sandbox) { - o.Items = v -} - -// GetTotal returns the Total field value -func (o *PaginatedSandboxes) GetTotal() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Total -} - -// GetTotalOk returns a tuple with the Total field value -// and a boolean to check if the value has been set. -func (o *PaginatedSandboxes) GetTotalOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Total, true -} - -// SetTotal sets field value -func (o *PaginatedSandboxes) SetTotal(v float32) { - o.Total = v -} - -// GetPage returns the Page field value -func (o *PaginatedSandboxes) GetPage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Page -} - -// GetPageOk returns a tuple with the Page field value -// and a boolean to check if the value has been set. -func (o *PaginatedSandboxes) GetPageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Page, true -} - -// SetPage sets field value -func (o *PaginatedSandboxes) SetPage(v float32) { - o.Page = v -} - -// GetTotalPages returns the TotalPages field value -func (o *PaginatedSandboxes) GetTotalPages() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalPages -} - -// GetTotalPagesOk returns a tuple with the TotalPages field value -// and a boolean to check if the value has been set. -func (o *PaginatedSandboxes) GetTotalPagesOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalPages, true -} - -// SetTotalPages sets field value -func (o *PaginatedSandboxes) SetTotalPages(v float32) { - o.TotalPages = v -} - -func (o PaginatedSandboxes) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PaginatedSandboxes) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["items"] = o.Items - toSerialize["total"] = o.Total - toSerialize["page"] = o.Page - toSerialize["totalPages"] = o.TotalPages - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PaginatedSandboxes) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "items", - "total", - "page", - "totalPages", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPaginatedSandboxes := _PaginatedSandboxes{} - - err = json.Unmarshal(data, &varPaginatedSandboxes) - - if err != nil { - return err - } - - *o = PaginatedSandboxes(varPaginatedSandboxes) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "items") - delete(additionalProperties, "total") - delete(additionalProperties, "page") - delete(additionalProperties, "totalPages") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePaginatedSandboxes struct { - value *PaginatedSandboxes - isSet bool -} - -func (v NullablePaginatedSandboxes) Get() *PaginatedSandboxes { - return v.value -} - -func (v *NullablePaginatedSandboxes) Set(val *PaginatedSandboxes) { - v.value = val - v.isSet = true -} - -func (v NullablePaginatedSandboxes) IsSet() bool { - return v.isSet -} - -func (v *NullablePaginatedSandboxes) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePaginatedSandboxes(val *PaginatedSandboxes) *NullablePaginatedSandboxes { - return &NullablePaginatedSandboxes{value: val, isSet: true} -} - -func (v NullablePaginatedSandboxes) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePaginatedSandboxes) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_paginated_snapshots.go b/apps/api-client-go/model_paginated_snapshots.go deleted file mode 100644 index 2ea919e1e..000000000 --- a/apps/api-client-go/model_paginated_snapshots.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PaginatedSnapshots type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PaginatedSnapshots{} - -// PaginatedSnapshots struct for PaginatedSnapshots -type PaginatedSnapshots struct { - Items []SnapshotDto `json:"items"` - Total float32 `json:"total"` - Page float32 `json:"page"` - TotalPages float32 `json:"totalPages"` - AdditionalProperties map[string]interface{} -} - -type _PaginatedSnapshots PaginatedSnapshots - -// NewPaginatedSnapshots instantiates a new PaginatedSnapshots object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPaginatedSnapshots(items []SnapshotDto, total float32, page float32, totalPages float32) *PaginatedSnapshots { - this := PaginatedSnapshots{} - this.Items = items - this.Total = total - this.Page = page - this.TotalPages = totalPages - return &this -} - -// NewPaginatedSnapshotsWithDefaults instantiates a new PaginatedSnapshots object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPaginatedSnapshotsWithDefaults() *PaginatedSnapshots { - this := PaginatedSnapshots{} - return &this -} - -// GetItems returns the Items field value -func (o *PaginatedSnapshots) GetItems() []SnapshotDto { - if o == nil { - var ret []SnapshotDto - return ret - } - - return o.Items -} - -// GetItemsOk returns a tuple with the Items field value -// and a boolean to check if the value has been set. -func (o *PaginatedSnapshots) GetItemsOk() ([]SnapshotDto, bool) { - if o == nil { - return nil, false - } - return o.Items, true -} - -// SetItems sets field value -func (o *PaginatedSnapshots) SetItems(v []SnapshotDto) { - o.Items = v -} - -// GetTotal returns the Total field value -func (o *PaginatedSnapshots) GetTotal() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Total -} - -// GetTotalOk returns a tuple with the Total field value -// and a boolean to check if the value has been set. -func (o *PaginatedSnapshots) GetTotalOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Total, true -} - -// SetTotal sets field value -func (o *PaginatedSnapshots) SetTotal(v float32) { - o.Total = v -} - -// GetPage returns the Page field value -func (o *PaginatedSnapshots) GetPage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Page -} - -// GetPageOk returns a tuple with the Page field value -// and a boolean to check if the value has been set. -func (o *PaginatedSnapshots) GetPageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Page, true -} - -// SetPage sets field value -func (o *PaginatedSnapshots) SetPage(v float32) { - o.Page = v -} - -// GetTotalPages returns the TotalPages field value -func (o *PaginatedSnapshots) GetTotalPages() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalPages -} - -// GetTotalPagesOk returns a tuple with the TotalPages field value -// and a boolean to check if the value has been set. -func (o *PaginatedSnapshots) GetTotalPagesOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalPages, true -} - -// SetTotalPages sets field value -func (o *PaginatedSnapshots) SetTotalPages(v float32) { - o.TotalPages = v -} - -func (o PaginatedSnapshots) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PaginatedSnapshots) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["items"] = o.Items - toSerialize["total"] = o.Total - toSerialize["page"] = o.Page - toSerialize["totalPages"] = o.TotalPages - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PaginatedSnapshots) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "items", - "total", - "page", - "totalPages", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPaginatedSnapshots := _PaginatedSnapshots{} - - err = json.Unmarshal(data, &varPaginatedSnapshots) - - if err != nil { - return err - } - - *o = PaginatedSnapshots(varPaginatedSnapshots) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "items") - delete(additionalProperties, "total") - delete(additionalProperties, "page") - delete(additionalProperties, "totalPages") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePaginatedSnapshots struct { - value *PaginatedSnapshots - isSet bool -} - -func (v NullablePaginatedSnapshots) Get() *PaginatedSnapshots { - return v.value -} - -func (v *NullablePaginatedSnapshots) Set(val *PaginatedSnapshots) { - v.value = val - v.isSet = true -} - -func (v NullablePaginatedSnapshots) IsSet() bool { - return v.isSet -} - -func (v *NullablePaginatedSnapshots) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePaginatedSnapshots(val *PaginatedSnapshots) *NullablePaginatedSnapshots { - return &NullablePaginatedSnapshots{value: val, isSet: true} -} - -func (v NullablePaginatedSnapshots) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePaginatedSnapshots) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_paginated_traces.go b/apps/api-client-go/model_paginated_traces.go index 0bdffedf3..c2757d1db 100644 --- a/apps/api-client-go/model_paginated_traces.go +++ b/apps/api-client-go/model_paginated_traces.go @@ -256,3 +256,5 @@ func (v *NullablePaginatedTraces) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_poll_jobs_response.go b/apps/api-client-go/model_poll_jobs_response.go index 8d3f0505f..7add8c9b2 100644 --- a/apps/api-client-go/model_poll_jobs_response.go +++ b/apps/api-client-go/model_poll_jobs_response.go @@ -166,3 +166,5 @@ func (v *NullablePollJobsResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_port_preview_url.go b/apps/api-client-go/model_port_preview_url.go index 16ae3fb0c..de69637ff 100644 --- a/apps/api-client-go/model_port_preview_url.go +++ b/apps/api-client-go/model_port_preview_url.go @@ -21,8 +21,8 @@ var _ MappedNullable = &PortPreviewUrl{} // PortPreviewUrl struct for PortPreviewUrl type PortPreviewUrl struct { - // ID of the sandbox - SandboxId string `json:"sandboxId"` + // ID of the box + BoxId string `json:"boxId"` // Preview url Url string `json:"url"` // Access token @@ -36,9 +36,9 @@ type _PortPreviewUrl PortPreviewUrl // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewPortPreviewUrl(sandboxId string, url string, token string) *PortPreviewUrl { +func NewPortPreviewUrl(boxId string, url string, token string) *PortPreviewUrl { this := PortPreviewUrl{} - this.SandboxId = sandboxId + this.BoxId = boxId this.Url = url this.Token = token return &this @@ -52,28 +52,28 @@ func NewPortPreviewUrlWithDefaults() *PortPreviewUrl { return &this } -// GetSandboxId returns the SandboxId field value -func (o *PortPreviewUrl) GetSandboxId() string { +// GetBoxId returns the BoxId field value +func (o *PortPreviewUrl) GetBoxId() string { if o == nil { var ret string return ret } - return o.SandboxId + return o.BoxId } -// GetSandboxIdOk returns a tuple with the SandboxId field value +// GetBoxIdOk returns a tuple with the BoxId field value // and a boolean to check if the value has been set. -func (o *PortPreviewUrl) GetSandboxIdOk() (*string, bool) { +func (o *PortPreviewUrl) GetBoxIdOk() (*string, bool) { if o == nil { return nil, false } - return &o.SandboxId, true + return &o.BoxId, true } -// SetSandboxId sets field value -func (o *PortPreviewUrl) SetSandboxId(v string) { - o.SandboxId = v +// SetBoxId sets field value +func (o *PortPreviewUrl) SetBoxId(v string) { + o.BoxId = v } // GetUrl returns the Url field value @@ -134,7 +134,7 @@ func (o PortPreviewUrl) MarshalJSON() ([]byte, error) { func (o PortPreviewUrl) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} - toSerialize["sandboxId"] = o.SandboxId + toSerialize["boxId"] = o.BoxId toSerialize["url"] = o.Url toSerialize["token"] = o.Token @@ -150,7 +150,7 @@ func (o *PortPreviewUrl) UnmarshalJSON(data []byte) (err error) { // by unmarshalling the object into a generic map with string keys and checking // that every required field exists as a key in the generic map. requiredProperties := []string{ - "sandboxId", + "boxId", "url", "token", } @@ -182,7 +182,7 @@ func (o *PortPreviewUrl) UnmarshalJSON(data []byte) (err error) { additionalProperties := make(map[string]interface{}) if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sandboxId") + delete(additionalProperties, "boxId") delete(additionalProperties, "url") delete(additionalProperties, "token") o.AdditionalProperties = additionalProperties @@ -226,3 +226,5 @@ func (v *NullablePortPreviewUrl) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_position.go b/apps/api-client-go/model_position.go deleted file mode 100644 index 9ac965c09..000000000 --- a/apps/api-client-go/model_position.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Position type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Position{} - -// Position struct for Position -type Position struct { - Line float32 `json:"line"` - Character float32 `json:"character"` - AdditionalProperties map[string]interface{} -} - -type _Position Position - -// NewPosition instantiates a new Position object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPosition(line float32, character float32) *Position { - this := Position{} - this.Line = line - this.Character = character - return &this -} - -// NewPositionWithDefaults instantiates a new Position object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPositionWithDefaults() *Position { - this := Position{} - return &this -} - -// GetLine returns the Line field value -func (o *Position) GetLine() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Line -} - -// GetLineOk returns a tuple with the Line field value -// and a boolean to check if the value has been set. -func (o *Position) GetLineOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Line, true -} - -// SetLine sets field value -func (o *Position) SetLine(v float32) { - o.Line = v -} - -// GetCharacter returns the Character field value -func (o *Position) GetCharacter() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Character -} - -// GetCharacterOk returns a tuple with the Character field value -// and a boolean to check if the value has been set. -func (o *Position) GetCharacterOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Character, true -} - -// SetCharacter sets field value -func (o *Position) SetCharacter(v float32) { - o.Character = v -} - -func (o Position) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Position) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["line"] = o.Line - toSerialize["character"] = o.Character - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Position) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "line", - "character", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPosition := _Position{} - - err = json.Unmarshal(data, &varPosition) - - if err != nil { - return err - } - - *o = Position(varPosition) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "line") - delete(additionalProperties, "character") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePosition struct { - value *Position - isSet bool -} - -func (v NullablePosition) Get() *Position { - return v.value -} - -func (v *NullablePosition) Set(val *Position) { - v.value = val - v.isSet = true -} - -func (v NullablePosition) IsSet() bool { - return v.isSet -} - -func (v *NullablePosition) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePosition(val *Position) *NullablePosition { - return &NullablePosition{value: val, isSet: true} -} - -func (v NullablePosition) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePosition) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_posthog_config.go b/apps/api-client-go/model_posthog_config.go index c0025d0ec..8ae857766 100644 --- a/apps/api-client-go/model_posthog_config.go +++ b/apps/api-client-go/model_posthog_config.go @@ -196,3 +196,5 @@ func (v *NullablePosthogConfig) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_process_errors_response.go b/apps/api-client-go/model_process_errors_response.go deleted file mode 100644 index 5e8d8226b..000000000 --- a/apps/api-client-go/model_process_errors_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ProcessErrorsResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ProcessErrorsResponse{} - -// ProcessErrorsResponse struct for ProcessErrorsResponse -type ProcessErrorsResponse struct { - // The name of the VNC process whose error logs were retrieved - ProcessName string `json:"processName"` - // The error log output from the specified VNC process - Errors string `json:"errors"` - AdditionalProperties map[string]interface{} -} - -type _ProcessErrorsResponse ProcessErrorsResponse - -// NewProcessErrorsResponse instantiates a new ProcessErrorsResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewProcessErrorsResponse(processName string, errors string) *ProcessErrorsResponse { - this := ProcessErrorsResponse{} - this.ProcessName = processName - this.Errors = errors - return &this -} - -// NewProcessErrorsResponseWithDefaults instantiates a new ProcessErrorsResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewProcessErrorsResponseWithDefaults() *ProcessErrorsResponse { - this := ProcessErrorsResponse{} - return &this -} - -// GetProcessName returns the ProcessName field value -func (o *ProcessErrorsResponse) GetProcessName() string { - if o == nil { - var ret string - return ret - } - - return o.ProcessName -} - -// GetProcessNameOk returns a tuple with the ProcessName field value -// and a boolean to check if the value has been set. -func (o *ProcessErrorsResponse) GetProcessNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ProcessName, true -} - -// SetProcessName sets field value -func (o *ProcessErrorsResponse) SetProcessName(v string) { - o.ProcessName = v -} - -// GetErrors returns the Errors field value -func (o *ProcessErrorsResponse) GetErrors() string { - if o == nil { - var ret string - return ret - } - - return o.Errors -} - -// GetErrorsOk returns a tuple with the Errors field value -// and a boolean to check if the value has been set. -func (o *ProcessErrorsResponse) GetErrorsOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Errors, true -} - -// SetErrors sets field value -func (o *ProcessErrorsResponse) SetErrors(v string) { - o.Errors = v -} - -func (o ProcessErrorsResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ProcessErrorsResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["processName"] = o.ProcessName - toSerialize["errors"] = o.Errors - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ProcessErrorsResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "processName", - "errors", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varProcessErrorsResponse := _ProcessErrorsResponse{} - - err = json.Unmarshal(data, &varProcessErrorsResponse) - - if err != nil { - return err - } - - *o = ProcessErrorsResponse(varProcessErrorsResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "processName") - delete(additionalProperties, "errors") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableProcessErrorsResponse struct { - value *ProcessErrorsResponse - isSet bool -} - -func (v NullableProcessErrorsResponse) Get() *ProcessErrorsResponse { - return v.value -} - -func (v *NullableProcessErrorsResponse) Set(val *ProcessErrorsResponse) { - v.value = val - v.isSet = true -} - -func (v NullableProcessErrorsResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableProcessErrorsResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableProcessErrorsResponse(val *ProcessErrorsResponse) *NullableProcessErrorsResponse { - return &NullableProcessErrorsResponse{value: val, isSet: true} -} - -func (v NullableProcessErrorsResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableProcessErrorsResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_process_logs_response.go b/apps/api-client-go/model_process_logs_response.go deleted file mode 100644 index 5f04f39c8..000000000 --- a/apps/api-client-go/model_process_logs_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ProcessLogsResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ProcessLogsResponse{} - -// ProcessLogsResponse struct for ProcessLogsResponse -type ProcessLogsResponse struct { - // The name of the VNC process whose logs were retrieved - ProcessName string `json:"processName"` - // The log output from the specified VNC process - Logs string `json:"logs"` - AdditionalProperties map[string]interface{} -} - -type _ProcessLogsResponse ProcessLogsResponse - -// NewProcessLogsResponse instantiates a new ProcessLogsResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewProcessLogsResponse(processName string, logs string) *ProcessLogsResponse { - this := ProcessLogsResponse{} - this.ProcessName = processName - this.Logs = logs - return &this -} - -// NewProcessLogsResponseWithDefaults instantiates a new ProcessLogsResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewProcessLogsResponseWithDefaults() *ProcessLogsResponse { - this := ProcessLogsResponse{} - return &this -} - -// GetProcessName returns the ProcessName field value -func (o *ProcessLogsResponse) GetProcessName() string { - if o == nil { - var ret string - return ret - } - - return o.ProcessName -} - -// GetProcessNameOk returns a tuple with the ProcessName field value -// and a boolean to check if the value has been set. -func (o *ProcessLogsResponse) GetProcessNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ProcessName, true -} - -// SetProcessName sets field value -func (o *ProcessLogsResponse) SetProcessName(v string) { - o.ProcessName = v -} - -// GetLogs returns the Logs field value -func (o *ProcessLogsResponse) GetLogs() string { - if o == nil { - var ret string - return ret - } - - return o.Logs -} - -// GetLogsOk returns a tuple with the Logs field value -// and a boolean to check if the value has been set. -func (o *ProcessLogsResponse) GetLogsOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Logs, true -} - -// SetLogs sets field value -func (o *ProcessLogsResponse) SetLogs(v string) { - o.Logs = v -} - -func (o ProcessLogsResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ProcessLogsResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["processName"] = o.ProcessName - toSerialize["logs"] = o.Logs - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ProcessLogsResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "processName", - "logs", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varProcessLogsResponse := _ProcessLogsResponse{} - - err = json.Unmarshal(data, &varProcessLogsResponse) - - if err != nil { - return err - } - - *o = ProcessLogsResponse(varProcessLogsResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "processName") - delete(additionalProperties, "logs") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableProcessLogsResponse struct { - value *ProcessLogsResponse - isSet bool -} - -func (v NullableProcessLogsResponse) Get() *ProcessLogsResponse { - return v.value -} - -func (v *NullableProcessLogsResponse) Set(val *ProcessLogsResponse) { - v.value = val - v.isSet = true -} - -func (v NullableProcessLogsResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableProcessLogsResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableProcessLogsResponse(val *ProcessLogsResponse) *NullableProcessLogsResponse { - return &NullableProcessLogsResponse{value: val, isSet: true} -} - -func (v NullableProcessLogsResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableProcessLogsResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_process_restart_response.go b/apps/api-client-go/model_process_restart_response.go deleted file mode 100644 index 62a9ed077..000000000 --- a/apps/api-client-go/model_process_restart_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ProcessRestartResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ProcessRestartResponse{} - -// ProcessRestartResponse struct for ProcessRestartResponse -type ProcessRestartResponse struct { - // A message indicating the result of restarting the process - Message string `json:"message"` - // The name of the VNC process that was restarted - ProcessName string `json:"processName"` - AdditionalProperties map[string]interface{} -} - -type _ProcessRestartResponse ProcessRestartResponse - -// NewProcessRestartResponse instantiates a new ProcessRestartResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewProcessRestartResponse(message string, processName string) *ProcessRestartResponse { - this := ProcessRestartResponse{} - this.Message = message - this.ProcessName = processName - return &this -} - -// NewProcessRestartResponseWithDefaults instantiates a new ProcessRestartResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewProcessRestartResponseWithDefaults() *ProcessRestartResponse { - this := ProcessRestartResponse{} - return &this -} - -// GetMessage returns the Message field value -func (o *ProcessRestartResponse) GetMessage() string { - if o == nil { - var ret string - return ret - } - - return o.Message -} - -// GetMessageOk returns a tuple with the Message field value -// and a boolean to check if the value has been set. -func (o *ProcessRestartResponse) GetMessageOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Message, true -} - -// SetMessage sets field value -func (o *ProcessRestartResponse) SetMessage(v string) { - o.Message = v -} - -// GetProcessName returns the ProcessName field value -func (o *ProcessRestartResponse) GetProcessName() string { - if o == nil { - var ret string - return ret - } - - return o.ProcessName -} - -// GetProcessNameOk returns a tuple with the ProcessName field value -// and a boolean to check if the value has been set. -func (o *ProcessRestartResponse) GetProcessNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ProcessName, true -} - -// SetProcessName sets field value -func (o *ProcessRestartResponse) SetProcessName(v string) { - o.ProcessName = v -} - -func (o ProcessRestartResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ProcessRestartResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["message"] = o.Message - toSerialize["processName"] = o.ProcessName - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ProcessRestartResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "message", - "processName", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varProcessRestartResponse := _ProcessRestartResponse{} - - err = json.Unmarshal(data, &varProcessRestartResponse) - - if err != nil { - return err - } - - *o = ProcessRestartResponse(varProcessRestartResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "message") - delete(additionalProperties, "processName") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableProcessRestartResponse struct { - value *ProcessRestartResponse - isSet bool -} - -func (v NullableProcessRestartResponse) Get() *ProcessRestartResponse { - return v.value -} - -func (v *NullableProcessRestartResponse) Set(val *ProcessRestartResponse) { - v.value = val - v.isSet = true -} - -func (v NullableProcessRestartResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableProcessRestartResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableProcessRestartResponse(val *ProcessRestartResponse) *NullableProcessRestartResponse { - return &NullableProcessRestartResponse{value: val, isSet: true} -} - -func (v NullableProcessRestartResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableProcessRestartResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_process_status_response.go b/apps/api-client-go/model_process_status_response.go deleted file mode 100644 index 48ef2a5ab..000000000 --- a/apps/api-client-go/model_process_status_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ProcessStatusResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ProcessStatusResponse{} - -// ProcessStatusResponse struct for ProcessStatusResponse -type ProcessStatusResponse struct { - // The name of the VNC process being checked - ProcessName string `json:"processName"` - // Whether the specified VNC process is currently running - Running bool `json:"running"` - AdditionalProperties map[string]interface{} -} - -type _ProcessStatusResponse ProcessStatusResponse - -// NewProcessStatusResponse instantiates a new ProcessStatusResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewProcessStatusResponse(processName string, running bool) *ProcessStatusResponse { - this := ProcessStatusResponse{} - this.ProcessName = processName - this.Running = running - return &this -} - -// NewProcessStatusResponseWithDefaults instantiates a new ProcessStatusResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewProcessStatusResponseWithDefaults() *ProcessStatusResponse { - this := ProcessStatusResponse{} - return &this -} - -// GetProcessName returns the ProcessName field value -func (o *ProcessStatusResponse) GetProcessName() string { - if o == nil { - var ret string - return ret - } - - return o.ProcessName -} - -// GetProcessNameOk returns a tuple with the ProcessName field value -// and a boolean to check if the value has been set. -func (o *ProcessStatusResponse) GetProcessNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ProcessName, true -} - -// SetProcessName sets field value -func (o *ProcessStatusResponse) SetProcessName(v string) { - o.ProcessName = v -} - -// GetRunning returns the Running field value -func (o *ProcessStatusResponse) GetRunning() bool { - if o == nil { - var ret bool - return ret - } - - return o.Running -} - -// GetRunningOk returns a tuple with the Running field value -// and a boolean to check if the value has been set. -func (o *ProcessStatusResponse) GetRunningOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.Running, true -} - -// SetRunning sets field value -func (o *ProcessStatusResponse) SetRunning(v bool) { - o.Running = v -} - -func (o ProcessStatusResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ProcessStatusResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["processName"] = o.ProcessName - toSerialize["running"] = o.Running - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ProcessStatusResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "processName", - "running", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varProcessStatusResponse := _ProcessStatusResponse{} - - err = json.Unmarshal(data, &varProcessStatusResponse) - - if err != nil { - return err - } - - *o = ProcessStatusResponse(varProcessStatusResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "processName") - delete(additionalProperties, "running") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableProcessStatusResponse struct { - value *ProcessStatusResponse - isSet bool -} - -func (v NullableProcessStatusResponse) Get() *ProcessStatusResponse { - return v.value -} - -func (v *NullableProcessStatusResponse) Set(val *ProcessStatusResponse) { - v.value = val - v.isSet = true -} - -func (v NullableProcessStatusResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableProcessStatusResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableProcessStatusResponse(val *ProcessStatusResponse) *NullableProcessStatusResponse { - return &NullableProcessStatusResponse{value: val, isSet: true} -} - -func (v NullableProcessStatusResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableProcessStatusResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_project_dir_response.go b/apps/api-client-go/model_project_dir_response.go deleted file mode 100644 index db288d0f8..000000000 --- a/apps/api-client-go/model_project_dir_response.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the ProjectDirResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ProjectDirResponse{} - -// ProjectDirResponse struct for ProjectDirResponse -type ProjectDirResponse struct { - Dir *string `json:"dir,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _ProjectDirResponse ProjectDirResponse - -// NewProjectDirResponse instantiates a new ProjectDirResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewProjectDirResponse() *ProjectDirResponse { - this := ProjectDirResponse{} - return &this -} - -// NewProjectDirResponseWithDefaults instantiates a new ProjectDirResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewProjectDirResponseWithDefaults() *ProjectDirResponse { - this := ProjectDirResponse{} - return &this -} - -// GetDir returns the Dir field value if set, zero value otherwise. -func (o *ProjectDirResponse) GetDir() string { - if o == nil || IsNil(o.Dir) { - var ret string - return ret - } - return *o.Dir -} - -// GetDirOk returns a tuple with the Dir field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ProjectDirResponse) GetDirOk() (*string, bool) { - if o == nil || IsNil(o.Dir) { - return nil, false - } - return o.Dir, true -} - -// HasDir returns a boolean if a field has been set. -func (o *ProjectDirResponse) HasDir() bool { - if o != nil && !IsNil(o.Dir) { - return true - } - - return false -} - -// SetDir gets a reference to the given string and assigns it to the Dir field. -func (o *ProjectDirResponse) SetDir(v string) { - o.Dir = &v -} - -func (o ProjectDirResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ProjectDirResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Dir) { - toSerialize["dir"] = o.Dir - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ProjectDirResponse) UnmarshalJSON(data []byte) (err error) { - varProjectDirResponse := _ProjectDirResponse{} - - err = json.Unmarshal(data, &varProjectDirResponse) - - if err != nil { - return err - } - - *o = ProjectDirResponse(varProjectDirResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "dir") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableProjectDirResponse struct { - value *ProjectDirResponse - isSet bool -} - -func (v NullableProjectDirResponse) Get() *ProjectDirResponse { - return v.value -} - -func (v *NullableProjectDirResponse) Set(val *ProjectDirResponse) { - v.value = val - v.isSet = true -} - -func (v NullableProjectDirResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableProjectDirResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableProjectDirResponse(val *ProjectDirResponse) *NullableProjectDirResponse { - return &NullableProjectDirResponse{value: val, isSet: true} -} - -func (v NullableProjectDirResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableProjectDirResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_pty_create_request.go b/apps/api-client-go/model_pty_create_request.go deleted file mode 100644 index c160248ff..000000000 --- a/apps/api-client-go/model_pty_create_request.go +++ /dev/null @@ -1,362 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PtyCreateRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PtyCreateRequest{} - -// PtyCreateRequest struct for PtyCreateRequest -type PtyCreateRequest struct { - // The unique identifier for the PTY session - Id string `json:"id"` - // Starting directory for the PTY session, defaults to the sandbox's working directory - Cwd *string `json:"cwd,omitempty"` - // Environment variables for the PTY session - Envs map[string]interface{} `json:"envs,omitempty"` - // Number of terminal columns - Cols *float32 `json:"cols,omitempty"` - // Number of terminal rows - Rows *float32 `json:"rows,omitempty"` - // Whether to start the PTY session lazily (only start when first client connects) - LazyStart *bool `json:"lazyStart,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _PtyCreateRequest PtyCreateRequest - -// NewPtyCreateRequest instantiates a new PtyCreateRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPtyCreateRequest(id string) *PtyCreateRequest { - this := PtyCreateRequest{} - this.Id = id - var lazyStart bool = false - this.LazyStart = &lazyStart - return &this -} - -// NewPtyCreateRequestWithDefaults instantiates a new PtyCreateRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPtyCreateRequestWithDefaults() *PtyCreateRequest { - this := PtyCreateRequest{} - var lazyStart bool = false - this.LazyStart = &lazyStart - return &this -} - -// GetId returns the Id field value -func (o *PtyCreateRequest) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *PtyCreateRequest) SetId(v string) { - o.Id = v -} - -// GetCwd returns the Cwd field value if set, zero value otherwise. -func (o *PtyCreateRequest) GetCwd() string { - if o == nil || IsNil(o.Cwd) { - var ret string - return ret - } - return *o.Cwd -} - -// GetCwdOk returns a tuple with the Cwd field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetCwdOk() (*string, bool) { - if o == nil || IsNil(o.Cwd) { - return nil, false - } - return o.Cwd, true -} - -// HasCwd returns a boolean if a field has been set. -func (o *PtyCreateRequest) HasCwd() bool { - if o != nil && !IsNil(o.Cwd) { - return true - } - - return false -} - -// SetCwd gets a reference to the given string and assigns it to the Cwd field. -func (o *PtyCreateRequest) SetCwd(v string) { - o.Cwd = &v -} - -// GetEnvs returns the Envs field value if set, zero value otherwise. -func (o *PtyCreateRequest) GetEnvs() map[string]interface{} { - if o == nil || IsNil(o.Envs) { - var ret map[string]interface{} - return ret - } - return o.Envs -} - -// GetEnvsOk returns a tuple with the Envs field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetEnvsOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.Envs) { - return map[string]interface{}{}, false - } - return o.Envs, true -} - -// HasEnvs returns a boolean if a field has been set. -func (o *PtyCreateRequest) HasEnvs() bool { - if o != nil && !IsNil(o.Envs) { - return true - } - - return false -} - -// SetEnvs gets a reference to the given map[string]interface{} and assigns it to the Envs field. -func (o *PtyCreateRequest) SetEnvs(v map[string]interface{}) { - o.Envs = v -} - -// GetCols returns the Cols field value if set, zero value otherwise. -func (o *PtyCreateRequest) GetCols() float32 { - if o == nil || IsNil(o.Cols) { - var ret float32 - return ret - } - return *o.Cols -} - -// GetColsOk returns a tuple with the Cols field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetColsOk() (*float32, bool) { - if o == nil || IsNil(o.Cols) { - return nil, false - } - return o.Cols, true -} - -// HasCols returns a boolean if a field has been set. -func (o *PtyCreateRequest) HasCols() bool { - if o != nil && !IsNil(o.Cols) { - return true - } - - return false -} - -// SetCols gets a reference to the given float32 and assigns it to the Cols field. -func (o *PtyCreateRequest) SetCols(v float32) { - o.Cols = &v -} - -// GetRows returns the Rows field value if set, zero value otherwise. -func (o *PtyCreateRequest) GetRows() float32 { - if o == nil || IsNil(o.Rows) { - var ret float32 - return ret - } - return *o.Rows -} - -// GetRowsOk returns a tuple with the Rows field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetRowsOk() (*float32, bool) { - if o == nil || IsNil(o.Rows) { - return nil, false - } - return o.Rows, true -} - -// HasRows returns a boolean if a field has been set. -func (o *PtyCreateRequest) HasRows() bool { - if o != nil && !IsNil(o.Rows) { - return true - } - - return false -} - -// SetRows gets a reference to the given float32 and assigns it to the Rows field. -func (o *PtyCreateRequest) SetRows(v float32) { - o.Rows = &v -} - -// GetLazyStart returns the LazyStart field value if set, zero value otherwise. -func (o *PtyCreateRequest) GetLazyStart() bool { - if o == nil || IsNil(o.LazyStart) { - var ret bool - return ret - } - return *o.LazyStart -} - -// GetLazyStartOk returns a tuple with the LazyStart field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *PtyCreateRequest) GetLazyStartOk() (*bool, bool) { - if o == nil || IsNil(o.LazyStart) { - return nil, false - } - return o.LazyStart, true -} - -// HasLazyStart returns a boolean if a field has been set. -func (o *PtyCreateRequest) HasLazyStart() bool { - if o != nil && !IsNil(o.LazyStart) { - return true - } - - return false -} - -// SetLazyStart gets a reference to the given bool and assigns it to the LazyStart field. -func (o *PtyCreateRequest) SetLazyStart(v bool) { - o.LazyStart = &v -} - -func (o PtyCreateRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PtyCreateRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - if !IsNil(o.Cwd) { - toSerialize["cwd"] = o.Cwd - } - if !IsNil(o.Envs) { - toSerialize["envs"] = o.Envs - } - if !IsNil(o.Cols) { - toSerialize["cols"] = o.Cols - } - if !IsNil(o.Rows) { - toSerialize["rows"] = o.Rows - } - if !IsNil(o.LazyStart) { - toSerialize["lazyStart"] = o.LazyStart - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PtyCreateRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPtyCreateRequest := _PtyCreateRequest{} - - err = json.Unmarshal(data, &varPtyCreateRequest) - - if err != nil { - return err - } - - *o = PtyCreateRequest(varPtyCreateRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "cwd") - delete(additionalProperties, "envs") - delete(additionalProperties, "cols") - delete(additionalProperties, "rows") - delete(additionalProperties, "lazyStart") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePtyCreateRequest struct { - value *PtyCreateRequest - isSet bool -} - -func (v NullablePtyCreateRequest) Get() *PtyCreateRequest { - return v.value -} - -func (v *NullablePtyCreateRequest) Set(val *PtyCreateRequest) { - v.value = val - v.isSet = true -} - -func (v NullablePtyCreateRequest) IsSet() bool { - return v.isSet -} - -func (v *NullablePtyCreateRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePtyCreateRequest(val *PtyCreateRequest) *NullablePtyCreateRequest { - return &NullablePtyCreateRequest{value: val, isSet: true} -} - -func (v NullablePtyCreateRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePtyCreateRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_pty_create_response.go b/apps/api-client-go/model_pty_create_response.go deleted file mode 100644 index 27587cb7d..000000000 --- a/apps/api-client-go/model_pty_create_response.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PtyCreateResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PtyCreateResponse{} - -// PtyCreateResponse struct for PtyCreateResponse -type PtyCreateResponse struct { - // The unique identifier for the created PTY session - SessionId string `json:"sessionId"` - AdditionalProperties map[string]interface{} -} - -type _PtyCreateResponse PtyCreateResponse - -// NewPtyCreateResponse instantiates a new PtyCreateResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPtyCreateResponse(sessionId string) *PtyCreateResponse { - this := PtyCreateResponse{} - this.SessionId = sessionId - return &this -} - -// NewPtyCreateResponseWithDefaults instantiates a new PtyCreateResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPtyCreateResponseWithDefaults() *PtyCreateResponse { - this := PtyCreateResponse{} - return &this -} - -// GetSessionId returns the SessionId field value -func (o *PtyCreateResponse) GetSessionId() string { - if o == nil { - var ret string - return ret - } - - return o.SessionId -} - -// GetSessionIdOk returns a tuple with the SessionId field value -// and a boolean to check if the value has been set. -func (o *PtyCreateResponse) GetSessionIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.SessionId, true -} - -// SetSessionId sets field value -func (o *PtyCreateResponse) SetSessionId(v string) { - o.SessionId = v -} - -func (o PtyCreateResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PtyCreateResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["sessionId"] = o.SessionId - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PtyCreateResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "sessionId", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPtyCreateResponse := _PtyCreateResponse{} - - err = json.Unmarshal(data, &varPtyCreateResponse) - - if err != nil { - return err - } - - *o = PtyCreateResponse(varPtyCreateResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sessionId") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePtyCreateResponse struct { - value *PtyCreateResponse - isSet bool -} - -func (v NullablePtyCreateResponse) Get() *PtyCreateResponse { - return v.value -} - -func (v *NullablePtyCreateResponse) Set(val *PtyCreateResponse) { - v.value = val - v.isSet = true -} - -func (v NullablePtyCreateResponse) IsSet() bool { - return v.isSet -} - -func (v *NullablePtyCreateResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePtyCreateResponse(val *PtyCreateResponse) *NullablePtyCreateResponse { - return &NullablePtyCreateResponse{value: val, isSet: true} -} - -func (v NullablePtyCreateResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePtyCreateResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_pty_list_response.go b/apps/api-client-go/model_pty_list_response.go deleted file mode 100644 index 82968f816..000000000 --- a/apps/api-client-go/model_pty_list_response.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PtyListResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PtyListResponse{} - -// PtyListResponse struct for PtyListResponse -type PtyListResponse struct { - // List of active PTY sessions - Sessions []PtySessionInfo `json:"sessions"` - AdditionalProperties map[string]interface{} -} - -type _PtyListResponse PtyListResponse - -// NewPtyListResponse instantiates a new PtyListResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPtyListResponse(sessions []PtySessionInfo) *PtyListResponse { - this := PtyListResponse{} - this.Sessions = sessions - return &this -} - -// NewPtyListResponseWithDefaults instantiates a new PtyListResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPtyListResponseWithDefaults() *PtyListResponse { - this := PtyListResponse{} - return &this -} - -// GetSessions returns the Sessions field value -func (o *PtyListResponse) GetSessions() []PtySessionInfo { - if o == nil { - var ret []PtySessionInfo - return ret - } - - return o.Sessions -} - -// GetSessionsOk returns a tuple with the Sessions field value -// and a boolean to check if the value has been set. -func (o *PtyListResponse) GetSessionsOk() ([]PtySessionInfo, bool) { - if o == nil { - return nil, false - } - return o.Sessions, true -} - -// SetSessions sets field value -func (o *PtyListResponse) SetSessions(v []PtySessionInfo) { - o.Sessions = v -} - -func (o PtyListResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PtyListResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["sessions"] = o.Sessions - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PtyListResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "sessions", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPtyListResponse := _PtyListResponse{} - - err = json.Unmarshal(data, &varPtyListResponse) - - if err != nil { - return err - } - - *o = PtyListResponse(varPtyListResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sessions") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePtyListResponse struct { - value *PtyListResponse - isSet bool -} - -func (v NullablePtyListResponse) Get() *PtyListResponse { - return v.value -} - -func (v *NullablePtyListResponse) Set(val *PtyListResponse) { - v.value = val - v.isSet = true -} - -func (v NullablePtyListResponse) IsSet() bool { - return v.isSet -} - -func (v *NullablePtyListResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePtyListResponse(val *PtyListResponse) *NullablePtyListResponse { - return &NullablePtyListResponse{value: val, isSet: true} -} - -func (v NullablePtyListResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePtyListResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_pty_resize_request.go b/apps/api-client-go/model_pty_resize_request.go deleted file mode 100644 index 92e4f1dff..000000000 --- a/apps/api-client-go/model_pty_resize_request.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PtyResizeRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PtyResizeRequest{} - -// PtyResizeRequest struct for PtyResizeRequest -type PtyResizeRequest struct { - // Number of terminal columns - Cols float32 `json:"cols"` - // Number of terminal rows - Rows float32 `json:"rows"` - AdditionalProperties map[string]interface{} -} - -type _PtyResizeRequest PtyResizeRequest - -// NewPtyResizeRequest instantiates a new PtyResizeRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPtyResizeRequest(cols float32, rows float32) *PtyResizeRequest { - this := PtyResizeRequest{} - this.Cols = cols - this.Rows = rows - return &this -} - -// NewPtyResizeRequestWithDefaults instantiates a new PtyResizeRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPtyResizeRequestWithDefaults() *PtyResizeRequest { - this := PtyResizeRequest{} - return &this -} - -// GetCols returns the Cols field value -func (o *PtyResizeRequest) GetCols() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Cols -} - -// GetColsOk returns a tuple with the Cols field value -// and a boolean to check if the value has been set. -func (o *PtyResizeRequest) GetColsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Cols, true -} - -// SetCols sets field value -func (o *PtyResizeRequest) SetCols(v float32) { - o.Cols = v -} - -// GetRows returns the Rows field value -func (o *PtyResizeRequest) GetRows() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Rows -} - -// GetRowsOk returns a tuple with the Rows field value -// and a boolean to check if the value has been set. -func (o *PtyResizeRequest) GetRowsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Rows, true -} - -// SetRows sets field value -func (o *PtyResizeRequest) SetRows(v float32) { - o.Rows = v -} - -func (o PtyResizeRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PtyResizeRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["cols"] = o.Cols - toSerialize["rows"] = o.Rows - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PtyResizeRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "cols", - "rows", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPtyResizeRequest := _PtyResizeRequest{} - - err = json.Unmarshal(data, &varPtyResizeRequest) - - if err != nil { - return err - } - - *o = PtyResizeRequest(varPtyResizeRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "cols") - delete(additionalProperties, "rows") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePtyResizeRequest struct { - value *PtyResizeRequest - isSet bool -} - -func (v NullablePtyResizeRequest) Get() *PtyResizeRequest { - return v.value -} - -func (v *NullablePtyResizeRequest) Set(val *PtyResizeRequest) { - v.value = val - v.isSet = true -} - -func (v NullablePtyResizeRequest) IsSet() bool { - return v.isSet -} - -func (v *NullablePtyResizeRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePtyResizeRequest(val *PtyResizeRequest) *NullablePtyResizeRequest { - return &NullablePtyResizeRequest{value: val, isSet: true} -} - -func (v NullablePtyResizeRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePtyResizeRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_pty_session_info.go b/apps/api-client-go/model_pty_session_info.go deleted file mode 100644 index e64e4647e..000000000 --- a/apps/api-client-go/model_pty_session_info.go +++ /dev/null @@ -1,380 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the PtySessionInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &PtySessionInfo{} - -// PtySessionInfo struct for PtySessionInfo -type PtySessionInfo struct { - // The unique identifier for the PTY session - Id string `json:"id"` - // Starting directory for the PTY session, defaults to the sandbox's working directory - Cwd string `json:"cwd"` - // Environment variables for the PTY session - Envs map[string]interface{} `json:"envs"` - // Number of terminal columns - Cols float32 `json:"cols"` - // Number of terminal rows - Rows float32 `json:"rows"` - // When the PTY session was created - CreatedAt string `json:"createdAt"` - // Whether the PTY session is currently active - Active bool `json:"active"` - // Whether the PTY session uses lazy start (only start when first client connects) - LazyStart bool `json:"lazyStart"` - AdditionalProperties map[string]interface{} -} - -type _PtySessionInfo PtySessionInfo - -// NewPtySessionInfo instantiates a new PtySessionInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewPtySessionInfo(id string, cwd string, envs map[string]interface{}, cols float32, rows float32, createdAt string, active bool, lazyStart bool) *PtySessionInfo { - this := PtySessionInfo{} - this.Id = id - this.Cwd = cwd - this.Envs = envs - this.Cols = cols - this.Rows = rows - this.CreatedAt = createdAt - this.Active = active - this.LazyStart = lazyStart - return &this -} - -// NewPtySessionInfoWithDefaults instantiates a new PtySessionInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewPtySessionInfoWithDefaults() *PtySessionInfo { - this := PtySessionInfo{} - var lazyStart bool = false - this.LazyStart = lazyStart - return &this -} - -// GetId returns the Id field value -func (o *PtySessionInfo) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *PtySessionInfo) SetId(v string) { - o.Id = v -} - -// GetCwd returns the Cwd field value -func (o *PtySessionInfo) GetCwd() string { - if o == nil { - var ret string - return ret - } - - return o.Cwd -} - -// GetCwdOk returns a tuple with the Cwd field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetCwdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Cwd, true -} - -// SetCwd sets field value -func (o *PtySessionInfo) SetCwd(v string) { - o.Cwd = v -} - -// GetEnvs returns the Envs field value -func (o *PtySessionInfo) GetEnvs() map[string]interface{} { - if o == nil { - var ret map[string]interface{} - return ret - } - - return o.Envs -} - -// GetEnvsOk returns a tuple with the Envs field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetEnvsOk() (map[string]interface{}, bool) { - if o == nil { - return map[string]interface{}{}, false - } - return o.Envs, true -} - -// SetEnvs sets field value -func (o *PtySessionInfo) SetEnvs(v map[string]interface{}) { - o.Envs = v -} - -// GetCols returns the Cols field value -func (o *PtySessionInfo) GetCols() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Cols -} - -// GetColsOk returns a tuple with the Cols field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetColsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Cols, true -} - -// SetCols sets field value -func (o *PtySessionInfo) SetCols(v float32) { - o.Cols = v -} - -// GetRows returns the Rows field value -func (o *PtySessionInfo) GetRows() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Rows -} - -// GetRowsOk returns a tuple with the Rows field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetRowsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Rows, true -} - -// SetRows sets field value -func (o *PtySessionInfo) SetRows(v float32) { - o.Rows = v -} - -// GetCreatedAt returns the CreatedAt field value -func (o *PtySessionInfo) GetCreatedAt() string { - if o == nil { - var ret string - return ret - } - - return o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetCreatedAtOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.CreatedAt, true -} - -// SetCreatedAt sets field value -func (o *PtySessionInfo) SetCreatedAt(v string) { - o.CreatedAt = v -} - -// GetActive returns the Active field value -func (o *PtySessionInfo) GetActive() bool { - if o == nil { - var ret bool - return ret - } - - return o.Active -} - -// GetActiveOk returns a tuple with the Active field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetActiveOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.Active, true -} - -// SetActive sets field value -func (o *PtySessionInfo) SetActive(v bool) { - o.Active = v -} - -// GetLazyStart returns the LazyStart field value -func (o *PtySessionInfo) GetLazyStart() bool { - if o == nil { - var ret bool - return ret - } - - return o.LazyStart -} - -// GetLazyStartOk returns a tuple with the LazyStart field value -// and a boolean to check if the value has been set. -func (o *PtySessionInfo) GetLazyStartOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.LazyStart, true -} - -// SetLazyStart sets field value -func (o *PtySessionInfo) SetLazyStart(v bool) { - o.LazyStart = v -} - -func (o PtySessionInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o PtySessionInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - toSerialize["cwd"] = o.Cwd - toSerialize["envs"] = o.Envs - toSerialize["cols"] = o.Cols - toSerialize["rows"] = o.Rows - toSerialize["createdAt"] = o.CreatedAt - toSerialize["active"] = o.Active - toSerialize["lazyStart"] = o.LazyStart - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *PtySessionInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "cwd", - "envs", - "cols", - "rows", - "createdAt", - "active", - "lazyStart", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varPtySessionInfo := _PtySessionInfo{} - - err = json.Unmarshal(data, &varPtySessionInfo) - - if err != nil { - return err - } - - *o = PtySessionInfo(varPtySessionInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "cwd") - delete(additionalProperties, "envs") - delete(additionalProperties, "cols") - delete(additionalProperties, "rows") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "active") - delete(additionalProperties, "lazyStart") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullablePtySessionInfo struct { - value *PtySessionInfo - isSet bool -} - -func (v NullablePtySessionInfo) Get() *PtySessionInfo { - return v.value -} - -func (v *NullablePtySessionInfo) Set(val *PtySessionInfo) { - v.value = val - v.isSet = true -} - -func (v NullablePtySessionInfo) IsSet() bool { - return v.isSet -} - -func (v *NullablePtySessionInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullablePtySessionInfo(val *PtySessionInfo) *NullablePtySessionInfo { - return &NullablePtySessionInfo{value: val, isSet: true} -} - -func (v NullablePtySessionInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullablePtySessionInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_range.go b/apps/api-client-go/model_range.go deleted file mode 100644 index 87d95a048..000000000 --- a/apps/api-client-go/model_range.go +++ /dev/null @@ -1,196 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Range type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Range{} - -// Range struct for Range -type Range struct { - Start Position `json:"start"` - End Position `json:"end"` - AdditionalProperties map[string]interface{} -} - -type _Range Range - -// NewRange instantiates a new Range object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRange(start Position, end Position) *Range { - this := Range{} - this.Start = start - this.End = end - return &this -} - -// NewRangeWithDefaults instantiates a new Range object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRangeWithDefaults() *Range { - this := Range{} - return &this -} - -// GetStart returns the Start field value -func (o *Range) GetStart() Position { - if o == nil { - var ret Position - return ret - } - - return o.Start -} - -// GetStartOk returns a tuple with the Start field value -// and a boolean to check if the value has been set. -func (o *Range) GetStartOk() (*Position, bool) { - if o == nil { - return nil, false - } - return &o.Start, true -} - -// SetStart sets field value -func (o *Range) SetStart(v Position) { - o.Start = v -} - -// GetEnd returns the End field value -func (o *Range) GetEnd() Position { - if o == nil { - var ret Position - return ret - } - - return o.End -} - -// GetEndOk returns a tuple with the End field value -// and a boolean to check if the value has been set. -func (o *Range) GetEndOk() (*Position, bool) { - if o == nil { - return nil, false - } - return &o.End, true -} - -// SetEnd sets field value -func (o *Range) SetEnd(v Position) { - o.End = v -} - -func (o Range) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Range) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["start"] = o.Start - toSerialize["end"] = o.End - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Range) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "start", - "end", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRange := _Range{} - - err = json.Unmarshal(data, &varRange) - - if err != nil { - return err - } - - *o = Range(varRange) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "start") - delete(additionalProperties, "end") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRange struct { - value *Range - isSet bool -} - -func (v NullableRange) Get() *Range { - return v.value -} - -func (v *NullableRange) Set(val *Range) { - v.value = val - v.isSet = true -} - -func (v NullableRange) IsSet() bool { - return v.isSet -} - -func (v *NullableRange) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRange(val *Range) *NullableRange { - return &NullableRange{value: val, isSet: true} -} - -func (v NullableRange) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRange) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_rate_limit_config.go b/apps/api-client-go/model_rate_limit_config.go index f2d090b28..686198196 100644 --- a/apps/api-client-go/model_rate_limit_config.go +++ b/apps/api-client-go/model_rate_limit_config.go @@ -24,10 +24,10 @@ type RateLimitConfig struct { FailedAuth *RateLimitEntry `json:"failedAuth,omitempty"` // Authenticated rate limit Authenticated *RateLimitEntry `json:"authenticated,omitempty"` - // Sandbox create rate limit - SandboxCreate *RateLimitEntry `json:"sandboxCreate,omitempty"` - // Sandbox lifecycle rate limit - SandboxLifecycle *RateLimitEntry `json:"sandboxLifecycle,omitempty"` + // Box create rate limit + BoxCreate *RateLimitEntry `json:"boxCreate,omitempty"` + // Box lifecycle rate limit + BoxLifecycle *RateLimitEntry `json:"boxLifecycle,omitempty"` AdditionalProperties map[string]interface{} } @@ -114,68 +114,68 @@ func (o *RateLimitConfig) SetAuthenticated(v RateLimitEntry) { o.Authenticated = &v } -// GetSandboxCreate returns the SandboxCreate field value if set, zero value otherwise. -func (o *RateLimitConfig) GetSandboxCreate() RateLimitEntry { - if o == nil || IsNil(o.SandboxCreate) { +// GetBoxCreate returns the BoxCreate field value if set, zero value otherwise. +func (o *RateLimitConfig) GetBoxCreate() RateLimitEntry { + if o == nil || IsNil(o.BoxCreate) { var ret RateLimitEntry return ret } - return *o.SandboxCreate + return *o.BoxCreate } -// GetSandboxCreateOk returns a tuple with the SandboxCreate field value if set, nil otherwise +// GetBoxCreateOk returns a tuple with the BoxCreate field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *RateLimitConfig) GetSandboxCreateOk() (*RateLimitEntry, bool) { - if o == nil || IsNil(o.SandboxCreate) { +func (o *RateLimitConfig) GetBoxCreateOk() (*RateLimitEntry, bool) { + if o == nil || IsNil(o.BoxCreate) { return nil, false } - return o.SandboxCreate, true + return o.BoxCreate, true } -// HasSandboxCreate returns a boolean if a field has been set. -func (o *RateLimitConfig) HasSandboxCreate() bool { - if o != nil && !IsNil(o.SandboxCreate) { +// HasBoxCreate returns a boolean if a field has been set. +func (o *RateLimitConfig) HasBoxCreate() bool { + if o != nil && !IsNil(o.BoxCreate) { return true } return false } -// SetSandboxCreate gets a reference to the given RateLimitEntry and assigns it to the SandboxCreate field. -func (o *RateLimitConfig) SetSandboxCreate(v RateLimitEntry) { - o.SandboxCreate = &v +// SetBoxCreate gets a reference to the given RateLimitEntry and assigns it to the BoxCreate field. +func (o *RateLimitConfig) SetBoxCreate(v RateLimitEntry) { + o.BoxCreate = &v } -// GetSandboxLifecycle returns the SandboxLifecycle field value if set, zero value otherwise. -func (o *RateLimitConfig) GetSandboxLifecycle() RateLimitEntry { - if o == nil || IsNil(o.SandboxLifecycle) { +// GetBoxLifecycle returns the BoxLifecycle field value if set, zero value otherwise. +func (o *RateLimitConfig) GetBoxLifecycle() RateLimitEntry { + if o == nil || IsNil(o.BoxLifecycle) { var ret RateLimitEntry return ret } - return *o.SandboxLifecycle + return *o.BoxLifecycle } -// GetSandboxLifecycleOk returns a tuple with the SandboxLifecycle field value if set, nil otherwise +// GetBoxLifecycleOk returns a tuple with the BoxLifecycle field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *RateLimitConfig) GetSandboxLifecycleOk() (*RateLimitEntry, bool) { - if o == nil || IsNil(o.SandboxLifecycle) { +func (o *RateLimitConfig) GetBoxLifecycleOk() (*RateLimitEntry, bool) { + if o == nil || IsNil(o.BoxLifecycle) { return nil, false } - return o.SandboxLifecycle, true + return o.BoxLifecycle, true } -// HasSandboxLifecycle returns a boolean if a field has been set. -func (o *RateLimitConfig) HasSandboxLifecycle() bool { - if o != nil && !IsNil(o.SandboxLifecycle) { +// HasBoxLifecycle returns a boolean if a field has been set. +func (o *RateLimitConfig) HasBoxLifecycle() bool { + if o != nil && !IsNil(o.BoxLifecycle) { return true } return false } -// SetSandboxLifecycle gets a reference to the given RateLimitEntry and assigns it to the SandboxLifecycle field. -func (o *RateLimitConfig) SetSandboxLifecycle(v RateLimitEntry) { - o.SandboxLifecycle = &v +// SetBoxLifecycle gets a reference to the given RateLimitEntry and assigns it to the BoxLifecycle field. +func (o *RateLimitConfig) SetBoxLifecycle(v RateLimitEntry) { + o.BoxLifecycle = &v } func (o RateLimitConfig) MarshalJSON() ([]byte, error) { @@ -194,11 +194,11 @@ func (o RateLimitConfig) ToMap() (map[string]interface{}, error) { if !IsNil(o.Authenticated) { toSerialize["authenticated"] = o.Authenticated } - if !IsNil(o.SandboxCreate) { - toSerialize["sandboxCreate"] = o.SandboxCreate + if !IsNil(o.BoxCreate) { + toSerialize["boxCreate"] = o.BoxCreate } - if !IsNil(o.SandboxLifecycle) { - toSerialize["sandboxLifecycle"] = o.SandboxLifecycle + if !IsNil(o.BoxLifecycle) { + toSerialize["boxLifecycle"] = o.BoxLifecycle } for key, value := range o.AdditionalProperties { @@ -224,8 +224,8 @@ func (o *RateLimitConfig) UnmarshalJSON(data []byte) (err error) { if err = json.Unmarshal(data, &additionalProperties); err == nil { delete(additionalProperties, "failedAuth") delete(additionalProperties, "authenticated") - delete(additionalProperties, "sandboxCreate") - delete(additionalProperties, "sandboxLifecycle") + delete(additionalProperties, "boxCreate") + delete(additionalProperties, "boxLifecycle") o.AdditionalProperties = additionalProperties } @@ -267,3 +267,5 @@ func (v *NullableRateLimitConfig) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_rate_limit_entry.go b/apps/api-client-go/model_rate_limit_entry.go index c113c6187..5b7c513b1 100644 --- a/apps/api-client-go/model_rate_limit_entry.go +++ b/apps/api-client-go/model_rate_limit_entry.go @@ -191,3 +191,5 @@ func (v *NullableRateLimitEntry) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_regenerate_api_key_response.go b/apps/api-client-go/model_regenerate_api_key_response.go index fe284d50e..0ed8a0a96 100644 --- a/apps/api-client-go/model_regenerate_api_key_response.go +++ b/apps/api-client-go/model_regenerate_api_key_response.go @@ -166,3 +166,5 @@ func (v *NullableRegenerateApiKeyResponse) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_region.go b/apps/api-client-go/model_region.go index d85f3dc02..f5fbba541 100644 --- a/apps/api-client-go/model_region.go +++ b/apps/api-client-go/model_region.go @@ -37,8 +37,6 @@ type Region struct { ProxyUrl NullableString `json:"proxyUrl,omitempty"` // SSH Gateway URL for the region SshGatewayUrl NullableString `json:"sshGatewayUrl,omitempty"` - // Snapshot Manager URL for the region - SnapshotManagerUrl NullableString `json:"snapshotManagerUrl,omitempty"` AdditionalProperties map[string]interface{} } @@ -312,48 +310,6 @@ func (o *Region) UnsetSshGatewayUrl() { o.SshGatewayUrl.Unset() } -// GetSnapshotManagerUrl returns the SnapshotManagerUrl field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *Region) GetSnapshotManagerUrl() string { - if o == nil || IsNil(o.SnapshotManagerUrl.Get()) { - var ret string - return ret - } - return *o.SnapshotManagerUrl.Get() -} - -// GetSnapshotManagerUrlOk returns a tuple with the SnapshotManagerUrl field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Region) GetSnapshotManagerUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.SnapshotManagerUrl.Get(), o.SnapshotManagerUrl.IsSet() -} - -// HasSnapshotManagerUrl returns a boolean if a field has been set. -func (o *Region) HasSnapshotManagerUrl() bool { - if o != nil && o.SnapshotManagerUrl.IsSet() { - return true - } - - return false -} - -// SetSnapshotManagerUrl gets a reference to the given NullableString and assigns it to the SnapshotManagerUrl field. -func (o *Region) SetSnapshotManagerUrl(v string) { - o.SnapshotManagerUrl.Set(&v) -} -// SetSnapshotManagerUrlNil sets the value for SnapshotManagerUrl to be an explicit nil -func (o *Region) SetSnapshotManagerUrlNil() { - o.SnapshotManagerUrl.Set(nil) -} - -// UnsetSnapshotManagerUrl ensures that no value is present for SnapshotManagerUrl, not even an explicit nil -func (o *Region) UnsetSnapshotManagerUrl() { - o.SnapshotManagerUrl.Unset() -} - func (o Region) MarshalJSON() ([]byte, error) { toSerialize,err := o.ToMap() if err != nil { @@ -378,9 +334,6 @@ func (o Region) ToMap() (map[string]interface{}, error) { if o.SshGatewayUrl.IsSet() { toSerialize["sshGatewayUrl"] = o.SshGatewayUrl.Get() } - if o.SnapshotManagerUrl.IsSet() { - toSerialize["snapshotManagerUrl"] = o.SnapshotManagerUrl.Get() - } for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -436,7 +389,6 @@ func (o *Region) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "updatedAt") delete(additionalProperties, "proxyUrl") delete(additionalProperties, "sshGatewayUrl") - delete(additionalProperties, "snapshotManagerUrl") o.AdditionalProperties = additionalProperties } @@ -478,3 +430,5 @@ func (v *NullableRegion) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_region_quota.go b/apps/api-client-go/model_region_quota.go deleted file mode 100644 index 145c4e8d7..000000000 --- a/apps/api-client-go/model_region_quota.go +++ /dev/null @@ -1,283 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the RegionQuota type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &RegionQuota{} - -// RegionQuota struct for RegionQuota -type RegionQuota struct { - OrganizationId string `json:"organizationId"` - RegionId string `json:"regionId"` - TotalCpuQuota float32 `json:"totalCpuQuota"` - TotalMemoryQuota float32 `json:"totalMemoryQuota"` - TotalDiskQuota float32 `json:"totalDiskQuota"` - AdditionalProperties map[string]interface{} -} - -type _RegionQuota RegionQuota - -// NewRegionQuota instantiates a new RegionQuota object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRegionQuota(organizationId string, regionId string, totalCpuQuota float32, totalMemoryQuota float32, totalDiskQuota float32) *RegionQuota { - this := RegionQuota{} - this.OrganizationId = organizationId - this.RegionId = regionId - this.TotalCpuQuota = totalCpuQuota - this.TotalMemoryQuota = totalMemoryQuota - this.TotalDiskQuota = totalDiskQuota - return &this -} - -// NewRegionQuotaWithDefaults instantiates a new RegionQuota object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRegionQuotaWithDefaults() *RegionQuota { - this := RegionQuota{} - return &this -} - -// GetOrganizationId returns the OrganizationId field value -func (o *RegionQuota) GetOrganizationId() string { - if o == nil { - var ret string - return ret - } - - return o.OrganizationId -} - -// GetOrganizationIdOk returns a tuple with the OrganizationId field value -// and a boolean to check if the value has been set. -func (o *RegionQuota) GetOrganizationIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.OrganizationId, true -} - -// SetOrganizationId sets field value -func (o *RegionQuota) SetOrganizationId(v string) { - o.OrganizationId = v -} - -// GetRegionId returns the RegionId field value -func (o *RegionQuota) GetRegionId() string { - if o == nil { - var ret string - return ret - } - - return o.RegionId -} - -// GetRegionIdOk returns a tuple with the RegionId field value -// and a boolean to check if the value has been set. -func (o *RegionQuota) GetRegionIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegionId, true -} - -// SetRegionId sets field value -func (o *RegionQuota) SetRegionId(v string) { - o.RegionId = v -} - -// GetTotalCpuQuota returns the TotalCpuQuota field value -func (o *RegionQuota) GetTotalCpuQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalCpuQuota -} - -// GetTotalCpuQuotaOk returns a tuple with the TotalCpuQuota field value -// and a boolean to check if the value has been set. -func (o *RegionQuota) GetTotalCpuQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalCpuQuota, true -} - -// SetTotalCpuQuota sets field value -func (o *RegionQuota) SetTotalCpuQuota(v float32) { - o.TotalCpuQuota = v -} - -// GetTotalMemoryQuota returns the TotalMemoryQuota field value -func (o *RegionQuota) GetTotalMemoryQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalMemoryQuota -} - -// GetTotalMemoryQuotaOk returns a tuple with the TotalMemoryQuota field value -// and a boolean to check if the value has been set. -func (o *RegionQuota) GetTotalMemoryQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalMemoryQuota, true -} - -// SetTotalMemoryQuota sets field value -func (o *RegionQuota) SetTotalMemoryQuota(v float32) { - o.TotalMemoryQuota = v -} - -// GetTotalDiskQuota returns the TotalDiskQuota field value -func (o *RegionQuota) GetTotalDiskQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalDiskQuota -} - -// GetTotalDiskQuotaOk returns a tuple with the TotalDiskQuota field value -// and a boolean to check if the value has been set. -func (o *RegionQuota) GetTotalDiskQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalDiskQuota, true -} - -// SetTotalDiskQuota sets field value -func (o *RegionQuota) SetTotalDiskQuota(v float32) { - o.TotalDiskQuota = v -} - -func (o RegionQuota) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o RegionQuota) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["organizationId"] = o.OrganizationId - toSerialize["regionId"] = o.RegionId - toSerialize["totalCpuQuota"] = o.TotalCpuQuota - toSerialize["totalMemoryQuota"] = o.TotalMemoryQuota - toSerialize["totalDiskQuota"] = o.TotalDiskQuota - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *RegionQuota) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "organizationId", - "regionId", - "totalCpuQuota", - "totalMemoryQuota", - "totalDiskQuota", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRegionQuota := _RegionQuota{} - - err = json.Unmarshal(data, &varRegionQuota) - - if err != nil { - return err - } - - *o = RegionQuota(varRegionQuota) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "organizationId") - delete(additionalProperties, "regionId") - delete(additionalProperties, "totalCpuQuota") - delete(additionalProperties, "totalMemoryQuota") - delete(additionalProperties, "totalDiskQuota") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRegionQuota struct { - value *RegionQuota - isSet bool -} - -func (v NullableRegionQuota) Get() *RegionQuota { - return v.value -} - -func (v *NullableRegionQuota) Set(val *RegionQuota) { - v.value = val - v.isSet = true -} - -func (v NullableRegionQuota) IsSet() bool { - return v.isSet -} - -func (v *NullableRegionQuota) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRegionQuota(val *RegionQuota) *NullableRegionQuota { - return &NullableRegionQuota{value: val, isSet: true} -} - -func (v NullableRegionQuota) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRegionQuota) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_region_screenshot_response.go b/apps/api-client-go/model_region_screenshot_response.go deleted file mode 100644 index 1c494fd47..000000000 --- a/apps/api-client-go/model_region_screenshot_response.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the RegionScreenshotResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &RegionScreenshotResponse{} - -// RegionScreenshotResponse struct for RegionScreenshotResponse -type RegionScreenshotResponse struct { - // Base64 encoded screenshot image data of the specified region - Screenshot string `json:"screenshot"` - // The current cursor position when the region screenshot was taken - CursorPosition map[string]interface{} `json:"cursorPosition,omitempty"` - // The size of the screenshot data in bytes - SizeBytes *float32 `json:"sizeBytes,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _RegionScreenshotResponse RegionScreenshotResponse - -// NewRegionScreenshotResponse instantiates a new RegionScreenshotResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRegionScreenshotResponse(screenshot string) *RegionScreenshotResponse { - this := RegionScreenshotResponse{} - this.Screenshot = screenshot - return &this -} - -// NewRegionScreenshotResponseWithDefaults instantiates a new RegionScreenshotResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRegionScreenshotResponseWithDefaults() *RegionScreenshotResponse { - this := RegionScreenshotResponse{} - return &this -} - -// GetScreenshot returns the Screenshot field value -func (o *RegionScreenshotResponse) GetScreenshot() string { - if o == nil { - var ret string - return ret - } - - return o.Screenshot -} - -// GetScreenshotOk returns a tuple with the Screenshot field value -// and a boolean to check if the value has been set. -func (o *RegionScreenshotResponse) GetScreenshotOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Screenshot, true -} - -// SetScreenshot sets field value -func (o *RegionScreenshotResponse) SetScreenshot(v string) { - o.Screenshot = v -} - -// GetCursorPosition returns the CursorPosition field value if set, zero value otherwise. -func (o *RegionScreenshotResponse) GetCursorPosition() map[string]interface{} { - if o == nil || IsNil(o.CursorPosition) { - var ret map[string]interface{} - return ret - } - return o.CursorPosition -} - -// GetCursorPositionOk returns a tuple with the CursorPosition field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *RegionScreenshotResponse) GetCursorPositionOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.CursorPosition) { - return map[string]interface{}{}, false - } - return o.CursorPosition, true -} - -// HasCursorPosition returns a boolean if a field has been set. -func (o *RegionScreenshotResponse) HasCursorPosition() bool { - if o != nil && !IsNil(o.CursorPosition) { - return true - } - - return false -} - -// SetCursorPosition gets a reference to the given map[string]interface{} and assigns it to the CursorPosition field. -func (o *RegionScreenshotResponse) SetCursorPosition(v map[string]interface{}) { - o.CursorPosition = v -} - -// GetSizeBytes returns the SizeBytes field value if set, zero value otherwise. -func (o *RegionScreenshotResponse) GetSizeBytes() float32 { - if o == nil || IsNil(o.SizeBytes) { - var ret float32 - return ret - } - return *o.SizeBytes -} - -// GetSizeBytesOk returns a tuple with the SizeBytes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *RegionScreenshotResponse) GetSizeBytesOk() (*float32, bool) { - if o == nil || IsNil(o.SizeBytes) { - return nil, false - } - return o.SizeBytes, true -} - -// HasSizeBytes returns a boolean if a field has been set. -func (o *RegionScreenshotResponse) HasSizeBytes() bool { - if o != nil && !IsNil(o.SizeBytes) { - return true - } - - return false -} - -// SetSizeBytes gets a reference to the given float32 and assigns it to the SizeBytes field. -func (o *RegionScreenshotResponse) SetSizeBytes(v float32) { - o.SizeBytes = &v -} - -func (o RegionScreenshotResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o RegionScreenshotResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["screenshot"] = o.Screenshot - if !IsNil(o.CursorPosition) { - toSerialize["cursorPosition"] = o.CursorPosition - } - if !IsNil(o.SizeBytes) { - toSerialize["sizeBytes"] = o.SizeBytes - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *RegionScreenshotResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "screenshot", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRegionScreenshotResponse := _RegionScreenshotResponse{} - - err = json.Unmarshal(data, &varRegionScreenshotResponse) - - if err != nil { - return err - } - - *o = RegionScreenshotResponse(varRegionScreenshotResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "screenshot") - delete(additionalProperties, "cursorPosition") - delete(additionalProperties, "sizeBytes") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRegionScreenshotResponse struct { - value *RegionScreenshotResponse - isSet bool -} - -func (v NullableRegionScreenshotResponse) Get() *RegionScreenshotResponse { - return v.value -} - -func (v *NullableRegionScreenshotResponse) Set(val *RegionScreenshotResponse) { - v.value = val - v.isSet = true -} - -func (v NullableRegionScreenshotResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableRegionScreenshotResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRegionScreenshotResponse(val *RegionScreenshotResponse) *NullableRegionScreenshotResponse { - return &NullableRegionScreenshotResponse{value: val, isSet: true} -} - -func (v NullableRegionScreenshotResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRegionScreenshotResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_region_type.go b/apps/api-client-go/model_region_type.go index 0cccd3813..fed4bfe9c 100644 --- a/apps/api-client-go/model_region_type.go +++ b/apps/api-client-go/model_region_type.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // RegionType The type of the region @@ -24,6 +23,7 @@ const ( REGIONTYPE_SHARED RegionType = "shared" REGIONTYPE_DEDICATED RegionType = "dedicated" REGIONTYPE_CUSTOM RegionType = "custom" + REGIONTYPE_UNKNOWN_DEFAULT_OPEN_API RegionType = "unknown_default_open_api" ) // All allowed values of RegionType enum @@ -31,6 +31,7 @@ var AllowedRegionTypeEnumValues = []RegionType{ "shared", "dedicated", "custom", + "unknown_default_open_api", } func (v *RegionType) UnmarshalJSON(src []byte) error { @@ -47,7 +48,8 @@ func (v *RegionType) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid RegionType", value) + *v = REGIONTYPE_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewRegionTypeFromValue returns a pointer to a valid RegionType @@ -57,7 +59,8 @@ func NewRegionTypeFromValue(v string) (*RegionType, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for RegionType: valid values are %v", v, AllowedRegionTypeEnumValues) + enumValue := REGIONTYPE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -111,3 +114,4 @@ func (v *NullableRegionType) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_region_usage_overview.go b/apps/api-client-go/model_region_usage_overview.go deleted file mode 100644 index 7b5ed0844..000000000 --- a/apps/api-client-go/model_region_usage_overview.go +++ /dev/null @@ -1,341 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the RegionUsageOverview type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &RegionUsageOverview{} - -// RegionUsageOverview struct for RegionUsageOverview -type RegionUsageOverview struct { - RegionId string `json:"regionId"` - TotalCpuQuota float32 `json:"totalCpuQuota"` - CurrentCpuUsage float32 `json:"currentCpuUsage"` - TotalMemoryQuota float32 `json:"totalMemoryQuota"` - CurrentMemoryUsage float32 `json:"currentMemoryUsage"` - TotalDiskQuota float32 `json:"totalDiskQuota"` - CurrentDiskUsage float32 `json:"currentDiskUsage"` - AdditionalProperties map[string]interface{} -} - -type _RegionUsageOverview RegionUsageOverview - -// NewRegionUsageOverview instantiates a new RegionUsageOverview object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRegionUsageOverview(regionId string, totalCpuQuota float32, currentCpuUsage float32, totalMemoryQuota float32, currentMemoryUsage float32, totalDiskQuota float32, currentDiskUsage float32) *RegionUsageOverview { - this := RegionUsageOverview{} - this.RegionId = regionId - this.TotalCpuQuota = totalCpuQuota - this.CurrentCpuUsage = currentCpuUsage - this.TotalMemoryQuota = totalMemoryQuota - this.CurrentMemoryUsage = currentMemoryUsage - this.TotalDiskQuota = totalDiskQuota - this.CurrentDiskUsage = currentDiskUsage - return &this -} - -// NewRegionUsageOverviewWithDefaults instantiates a new RegionUsageOverview object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRegionUsageOverviewWithDefaults() *RegionUsageOverview { - this := RegionUsageOverview{} - return &this -} - -// GetRegionId returns the RegionId field value -func (o *RegionUsageOverview) GetRegionId() string { - if o == nil { - var ret string - return ret - } - - return o.RegionId -} - -// GetRegionIdOk returns a tuple with the RegionId field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetRegionIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegionId, true -} - -// SetRegionId sets field value -func (o *RegionUsageOverview) SetRegionId(v string) { - o.RegionId = v -} - -// GetTotalCpuQuota returns the TotalCpuQuota field value -func (o *RegionUsageOverview) GetTotalCpuQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalCpuQuota -} - -// GetTotalCpuQuotaOk returns a tuple with the TotalCpuQuota field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetTotalCpuQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalCpuQuota, true -} - -// SetTotalCpuQuota sets field value -func (o *RegionUsageOverview) SetTotalCpuQuota(v float32) { - o.TotalCpuQuota = v -} - -// GetCurrentCpuUsage returns the CurrentCpuUsage field value -func (o *RegionUsageOverview) GetCurrentCpuUsage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentCpuUsage -} - -// GetCurrentCpuUsageOk returns a tuple with the CurrentCpuUsage field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetCurrentCpuUsageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentCpuUsage, true -} - -// SetCurrentCpuUsage sets field value -func (o *RegionUsageOverview) SetCurrentCpuUsage(v float32) { - o.CurrentCpuUsage = v -} - -// GetTotalMemoryQuota returns the TotalMemoryQuota field value -func (o *RegionUsageOverview) GetTotalMemoryQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalMemoryQuota -} - -// GetTotalMemoryQuotaOk returns a tuple with the TotalMemoryQuota field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetTotalMemoryQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalMemoryQuota, true -} - -// SetTotalMemoryQuota sets field value -func (o *RegionUsageOverview) SetTotalMemoryQuota(v float32) { - o.TotalMemoryQuota = v -} - -// GetCurrentMemoryUsage returns the CurrentMemoryUsage field value -func (o *RegionUsageOverview) GetCurrentMemoryUsage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentMemoryUsage -} - -// GetCurrentMemoryUsageOk returns a tuple with the CurrentMemoryUsage field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetCurrentMemoryUsageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentMemoryUsage, true -} - -// SetCurrentMemoryUsage sets field value -func (o *RegionUsageOverview) SetCurrentMemoryUsage(v float32) { - o.CurrentMemoryUsage = v -} - -// GetTotalDiskQuota returns the TotalDiskQuota field value -func (o *RegionUsageOverview) GetTotalDiskQuota() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.TotalDiskQuota -} - -// GetTotalDiskQuotaOk returns a tuple with the TotalDiskQuota field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetTotalDiskQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.TotalDiskQuota, true -} - -// SetTotalDiskQuota sets field value -func (o *RegionUsageOverview) SetTotalDiskQuota(v float32) { - o.TotalDiskQuota = v -} - -// GetCurrentDiskUsage returns the CurrentDiskUsage field value -func (o *RegionUsageOverview) GetCurrentDiskUsage() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentDiskUsage -} - -// GetCurrentDiskUsageOk returns a tuple with the CurrentDiskUsage field value -// and a boolean to check if the value has been set. -func (o *RegionUsageOverview) GetCurrentDiskUsageOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentDiskUsage, true -} - -// SetCurrentDiskUsage sets field value -func (o *RegionUsageOverview) SetCurrentDiskUsage(v float32) { - o.CurrentDiskUsage = v -} - -func (o RegionUsageOverview) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o RegionUsageOverview) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["regionId"] = o.RegionId - toSerialize["totalCpuQuota"] = o.TotalCpuQuota - toSerialize["currentCpuUsage"] = o.CurrentCpuUsage - toSerialize["totalMemoryQuota"] = o.TotalMemoryQuota - toSerialize["currentMemoryUsage"] = o.CurrentMemoryUsage - toSerialize["totalDiskQuota"] = o.TotalDiskQuota - toSerialize["currentDiskUsage"] = o.CurrentDiskUsage - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *RegionUsageOverview) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "regionId", - "totalCpuQuota", - "currentCpuUsage", - "totalMemoryQuota", - "currentMemoryUsage", - "totalDiskQuota", - "currentDiskUsage", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRegionUsageOverview := _RegionUsageOverview{} - - err = json.Unmarshal(data, &varRegionUsageOverview) - - if err != nil { - return err - } - - *o = RegionUsageOverview(varRegionUsageOverview) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "regionId") - delete(additionalProperties, "totalCpuQuota") - delete(additionalProperties, "currentCpuUsage") - delete(additionalProperties, "totalMemoryQuota") - delete(additionalProperties, "currentMemoryUsage") - delete(additionalProperties, "totalDiskQuota") - delete(additionalProperties, "currentDiskUsage") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRegionUsageOverview struct { - value *RegionUsageOverview - isSet bool -} - -func (v NullableRegionUsageOverview) Get() *RegionUsageOverview { - return v.value -} - -func (v *NullableRegionUsageOverview) Set(val *RegionUsageOverview) { - v.value = val - v.isSet = true -} - -func (v NullableRegionUsageOverview) IsSet() bool { - return v.isSet -} - -func (v *NullableRegionUsageOverview) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRegionUsageOverview(val *RegionUsageOverview) *NullableRegionUsageOverview { - return &NullableRegionUsageOverview{value: val, isSet: true} -} - -func (v NullableRegionUsageOverview) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRegionUsageOverview) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_registry_push_access_dto.go b/apps/api-client-go/model_registry_push_access_dto.go deleted file mode 100644 index c0fd9434d..000000000 --- a/apps/api-client-go/model_registry_push_access_dto.go +++ /dev/null @@ -1,318 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the RegistryPushAccessDto type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &RegistryPushAccessDto{} - -// RegistryPushAccessDto struct for RegistryPushAccessDto -type RegistryPushAccessDto struct { - // Temporary username for registry authentication - Username string `json:"username"` - // Temporary secret for registry authentication - Secret string `json:"secret"` - // Registry URL - RegistryUrl string `json:"registryUrl"` - // Registry ID - RegistryId string `json:"registryId"` - // Registry project ID - Project string `json:"project"` - // Token expiration time in ISO format - ExpiresAt string `json:"expiresAt"` - AdditionalProperties map[string]interface{} -} - -type _RegistryPushAccessDto RegistryPushAccessDto - -// NewRegistryPushAccessDto instantiates a new RegistryPushAccessDto object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRegistryPushAccessDto(username string, secret string, registryUrl string, registryId string, project string, expiresAt string) *RegistryPushAccessDto { - this := RegistryPushAccessDto{} - this.Username = username - this.Secret = secret - this.RegistryUrl = registryUrl - this.RegistryId = registryId - this.Project = project - this.ExpiresAt = expiresAt - return &this -} - -// NewRegistryPushAccessDtoWithDefaults instantiates a new RegistryPushAccessDto object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRegistryPushAccessDtoWithDefaults() *RegistryPushAccessDto { - this := RegistryPushAccessDto{} - return &this -} - -// GetUsername returns the Username field value -func (o *RegistryPushAccessDto) GetUsername() string { - if o == nil { - var ret string - return ret - } - - return o.Username -} - -// GetUsernameOk returns a tuple with the Username field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Username, true -} - -// SetUsername sets field value -func (o *RegistryPushAccessDto) SetUsername(v string) { - o.Username = v -} - -// GetSecret returns the Secret field value -func (o *RegistryPushAccessDto) GetSecret() string { - if o == nil { - var ret string - return ret - } - - return o.Secret -} - -// GetSecretOk returns a tuple with the Secret field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetSecretOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Secret, true -} - -// SetSecret sets field value -func (o *RegistryPushAccessDto) SetSecret(v string) { - o.Secret = v -} - -// GetRegistryUrl returns the RegistryUrl field value -func (o *RegistryPushAccessDto) GetRegistryUrl() string { - if o == nil { - var ret string - return ret - } - - return o.RegistryUrl -} - -// GetRegistryUrlOk returns a tuple with the RegistryUrl field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetRegistryUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegistryUrl, true -} - -// SetRegistryUrl sets field value -func (o *RegistryPushAccessDto) SetRegistryUrl(v string) { - o.RegistryUrl = v -} - -// GetRegistryId returns the RegistryId field value -func (o *RegistryPushAccessDto) GetRegistryId() string { - if o == nil { - var ret string - return ret - } - - return o.RegistryId -} - -// GetRegistryIdOk returns a tuple with the RegistryId field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetRegistryIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RegistryId, true -} - -// SetRegistryId sets field value -func (o *RegistryPushAccessDto) SetRegistryId(v string) { - o.RegistryId = v -} - -// GetProject returns the Project field value -func (o *RegistryPushAccessDto) GetProject() string { - if o == nil { - var ret string - return ret - } - - return o.Project -} - -// GetProjectOk returns a tuple with the Project field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetProjectOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Project, true -} - -// SetProject sets field value -func (o *RegistryPushAccessDto) SetProject(v string) { - o.Project = v -} - -// GetExpiresAt returns the ExpiresAt field value -func (o *RegistryPushAccessDto) GetExpiresAt() string { - if o == nil { - var ret string - return ret - } - - return o.ExpiresAt -} - -// GetExpiresAtOk returns a tuple with the ExpiresAt field value -// and a boolean to check if the value has been set. -func (o *RegistryPushAccessDto) GetExpiresAtOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ExpiresAt, true -} - -// SetExpiresAt sets field value -func (o *RegistryPushAccessDto) SetExpiresAt(v string) { - o.ExpiresAt = v -} - -func (o RegistryPushAccessDto) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o RegistryPushAccessDto) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["username"] = o.Username - toSerialize["secret"] = o.Secret - toSerialize["registryUrl"] = o.RegistryUrl - toSerialize["registryId"] = o.RegistryId - toSerialize["project"] = o.Project - toSerialize["expiresAt"] = o.ExpiresAt - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *RegistryPushAccessDto) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "username", - "secret", - "registryUrl", - "registryId", - "project", - "expiresAt", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRegistryPushAccessDto := _RegistryPushAccessDto{} - - err = json.Unmarshal(data, &varRegistryPushAccessDto) - - if err != nil { - return err - } - - *o = RegistryPushAccessDto(varRegistryPushAccessDto) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "username") - delete(additionalProperties, "secret") - delete(additionalProperties, "registryUrl") - delete(additionalProperties, "registryId") - delete(additionalProperties, "project") - delete(additionalProperties, "expiresAt") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRegistryPushAccessDto struct { - value *RegistryPushAccessDto - isSet bool -} - -func (v NullableRegistryPushAccessDto) Get() *RegistryPushAccessDto { - return v.value -} - -func (v *NullableRegistryPushAccessDto) Set(val *RegistryPushAccessDto) { - v.value = val - v.isSet = true -} - -func (v NullableRegistryPushAccessDto) IsSet() bool { - return v.isSet -} - -func (v *NullableRegistryPushAccessDto) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRegistryPushAccessDto(val *RegistryPushAccessDto) *NullableRegistryPushAccessDto { - return &NullableRegistryPushAccessDto{value: val, isSet: true} -} - -func (v NullableRegistryPushAccessDto) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRegistryPushAccessDto) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_replace_request.go b/apps/api-client-go/model_replace_request.go deleted file mode 100644 index 0427f54df..000000000 --- a/apps/api-client-go/model_replace_request.go +++ /dev/null @@ -1,225 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ReplaceRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ReplaceRequest{} - -// ReplaceRequest struct for ReplaceRequest -type ReplaceRequest struct { - Files []string `json:"files"` - Pattern string `json:"pattern"` - NewValue string `json:"newValue"` - AdditionalProperties map[string]interface{} -} - -type _ReplaceRequest ReplaceRequest - -// NewReplaceRequest instantiates a new ReplaceRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewReplaceRequest(files []string, pattern string, newValue string) *ReplaceRequest { - this := ReplaceRequest{} - this.Files = files - this.Pattern = pattern - this.NewValue = newValue - return &this -} - -// NewReplaceRequestWithDefaults instantiates a new ReplaceRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewReplaceRequestWithDefaults() *ReplaceRequest { - this := ReplaceRequest{} - return &this -} - -// GetFiles returns the Files field value -func (o *ReplaceRequest) GetFiles() []string { - if o == nil { - var ret []string - return ret - } - - return o.Files -} - -// GetFilesOk returns a tuple with the Files field value -// and a boolean to check if the value has been set. -func (o *ReplaceRequest) GetFilesOk() ([]string, bool) { - if o == nil { - return nil, false - } - return o.Files, true -} - -// SetFiles sets field value -func (o *ReplaceRequest) SetFiles(v []string) { - o.Files = v -} - -// GetPattern returns the Pattern field value -func (o *ReplaceRequest) GetPattern() string { - if o == nil { - var ret string - return ret - } - - return o.Pattern -} - -// GetPatternOk returns a tuple with the Pattern field value -// and a boolean to check if the value has been set. -func (o *ReplaceRequest) GetPatternOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Pattern, true -} - -// SetPattern sets field value -func (o *ReplaceRequest) SetPattern(v string) { - o.Pattern = v -} - -// GetNewValue returns the NewValue field value -func (o *ReplaceRequest) GetNewValue() string { - if o == nil { - var ret string - return ret - } - - return o.NewValue -} - -// GetNewValueOk returns a tuple with the NewValue field value -// and a boolean to check if the value has been set. -func (o *ReplaceRequest) GetNewValueOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.NewValue, true -} - -// SetNewValue sets field value -func (o *ReplaceRequest) SetNewValue(v string) { - o.NewValue = v -} - -func (o ReplaceRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ReplaceRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["files"] = o.Files - toSerialize["pattern"] = o.Pattern - toSerialize["newValue"] = o.NewValue - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ReplaceRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "files", - "pattern", - "newValue", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varReplaceRequest := _ReplaceRequest{} - - err = json.Unmarshal(data, &varReplaceRequest) - - if err != nil { - return err - } - - *o = ReplaceRequest(varReplaceRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "files") - delete(additionalProperties, "pattern") - delete(additionalProperties, "newValue") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableReplaceRequest struct { - value *ReplaceRequest - isSet bool -} - -func (v NullableReplaceRequest) Get() *ReplaceRequest { - return v.value -} - -func (v *NullableReplaceRequest) Set(val *ReplaceRequest) { - v.value = val - v.isSet = true -} - -func (v NullableReplaceRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableReplaceRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableReplaceRequest(val *ReplaceRequest) *NullableReplaceRequest { - return &NullableReplaceRequest{value: val, isSet: true} -} - -func (v NullableReplaceRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableReplaceRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_replace_result.go b/apps/api-client-go/model_replace_result.go deleted file mode 100644 index 5f210d0af..000000000 --- a/apps/api-client-go/model_replace_result.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the ReplaceResult type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ReplaceResult{} - -// ReplaceResult struct for ReplaceResult -type ReplaceResult struct { - File *string `json:"file,omitempty"` - Success *bool `json:"success,omitempty"` - Error *string `json:"error,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _ReplaceResult ReplaceResult - -// NewReplaceResult instantiates a new ReplaceResult object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewReplaceResult() *ReplaceResult { - this := ReplaceResult{} - return &this -} - -// NewReplaceResultWithDefaults instantiates a new ReplaceResult object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewReplaceResultWithDefaults() *ReplaceResult { - this := ReplaceResult{} - return &this -} - -// GetFile returns the File field value if set, zero value otherwise. -func (o *ReplaceResult) GetFile() string { - if o == nil || IsNil(o.File) { - var ret string - return ret - } - return *o.File -} - -// GetFileOk returns a tuple with the File field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ReplaceResult) GetFileOk() (*string, bool) { - if o == nil || IsNil(o.File) { - return nil, false - } - return o.File, true -} - -// HasFile returns a boolean if a field has been set. -func (o *ReplaceResult) HasFile() bool { - if o != nil && !IsNil(o.File) { - return true - } - - return false -} - -// SetFile gets a reference to the given string and assigns it to the File field. -func (o *ReplaceResult) SetFile(v string) { - o.File = &v -} - -// GetSuccess returns the Success field value if set, zero value otherwise. -func (o *ReplaceResult) GetSuccess() bool { - if o == nil || IsNil(o.Success) { - var ret bool - return ret - } - return *o.Success -} - -// GetSuccessOk returns a tuple with the Success field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ReplaceResult) GetSuccessOk() (*bool, bool) { - if o == nil || IsNil(o.Success) { - return nil, false - } - return o.Success, true -} - -// HasSuccess returns a boolean if a field has been set. -func (o *ReplaceResult) HasSuccess() bool { - if o != nil && !IsNil(o.Success) { - return true - } - - return false -} - -// SetSuccess gets a reference to the given bool and assigns it to the Success field. -func (o *ReplaceResult) SetSuccess(v bool) { - o.Success = &v -} - -// GetError returns the Error field value if set, zero value otherwise. -func (o *ReplaceResult) GetError() string { - if o == nil || IsNil(o.Error) { - var ret string - return ret - } - return *o.Error -} - -// GetErrorOk returns a tuple with the Error field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ReplaceResult) GetErrorOk() (*string, bool) { - if o == nil || IsNil(o.Error) { - return nil, false - } - return o.Error, true -} - -// HasError returns a boolean if a field has been set. -func (o *ReplaceResult) HasError() bool { - if o != nil && !IsNil(o.Error) { - return true - } - - return false -} - -// SetError gets a reference to the given string and assigns it to the Error field. -func (o *ReplaceResult) SetError(v string) { - o.Error = &v -} - -func (o ReplaceResult) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ReplaceResult) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.File) { - toSerialize["file"] = o.File - } - if !IsNil(o.Success) { - toSerialize["success"] = o.Success - } - if !IsNil(o.Error) { - toSerialize["error"] = o.Error - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ReplaceResult) UnmarshalJSON(data []byte) (err error) { - varReplaceResult := _ReplaceResult{} - - err = json.Unmarshal(data, &varReplaceResult) - - if err != nil { - return err - } - - *o = ReplaceResult(varReplaceResult) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "file") - delete(additionalProperties, "success") - delete(additionalProperties, "error") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableReplaceResult struct { - value *ReplaceResult - isSet bool -} - -func (v NullableReplaceResult) Get() *ReplaceResult { - return v.value -} - -func (v *NullableReplaceResult) Set(val *ReplaceResult) { - v.value = val - v.isSet = true -} - -func (v NullableReplaceResult) IsSet() bool { - return v.isSet -} - -func (v *NullableReplaceResult) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableReplaceResult(val *ReplaceResult) *NullableReplaceResult { - return &NullableReplaceResult{value: val, isSet: true} -} - -func (v NullableReplaceResult) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableReplaceResult) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_resize_box.go b/apps/api-client-go/model_resize_box.go new file mode 100644 index 000000000..ad9a50e6f --- /dev/null +++ b/apps/api-client-go/model_resize_box.go @@ -0,0 +1,233 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" +) + +// checks if the ResizeBox type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ResizeBox{} + +// ResizeBox struct for ResizeBox +type ResizeBox struct { + // CPU cores to allocate to the box (minimum: 1) + Cpu *int32 `json:"cpu,omitempty"` + // Memory in GB to allocate to the box (minimum: 1) + Memory *int32 `json:"memory,omitempty"` + // Disk space in GB to allocate to the box (can only be increased) + Disk *int32 `json:"disk,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _ResizeBox ResizeBox + +// NewResizeBox instantiates a new ResizeBox object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewResizeBox() *ResizeBox { + this := ResizeBox{} + return &this +} + +// NewResizeBoxWithDefaults instantiates a new ResizeBox object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewResizeBoxWithDefaults() *ResizeBox { + this := ResizeBox{} + return &this +} + +// GetCpu returns the Cpu field value if set, zero value otherwise. +func (o *ResizeBox) GetCpu() int32 { + if o == nil || IsNil(o.Cpu) { + var ret int32 + return ret + } + return *o.Cpu +} + +// GetCpuOk returns a tuple with the Cpu field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ResizeBox) GetCpuOk() (*int32, bool) { + if o == nil || IsNil(o.Cpu) { + return nil, false + } + return o.Cpu, true +} + +// HasCpu returns a boolean if a field has been set. +func (o *ResizeBox) HasCpu() bool { + if o != nil && !IsNil(o.Cpu) { + return true + } + + return false +} + +// SetCpu gets a reference to the given int32 and assigns it to the Cpu field. +func (o *ResizeBox) SetCpu(v int32) { + o.Cpu = &v +} + +// GetMemory returns the Memory field value if set, zero value otherwise. +func (o *ResizeBox) GetMemory() int32 { + if o == nil || IsNil(o.Memory) { + var ret int32 + return ret + } + return *o.Memory +} + +// GetMemoryOk returns a tuple with the Memory field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ResizeBox) GetMemoryOk() (*int32, bool) { + if o == nil || IsNil(o.Memory) { + return nil, false + } + return o.Memory, true +} + +// HasMemory returns a boolean if a field has been set. +func (o *ResizeBox) HasMemory() bool { + if o != nil && !IsNil(o.Memory) { + return true + } + + return false +} + +// SetMemory gets a reference to the given int32 and assigns it to the Memory field. +func (o *ResizeBox) SetMemory(v int32) { + o.Memory = &v +} + +// GetDisk returns the Disk field value if set, zero value otherwise. +func (o *ResizeBox) GetDisk() int32 { + if o == nil || IsNil(o.Disk) { + var ret int32 + return ret + } + return *o.Disk +} + +// GetDiskOk returns a tuple with the Disk field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ResizeBox) GetDiskOk() (*int32, bool) { + if o == nil || IsNil(o.Disk) { + return nil, false + } + return o.Disk, true +} + +// HasDisk returns a boolean if a field has been set. +func (o *ResizeBox) HasDisk() bool { + if o != nil && !IsNil(o.Disk) { + return true + } + + return false +} + +// SetDisk gets a reference to the given int32 and assigns it to the Disk field. +func (o *ResizeBox) SetDisk(v int32) { + o.Disk = &v +} + +func (o ResizeBox) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ResizeBox) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Cpu) { + toSerialize["cpu"] = o.Cpu + } + if !IsNil(o.Memory) { + toSerialize["memory"] = o.Memory + } + if !IsNil(o.Disk) { + toSerialize["disk"] = o.Disk + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *ResizeBox) UnmarshalJSON(data []byte) (err error) { + varResizeBox := _ResizeBox{} + + err = json.Unmarshal(data, &varResizeBox) + + if err != nil { + return err + } + + *o = ResizeBox(varResizeBox) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "cpu") + delete(additionalProperties, "memory") + delete(additionalProperties, "disk") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableResizeBox struct { + value *ResizeBox + isSet bool +} + +func (v NullableResizeBox) Get() *ResizeBox { + return v.value +} + +func (v *NullableResizeBox) Set(val *ResizeBox) { + v.value = val + v.isSet = true +} + +func (v NullableResizeBox) IsSet() bool { + return v.isSet +} + +func (v *NullableResizeBox) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableResizeBox(val *ResizeBox) *NullableResizeBox { + return &NullableResizeBox{value: val, isSet: true} +} + +func (v NullableResizeBox) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableResizeBox) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_resize_sandbox.go b/apps/api-client-go/model_resize_sandbox.go deleted file mode 100644 index 31b6f1806..000000000 --- a/apps/api-client-go/model_resize_sandbox.go +++ /dev/null @@ -1,231 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the ResizeSandbox type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ResizeSandbox{} - -// ResizeSandbox struct for ResizeSandbox -type ResizeSandbox struct { - // CPU cores to allocate to the sandbox (minimum: 1) - Cpu *int32 `json:"cpu,omitempty"` - // Memory in GB to allocate to the sandbox (minimum: 1) - Memory *int32 `json:"memory,omitempty"` - // Disk space in GB to allocate to the sandbox (can only be increased) - Disk *int32 `json:"disk,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _ResizeSandbox ResizeSandbox - -// NewResizeSandbox instantiates a new ResizeSandbox object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewResizeSandbox() *ResizeSandbox { - this := ResizeSandbox{} - return &this -} - -// NewResizeSandboxWithDefaults instantiates a new ResizeSandbox object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewResizeSandboxWithDefaults() *ResizeSandbox { - this := ResizeSandbox{} - return &this -} - -// GetCpu returns the Cpu field value if set, zero value otherwise. -func (o *ResizeSandbox) GetCpu() int32 { - if o == nil || IsNil(o.Cpu) { - var ret int32 - return ret - } - return *o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResizeSandbox) GetCpuOk() (*int32, bool) { - if o == nil || IsNil(o.Cpu) { - return nil, false - } - return o.Cpu, true -} - -// HasCpu returns a boolean if a field has been set. -func (o *ResizeSandbox) HasCpu() bool { - if o != nil && !IsNil(o.Cpu) { - return true - } - - return false -} - -// SetCpu gets a reference to the given int32 and assigns it to the Cpu field. -func (o *ResizeSandbox) SetCpu(v int32) { - o.Cpu = &v -} - -// GetMemory returns the Memory field value if set, zero value otherwise. -func (o *ResizeSandbox) GetMemory() int32 { - if o == nil || IsNil(o.Memory) { - var ret int32 - return ret - } - return *o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResizeSandbox) GetMemoryOk() (*int32, bool) { - if o == nil || IsNil(o.Memory) { - return nil, false - } - return o.Memory, true -} - -// HasMemory returns a boolean if a field has been set. -func (o *ResizeSandbox) HasMemory() bool { - if o != nil && !IsNil(o.Memory) { - return true - } - - return false -} - -// SetMemory gets a reference to the given int32 and assigns it to the Memory field. -func (o *ResizeSandbox) SetMemory(v int32) { - o.Memory = &v -} - -// GetDisk returns the Disk field value if set, zero value otherwise. -func (o *ResizeSandbox) GetDisk() int32 { - if o == nil || IsNil(o.Disk) { - var ret int32 - return ret - } - return *o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ResizeSandbox) GetDiskOk() (*int32, bool) { - if o == nil || IsNil(o.Disk) { - return nil, false - } - return o.Disk, true -} - -// HasDisk returns a boolean if a field has been set. -func (o *ResizeSandbox) HasDisk() bool { - if o != nil && !IsNil(o.Disk) { - return true - } - - return false -} - -// SetDisk gets a reference to the given int32 and assigns it to the Disk field. -func (o *ResizeSandbox) SetDisk(v int32) { - o.Disk = &v -} - -func (o ResizeSandbox) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ResizeSandbox) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Cpu) { - toSerialize["cpu"] = o.Cpu - } - if !IsNil(o.Memory) { - toSerialize["memory"] = o.Memory - } - if !IsNil(o.Disk) { - toSerialize["disk"] = o.Disk - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ResizeSandbox) UnmarshalJSON(data []byte) (err error) { - varResizeSandbox := _ResizeSandbox{} - - err = json.Unmarshal(data, &varResizeSandbox) - - if err != nil { - return err - } - - *o = ResizeSandbox(varResizeSandbox) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "cpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableResizeSandbox struct { - value *ResizeSandbox - isSet bool -} - -func (v NullableResizeSandbox) Get() *ResizeSandbox { - return v.value -} - -func (v *NullableResizeSandbox) Set(val *ResizeSandbox) { - v.value = val - v.isSet = true -} - -func (v NullableResizeSandbox) IsSet() bool { - return v.isSet -} - -func (v *NullableResizeSandbox) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableResizeSandbox(val *ResizeSandbox) *NullableResizeSandbox { - return &NullableResizeSandbox{value: val, isSet: true} -} - -func (v NullableResizeSandbox) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableResizeSandbox) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_runner.go b/apps/api-client-go/model_runner.go index 9bf43d8ec..8da14531a 100644 --- a/apps/api-client-go/model_runner.go +++ b/apps/api-client-go/model_runner.go @@ -40,7 +40,7 @@ type Runner struct { // The type of GPU GpuType *string `json:"gpuType,omitempty"` // The class of the runner - Class SandboxClass `json:"class"` + Class BoxClass `json:"class"` // Current CPU usage percentage CurrentCpuUsagePercentage *float32 `json:"currentCpuUsagePercentage,omitempty"` // Current RAM usage percentage @@ -53,10 +53,8 @@ type Runner struct { CurrentAllocatedMemoryGiB *float32 `json:"currentAllocatedMemoryGiB,omitempty"` // Current allocated disk in GiB CurrentAllocatedDiskGiB *float32 `json:"currentAllocatedDiskGiB,omitempty"` - // Current snapshot count - CurrentSnapshotCount *float32 `json:"currentSnapshotCount,omitempty"` - // Current number of started sandboxes - CurrentStartedSandboxes *float32 `json:"currentStartedSandboxes,omitempty"` + // Current number of started boxes + CurrentStartedBoxes *float32 `json:"currentStartedBoxes,omitempty"` // Runner availability score AvailabilityScore *float32 `json:"availabilityScore,omitempty"` // The region of the runner @@ -91,7 +89,7 @@ type _Runner Runner // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewRunner(id string, cpu float32, memory float32, disk float32, class SandboxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string) *Runner { +func NewRunner(id string, cpu float32, memory float32, disk float32, class BoxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string) *Runner { this := Runner{} this.Id = id this.Cpu = cpu @@ -374,9 +372,9 @@ func (o *Runner) SetGpuType(v string) { } // GetClass returns the Class field value -func (o *Runner) GetClass() SandboxClass { +func (o *Runner) GetClass() BoxClass { if o == nil { - var ret SandboxClass + var ret BoxClass return ret } @@ -385,7 +383,7 @@ func (o *Runner) GetClass() SandboxClass { // GetClassOk returns a tuple with the Class field value // and a boolean to check if the value has been set. -func (o *Runner) GetClassOk() (*SandboxClass, bool) { +func (o *Runner) GetClassOk() (*BoxClass, bool) { if o == nil { return nil, false } @@ -393,7 +391,7 @@ func (o *Runner) GetClassOk() (*SandboxClass, bool) { } // SetClass sets field value -func (o *Runner) SetClass(v SandboxClass) { +func (o *Runner) SetClass(v BoxClass) { o.Class = v } @@ -589,68 +587,36 @@ func (o *Runner) SetCurrentAllocatedDiskGiB(v float32) { o.CurrentAllocatedDiskGiB = &v } -// GetCurrentSnapshotCount returns the CurrentSnapshotCount field value if set, zero value otherwise. -func (o *Runner) GetCurrentSnapshotCount() float32 { - if o == nil || IsNil(o.CurrentSnapshotCount) { +// GetCurrentStartedBoxes returns the CurrentStartedBoxes field value if set, zero value otherwise. +func (o *Runner) GetCurrentStartedBoxes() float32 { + if o == nil || IsNil(o.CurrentStartedBoxes) { var ret float32 return ret } - return *o.CurrentSnapshotCount + return *o.CurrentStartedBoxes } -// GetCurrentSnapshotCountOk returns a tuple with the CurrentSnapshotCount field value if set, nil otherwise +// GetCurrentStartedBoxesOk returns a tuple with the CurrentStartedBoxes field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *Runner) GetCurrentSnapshotCountOk() (*float32, bool) { - if o == nil || IsNil(o.CurrentSnapshotCount) { +func (o *Runner) GetCurrentStartedBoxesOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentStartedBoxes) { return nil, false } - return o.CurrentSnapshotCount, true + return o.CurrentStartedBoxes, true } -// HasCurrentSnapshotCount returns a boolean if a field has been set. -func (o *Runner) HasCurrentSnapshotCount() bool { - if o != nil && !IsNil(o.CurrentSnapshotCount) { +// HasCurrentStartedBoxes returns a boolean if a field has been set. +func (o *Runner) HasCurrentStartedBoxes() bool { + if o != nil && !IsNil(o.CurrentStartedBoxes) { return true } return false } -// SetCurrentSnapshotCount gets a reference to the given float32 and assigns it to the CurrentSnapshotCount field. -func (o *Runner) SetCurrentSnapshotCount(v float32) { - o.CurrentSnapshotCount = &v -} - -// GetCurrentStartedSandboxes returns the CurrentStartedSandboxes field value if set, zero value otherwise. -func (o *Runner) GetCurrentStartedSandboxes() float32 { - if o == nil || IsNil(o.CurrentStartedSandboxes) { - var ret float32 - return ret - } - return *o.CurrentStartedSandboxes -} - -// GetCurrentStartedSandboxesOk returns a tuple with the CurrentStartedSandboxes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Runner) GetCurrentStartedSandboxesOk() (*float32, bool) { - if o == nil || IsNil(o.CurrentStartedSandboxes) { - return nil, false - } - return o.CurrentStartedSandboxes, true -} - -// HasCurrentStartedSandboxes returns a boolean if a field has been set. -func (o *Runner) HasCurrentStartedSandboxes() bool { - if o != nil && !IsNil(o.CurrentStartedSandboxes) { - return true - } - - return false -} - -// SetCurrentStartedSandboxes gets a reference to the given float32 and assigns it to the CurrentStartedSandboxes field. -func (o *Runner) SetCurrentStartedSandboxes(v float32) { - o.CurrentStartedSandboxes = &v +// SetCurrentStartedBoxes gets a reference to the given float32 and assigns it to the CurrentStartedBoxes field. +func (o *Runner) SetCurrentStartedBoxes(v float32) { + o.CurrentStartedBoxes = &v } // GetAvailabilityScore returns the AvailabilityScore field value if set, zero value otherwise. @@ -998,11 +964,8 @@ func (o Runner) ToMap() (map[string]interface{}, error) { if !IsNil(o.CurrentAllocatedDiskGiB) { toSerialize["currentAllocatedDiskGiB"] = o.CurrentAllocatedDiskGiB } - if !IsNil(o.CurrentSnapshotCount) { - toSerialize["currentSnapshotCount"] = o.CurrentSnapshotCount - } - if !IsNil(o.CurrentStartedSandboxes) { - toSerialize["currentStartedSandboxes"] = o.CurrentStartedSandboxes + if !IsNil(o.CurrentStartedBoxes) { + toSerialize["currentStartedBoxes"] = o.CurrentStartedBoxes } if !IsNil(o.AvailabilityScore) { toSerialize["availabilityScore"] = o.AvailabilityScore @@ -1092,8 +1055,7 @@ func (o *Runner) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "currentAllocatedCpu") delete(additionalProperties, "currentAllocatedMemoryGiB") delete(additionalProperties, "currentAllocatedDiskGiB") - delete(additionalProperties, "currentSnapshotCount") - delete(additionalProperties, "currentStartedSandboxes") + delete(additionalProperties, "currentStartedBoxes") delete(additionalProperties, "availabilityScore") delete(additionalProperties, "region") delete(additionalProperties, "name") @@ -1146,3 +1108,5 @@ func (v *NullableRunner) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_runner_full.go b/apps/api-client-go/model_runner_full.go index 26c8825ba..8820c7123 100644 --- a/apps/api-client-go/model_runner_full.go +++ b/apps/api-client-go/model_runner_full.go @@ -40,7 +40,7 @@ type RunnerFull struct { // The type of GPU GpuType *string `json:"gpuType,omitempty"` // The class of the runner - Class SandboxClass `json:"class"` + Class BoxClass `json:"class"` // Current CPU usage percentage CurrentCpuUsagePercentage *float32 `json:"currentCpuUsagePercentage,omitempty"` // Current RAM usage percentage @@ -53,10 +53,8 @@ type RunnerFull struct { CurrentAllocatedMemoryGiB *float32 `json:"currentAllocatedMemoryGiB,omitempty"` // Current allocated disk in GiB CurrentAllocatedDiskGiB *float32 `json:"currentAllocatedDiskGiB,omitempty"` - // Current snapshot count - CurrentSnapshotCount *float32 `json:"currentSnapshotCount,omitempty"` - // Current number of started sandboxes - CurrentStartedSandboxes *float32 `json:"currentStartedSandboxes,omitempty"` + // Current number of started boxes + CurrentStartedBoxes *float32 `json:"currentStartedBoxes,omitempty"` // Runner availability score AvailabilityScore *float32 `json:"availabilityScore,omitempty"` // The region of the runner @@ -95,7 +93,7 @@ type _RunnerFull RunnerFull // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewRunnerFull(id string, cpu float32, memory float32, disk float32, class SandboxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string, apiKey string) *RunnerFull { +func NewRunnerFull(id string, cpu float32, memory float32, disk float32, class BoxClass, region string, name string, state RunnerState, unschedulable bool, createdAt string, updatedAt string, version string, apiVersion string, apiKey string) *RunnerFull { this := RunnerFull{} this.Id = id this.Cpu = cpu @@ -379,9 +377,9 @@ func (o *RunnerFull) SetGpuType(v string) { } // GetClass returns the Class field value -func (o *RunnerFull) GetClass() SandboxClass { +func (o *RunnerFull) GetClass() BoxClass { if o == nil { - var ret SandboxClass + var ret BoxClass return ret } @@ -390,7 +388,7 @@ func (o *RunnerFull) GetClass() SandboxClass { // GetClassOk returns a tuple with the Class field value // and a boolean to check if the value has been set. -func (o *RunnerFull) GetClassOk() (*SandboxClass, bool) { +func (o *RunnerFull) GetClassOk() (*BoxClass, bool) { if o == nil { return nil, false } @@ -398,7 +396,7 @@ func (o *RunnerFull) GetClassOk() (*SandboxClass, bool) { } // SetClass sets field value -func (o *RunnerFull) SetClass(v SandboxClass) { +func (o *RunnerFull) SetClass(v BoxClass) { o.Class = v } @@ -594,68 +592,36 @@ func (o *RunnerFull) SetCurrentAllocatedDiskGiB(v float32) { o.CurrentAllocatedDiskGiB = &v } -// GetCurrentSnapshotCount returns the CurrentSnapshotCount field value if set, zero value otherwise. -func (o *RunnerFull) GetCurrentSnapshotCount() float32 { - if o == nil || IsNil(o.CurrentSnapshotCount) { +// GetCurrentStartedBoxes returns the CurrentStartedBoxes field value if set, zero value otherwise. +func (o *RunnerFull) GetCurrentStartedBoxes() float32 { + if o == nil || IsNil(o.CurrentStartedBoxes) { var ret float32 return ret } - return *o.CurrentSnapshotCount + return *o.CurrentStartedBoxes } -// GetCurrentSnapshotCountOk returns a tuple with the CurrentSnapshotCount field value if set, nil otherwise +// GetCurrentStartedBoxesOk returns a tuple with the CurrentStartedBoxes field value if set, nil otherwise // and a boolean to check if the value has been set. -func (o *RunnerFull) GetCurrentSnapshotCountOk() (*float32, bool) { - if o == nil || IsNil(o.CurrentSnapshotCount) { +func (o *RunnerFull) GetCurrentStartedBoxesOk() (*float32, bool) { + if o == nil || IsNil(o.CurrentStartedBoxes) { return nil, false } - return o.CurrentSnapshotCount, true + return o.CurrentStartedBoxes, true } -// HasCurrentSnapshotCount returns a boolean if a field has been set. -func (o *RunnerFull) HasCurrentSnapshotCount() bool { - if o != nil && !IsNil(o.CurrentSnapshotCount) { +// HasCurrentStartedBoxes returns a boolean if a field has been set. +func (o *RunnerFull) HasCurrentStartedBoxes() bool { + if o != nil && !IsNil(o.CurrentStartedBoxes) { return true } return false } -// SetCurrentSnapshotCount gets a reference to the given float32 and assigns it to the CurrentSnapshotCount field. -func (o *RunnerFull) SetCurrentSnapshotCount(v float32) { - o.CurrentSnapshotCount = &v -} - -// GetCurrentStartedSandboxes returns the CurrentStartedSandboxes field value if set, zero value otherwise. -func (o *RunnerFull) GetCurrentStartedSandboxes() float32 { - if o == nil || IsNil(o.CurrentStartedSandboxes) { - var ret float32 - return ret - } - return *o.CurrentStartedSandboxes -} - -// GetCurrentStartedSandboxesOk returns a tuple with the CurrentStartedSandboxes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *RunnerFull) GetCurrentStartedSandboxesOk() (*float32, bool) { - if o == nil || IsNil(o.CurrentStartedSandboxes) { - return nil, false - } - return o.CurrentStartedSandboxes, true -} - -// HasCurrentStartedSandboxes returns a boolean if a field has been set. -func (o *RunnerFull) HasCurrentStartedSandboxes() bool { - if o != nil && !IsNil(o.CurrentStartedSandboxes) { - return true - } - - return false -} - -// SetCurrentStartedSandboxes gets a reference to the given float32 and assigns it to the CurrentStartedSandboxes field. -func (o *RunnerFull) SetCurrentStartedSandboxes(v float32) { - o.CurrentStartedSandboxes = &v +// SetCurrentStartedBoxes gets a reference to the given float32 and assigns it to the CurrentStartedBoxes field. +func (o *RunnerFull) SetCurrentStartedBoxes(v float32) { + o.CurrentStartedBoxes = &v } // GetAvailabilityScore returns the AvailabilityScore field value if set, zero value otherwise. @@ -1059,11 +1025,8 @@ func (o RunnerFull) ToMap() (map[string]interface{}, error) { if !IsNil(o.CurrentAllocatedDiskGiB) { toSerialize["currentAllocatedDiskGiB"] = o.CurrentAllocatedDiskGiB } - if !IsNil(o.CurrentSnapshotCount) { - toSerialize["currentSnapshotCount"] = o.CurrentSnapshotCount - } - if !IsNil(o.CurrentStartedSandboxes) { - toSerialize["currentStartedSandboxes"] = o.CurrentStartedSandboxes + if !IsNil(o.CurrentStartedBoxes) { + toSerialize["currentStartedBoxes"] = o.CurrentStartedBoxes } if !IsNil(o.AvailabilityScore) { toSerialize["availabilityScore"] = o.AvailabilityScore @@ -1158,8 +1121,7 @@ func (o *RunnerFull) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "currentAllocatedCpu") delete(additionalProperties, "currentAllocatedMemoryGiB") delete(additionalProperties, "currentAllocatedDiskGiB") - delete(additionalProperties, "currentSnapshotCount") - delete(additionalProperties, "currentStartedSandboxes") + delete(additionalProperties, "currentStartedBoxes") delete(additionalProperties, "availabilityScore") delete(additionalProperties, "region") delete(additionalProperties, "name") @@ -1214,3 +1176,5 @@ func (v *NullableRunnerFull) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_runner_health_metrics.go b/apps/api-client-go/model_runner_health_metrics.go index 03139f05d..5e117b90d 100644 --- a/apps/api-client-go/model_runner_health_metrics.go +++ b/apps/api-client-go/model_runner_health_metrics.go @@ -35,10 +35,8 @@ type RunnerHealthMetrics struct { CurrentAllocatedMemoryGiB float32 `json:"currentAllocatedMemoryGiB"` // Currently allocated disk in GiB CurrentAllocatedDiskGiB float32 `json:"currentAllocatedDiskGiB"` - // Number of snapshots currently stored - CurrentSnapshotCount float32 `json:"currentSnapshotCount"` - // Number of started sandboxes - CurrentStartedSandboxes float32 `json:"currentStartedSandboxes"` + // Number of started boxes + CurrentStartedBoxes float32 `json:"currentStartedBoxes"` // Total CPU cores on the runner Cpu float32 `json:"cpu"` // Total RAM in GiB on the runner @@ -54,7 +52,7 @@ type _RunnerHealthMetrics RunnerHealthMetrics // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewRunnerHealthMetrics(currentCpuLoadAverage float32, currentCpuUsagePercentage float32, currentMemoryUsagePercentage float32, currentDiskUsagePercentage float32, currentAllocatedCpu float32, currentAllocatedMemoryGiB float32, currentAllocatedDiskGiB float32, currentSnapshotCount float32, currentStartedSandboxes float32, cpu float32, memoryGiB float32, diskGiB float32) *RunnerHealthMetrics { +func NewRunnerHealthMetrics(currentCpuLoadAverage float32, currentCpuUsagePercentage float32, currentMemoryUsagePercentage float32, currentDiskUsagePercentage float32, currentAllocatedCpu float32, currentAllocatedMemoryGiB float32, currentAllocatedDiskGiB float32, currentStartedBoxes float32, cpu float32, memoryGiB float32, diskGiB float32) *RunnerHealthMetrics { this := RunnerHealthMetrics{} this.CurrentCpuLoadAverage = currentCpuLoadAverage this.CurrentCpuUsagePercentage = currentCpuUsagePercentage @@ -63,8 +61,7 @@ func NewRunnerHealthMetrics(currentCpuLoadAverage float32, currentCpuUsagePercen this.CurrentAllocatedCpu = currentAllocatedCpu this.CurrentAllocatedMemoryGiB = currentAllocatedMemoryGiB this.CurrentAllocatedDiskGiB = currentAllocatedDiskGiB - this.CurrentSnapshotCount = currentSnapshotCount - this.CurrentStartedSandboxes = currentStartedSandboxes + this.CurrentStartedBoxes = currentStartedBoxes this.Cpu = cpu this.MemoryGiB = memoryGiB this.DiskGiB = diskGiB @@ -247,52 +244,28 @@ func (o *RunnerHealthMetrics) SetCurrentAllocatedDiskGiB(v float32) { o.CurrentAllocatedDiskGiB = v } -// GetCurrentSnapshotCount returns the CurrentSnapshotCount field value -func (o *RunnerHealthMetrics) GetCurrentSnapshotCount() float32 { +// GetCurrentStartedBoxes returns the CurrentStartedBoxes field value +func (o *RunnerHealthMetrics) GetCurrentStartedBoxes() float32 { if o == nil { var ret float32 return ret } - return o.CurrentSnapshotCount + return o.CurrentStartedBoxes } -// GetCurrentSnapshotCountOk returns a tuple with the CurrentSnapshotCount field value +// GetCurrentStartedBoxesOk returns a tuple with the CurrentStartedBoxes field value // and a boolean to check if the value has been set. -func (o *RunnerHealthMetrics) GetCurrentSnapshotCountOk() (*float32, bool) { +func (o *RunnerHealthMetrics) GetCurrentStartedBoxesOk() (*float32, bool) { if o == nil { return nil, false } - return &o.CurrentSnapshotCount, true + return &o.CurrentStartedBoxes, true } -// SetCurrentSnapshotCount sets field value -func (o *RunnerHealthMetrics) SetCurrentSnapshotCount(v float32) { - o.CurrentSnapshotCount = v -} - -// GetCurrentStartedSandboxes returns the CurrentStartedSandboxes field value -func (o *RunnerHealthMetrics) GetCurrentStartedSandboxes() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.CurrentStartedSandboxes -} - -// GetCurrentStartedSandboxesOk returns a tuple with the CurrentStartedSandboxes field value -// and a boolean to check if the value has been set. -func (o *RunnerHealthMetrics) GetCurrentStartedSandboxesOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.CurrentStartedSandboxes, true -} - -// SetCurrentStartedSandboxes sets field value -func (o *RunnerHealthMetrics) SetCurrentStartedSandboxes(v float32) { - o.CurrentStartedSandboxes = v +// SetCurrentStartedBoxes sets field value +func (o *RunnerHealthMetrics) SetCurrentStartedBoxes(v float32) { + o.CurrentStartedBoxes = v } // GetCpu returns the Cpu field value @@ -384,8 +357,7 @@ func (o RunnerHealthMetrics) ToMap() (map[string]interface{}, error) { toSerialize["currentAllocatedCpu"] = o.CurrentAllocatedCpu toSerialize["currentAllocatedMemoryGiB"] = o.CurrentAllocatedMemoryGiB toSerialize["currentAllocatedDiskGiB"] = o.CurrentAllocatedDiskGiB - toSerialize["currentSnapshotCount"] = o.CurrentSnapshotCount - toSerialize["currentStartedSandboxes"] = o.CurrentStartedSandboxes + toSerialize["currentStartedBoxes"] = o.CurrentStartedBoxes toSerialize["cpu"] = o.Cpu toSerialize["memoryGiB"] = o.MemoryGiB toSerialize["diskGiB"] = o.DiskGiB @@ -409,8 +381,7 @@ func (o *RunnerHealthMetrics) UnmarshalJSON(data []byte) (err error) { "currentAllocatedCpu", "currentAllocatedMemoryGiB", "currentAllocatedDiskGiB", - "currentSnapshotCount", - "currentStartedSandboxes", + "currentStartedBoxes", "cpu", "memoryGiB", "diskGiB", @@ -450,8 +421,7 @@ func (o *RunnerHealthMetrics) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "currentAllocatedCpu") delete(additionalProperties, "currentAllocatedMemoryGiB") delete(additionalProperties, "currentAllocatedDiskGiB") - delete(additionalProperties, "currentSnapshotCount") - delete(additionalProperties, "currentStartedSandboxes") + delete(additionalProperties, "currentStartedBoxes") delete(additionalProperties, "cpu") delete(additionalProperties, "memoryGiB") delete(additionalProperties, "diskGiB") @@ -496,3 +466,5 @@ func (v *NullableRunnerHealthMetrics) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_runner_healthcheck.go b/apps/api-client-go/model_runner_healthcheck.go index 2db6a378a..b2d8fa500 100644 --- a/apps/api-client-go/model_runner_healthcheck.go +++ b/apps/api-client-go/model_runner_healthcheck.go @@ -356,3 +356,5 @@ func (v *NullableRunnerHealthcheck) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_runner_service_health.go b/apps/api-client-go/model_runner_service_health.go index 7b97ebe45..f930416ab 100644 --- a/apps/api-client-go/model_runner_service_health.go +++ b/apps/api-client-go/model_runner_service_health.go @@ -234,3 +234,5 @@ func (v *NullableRunnerServiceHealth) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_runner_snapshot_dto.go b/apps/api-client-go/model_runner_snapshot_dto.go deleted file mode 100644 index a4c304a73..000000000 --- a/apps/api-client-go/model_runner_snapshot_dto.go +++ /dev/null @@ -1,236 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the RunnerSnapshotDto type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &RunnerSnapshotDto{} - -// RunnerSnapshotDto struct for RunnerSnapshotDto -type RunnerSnapshotDto struct { - // Runner snapshot ID - RunnerSnapshotId string `json:"runnerSnapshotId"` - // Runner ID - RunnerId string `json:"runnerId"` - // Runner domain - RunnerDomain *string `json:"runnerDomain,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _RunnerSnapshotDto RunnerSnapshotDto - -// NewRunnerSnapshotDto instantiates a new RunnerSnapshotDto object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewRunnerSnapshotDto(runnerSnapshotId string, runnerId string) *RunnerSnapshotDto { - this := RunnerSnapshotDto{} - this.RunnerSnapshotId = runnerSnapshotId - this.RunnerId = runnerId - return &this -} - -// NewRunnerSnapshotDtoWithDefaults instantiates a new RunnerSnapshotDto object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewRunnerSnapshotDtoWithDefaults() *RunnerSnapshotDto { - this := RunnerSnapshotDto{} - return &this -} - -// GetRunnerSnapshotId returns the RunnerSnapshotId field value -func (o *RunnerSnapshotDto) GetRunnerSnapshotId() string { - if o == nil { - var ret string - return ret - } - - return o.RunnerSnapshotId -} - -// GetRunnerSnapshotIdOk returns a tuple with the RunnerSnapshotId field value -// and a boolean to check if the value has been set. -func (o *RunnerSnapshotDto) GetRunnerSnapshotIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RunnerSnapshotId, true -} - -// SetRunnerSnapshotId sets field value -func (o *RunnerSnapshotDto) SetRunnerSnapshotId(v string) { - o.RunnerSnapshotId = v -} - -// GetRunnerId returns the RunnerId field value -func (o *RunnerSnapshotDto) GetRunnerId() string { - if o == nil { - var ret string - return ret - } - - return o.RunnerId -} - -// GetRunnerIdOk returns a tuple with the RunnerId field value -// and a boolean to check if the value has been set. -func (o *RunnerSnapshotDto) GetRunnerIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.RunnerId, true -} - -// SetRunnerId sets field value -func (o *RunnerSnapshotDto) SetRunnerId(v string) { - o.RunnerId = v -} - -// GetRunnerDomain returns the RunnerDomain field value if set, zero value otherwise. -func (o *RunnerSnapshotDto) GetRunnerDomain() string { - if o == nil || IsNil(o.RunnerDomain) { - var ret string - return ret - } - return *o.RunnerDomain -} - -// GetRunnerDomainOk returns a tuple with the RunnerDomain field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *RunnerSnapshotDto) GetRunnerDomainOk() (*string, bool) { - if o == nil || IsNil(o.RunnerDomain) { - return nil, false - } - return o.RunnerDomain, true -} - -// HasRunnerDomain returns a boolean if a field has been set. -func (o *RunnerSnapshotDto) HasRunnerDomain() bool { - if o != nil && !IsNil(o.RunnerDomain) { - return true - } - - return false -} - -// SetRunnerDomain gets a reference to the given string and assigns it to the RunnerDomain field. -func (o *RunnerSnapshotDto) SetRunnerDomain(v string) { - o.RunnerDomain = &v -} - -func (o RunnerSnapshotDto) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o RunnerSnapshotDto) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["runnerSnapshotId"] = o.RunnerSnapshotId - toSerialize["runnerId"] = o.RunnerId - if !IsNil(o.RunnerDomain) { - toSerialize["runnerDomain"] = o.RunnerDomain - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *RunnerSnapshotDto) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "runnerSnapshotId", - "runnerId", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varRunnerSnapshotDto := _RunnerSnapshotDto{} - - err = json.Unmarshal(data, &varRunnerSnapshotDto) - - if err != nil { - return err - } - - *o = RunnerSnapshotDto(varRunnerSnapshotDto) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "runnerSnapshotId") - delete(additionalProperties, "runnerId") - delete(additionalProperties, "runnerDomain") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableRunnerSnapshotDto struct { - value *RunnerSnapshotDto - isSet bool -} - -func (v NullableRunnerSnapshotDto) Get() *RunnerSnapshotDto { - return v.value -} - -func (v *NullableRunnerSnapshotDto) Set(val *RunnerSnapshotDto) { - v.value = val - v.isSet = true -} - -func (v NullableRunnerSnapshotDto) IsSet() bool { - return v.isSet -} - -func (v *NullableRunnerSnapshotDto) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableRunnerSnapshotDto(val *RunnerSnapshotDto) *NullableRunnerSnapshotDto { - return &NullableRunnerSnapshotDto{value: val, isSet: true} -} - -func (v NullableRunnerSnapshotDto) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableRunnerSnapshotDto) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_runner_state.go b/apps/api-client-go/model_runner_state.go index 478f4c6e0..b0d83448b 100644 --- a/apps/api-client-go/model_runner_state.go +++ b/apps/api-client-go/model_runner_state.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // RunnerState The state of the runner @@ -26,6 +25,7 @@ const ( RUNNERSTATE_DISABLED RunnerState = "disabled" RUNNERSTATE_DECOMMISSIONED RunnerState = "decommissioned" RUNNERSTATE_UNRESPONSIVE RunnerState = "unresponsive" + RUNNERSTATE_UNKNOWN_DEFAULT_OPEN_API RunnerState = "unknown_default_open_api" ) // All allowed values of RunnerState enum @@ -35,6 +35,7 @@ var AllowedRunnerStateEnumValues = []RunnerState{ "disabled", "decommissioned", "unresponsive", + "unknown_default_open_api", } func (v *RunnerState) UnmarshalJSON(src []byte) error { @@ -51,7 +52,8 @@ func (v *RunnerState) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid RunnerState", value) + *v = RUNNERSTATE_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewRunnerStateFromValue returns a pointer to a valid RunnerState @@ -61,7 +63,8 @@ func NewRunnerStateFromValue(v string) (*RunnerState, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for RunnerState: valid values are %v", v, AllowedRunnerStateEnumValues) + enumValue := RUNNERSTATE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -115,3 +118,4 @@ func (v *NullableRunnerState) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_sandbox.go b/apps/api-client-go/model_sandbox.go deleted file mode 100644 index 3307507f1..000000000 --- a/apps/api-client-go/model_sandbox.go +++ /dev/null @@ -1,1246 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Sandbox type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Sandbox{} - -// Sandbox struct for Sandbox -type Sandbox struct { - // The ID of the sandbox - Id string `json:"id"` - // The organization ID of the sandbox - OrganizationId string `json:"organizationId"` - // The name of the sandbox - Name string `json:"name"` - // The snapshot used for the sandbox - Snapshot *string `json:"snapshot,omitempty"` - // The user associated with the project - User string `json:"user"` - // Environment variables for the sandbox - Env map[string]string `json:"env"` - // Labels for the sandbox - Labels map[string]string `json:"labels"` - // Whether the sandbox http preview is public - Public bool `json:"public"` - // Whether to block all network access for the sandbox - NetworkBlockAll bool `json:"networkBlockAll"` - // Comma-separated list of allowed CIDR network addresses for the sandbox - NetworkAllowList *string `json:"networkAllowList,omitempty"` - // The target environment for the sandbox - Target string `json:"target"` - // The CPU quota for the sandbox - Cpu float32 `json:"cpu"` - // The GPU quota for the sandbox - Gpu float32 `json:"gpu"` - // The memory quota for the sandbox - Memory float32 `json:"memory"` - // The disk quota for the sandbox - Disk float32 `json:"disk"` - // The state of the sandbox - State *SandboxState `json:"state,omitempty"` - // The desired state of the sandbox - DesiredState *SandboxDesiredState `json:"desiredState,omitempty"` - // The error reason of the sandbox - ErrorReason *string `json:"errorReason,omitempty"` - // Whether the sandbox error is recoverable. - Recoverable *bool `json:"recoverable,omitempty"` - // The state of the backup - BackupState *string `json:"backupState,omitempty"` - // The creation timestamp of the last backup - BackupCreatedAt *string `json:"backupCreatedAt,omitempty"` - // Auto-stop interval in minutes (0 means disabled) - AutoStopInterval *float32 `json:"autoStopInterval,omitempty"` - // Auto-archive interval in minutes - AutoArchiveInterval *float32 `json:"autoArchiveInterval,omitempty"` - // Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - AutoDeleteInterval *float32 `json:"autoDeleteInterval,omitempty"` - // Array of volumes attached to the sandbox - Volumes []SandboxVolume `json:"volumes,omitempty"` - // Build information for the sandbox - BuildInfo *BuildInfo `json:"buildInfo,omitempty"` - // The creation timestamp of the sandbox - CreatedAt *string `json:"createdAt,omitempty"` - // The last update timestamp of the sandbox - UpdatedAt *string `json:"updatedAt,omitempty"` - // The class of the sandbox - // Deprecated - Class *string `json:"class,omitempty"` - // The version of the daemon running in the sandbox - DaemonVersion *string `json:"daemonVersion,omitempty"` - // The runner ID of the sandbox - RunnerId *string `json:"runnerId,omitempty"` - // The toolbox proxy URL for the sandbox - ToolboxProxyUrl string `json:"toolboxProxyUrl"` - AdditionalProperties map[string]interface{} -} - -type _Sandbox Sandbox - -// NewSandbox instantiates a new Sandbox object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSandbox(id string, organizationId string, name string, user string, env map[string]string, labels map[string]string, public bool, networkBlockAll bool, target string, cpu float32, gpu float32, memory float32, disk float32, toolboxProxyUrl string) *Sandbox { - this := Sandbox{} - this.Id = id - this.OrganizationId = organizationId - this.Name = name - this.User = user - this.Env = env - this.Labels = labels - this.Public = public - this.NetworkBlockAll = networkBlockAll - this.Target = target - this.Cpu = cpu - this.Gpu = gpu - this.Memory = memory - this.Disk = disk - this.ToolboxProxyUrl = toolboxProxyUrl - return &this -} - -// NewSandboxWithDefaults instantiates a new Sandbox object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSandboxWithDefaults() *Sandbox { - this := Sandbox{} - return &this -} - -// GetId returns the Id field value -func (o *Sandbox) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *Sandbox) SetId(v string) { - o.Id = v -} - -// GetOrganizationId returns the OrganizationId field value -func (o *Sandbox) GetOrganizationId() string { - if o == nil { - var ret string - return ret - } - - return o.OrganizationId -} - -// GetOrganizationIdOk returns a tuple with the OrganizationId field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetOrganizationIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.OrganizationId, true -} - -// SetOrganizationId sets field value -func (o *Sandbox) SetOrganizationId(v string) { - o.OrganizationId = v -} - -// GetName returns the Name field value -func (o *Sandbox) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *Sandbox) SetName(v string) { - o.Name = v -} - -// GetSnapshot returns the Snapshot field value if set, zero value otherwise. -func (o *Sandbox) GetSnapshot() string { - if o == nil || IsNil(o.Snapshot) { - var ret string - return ret - } - return *o.Snapshot -} - -// GetSnapshotOk returns a tuple with the Snapshot field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetSnapshotOk() (*string, bool) { - if o == nil || IsNil(o.Snapshot) { - return nil, false - } - return o.Snapshot, true -} - -// HasSnapshot returns a boolean if a field has been set. -func (o *Sandbox) HasSnapshot() bool { - if o != nil && !IsNil(o.Snapshot) { - return true - } - - return false -} - -// SetSnapshot gets a reference to the given string and assigns it to the Snapshot field. -func (o *Sandbox) SetSnapshot(v string) { - o.Snapshot = &v -} - -// GetUser returns the User field value -func (o *Sandbox) GetUser() string { - if o == nil { - var ret string - return ret - } - - return o.User -} - -// GetUserOk returns a tuple with the User field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetUserOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.User, true -} - -// SetUser sets field value -func (o *Sandbox) SetUser(v string) { - o.User = v -} - -// GetEnv returns the Env field value -func (o *Sandbox) GetEnv() map[string]string { - if o == nil { - var ret map[string]string - return ret - } - - return o.Env -} - -// GetEnvOk returns a tuple with the Env field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetEnvOk() (*map[string]string, bool) { - if o == nil { - return nil, false - } - return &o.Env, true -} - -// SetEnv sets field value -func (o *Sandbox) SetEnv(v map[string]string) { - o.Env = v -} - -// GetLabels returns the Labels field value -func (o *Sandbox) GetLabels() map[string]string { - if o == nil { - var ret map[string]string - return ret - } - - return o.Labels -} - -// GetLabelsOk returns a tuple with the Labels field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetLabelsOk() (*map[string]string, bool) { - if o == nil { - return nil, false - } - return &o.Labels, true -} - -// SetLabels sets field value -func (o *Sandbox) SetLabels(v map[string]string) { - o.Labels = v -} - -// GetPublic returns the Public field value -func (o *Sandbox) GetPublic() bool { - if o == nil { - var ret bool - return ret - } - - return o.Public -} - -// GetPublicOk returns a tuple with the Public field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetPublicOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.Public, true -} - -// SetPublic sets field value -func (o *Sandbox) SetPublic(v bool) { - o.Public = v -} - -// GetNetworkBlockAll returns the NetworkBlockAll field value -func (o *Sandbox) GetNetworkBlockAll() bool { - if o == nil { - var ret bool - return ret - } - - return o.NetworkBlockAll -} - -// GetNetworkBlockAllOk returns a tuple with the NetworkBlockAll field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetNetworkBlockAllOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.NetworkBlockAll, true -} - -// SetNetworkBlockAll sets field value -func (o *Sandbox) SetNetworkBlockAll(v bool) { - o.NetworkBlockAll = v -} - -// GetNetworkAllowList returns the NetworkAllowList field value if set, zero value otherwise. -func (o *Sandbox) GetNetworkAllowList() string { - if o == nil || IsNil(o.NetworkAllowList) { - var ret string - return ret - } - return *o.NetworkAllowList -} - -// GetNetworkAllowListOk returns a tuple with the NetworkAllowList field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetNetworkAllowListOk() (*string, bool) { - if o == nil || IsNil(o.NetworkAllowList) { - return nil, false - } - return o.NetworkAllowList, true -} - -// HasNetworkAllowList returns a boolean if a field has been set. -func (o *Sandbox) HasNetworkAllowList() bool { - if o != nil && !IsNil(o.NetworkAllowList) { - return true - } - - return false -} - -// SetNetworkAllowList gets a reference to the given string and assigns it to the NetworkAllowList field. -func (o *Sandbox) SetNetworkAllowList(v string) { - o.NetworkAllowList = &v -} - -// GetTarget returns the Target field value -func (o *Sandbox) GetTarget() string { - if o == nil { - var ret string - return ret - } - - return o.Target -} - -// GetTargetOk returns a tuple with the Target field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetTargetOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Target, true -} - -// SetTarget sets field value -func (o *Sandbox) SetTarget(v string) { - o.Target = v -} - -// GetCpu returns the Cpu field value -func (o *Sandbox) GetCpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetCpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Cpu, true -} - -// SetCpu sets field value -func (o *Sandbox) SetCpu(v float32) { - o.Cpu = v -} - -// GetGpu returns the Gpu field value -func (o *Sandbox) GetGpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetGpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Gpu, true -} - -// SetGpu sets field value -func (o *Sandbox) SetGpu(v float32) { - o.Gpu = v -} - -// GetMemory returns the Memory field value -func (o *Sandbox) GetMemory() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetMemoryOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Memory, true -} - -// SetMemory sets field value -func (o *Sandbox) SetMemory(v float32) { - o.Memory = v -} - -// GetDisk returns the Disk field value -func (o *Sandbox) GetDisk() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetDiskOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Disk, true -} - -// SetDisk sets field value -func (o *Sandbox) SetDisk(v float32) { - o.Disk = v -} - -// GetState returns the State field value if set, zero value otherwise. -func (o *Sandbox) GetState() SandboxState { - if o == nil || IsNil(o.State) { - var ret SandboxState - return ret - } - return *o.State -} - -// GetStateOk returns a tuple with the State field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetStateOk() (*SandboxState, bool) { - if o == nil || IsNil(o.State) { - return nil, false - } - return o.State, true -} - -// HasState returns a boolean if a field has been set. -func (o *Sandbox) HasState() bool { - if o != nil && !IsNil(o.State) { - return true - } - - return false -} - -// SetState gets a reference to the given SandboxState and assigns it to the State field. -func (o *Sandbox) SetState(v SandboxState) { - o.State = &v -} - -// GetDesiredState returns the DesiredState field value if set, zero value otherwise. -func (o *Sandbox) GetDesiredState() SandboxDesiredState { - if o == nil || IsNil(o.DesiredState) { - var ret SandboxDesiredState - return ret - } - return *o.DesiredState -} - -// GetDesiredStateOk returns a tuple with the DesiredState field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetDesiredStateOk() (*SandboxDesiredState, bool) { - if o == nil || IsNil(o.DesiredState) { - return nil, false - } - return o.DesiredState, true -} - -// HasDesiredState returns a boolean if a field has been set. -func (o *Sandbox) HasDesiredState() bool { - if o != nil && !IsNil(o.DesiredState) { - return true - } - - return false -} - -// SetDesiredState gets a reference to the given SandboxDesiredState and assigns it to the DesiredState field. -func (o *Sandbox) SetDesiredState(v SandboxDesiredState) { - o.DesiredState = &v -} - -// GetErrorReason returns the ErrorReason field value if set, zero value otherwise. -func (o *Sandbox) GetErrorReason() string { - if o == nil || IsNil(o.ErrorReason) { - var ret string - return ret - } - return *o.ErrorReason -} - -// GetErrorReasonOk returns a tuple with the ErrorReason field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetErrorReasonOk() (*string, bool) { - if o == nil || IsNil(o.ErrorReason) { - return nil, false - } - return o.ErrorReason, true -} - -// HasErrorReason returns a boolean if a field has been set. -func (o *Sandbox) HasErrorReason() bool { - if o != nil && !IsNil(o.ErrorReason) { - return true - } - - return false -} - -// SetErrorReason gets a reference to the given string and assigns it to the ErrorReason field. -func (o *Sandbox) SetErrorReason(v string) { - o.ErrorReason = &v -} - -// GetRecoverable returns the Recoverable field value if set, zero value otherwise. -func (o *Sandbox) GetRecoverable() bool { - if o == nil || IsNil(o.Recoverable) { - var ret bool - return ret - } - return *o.Recoverable -} - -// GetRecoverableOk returns a tuple with the Recoverable field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetRecoverableOk() (*bool, bool) { - if o == nil || IsNil(o.Recoverable) { - return nil, false - } - return o.Recoverable, true -} - -// HasRecoverable returns a boolean if a field has been set. -func (o *Sandbox) HasRecoverable() bool { - if o != nil && !IsNil(o.Recoverable) { - return true - } - - return false -} - -// SetRecoverable gets a reference to the given bool and assigns it to the Recoverable field. -func (o *Sandbox) SetRecoverable(v bool) { - o.Recoverable = &v -} - -// GetBackupState returns the BackupState field value if set, zero value otherwise. -func (o *Sandbox) GetBackupState() string { - if o == nil || IsNil(o.BackupState) { - var ret string - return ret - } - return *o.BackupState -} - -// GetBackupStateOk returns a tuple with the BackupState field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetBackupStateOk() (*string, bool) { - if o == nil || IsNil(o.BackupState) { - return nil, false - } - return o.BackupState, true -} - -// HasBackupState returns a boolean if a field has been set. -func (o *Sandbox) HasBackupState() bool { - if o != nil && !IsNil(o.BackupState) { - return true - } - - return false -} - -// SetBackupState gets a reference to the given string and assigns it to the BackupState field. -func (o *Sandbox) SetBackupState(v string) { - o.BackupState = &v -} - -// GetBackupCreatedAt returns the BackupCreatedAt field value if set, zero value otherwise. -func (o *Sandbox) GetBackupCreatedAt() string { - if o == nil || IsNil(o.BackupCreatedAt) { - var ret string - return ret - } - return *o.BackupCreatedAt -} - -// GetBackupCreatedAtOk returns a tuple with the BackupCreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetBackupCreatedAtOk() (*string, bool) { - if o == nil || IsNil(o.BackupCreatedAt) { - return nil, false - } - return o.BackupCreatedAt, true -} - -// HasBackupCreatedAt returns a boolean if a field has been set. -func (o *Sandbox) HasBackupCreatedAt() bool { - if o != nil && !IsNil(o.BackupCreatedAt) { - return true - } - - return false -} - -// SetBackupCreatedAt gets a reference to the given string and assigns it to the BackupCreatedAt field. -func (o *Sandbox) SetBackupCreatedAt(v string) { - o.BackupCreatedAt = &v -} - -// GetAutoStopInterval returns the AutoStopInterval field value if set, zero value otherwise. -func (o *Sandbox) GetAutoStopInterval() float32 { - if o == nil || IsNil(o.AutoStopInterval) { - var ret float32 - return ret - } - return *o.AutoStopInterval -} - -// GetAutoStopIntervalOk returns a tuple with the AutoStopInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetAutoStopIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoStopInterval) { - return nil, false - } - return o.AutoStopInterval, true -} - -// HasAutoStopInterval returns a boolean if a field has been set. -func (o *Sandbox) HasAutoStopInterval() bool { - if o != nil && !IsNil(o.AutoStopInterval) { - return true - } - - return false -} - -// SetAutoStopInterval gets a reference to the given float32 and assigns it to the AutoStopInterval field. -func (o *Sandbox) SetAutoStopInterval(v float32) { - o.AutoStopInterval = &v -} - -// GetAutoArchiveInterval returns the AutoArchiveInterval field value if set, zero value otherwise. -func (o *Sandbox) GetAutoArchiveInterval() float32 { - if o == nil || IsNil(o.AutoArchiveInterval) { - var ret float32 - return ret - } - return *o.AutoArchiveInterval -} - -// GetAutoArchiveIntervalOk returns a tuple with the AutoArchiveInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetAutoArchiveIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoArchiveInterval) { - return nil, false - } - return o.AutoArchiveInterval, true -} - -// HasAutoArchiveInterval returns a boolean if a field has been set. -func (o *Sandbox) HasAutoArchiveInterval() bool { - if o != nil && !IsNil(o.AutoArchiveInterval) { - return true - } - - return false -} - -// SetAutoArchiveInterval gets a reference to the given float32 and assigns it to the AutoArchiveInterval field. -func (o *Sandbox) SetAutoArchiveInterval(v float32) { - o.AutoArchiveInterval = &v -} - -// GetAutoDeleteInterval returns the AutoDeleteInterval field value if set, zero value otherwise. -func (o *Sandbox) GetAutoDeleteInterval() float32 { - if o == nil || IsNil(o.AutoDeleteInterval) { - var ret float32 - return ret - } - return *o.AutoDeleteInterval -} - -// GetAutoDeleteIntervalOk returns a tuple with the AutoDeleteInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetAutoDeleteIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoDeleteInterval) { - return nil, false - } - return o.AutoDeleteInterval, true -} - -// HasAutoDeleteInterval returns a boolean if a field has been set. -func (o *Sandbox) HasAutoDeleteInterval() bool { - if o != nil && !IsNil(o.AutoDeleteInterval) { - return true - } - - return false -} - -// SetAutoDeleteInterval gets a reference to the given float32 and assigns it to the AutoDeleteInterval field. -func (o *Sandbox) SetAutoDeleteInterval(v float32) { - o.AutoDeleteInterval = &v -} - -// GetVolumes returns the Volumes field value if set, zero value otherwise. -func (o *Sandbox) GetVolumes() []SandboxVolume { - if o == nil || IsNil(o.Volumes) { - var ret []SandboxVolume - return ret - } - return o.Volumes -} - -// GetVolumesOk returns a tuple with the Volumes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetVolumesOk() ([]SandboxVolume, bool) { - if o == nil || IsNil(o.Volumes) { - return nil, false - } - return o.Volumes, true -} - -// HasVolumes returns a boolean if a field has been set. -func (o *Sandbox) HasVolumes() bool { - if o != nil && !IsNil(o.Volumes) { - return true - } - - return false -} - -// SetVolumes gets a reference to the given []SandboxVolume and assigns it to the Volumes field. -func (o *Sandbox) SetVolumes(v []SandboxVolume) { - o.Volumes = v -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *Sandbox) GetBuildInfo() BuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret BuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetBuildInfoOk() (*BuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *Sandbox) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given BuildInfo and assigns it to the BuildInfo field. -func (o *Sandbox) SetBuildInfo(v BuildInfo) { - o.BuildInfo = &v -} - -// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. -func (o *Sandbox) GetCreatedAt() string { - if o == nil || IsNil(o.CreatedAt) { - var ret string - return ret - } - return *o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetCreatedAtOk() (*string, bool) { - if o == nil || IsNil(o.CreatedAt) { - return nil, false - } - return o.CreatedAt, true -} - -// HasCreatedAt returns a boolean if a field has been set. -func (o *Sandbox) HasCreatedAt() bool { - if o != nil && !IsNil(o.CreatedAt) { - return true - } - - return false -} - -// SetCreatedAt gets a reference to the given string and assigns it to the CreatedAt field. -func (o *Sandbox) SetCreatedAt(v string) { - o.CreatedAt = &v -} - -// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. -func (o *Sandbox) GetUpdatedAt() string { - if o == nil || IsNil(o.UpdatedAt) { - var ret string - return ret - } - return *o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetUpdatedAtOk() (*string, bool) { - if o == nil || IsNil(o.UpdatedAt) { - return nil, false - } - return o.UpdatedAt, true -} - -// HasUpdatedAt returns a boolean if a field has been set. -func (o *Sandbox) HasUpdatedAt() bool { - if o != nil && !IsNil(o.UpdatedAt) { - return true - } - - return false -} - -// SetUpdatedAt gets a reference to the given string and assigns it to the UpdatedAt field. -func (o *Sandbox) SetUpdatedAt(v string) { - o.UpdatedAt = &v -} - -// GetClass returns the Class field value if set, zero value otherwise. -// Deprecated -func (o *Sandbox) GetClass() string { - if o == nil || IsNil(o.Class) { - var ret string - return ret - } - return *o.Class -} - -// GetClassOk returns a tuple with the Class field value if set, nil otherwise -// and a boolean to check if the value has been set. -// Deprecated -func (o *Sandbox) GetClassOk() (*string, bool) { - if o == nil || IsNil(o.Class) { - return nil, false - } - return o.Class, true -} - -// HasClass returns a boolean if a field has been set. -func (o *Sandbox) HasClass() bool { - if o != nil && !IsNil(o.Class) { - return true - } - - return false -} - -// SetClass gets a reference to the given string and assigns it to the Class field. -// Deprecated -func (o *Sandbox) SetClass(v string) { - o.Class = &v -} - -// GetDaemonVersion returns the DaemonVersion field value if set, zero value otherwise. -func (o *Sandbox) GetDaemonVersion() string { - if o == nil || IsNil(o.DaemonVersion) { - var ret string - return ret - } - return *o.DaemonVersion -} - -// GetDaemonVersionOk returns a tuple with the DaemonVersion field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetDaemonVersionOk() (*string, bool) { - if o == nil || IsNil(o.DaemonVersion) { - return nil, false - } - return o.DaemonVersion, true -} - -// HasDaemonVersion returns a boolean if a field has been set. -func (o *Sandbox) HasDaemonVersion() bool { - if o != nil && !IsNil(o.DaemonVersion) { - return true - } - - return false -} - -// SetDaemonVersion gets a reference to the given string and assigns it to the DaemonVersion field. -func (o *Sandbox) SetDaemonVersion(v string) { - o.DaemonVersion = &v -} - -// GetRunnerId returns the RunnerId field value if set, zero value otherwise. -func (o *Sandbox) GetRunnerId() string { - if o == nil || IsNil(o.RunnerId) { - var ret string - return ret - } - return *o.RunnerId -} - -// GetRunnerIdOk returns a tuple with the RunnerId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Sandbox) GetRunnerIdOk() (*string, bool) { - if o == nil || IsNil(o.RunnerId) { - return nil, false - } - return o.RunnerId, true -} - -// HasRunnerId returns a boolean if a field has been set. -func (o *Sandbox) HasRunnerId() bool { - if o != nil && !IsNil(o.RunnerId) { - return true - } - - return false -} - -// SetRunnerId gets a reference to the given string and assigns it to the RunnerId field. -func (o *Sandbox) SetRunnerId(v string) { - o.RunnerId = &v -} - -// GetToolboxProxyUrl returns the ToolboxProxyUrl field value -func (o *Sandbox) GetToolboxProxyUrl() string { - if o == nil { - var ret string - return ret - } - - return o.ToolboxProxyUrl -} - -// GetToolboxProxyUrlOk returns a tuple with the ToolboxProxyUrl field value -// and a boolean to check if the value has been set. -func (o *Sandbox) GetToolboxProxyUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ToolboxProxyUrl, true -} - -// SetToolboxProxyUrl sets field value -func (o *Sandbox) SetToolboxProxyUrl(v string) { - o.ToolboxProxyUrl = v -} - -func (o Sandbox) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Sandbox) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - toSerialize["organizationId"] = o.OrganizationId - toSerialize["name"] = o.Name - if !IsNil(o.Snapshot) { - toSerialize["snapshot"] = o.Snapshot - } - toSerialize["user"] = o.User - toSerialize["env"] = o.Env - toSerialize["labels"] = o.Labels - toSerialize["public"] = o.Public - toSerialize["networkBlockAll"] = o.NetworkBlockAll - if !IsNil(o.NetworkAllowList) { - toSerialize["networkAllowList"] = o.NetworkAllowList - } - toSerialize["target"] = o.Target - toSerialize["cpu"] = o.Cpu - toSerialize["gpu"] = o.Gpu - toSerialize["memory"] = o.Memory - toSerialize["disk"] = o.Disk - if !IsNil(o.State) { - toSerialize["state"] = o.State - } - if !IsNil(o.DesiredState) { - toSerialize["desiredState"] = o.DesiredState - } - if !IsNil(o.ErrorReason) { - toSerialize["errorReason"] = o.ErrorReason - } - if !IsNil(o.Recoverable) { - toSerialize["recoverable"] = o.Recoverable - } - if !IsNil(o.BackupState) { - toSerialize["backupState"] = o.BackupState - } - if !IsNil(o.BackupCreatedAt) { - toSerialize["backupCreatedAt"] = o.BackupCreatedAt - } - if !IsNil(o.AutoStopInterval) { - toSerialize["autoStopInterval"] = o.AutoStopInterval - } - if !IsNil(o.AutoArchiveInterval) { - toSerialize["autoArchiveInterval"] = o.AutoArchiveInterval - } - if !IsNil(o.AutoDeleteInterval) { - toSerialize["autoDeleteInterval"] = o.AutoDeleteInterval - } - if !IsNil(o.Volumes) { - toSerialize["volumes"] = o.Volumes - } - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - if !IsNil(o.CreatedAt) { - toSerialize["createdAt"] = o.CreatedAt - } - if !IsNil(o.UpdatedAt) { - toSerialize["updatedAt"] = o.UpdatedAt - } - if !IsNil(o.Class) { - toSerialize["class"] = o.Class - } - if !IsNil(o.DaemonVersion) { - toSerialize["daemonVersion"] = o.DaemonVersion - } - if !IsNil(o.RunnerId) { - toSerialize["runnerId"] = o.RunnerId - } - toSerialize["toolboxProxyUrl"] = o.ToolboxProxyUrl - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Sandbox) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "organizationId", - "name", - "user", - "env", - "labels", - "public", - "networkBlockAll", - "target", - "cpu", - "gpu", - "memory", - "disk", - "toolboxProxyUrl", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSandbox := _Sandbox{} - - err = json.Unmarshal(data, &varSandbox) - - if err != nil { - return err - } - - *o = Sandbox(varSandbox) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "organizationId") - delete(additionalProperties, "name") - delete(additionalProperties, "snapshot") - delete(additionalProperties, "user") - delete(additionalProperties, "env") - delete(additionalProperties, "labels") - delete(additionalProperties, "public") - delete(additionalProperties, "networkBlockAll") - delete(additionalProperties, "networkAllowList") - delete(additionalProperties, "target") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - delete(additionalProperties, "state") - delete(additionalProperties, "desiredState") - delete(additionalProperties, "errorReason") - delete(additionalProperties, "recoverable") - delete(additionalProperties, "backupState") - delete(additionalProperties, "backupCreatedAt") - delete(additionalProperties, "autoStopInterval") - delete(additionalProperties, "autoArchiveInterval") - delete(additionalProperties, "autoDeleteInterval") - delete(additionalProperties, "volumes") - delete(additionalProperties, "buildInfo") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "updatedAt") - delete(additionalProperties, "class") - delete(additionalProperties, "daemonVersion") - delete(additionalProperties, "runnerId") - delete(additionalProperties, "toolboxProxyUrl") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSandbox struct { - value *Sandbox - isSet bool -} - -func (v NullableSandbox) Get() *Sandbox { - return v.value -} - -func (v *NullableSandbox) Set(val *Sandbox) { - v.value = val - v.isSet = true -} - -func (v NullableSandbox) IsSet() bool { - return v.isSet -} - -func (v *NullableSandbox) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandbox(val *Sandbox) *NullableSandbox { - return &NullableSandbox{value: val, isSet: true} -} - -func (v NullableSandbox) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandbox) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_class.go b/apps/api-client-go/model_sandbox_class.go deleted file mode 100644 index a4c629dda..000000000 --- a/apps/api-client-go/model_sandbox_class.go +++ /dev/null @@ -1,113 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// SandboxClass The class of the runner -type SandboxClass string - -// List of SandboxClass -const ( - SANDBOXCLASS_SMALL SandboxClass = "small" - SANDBOXCLASS_MEDIUM SandboxClass = "medium" - SANDBOXCLASS_LARGE SandboxClass = "large" -) - -// All allowed values of SandboxClass enum -var AllowedSandboxClassEnumValues = []SandboxClass{ - "small", - "medium", - "large", -} - -func (v *SandboxClass) UnmarshalJSON(src []byte) error { - var value string - err := json.Unmarshal(src, &value) - if err != nil { - return err - } - enumTypeValue := SandboxClass(value) - for _, existing := range AllowedSandboxClassEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue - return nil - } - } - - return fmt.Errorf("%+v is not a valid SandboxClass", value) -} - -// NewSandboxClassFromValue returns a pointer to a valid SandboxClass -// for the value passed as argument, or an error if the value passed is not allowed by the enum -func NewSandboxClassFromValue(v string) (*SandboxClass, error) { - ev := SandboxClass(v) - if ev.IsValid() { - return &ev, nil - } else { - return nil, fmt.Errorf("invalid value '%v' for SandboxClass: valid values are %v", v, AllowedSandboxClassEnumValues) - } -} - -// IsValid return true if the value is valid for the enum, false otherwise -func (v SandboxClass) IsValid() bool { - for _, existing := range AllowedSandboxClassEnumValues { - if existing == v { - return true - } - } - return false -} - -// Ptr returns reference to SandboxClass value -func (v SandboxClass) Ptr() *SandboxClass { - return &v -} - -type NullableSandboxClass struct { - value *SandboxClass - isSet bool -} - -func (v NullableSandboxClass) Get() *SandboxClass { - return v.value -} - -func (v *NullableSandboxClass) Set(val *SandboxClass) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxClass) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxClass) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxClass(val *SandboxClass) *NullableSandboxClass { - return &NullableSandboxClass{value: val, isSet: true} -} - -func (v NullableSandboxClass) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxClass) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_desired_state.go b/apps/api-client-go/model_sandbox_desired_state.go deleted file mode 100644 index d769693e6..000000000 --- a/apps/api-client-go/model_sandbox_desired_state.go +++ /dev/null @@ -1,117 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// SandboxDesiredState The desired state of the sandbox -type SandboxDesiredState string - -// List of SandboxDesiredState -const ( - SANDBOXDESIREDSTATE_DESTROYED SandboxDesiredState = "destroyed" - SANDBOXDESIREDSTATE_STARTED SandboxDesiredState = "started" - SANDBOXDESIREDSTATE_STOPPED SandboxDesiredState = "stopped" - SANDBOXDESIREDSTATE_RESIZED SandboxDesiredState = "resized" - SANDBOXDESIREDSTATE_ARCHIVED SandboxDesiredState = "archived" -) - -// All allowed values of SandboxDesiredState enum -var AllowedSandboxDesiredStateEnumValues = []SandboxDesiredState{ - "destroyed", - "started", - "stopped", - "resized", - "archived", -} - -func (v *SandboxDesiredState) UnmarshalJSON(src []byte) error { - var value string - err := json.Unmarshal(src, &value) - if err != nil { - return err - } - enumTypeValue := SandboxDesiredState(value) - for _, existing := range AllowedSandboxDesiredStateEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue - return nil - } - } - - return fmt.Errorf("%+v is not a valid SandboxDesiredState", value) -} - -// NewSandboxDesiredStateFromValue returns a pointer to a valid SandboxDesiredState -// for the value passed as argument, or an error if the value passed is not allowed by the enum -func NewSandboxDesiredStateFromValue(v string) (*SandboxDesiredState, error) { - ev := SandboxDesiredState(v) - if ev.IsValid() { - return &ev, nil - } else { - return nil, fmt.Errorf("invalid value '%v' for SandboxDesiredState: valid values are %v", v, AllowedSandboxDesiredStateEnumValues) - } -} - -// IsValid return true if the value is valid for the enum, false otherwise -func (v SandboxDesiredState) IsValid() bool { - for _, existing := range AllowedSandboxDesiredStateEnumValues { - if existing == v { - return true - } - } - return false -} - -// Ptr returns reference to SandboxDesiredState value -func (v SandboxDesiredState) Ptr() *SandboxDesiredState { - return &v -} - -type NullableSandboxDesiredState struct { - value *SandboxDesiredState - isSet bool -} - -func (v NullableSandboxDesiredState) Get() *SandboxDesiredState { - return v.value -} - -func (v *NullableSandboxDesiredState) Set(val *SandboxDesiredState) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxDesiredState) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxDesiredState) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxDesiredState(val *SandboxDesiredState) *NullableSandboxDesiredState { - return &NullableSandboxDesiredState{value: val, isSet: true} -} - -func (v NullableSandboxDesiredState) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxDesiredState) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_info.go b/apps/api-client-go/model_sandbox_info.go deleted file mode 100644 index 796686eb2..000000000 --- a/apps/api-client-go/model_sandbox_info.go +++ /dev/null @@ -1,242 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SandboxInfo type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SandboxInfo{} - -// SandboxInfo struct for SandboxInfo -type SandboxInfo struct { - // The creation timestamp of the project - Created string `json:"created"` - // Deprecated: The name of the sandbox - // Deprecated - Name string `json:"name"` - // Additional metadata provided by the provider - ProviderMetadata *string `json:"providerMetadata,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _SandboxInfo SandboxInfo - -// NewSandboxInfo instantiates a new SandboxInfo object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSandboxInfo(created string, name string) *SandboxInfo { - this := SandboxInfo{} - this.Created = created - this.Name = name - return &this -} - -// NewSandboxInfoWithDefaults instantiates a new SandboxInfo object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSandboxInfoWithDefaults() *SandboxInfo { - this := SandboxInfo{} - var name string = "" - this.Name = name - return &this -} - -// GetCreated returns the Created field value -func (o *SandboxInfo) GetCreated() string { - if o == nil { - var ret string - return ret - } - - return o.Created -} - -// GetCreatedOk returns a tuple with the Created field value -// and a boolean to check if the value has been set. -func (o *SandboxInfo) GetCreatedOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Created, true -} - -// SetCreated sets field value -func (o *SandboxInfo) SetCreated(v string) { - o.Created = v -} - -// GetName returns the Name field value -// Deprecated -func (o *SandboxInfo) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -// Deprecated -func (o *SandboxInfo) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -// Deprecated -func (o *SandboxInfo) SetName(v string) { - o.Name = v -} - -// GetProviderMetadata returns the ProviderMetadata field value if set, zero value otherwise. -func (o *SandboxInfo) GetProviderMetadata() string { - if o == nil || IsNil(o.ProviderMetadata) { - var ret string - return ret - } - return *o.ProviderMetadata -} - -// GetProviderMetadataOk returns a tuple with the ProviderMetadata field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SandboxInfo) GetProviderMetadataOk() (*string, bool) { - if o == nil || IsNil(o.ProviderMetadata) { - return nil, false - } - return o.ProviderMetadata, true -} - -// HasProviderMetadata returns a boolean if a field has been set. -func (o *SandboxInfo) HasProviderMetadata() bool { - if o != nil && !IsNil(o.ProviderMetadata) { - return true - } - - return false -} - -// SetProviderMetadata gets a reference to the given string and assigns it to the ProviderMetadata field. -func (o *SandboxInfo) SetProviderMetadata(v string) { - o.ProviderMetadata = &v -} - -func (o SandboxInfo) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SandboxInfo) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["created"] = o.Created - toSerialize["name"] = o.Name - if !IsNil(o.ProviderMetadata) { - toSerialize["providerMetadata"] = o.ProviderMetadata - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SandboxInfo) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "created", - "name", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSandboxInfo := _SandboxInfo{} - - err = json.Unmarshal(data, &varSandboxInfo) - - if err != nil { - return err - } - - *o = SandboxInfo(varSandboxInfo) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "created") - delete(additionalProperties, "name") - delete(additionalProperties, "providerMetadata") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSandboxInfo struct { - value *SandboxInfo - isSet bool -} - -func (v NullableSandboxInfo) Get() *SandboxInfo { - return v.value -} - -func (v *NullableSandboxInfo) Set(val *SandboxInfo) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxInfo) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxInfo) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxInfo(val *SandboxInfo) *NullableSandboxInfo { - return &NullableSandboxInfo{value: val, isSet: true} -} - -func (v NullableSandboxInfo) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxInfo) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_labels.go b/apps/api-client-go/model_sandbox_labels.go deleted file mode 100644 index c20cd25ae..000000000 --- a/apps/api-client-go/model_sandbox_labels.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SandboxLabels type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SandboxLabels{} - -// SandboxLabels struct for SandboxLabels -type SandboxLabels struct { - // Key-value pairs of labels - Labels map[string]string `json:"labels"` - AdditionalProperties map[string]interface{} -} - -type _SandboxLabels SandboxLabels - -// NewSandboxLabels instantiates a new SandboxLabels object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSandboxLabels(labels map[string]string) *SandboxLabels { - this := SandboxLabels{} - this.Labels = labels - return &this -} - -// NewSandboxLabelsWithDefaults instantiates a new SandboxLabels object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSandboxLabelsWithDefaults() *SandboxLabels { - this := SandboxLabels{} - return &this -} - -// GetLabels returns the Labels field value -func (o *SandboxLabels) GetLabels() map[string]string { - if o == nil { - var ret map[string]string - return ret - } - - return o.Labels -} - -// GetLabelsOk returns a tuple with the Labels field value -// and a boolean to check if the value has been set. -func (o *SandboxLabels) GetLabelsOk() (*map[string]string, bool) { - if o == nil { - return nil, false - } - return &o.Labels, true -} - -// SetLabels sets field value -func (o *SandboxLabels) SetLabels(v map[string]string) { - o.Labels = v -} - -func (o SandboxLabels) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SandboxLabels) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["labels"] = o.Labels - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SandboxLabels) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "labels", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSandboxLabels := _SandboxLabels{} - - err = json.Unmarshal(data, &varSandboxLabels) - - if err != nil { - return err - } - - *o = SandboxLabels(varSandboxLabels) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "labels") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSandboxLabels struct { - value *SandboxLabels - isSet bool -} - -func (v NullableSandboxLabels) Get() *SandboxLabels { - return v.value -} - -func (v *NullableSandboxLabels) Set(val *SandboxLabels) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxLabels) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxLabels) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxLabels(val *SandboxLabels) *NullableSandboxLabels { - return &NullableSandboxLabels{value: val, isSet: true} -} - -func (v NullableSandboxLabels) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxLabels) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_state.go b/apps/api-client-go/model_sandbox_state.go deleted file mode 100644 index a159e1f32..000000000 --- a/apps/api-client-go/model_sandbox_state.go +++ /dev/null @@ -1,141 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// SandboxState The state of the sandbox -type SandboxState string - -// List of SandboxState -const ( - SANDBOXSTATE_CREATING SandboxState = "creating" - SANDBOXSTATE_RESTORING SandboxState = "restoring" - SANDBOXSTATE_DESTROYED SandboxState = "destroyed" - SANDBOXSTATE_DESTROYING SandboxState = "destroying" - SANDBOXSTATE_STARTED SandboxState = "started" - SANDBOXSTATE_STOPPED SandboxState = "stopped" - SANDBOXSTATE_STARTING SandboxState = "starting" - SANDBOXSTATE_STOPPING SandboxState = "stopping" - SANDBOXSTATE_ERROR SandboxState = "error" - SANDBOXSTATE_BUILD_FAILED SandboxState = "build_failed" - SANDBOXSTATE_PENDING_BUILD SandboxState = "pending_build" - SANDBOXSTATE_BUILDING_SNAPSHOT SandboxState = "building_snapshot" - SANDBOXSTATE_UNKNOWN SandboxState = "unknown" - SANDBOXSTATE_PULLING_SNAPSHOT SandboxState = "pulling_snapshot" - SANDBOXSTATE_ARCHIVED SandboxState = "archived" - SANDBOXSTATE_ARCHIVING SandboxState = "archiving" - SANDBOXSTATE_RESIZING SandboxState = "resizing" -) - -// All allowed values of SandboxState enum -var AllowedSandboxStateEnumValues = []SandboxState{ - "creating", - "restoring", - "destroyed", - "destroying", - "started", - "stopped", - "starting", - "stopping", - "error", - "build_failed", - "pending_build", - "building_snapshot", - "unknown", - "pulling_snapshot", - "archived", - "archiving", - "resizing", -} - -func (v *SandboxState) UnmarshalJSON(src []byte) error { - var value string - err := json.Unmarshal(src, &value) - if err != nil { - return err - } - enumTypeValue := SandboxState(value) - for _, existing := range AllowedSandboxStateEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue - return nil - } - } - - return fmt.Errorf("%+v is not a valid SandboxState", value) -} - -// NewSandboxStateFromValue returns a pointer to a valid SandboxState -// for the value passed as argument, or an error if the value passed is not allowed by the enum -func NewSandboxStateFromValue(v string) (*SandboxState, error) { - ev := SandboxState(v) - if ev.IsValid() { - return &ev, nil - } else { - return nil, fmt.Errorf("invalid value '%v' for SandboxState: valid values are %v", v, AllowedSandboxStateEnumValues) - } -} - -// IsValid return true if the value is valid for the enum, false otherwise -func (v SandboxState) IsValid() bool { - for _, existing := range AllowedSandboxStateEnumValues { - if existing == v { - return true - } - } - return false -} - -// Ptr returns reference to SandboxState value -func (v SandboxState) Ptr() *SandboxState { - return &v -} - -type NullableSandboxState struct { - value *SandboxState - isSet bool -} - -func (v NullableSandboxState) Get() *SandboxState { - return v.value -} - -func (v *NullableSandboxState) Set(val *SandboxState) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxState) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxState) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxState(val *SandboxState) *NullableSandboxState { - return &NullableSandboxState{value: val, isSet: true} -} - -func (v NullableSandboxState) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxState) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_sandbox_volume.go b/apps/api-client-go/model_sandbox_volume.go deleted file mode 100644 index 874f29fb5..000000000 --- a/apps/api-client-go/model_sandbox_volume.go +++ /dev/null @@ -1,236 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SandboxVolume type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SandboxVolume{} - -// SandboxVolume struct for SandboxVolume -type SandboxVolume struct { - // The ID of the volume - VolumeId string `json:"volumeId"` - // The mount path for the volume - MountPath string `json:"mountPath"` - // Optional subpath within the volume to mount. When specified, only this S3 prefix will be accessible. When omitted, the entire volume is mounted. - Subpath *string `json:"subpath,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _SandboxVolume SandboxVolume - -// NewSandboxVolume instantiates a new SandboxVolume object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSandboxVolume(volumeId string, mountPath string) *SandboxVolume { - this := SandboxVolume{} - this.VolumeId = volumeId - this.MountPath = mountPath - return &this -} - -// NewSandboxVolumeWithDefaults instantiates a new SandboxVolume object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSandboxVolumeWithDefaults() *SandboxVolume { - this := SandboxVolume{} - return &this -} - -// GetVolumeId returns the VolumeId field value -func (o *SandboxVolume) GetVolumeId() string { - if o == nil { - var ret string - return ret - } - - return o.VolumeId -} - -// GetVolumeIdOk returns a tuple with the VolumeId field value -// and a boolean to check if the value has been set. -func (o *SandboxVolume) GetVolumeIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.VolumeId, true -} - -// SetVolumeId sets field value -func (o *SandboxVolume) SetVolumeId(v string) { - o.VolumeId = v -} - -// GetMountPath returns the MountPath field value -func (o *SandboxVolume) GetMountPath() string { - if o == nil { - var ret string - return ret - } - - return o.MountPath -} - -// GetMountPathOk returns a tuple with the MountPath field value -// and a boolean to check if the value has been set. -func (o *SandboxVolume) GetMountPathOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.MountPath, true -} - -// SetMountPath sets field value -func (o *SandboxVolume) SetMountPath(v string) { - o.MountPath = v -} - -// GetSubpath returns the Subpath field value if set, zero value otherwise. -func (o *SandboxVolume) GetSubpath() string { - if o == nil || IsNil(o.Subpath) { - var ret string - return ret - } - return *o.Subpath -} - -// GetSubpathOk returns a tuple with the Subpath field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SandboxVolume) GetSubpathOk() (*string, bool) { - if o == nil || IsNil(o.Subpath) { - return nil, false - } - return o.Subpath, true -} - -// HasSubpath returns a boolean if a field has been set. -func (o *SandboxVolume) HasSubpath() bool { - if o != nil && !IsNil(o.Subpath) { - return true - } - - return false -} - -// SetSubpath gets a reference to the given string and assigns it to the Subpath field. -func (o *SandboxVolume) SetSubpath(v string) { - o.Subpath = &v -} - -func (o SandboxVolume) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SandboxVolume) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["volumeId"] = o.VolumeId - toSerialize["mountPath"] = o.MountPath - if !IsNil(o.Subpath) { - toSerialize["subpath"] = o.Subpath - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SandboxVolume) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "volumeId", - "mountPath", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSandboxVolume := _SandboxVolume{} - - err = json.Unmarshal(data, &varSandboxVolume) - - if err != nil { - return err - } - - *o = SandboxVolume(varSandboxVolume) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "volumeId") - delete(additionalProperties, "mountPath") - delete(additionalProperties, "subpath") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSandboxVolume struct { - value *SandboxVolume - isSet bool -} - -func (v NullableSandboxVolume) Get() *SandboxVolume { - return v.value -} - -func (v *NullableSandboxVolume) Set(val *SandboxVolume) { - v.value = val - v.isSet = true -} - -func (v NullableSandboxVolume) IsSet() bool { - return v.isSet -} - -func (v *NullableSandboxVolume) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSandboxVolume(val *SandboxVolume) *NullableSandboxVolume { - return &NullableSandboxVolume{value: val, isSet: true} -} - -func (v NullableSandboxVolume) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSandboxVolume) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_screenshot_response.go b/apps/api-client-go/model_screenshot_response.go deleted file mode 100644 index 34703b7b9..000000000 --- a/apps/api-client-go/model_screenshot_response.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the ScreenshotResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &ScreenshotResponse{} - -// ScreenshotResponse struct for ScreenshotResponse -type ScreenshotResponse struct { - // Base64 encoded screenshot image data - Screenshot string `json:"screenshot"` - // The current cursor position when the screenshot was taken - CursorPosition map[string]interface{} `json:"cursorPosition,omitempty"` - // The size of the screenshot data in bytes - SizeBytes *float32 `json:"sizeBytes,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _ScreenshotResponse ScreenshotResponse - -// NewScreenshotResponse instantiates a new ScreenshotResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewScreenshotResponse(screenshot string) *ScreenshotResponse { - this := ScreenshotResponse{} - this.Screenshot = screenshot - return &this -} - -// NewScreenshotResponseWithDefaults instantiates a new ScreenshotResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewScreenshotResponseWithDefaults() *ScreenshotResponse { - this := ScreenshotResponse{} - return &this -} - -// GetScreenshot returns the Screenshot field value -func (o *ScreenshotResponse) GetScreenshot() string { - if o == nil { - var ret string - return ret - } - - return o.Screenshot -} - -// GetScreenshotOk returns a tuple with the Screenshot field value -// and a boolean to check if the value has been set. -func (o *ScreenshotResponse) GetScreenshotOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Screenshot, true -} - -// SetScreenshot sets field value -func (o *ScreenshotResponse) SetScreenshot(v string) { - o.Screenshot = v -} - -// GetCursorPosition returns the CursorPosition field value if set, zero value otherwise. -func (o *ScreenshotResponse) GetCursorPosition() map[string]interface{} { - if o == nil || IsNil(o.CursorPosition) { - var ret map[string]interface{} - return ret - } - return o.CursorPosition -} - -// GetCursorPositionOk returns a tuple with the CursorPosition field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ScreenshotResponse) GetCursorPositionOk() (map[string]interface{}, bool) { - if o == nil || IsNil(o.CursorPosition) { - return map[string]interface{}{}, false - } - return o.CursorPosition, true -} - -// HasCursorPosition returns a boolean if a field has been set. -func (o *ScreenshotResponse) HasCursorPosition() bool { - if o != nil && !IsNil(o.CursorPosition) { - return true - } - - return false -} - -// SetCursorPosition gets a reference to the given map[string]interface{} and assigns it to the CursorPosition field. -func (o *ScreenshotResponse) SetCursorPosition(v map[string]interface{}) { - o.CursorPosition = v -} - -// GetSizeBytes returns the SizeBytes field value if set, zero value otherwise. -func (o *ScreenshotResponse) GetSizeBytes() float32 { - if o == nil || IsNil(o.SizeBytes) { - var ret float32 - return ret - } - return *o.SizeBytes -} - -// GetSizeBytesOk returns a tuple with the SizeBytes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *ScreenshotResponse) GetSizeBytesOk() (*float32, bool) { - if o == nil || IsNil(o.SizeBytes) { - return nil, false - } - return o.SizeBytes, true -} - -// HasSizeBytes returns a boolean if a field has been set. -func (o *ScreenshotResponse) HasSizeBytes() bool { - if o != nil && !IsNil(o.SizeBytes) { - return true - } - - return false -} - -// SetSizeBytes gets a reference to the given float32 and assigns it to the SizeBytes field. -func (o *ScreenshotResponse) SetSizeBytes(v float32) { - o.SizeBytes = &v -} - -func (o ScreenshotResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o ScreenshotResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["screenshot"] = o.Screenshot - if !IsNil(o.CursorPosition) { - toSerialize["cursorPosition"] = o.CursorPosition - } - if !IsNil(o.SizeBytes) { - toSerialize["sizeBytes"] = o.SizeBytes - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *ScreenshotResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "screenshot", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varScreenshotResponse := _ScreenshotResponse{} - - err = json.Unmarshal(data, &varScreenshotResponse) - - if err != nil { - return err - } - - *o = ScreenshotResponse(varScreenshotResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "screenshot") - delete(additionalProperties, "cursorPosition") - delete(additionalProperties, "sizeBytes") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableScreenshotResponse struct { - value *ScreenshotResponse - isSet bool -} - -func (v NullableScreenshotResponse) Get() *ScreenshotResponse { - return v.value -} - -func (v *NullableScreenshotResponse) Set(val *ScreenshotResponse) { - v.value = val - v.isSet = true -} - -func (v NullableScreenshotResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableScreenshotResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableScreenshotResponse(val *ScreenshotResponse) *NullableScreenshotResponse { - return &NullableScreenshotResponse{value: val, isSet: true} -} - -func (v NullableScreenshotResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableScreenshotResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_search_files_response.go b/apps/api-client-go/model_search_files_response.go deleted file mode 100644 index d3d8671a7..000000000 --- a/apps/api-client-go/model_search_files_response.go +++ /dev/null @@ -1,167 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SearchFilesResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SearchFilesResponse{} - -// SearchFilesResponse struct for SearchFilesResponse -type SearchFilesResponse struct { - Files []string `json:"files"` - AdditionalProperties map[string]interface{} -} - -type _SearchFilesResponse SearchFilesResponse - -// NewSearchFilesResponse instantiates a new SearchFilesResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSearchFilesResponse(files []string) *SearchFilesResponse { - this := SearchFilesResponse{} - this.Files = files - return &this -} - -// NewSearchFilesResponseWithDefaults instantiates a new SearchFilesResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSearchFilesResponseWithDefaults() *SearchFilesResponse { - this := SearchFilesResponse{} - return &this -} - -// GetFiles returns the Files field value -func (o *SearchFilesResponse) GetFiles() []string { - if o == nil { - var ret []string - return ret - } - - return o.Files -} - -// GetFilesOk returns a tuple with the Files field value -// and a boolean to check if the value has been set. -func (o *SearchFilesResponse) GetFilesOk() ([]string, bool) { - if o == nil { - return nil, false - } - return o.Files, true -} - -// SetFiles sets field value -func (o *SearchFilesResponse) SetFiles(v []string) { - o.Files = v -} - -func (o SearchFilesResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SearchFilesResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["files"] = o.Files - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SearchFilesResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "files", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSearchFilesResponse := _SearchFilesResponse{} - - err = json.Unmarshal(data, &varSearchFilesResponse) - - if err != nil { - return err - } - - *o = SearchFilesResponse(varSearchFilesResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "files") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSearchFilesResponse struct { - value *SearchFilesResponse - isSet bool -} - -func (v NullableSearchFilesResponse) Get() *SearchFilesResponse { - return v.value -} - -func (v *NullableSearchFilesResponse) Set(val *SearchFilesResponse) { - v.value = val - v.isSet = true -} - -func (v NullableSearchFilesResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableSearchFilesResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSearchFilesResponse(val *SearchFilesResponse) *NullableSearchFilesResponse { - return &NullableSearchFilesResponse{value: val, isSet: true} -} - -func (v NullableSearchFilesResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSearchFilesResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_send_webhook_dto.go b/apps/api-client-go/model_send_webhook_dto.go index 460fb497d..e7838d5b2 100644 --- a/apps/api-client-go/model_send_webhook_dto.go +++ b/apps/api-client-go/model_send_webhook_dto.go @@ -234,3 +234,5 @@ func (v *NullableSendWebhookDto) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_session.go b/apps/api-client-go/model_session.go deleted file mode 100644 index 202d0405a..000000000 --- a/apps/api-client-go/model_session.go +++ /dev/null @@ -1,202 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Session type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Session{} - -// Session struct for Session -type Session struct { - // The ID of the session - SessionId string `json:"sessionId"` - // The list of commands executed in this session - Commands []Command `json:"commands"` - AdditionalProperties map[string]interface{} -} - -type _Session Session - -// NewSession instantiates a new Session object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSession(sessionId string, commands []Command) *Session { - this := Session{} - this.SessionId = sessionId - this.Commands = commands - return &this -} - -// NewSessionWithDefaults instantiates a new Session object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSessionWithDefaults() *Session { - this := Session{} - return &this -} - -// GetSessionId returns the SessionId field value -func (o *Session) GetSessionId() string { - if o == nil { - var ret string - return ret - } - - return o.SessionId -} - -// GetSessionIdOk returns a tuple with the SessionId field value -// and a boolean to check if the value has been set. -func (o *Session) GetSessionIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.SessionId, true -} - -// SetSessionId sets field value -func (o *Session) SetSessionId(v string) { - o.SessionId = v -} - -// GetCommands returns the Commands field value -// If the value is explicit nil, the zero value for []Command will be returned -func (o *Session) GetCommands() []Command { - if o == nil { - var ret []Command - return ret - } - - return o.Commands -} - -// GetCommandsOk returns a tuple with the Commands field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *Session) GetCommandsOk() ([]Command, bool) { - if o == nil || IsNil(o.Commands) { - return nil, false - } - return o.Commands, true -} - -// SetCommands sets field value -func (o *Session) SetCommands(v []Command) { - o.Commands = v -} - -func (o Session) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Session) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["sessionId"] = o.SessionId - if o.Commands != nil { - toSerialize["commands"] = o.Commands - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Session) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "sessionId", - "commands", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSession := _Session{} - - err = json.Unmarshal(data, &varSession) - - if err != nil { - return err - } - - *o = Session(varSession) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sessionId") - delete(additionalProperties, "commands") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSession struct { - value *Session - isSet bool -} - -func (v NullableSession) Get() *Session { - return v.value -} - -func (v *NullableSession) Set(val *Session) { - v.value = val - v.isSet = true -} - -func (v NullableSession) IsSet() bool { - return v.isSet -} - -func (v *NullableSession) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSession(val *Session) *NullableSession { - return &NullableSession{value: val, isSet: true} -} - -func (v NullableSession) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSession) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_session_execute_request.go b/apps/api-client-go/model_session_execute_request.go deleted file mode 100644 index 827833a72..000000000 --- a/apps/api-client-go/model_session_execute_request.go +++ /dev/null @@ -1,248 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SessionExecuteRequest type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SessionExecuteRequest{} - -// SessionExecuteRequest struct for SessionExecuteRequest -type SessionExecuteRequest struct { - // The command to execute - Command string `json:"command"` - // Whether to execute the command asynchronously - RunAsync *bool `json:"runAsync,omitempty"` - // Deprecated: Use runAsync instead. Whether to execute the command asynchronously - // Deprecated - Async *bool `json:"async,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _SessionExecuteRequest SessionExecuteRequest - -// NewSessionExecuteRequest instantiates a new SessionExecuteRequest object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSessionExecuteRequest(command string) *SessionExecuteRequest { - this := SessionExecuteRequest{} - this.Command = command - return &this -} - -// NewSessionExecuteRequestWithDefaults instantiates a new SessionExecuteRequest object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSessionExecuteRequestWithDefaults() *SessionExecuteRequest { - this := SessionExecuteRequest{} - return &this -} - -// GetCommand returns the Command field value -func (o *SessionExecuteRequest) GetCommand() string { - if o == nil { - var ret string - return ret - } - - return o.Command -} - -// GetCommandOk returns a tuple with the Command field value -// and a boolean to check if the value has been set. -func (o *SessionExecuteRequest) GetCommandOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Command, true -} - -// SetCommand sets field value -func (o *SessionExecuteRequest) SetCommand(v string) { - o.Command = v -} - -// GetRunAsync returns the RunAsync field value if set, zero value otherwise. -func (o *SessionExecuteRequest) GetRunAsync() bool { - if o == nil || IsNil(o.RunAsync) { - var ret bool - return ret - } - return *o.RunAsync -} - -// GetRunAsyncOk returns a tuple with the RunAsync field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SessionExecuteRequest) GetRunAsyncOk() (*bool, bool) { - if o == nil || IsNil(o.RunAsync) { - return nil, false - } - return o.RunAsync, true -} - -// HasRunAsync returns a boolean if a field has been set. -func (o *SessionExecuteRequest) HasRunAsync() bool { - if o != nil && !IsNil(o.RunAsync) { - return true - } - - return false -} - -// SetRunAsync gets a reference to the given bool and assigns it to the RunAsync field. -func (o *SessionExecuteRequest) SetRunAsync(v bool) { - o.RunAsync = &v -} - -// GetAsync returns the Async field value if set, zero value otherwise. -// Deprecated -func (o *SessionExecuteRequest) GetAsync() bool { - if o == nil || IsNil(o.Async) { - var ret bool - return ret - } - return *o.Async -} - -// GetAsyncOk returns a tuple with the Async field value if set, nil otherwise -// and a boolean to check if the value has been set. -// Deprecated -func (o *SessionExecuteRequest) GetAsyncOk() (*bool, bool) { - if o == nil || IsNil(o.Async) { - return nil, false - } - return o.Async, true -} - -// HasAsync returns a boolean if a field has been set. -func (o *SessionExecuteRequest) HasAsync() bool { - if o != nil && !IsNil(o.Async) { - return true - } - - return false -} - -// SetAsync gets a reference to the given bool and assigns it to the Async field. -// Deprecated -func (o *SessionExecuteRequest) SetAsync(v bool) { - o.Async = &v -} - -func (o SessionExecuteRequest) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SessionExecuteRequest) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["command"] = o.Command - if !IsNil(o.RunAsync) { - toSerialize["runAsync"] = o.RunAsync - } - if !IsNil(o.Async) { - toSerialize["async"] = o.Async - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SessionExecuteRequest) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "command", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSessionExecuteRequest := _SessionExecuteRequest{} - - err = json.Unmarshal(data, &varSessionExecuteRequest) - - if err != nil { - return err - } - - *o = SessionExecuteRequest(varSessionExecuteRequest) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "command") - delete(additionalProperties, "runAsync") - delete(additionalProperties, "async") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSessionExecuteRequest struct { - value *SessionExecuteRequest - isSet bool -} - -func (v NullableSessionExecuteRequest) Get() *SessionExecuteRequest { - return v.value -} - -func (v *NullableSessionExecuteRequest) Set(val *SessionExecuteRequest) { - v.value = val - v.isSet = true -} - -func (v NullableSessionExecuteRequest) IsSet() bool { - return v.isSet -} - -func (v *NullableSessionExecuteRequest) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSessionExecuteRequest(val *SessionExecuteRequest) *NullableSessionExecuteRequest { - return &NullableSessionExecuteRequest{value: val, isSet: true} -} - -func (v NullableSessionExecuteRequest) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSessionExecuteRequest) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_session_execute_response.go b/apps/api-client-go/model_session_execute_response.go deleted file mode 100644 index c2e4ee3e5..000000000 --- a/apps/api-client-go/model_session_execute_response.go +++ /dev/null @@ -1,231 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the SessionExecuteResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SessionExecuteResponse{} - -// SessionExecuteResponse struct for SessionExecuteResponse -type SessionExecuteResponse struct { - // The ID of the executed command - CmdId *string `json:"cmdId,omitempty"` - // The output of the executed command marked with stdout and stderr prefixes - Output *string `json:"output,omitempty"` - // The exit code of the executed command - ExitCode *float32 `json:"exitCode,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _SessionExecuteResponse SessionExecuteResponse - -// NewSessionExecuteResponse instantiates a new SessionExecuteResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSessionExecuteResponse() *SessionExecuteResponse { - this := SessionExecuteResponse{} - return &this -} - -// NewSessionExecuteResponseWithDefaults instantiates a new SessionExecuteResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSessionExecuteResponseWithDefaults() *SessionExecuteResponse { - this := SessionExecuteResponse{} - return &this -} - -// GetCmdId returns the CmdId field value if set, zero value otherwise. -func (o *SessionExecuteResponse) GetCmdId() string { - if o == nil || IsNil(o.CmdId) { - var ret string - return ret - } - return *o.CmdId -} - -// GetCmdIdOk returns a tuple with the CmdId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SessionExecuteResponse) GetCmdIdOk() (*string, bool) { - if o == nil || IsNil(o.CmdId) { - return nil, false - } - return o.CmdId, true -} - -// HasCmdId returns a boolean if a field has been set. -func (o *SessionExecuteResponse) HasCmdId() bool { - if o != nil && !IsNil(o.CmdId) { - return true - } - - return false -} - -// SetCmdId gets a reference to the given string and assigns it to the CmdId field. -func (o *SessionExecuteResponse) SetCmdId(v string) { - o.CmdId = &v -} - -// GetOutput returns the Output field value if set, zero value otherwise. -func (o *SessionExecuteResponse) GetOutput() string { - if o == nil || IsNil(o.Output) { - var ret string - return ret - } - return *o.Output -} - -// GetOutputOk returns a tuple with the Output field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SessionExecuteResponse) GetOutputOk() (*string, bool) { - if o == nil || IsNil(o.Output) { - return nil, false - } - return o.Output, true -} - -// HasOutput returns a boolean if a field has been set. -func (o *SessionExecuteResponse) HasOutput() bool { - if o != nil && !IsNil(o.Output) { - return true - } - - return false -} - -// SetOutput gets a reference to the given string and assigns it to the Output field. -func (o *SessionExecuteResponse) SetOutput(v string) { - o.Output = &v -} - -// GetExitCode returns the ExitCode field value if set, zero value otherwise. -func (o *SessionExecuteResponse) GetExitCode() float32 { - if o == nil || IsNil(o.ExitCode) { - var ret float32 - return ret - } - return *o.ExitCode -} - -// GetExitCodeOk returns a tuple with the ExitCode field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SessionExecuteResponse) GetExitCodeOk() (*float32, bool) { - if o == nil || IsNil(o.ExitCode) { - return nil, false - } - return o.ExitCode, true -} - -// HasExitCode returns a boolean if a field has been set. -func (o *SessionExecuteResponse) HasExitCode() bool { - if o != nil && !IsNil(o.ExitCode) { - return true - } - - return false -} - -// SetExitCode gets a reference to the given float32 and assigns it to the ExitCode field. -func (o *SessionExecuteResponse) SetExitCode(v float32) { - o.ExitCode = &v -} - -func (o SessionExecuteResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SessionExecuteResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.CmdId) { - toSerialize["cmdId"] = o.CmdId - } - if !IsNil(o.Output) { - toSerialize["output"] = o.Output - } - if !IsNil(o.ExitCode) { - toSerialize["exitCode"] = o.ExitCode - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SessionExecuteResponse) UnmarshalJSON(data []byte) (err error) { - varSessionExecuteResponse := _SessionExecuteResponse{} - - err = json.Unmarshal(data, &varSessionExecuteResponse) - - if err != nil { - return err - } - - *o = SessionExecuteResponse(varSessionExecuteResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "cmdId") - delete(additionalProperties, "output") - delete(additionalProperties, "exitCode") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSessionExecuteResponse struct { - value *SessionExecuteResponse - isSet bool -} - -func (v NullableSessionExecuteResponse) Get() *SessionExecuteResponse { - return v.value -} - -func (v *NullableSessionExecuteResponse) Set(val *SessionExecuteResponse) { - v.value = val - v.isSet = true -} - -func (v NullableSessionExecuteResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableSessionExecuteResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSessionExecuteResponse(val *SessionExecuteResponse) *NullableSessionExecuteResponse { - return &NullableSessionExecuteResponse{value: val, isSet: true} -} - -func (v NullableSessionExecuteResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSessionExecuteResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_set_snapshot_general_status_dto.go b/apps/api-client-go/model_set_snapshot_general_status_dto.go deleted file mode 100644 index 17526d405..000000000 --- a/apps/api-client-go/model_set_snapshot_general_status_dto.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SetSnapshotGeneralStatusDto type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SetSnapshotGeneralStatusDto{} - -// SetSnapshotGeneralStatusDto struct for SetSnapshotGeneralStatusDto -type SetSnapshotGeneralStatusDto struct { - // Whether the snapshot is general - General bool `json:"general"` - AdditionalProperties map[string]interface{} -} - -type _SetSnapshotGeneralStatusDto SetSnapshotGeneralStatusDto - -// NewSetSnapshotGeneralStatusDto instantiates a new SetSnapshotGeneralStatusDto object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSetSnapshotGeneralStatusDto(general bool) *SetSnapshotGeneralStatusDto { - this := SetSnapshotGeneralStatusDto{} - this.General = general - return &this -} - -// NewSetSnapshotGeneralStatusDtoWithDefaults instantiates a new SetSnapshotGeneralStatusDto object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSetSnapshotGeneralStatusDtoWithDefaults() *SetSnapshotGeneralStatusDto { - this := SetSnapshotGeneralStatusDto{} - return &this -} - -// GetGeneral returns the General field value -func (o *SetSnapshotGeneralStatusDto) GetGeneral() bool { - if o == nil { - var ret bool - return ret - } - - return o.General -} - -// GetGeneralOk returns a tuple with the General field value -// and a boolean to check if the value has been set. -func (o *SetSnapshotGeneralStatusDto) GetGeneralOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.General, true -} - -// SetGeneral sets field value -func (o *SetSnapshotGeneralStatusDto) SetGeneral(v bool) { - o.General = v -} - -func (o SetSnapshotGeneralStatusDto) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SetSnapshotGeneralStatusDto) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["general"] = o.General - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SetSnapshotGeneralStatusDto) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "general", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSetSnapshotGeneralStatusDto := _SetSnapshotGeneralStatusDto{} - - err = json.Unmarshal(data, &varSetSnapshotGeneralStatusDto) - - if err != nil { - return err - } - - *o = SetSnapshotGeneralStatusDto(varSetSnapshotGeneralStatusDto) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "general") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSetSnapshotGeneralStatusDto struct { - value *SetSnapshotGeneralStatusDto - isSet bool -} - -func (v NullableSetSnapshotGeneralStatusDto) Get() *SetSnapshotGeneralStatusDto { - return v.value -} - -func (v *NullableSetSnapshotGeneralStatusDto) Set(val *SetSnapshotGeneralStatusDto) { - v.value = val - v.isSet = true -} - -func (v NullableSetSnapshotGeneralStatusDto) IsSet() bool { - return v.isSet -} - -func (v *NullableSetSnapshotGeneralStatusDto) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSetSnapshotGeneralStatusDto(val *SetSnapshotGeneralStatusDto) *NullableSetSnapshotGeneralStatusDto { - return &NullableSetSnapshotGeneralStatusDto{value: val, isSet: true} -} - -func (v NullableSetSnapshotGeneralStatusDto) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSetSnapshotGeneralStatusDto) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_signed_port_preview_url.go b/apps/api-client-go/model_signed_port_preview_url.go index 2bef570f9..793f1d4c4 100644 --- a/apps/api-client-go/model_signed_port_preview_url.go +++ b/apps/api-client-go/model_signed_port_preview_url.go @@ -21,8 +21,8 @@ var _ MappedNullable = &SignedPortPreviewUrl{} // SignedPortPreviewUrl struct for SignedPortPreviewUrl type SignedPortPreviewUrl struct { - // ID of the sandbox - SandboxId string `json:"sandboxId"` + // ID of the box + BoxId string `json:"boxId"` // Port number of the signed preview URL Port int32 `json:"port"` // Token of the signed preview URL @@ -38,9 +38,9 @@ type _SignedPortPreviewUrl SignedPortPreviewUrl // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewSignedPortPreviewUrl(sandboxId string, port int32, token string, url string) *SignedPortPreviewUrl { +func NewSignedPortPreviewUrl(boxId string, port int32, token string, url string) *SignedPortPreviewUrl { this := SignedPortPreviewUrl{} - this.SandboxId = sandboxId + this.BoxId = boxId this.Port = port this.Token = token this.Url = url @@ -55,28 +55,28 @@ func NewSignedPortPreviewUrlWithDefaults() *SignedPortPreviewUrl { return &this } -// GetSandboxId returns the SandboxId field value -func (o *SignedPortPreviewUrl) GetSandboxId() string { +// GetBoxId returns the BoxId field value +func (o *SignedPortPreviewUrl) GetBoxId() string { if o == nil { var ret string return ret } - return o.SandboxId + return o.BoxId } -// GetSandboxIdOk returns a tuple with the SandboxId field value +// GetBoxIdOk returns a tuple with the BoxId field value // and a boolean to check if the value has been set. -func (o *SignedPortPreviewUrl) GetSandboxIdOk() (*string, bool) { +func (o *SignedPortPreviewUrl) GetBoxIdOk() (*string, bool) { if o == nil { return nil, false } - return &o.SandboxId, true + return &o.BoxId, true } -// SetSandboxId sets field value -func (o *SignedPortPreviewUrl) SetSandboxId(v string) { - o.SandboxId = v +// SetBoxId sets field value +func (o *SignedPortPreviewUrl) SetBoxId(v string) { + o.BoxId = v } // GetPort returns the Port field value @@ -161,7 +161,7 @@ func (o SignedPortPreviewUrl) MarshalJSON() ([]byte, error) { func (o SignedPortPreviewUrl) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} - toSerialize["sandboxId"] = o.SandboxId + toSerialize["boxId"] = o.BoxId toSerialize["port"] = o.Port toSerialize["token"] = o.Token toSerialize["url"] = o.Url @@ -178,7 +178,7 @@ func (o *SignedPortPreviewUrl) UnmarshalJSON(data []byte) (err error) { // by unmarshalling the object into a generic map with string keys and checking // that every required field exists as a key in the generic map. requiredProperties := []string{ - "sandboxId", + "boxId", "port", "token", "url", @@ -211,7 +211,7 @@ func (o *SignedPortPreviewUrl) UnmarshalJSON(data []byte) (err error) { additionalProperties := make(map[string]interface{}) if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "sandboxId") + delete(additionalProperties, "boxId") delete(additionalProperties, "port") delete(additionalProperties, "token") delete(additionalProperties, "url") @@ -256,3 +256,5 @@ func (v *NullableSignedPortPreviewUrl) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_snapshot_dto.go b/apps/api-client-go/model_snapshot_dto.go deleted file mode 100644 index 78924ee06..000000000 --- a/apps/api-client-go/model_snapshot_dto.go +++ /dev/null @@ -1,781 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "time" - "fmt" -) - -// checks if the SnapshotDto type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SnapshotDto{} - -// SnapshotDto struct for SnapshotDto -type SnapshotDto struct { - Id string `json:"id"` - OrganizationId *string `json:"organizationId,omitempty"` - General bool `json:"general"` - Name string `json:"name"` - ImageName *string `json:"imageName,omitempty"` - State SnapshotState `json:"state"` - Size NullableFloat32 `json:"size"` - Entrypoint []string `json:"entrypoint"` - Cpu float32 `json:"cpu"` - Gpu float32 `json:"gpu"` - Mem float32 `json:"mem"` - Disk float32 `json:"disk"` - ErrorReason NullableString `json:"errorReason"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - LastUsedAt NullableTime `json:"lastUsedAt"` - // Build information for the snapshot - BuildInfo *BuildInfo `json:"buildInfo,omitempty"` - // IDs of regions where the snapshot is available - RegionIds []string `json:"regionIds,omitempty"` - // The initial runner ID of the snapshot - InitialRunnerId *string `json:"initialRunnerId,omitempty"` - // The snapshot reference - Ref *string `json:"ref,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _SnapshotDto SnapshotDto - -// NewSnapshotDto instantiates a new SnapshotDto object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSnapshotDto(id string, general bool, name string, state SnapshotState, size NullableFloat32, entrypoint []string, cpu float32, gpu float32, mem float32, disk float32, errorReason NullableString, createdAt time.Time, updatedAt time.Time, lastUsedAt NullableTime) *SnapshotDto { - this := SnapshotDto{} - this.Id = id - this.General = general - this.Name = name - this.State = state - this.Size = size - this.Entrypoint = entrypoint - this.Cpu = cpu - this.Gpu = gpu - this.Mem = mem - this.Disk = disk - this.ErrorReason = errorReason - this.CreatedAt = createdAt - this.UpdatedAt = updatedAt - this.LastUsedAt = lastUsedAt - return &this -} - -// NewSnapshotDtoWithDefaults instantiates a new SnapshotDto object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSnapshotDtoWithDefaults() *SnapshotDto { - this := SnapshotDto{} - return &this -} - -// GetId returns the Id field value -func (o *SnapshotDto) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *SnapshotDto) SetId(v string) { - o.Id = v -} - -// GetOrganizationId returns the OrganizationId field value if set, zero value otherwise. -func (o *SnapshotDto) GetOrganizationId() string { - if o == nil || IsNil(o.OrganizationId) { - var ret string - return ret - } - return *o.OrganizationId -} - -// GetOrganizationIdOk returns a tuple with the OrganizationId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetOrganizationIdOk() (*string, bool) { - if o == nil || IsNil(o.OrganizationId) { - return nil, false - } - return o.OrganizationId, true -} - -// HasOrganizationId returns a boolean if a field has been set. -func (o *SnapshotDto) HasOrganizationId() bool { - if o != nil && !IsNil(o.OrganizationId) { - return true - } - - return false -} - -// SetOrganizationId gets a reference to the given string and assigns it to the OrganizationId field. -func (o *SnapshotDto) SetOrganizationId(v string) { - o.OrganizationId = &v -} - -// GetGeneral returns the General field value -func (o *SnapshotDto) GetGeneral() bool { - if o == nil { - var ret bool - return ret - } - - return o.General -} - -// GetGeneralOk returns a tuple with the General field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetGeneralOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.General, true -} - -// SetGeneral sets field value -func (o *SnapshotDto) SetGeneral(v bool) { - o.General = v -} - -// GetName returns the Name field value -func (o *SnapshotDto) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *SnapshotDto) SetName(v string) { - o.Name = v -} - -// GetImageName returns the ImageName field value if set, zero value otherwise. -func (o *SnapshotDto) GetImageName() string { - if o == nil || IsNil(o.ImageName) { - var ret string - return ret - } - return *o.ImageName -} - -// GetImageNameOk returns a tuple with the ImageName field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetImageNameOk() (*string, bool) { - if o == nil || IsNil(o.ImageName) { - return nil, false - } - return o.ImageName, true -} - -// HasImageName returns a boolean if a field has been set. -func (o *SnapshotDto) HasImageName() bool { - if o != nil && !IsNil(o.ImageName) { - return true - } - - return false -} - -// SetImageName gets a reference to the given string and assigns it to the ImageName field. -func (o *SnapshotDto) SetImageName(v string) { - o.ImageName = &v -} - -// GetState returns the State field value -func (o *SnapshotDto) GetState() SnapshotState { - if o == nil { - var ret SnapshotState - return ret - } - - return o.State -} - -// GetStateOk returns a tuple with the State field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetStateOk() (*SnapshotState, bool) { - if o == nil { - return nil, false - } - return &o.State, true -} - -// SetState sets field value -func (o *SnapshotDto) SetState(v SnapshotState) { - o.State = v -} - -// GetSize returns the Size field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *SnapshotDto) GetSize() float32 { - if o == nil || o.Size.Get() == nil { - var ret float32 - return ret - } - - return *o.Size.Get() -} - -// GetSizeOk returns a tuple with the Size field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *SnapshotDto) GetSizeOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.Size.Get(), o.Size.IsSet() -} - -// SetSize sets field value -func (o *SnapshotDto) SetSize(v float32) { - o.Size.Set(&v) -} - -// GetEntrypoint returns the Entrypoint field value -// If the value is explicit nil, the zero value for []string will be returned -func (o *SnapshotDto) GetEntrypoint() []string { - if o == nil { - var ret []string - return ret - } - - return o.Entrypoint -} - -// GetEntrypointOk returns a tuple with the Entrypoint field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *SnapshotDto) GetEntrypointOk() ([]string, bool) { - if o == nil || IsNil(o.Entrypoint) { - return nil, false - } - return o.Entrypoint, true -} - -// SetEntrypoint sets field value -func (o *SnapshotDto) SetEntrypoint(v []string) { - o.Entrypoint = v -} - -// GetCpu returns the Cpu field value -func (o *SnapshotDto) GetCpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetCpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Cpu, true -} - -// SetCpu sets field value -func (o *SnapshotDto) SetCpu(v float32) { - o.Cpu = v -} - -// GetGpu returns the Gpu field value -func (o *SnapshotDto) GetGpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetGpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Gpu, true -} - -// SetGpu sets field value -func (o *SnapshotDto) SetGpu(v float32) { - o.Gpu = v -} - -// GetMem returns the Mem field value -func (o *SnapshotDto) GetMem() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Mem -} - -// GetMemOk returns a tuple with the Mem field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetMemOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Mem, true -} - -// SetMem sets field value -func (o *SnapshotDto) SetMem(v float32) { - o.Mem = v -} - -// GetDisk returns the Disk field value -func (o *SnapshotDto) GetDisk() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetDiskOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Disk, true -} - -// SetDisk sets field value -func (o *SnapshotDto) SetDisk(v float32) { - o.Disk = v -} - -// GetErrorReason returns the ErrorReason field value -// If the value is explicit nil, the zero value for string will be returned -func (o *SnapshotDto) GetErrorReason() string { - if o == nil || o.ErrorReason.Get() == nil { - var ret string - return ret - } - - return *o.ErrorReason.Get() -} - -// GetErrorReasonOk returns a tuple with the ErrorReason field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *SnapshotDto) GetErrorReasonOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.ErrorReason.Get(), o.ErrorReason.IsSet() -} - -// SetErrorReason sets field value -func (o *SnapshotDto) SetErrorReason(v string) { - o.ErrorReason.Set(&v) -} - -// GetCreatedAt returns the CreatedAt field value -func (o *SnapshotDto) GetCreatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetCreatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.CreatedAt, true -} - -// SetCreatedAt sets field value -func (o *SnapshotDto) SetCreatedAt(v time.Time) { - o.CreatedAt = v -} - -// GetUpdatedAt returns the UpdatedAt field value -func (o *SnapshotDto) GetUpdatedAt() time.Time { - if o == nil { - var ret time.Time - return ret - } - - return o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetUpdatedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return &o.UpdatedAt, true -} - -// SetUpdatedAt sets field value -func (o *SnapshotDto) SetUpdatedAt(v time.Time) { - o.UpdatedAt = v -} - -// GetLastUsedAt returns the LastUsedAt field value -// If the value is explicit nil, the zero value for time.Time will be returned -func (o *SnapshotDto) GetLastUsedAt() time.Time { - if o == nil || o.LastUsedAt.Get() == nil { - var ret time.Time - return ret - } - - return *o.LastUsedAt.Get() -} - -// GetLastUsedAtOk returns a tuple with the LastUsedAt field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *SnapshotDto) GetLastUsedAtOk() (*time.Time, bool) { - if o == nil { - return nil, false - } - return o.LastUsedAt.Get(), o.LastUsedAt.IsSet() -} - -// SetLastUsedAt sets field value -func (o *SnapshotDto) SetLastUsedAt(v time.Time) { - o.LastUsedAt.Set(&v) -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *SnapshotDto) GetBuildInfo() BuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret BuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetBuildInfoOk() (*BuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *SnapshotDto) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given BuildInfo and assigns it to the BuildInfo field. -func (o *SnapshotDto) SetBuildInfo(v BuildInfo) { - o.BuildInfo = &v -} - -// GetRegionIds returns the RegionIds field value if set, zero value otherwise. -func (o *SnapshotDto) GetRegionIds() []string { - if o == nil || IsNil(o.RegionIds) { - var ret []string - return ret - } - return o.RegionIds -} - -// GetRegionIdsOk returns a tuple with the RegionIds field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetRegionIdsOk() ([]string, bool) { - if o == nil || IsNil(o.RegionIds) { - return nil, false - } - return o.RegionIds, true -} - -// HasRegionIds returns a boolean if a field has been set. -func (o *SnapshotDto) HasRegionIds() bool { - if o != nil && !IsNil(o.RegionIds) { - return true - } - - return false -} - -// SetRegionIds gets a reference to the given []string and assigns it to the RegionIds field. -func (o *SnapshotDto) SetRegionIds(v []string) { - o.RegionIds = v -} - -// GetInitialRunnerId returns the InitialRunnerId field value if set, zero value otherwise. -func (o *SnapshotDto) GetInitialRunnerId() string { - if o == nil || IsNil(o.InitialRunnerId) { - var ret string - return ret - } - return *o.InitialRunnerId -} - -// GetInitialRunnerIdOk returns a tuple with the InitialRunnerId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetInitialRunnerIdOk() (*string, bool) { - if o == nil || IsNil(o.InitialRunnerId) { - return nil, false - } - return o.InitialRunnerId, true -} - -// HasInitialRunnerId returns a boolean if a field has been set. -func (o *SnapshotDto) HasInitialRunnerId() bool { - if o != nil && !IsNil(o.InitialRunnerId) { - return true - } - - return false -} - -// SetInitialRunnerId gets a reference to the given string and assigns it to the InitialRunnerId field. -func (o *SnapshotDto) SetInitialRunnerId(v string) { - o.InitialRunnerId = &v -} - -// GetRef returns the Ref field value if set, zero value otherwise. -func (o *SnapshotDto) GetRef() string { - if o == nil || IsNil(o.Ref) { - var ret string - return ret - } - return *o.Ref -} - -// GetRefOk returns a tuple with the Ref field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *SnapshotDto) GetRefOk() (*string, bool) { - if o == nil || IsNil(o.Ref) { - return nil, false - } - return o.Ref, true -} - -// HasRef returns a boolean if a field has been set. -func (o *SnapshotDto) HasRef() bool { - if o != nil && !IsNil(o.Ref) { - return true - } - - return false -} - -// SetRef gets a reference to the given string and assigns it to the Ref field. -func (o *SnapshotDto) SetRef(v string) { - o.Ref = &v -} - -func (o SnapshotDto) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SnapshotDto) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - if !IsNil(o.OrganizationId) { - toSerialize["organizationId"] = o.OrganizationId - } - toSerialize["general"] = o.General - toSerialize["name"] = o.Name - if !IsNil(o.ImageName) { - toSerialize["imageName"] = o.ImageName - } - toSerialize["state"] = o.State - toSerialize["size"] = o.Size.Get() - if o.Entrypoint != nil { - toSerialize["entrypoint"] = o.Entrypoint - } - toSerialize["cpu"] = o.Cpu - toSerialize["gpu"] = o.Gpu - toSerialize["mem"] = o.Mem - toSerialize["disk"] = o.Disk - toSerialize["errorReason"] = o.ErrorReason.Get() - toSerialize["createdAt"] = o.CreatedAt - toSerialize["updatedAt"] = o.UpdatedAt - toSerialize["lastUsedAt"] = o.LastUsedAt.Get() - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - if !IsNil(o.RegionIds) { - toSerialize["regionIds"] = o.RegionIds - } - if !IsNil(o.InitialRunnerId) { - toSerialize["initialRunnerId"] = o.InitialRunnerId - } - if !IsNil(o.Ref) { - toSerialize["ref"] = o.Ref - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SnapshotDto) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "general", - "name", - "state", - "size", - "entrypoint", - "cpu", - "gpu", - "mem", - "disk", - "errorReason", - "createdAt", - "updatedAt", - "lastUsedAt", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSnapshotDto := _SnapshotDto{} - - err = json.Unmarshal(data, &varSnapshotDto) - - if err != nil { - return err - } - - *o = SnapshotDto(varSnapshotDto) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "organizationId") - delete(additionalProperties, "general") - delete(additionalProperties, "name") - delete(additionalProperties, "imageName") - delete(additionalProperties, "state") - delete(additionalProperties, "size") - delete(additionalProperties, "entrypoint") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "mem") - delete(additionalProperties, "disk") - delete(additionalProperties, "errorReason") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "updatedAt") - delete(additionalProperties, "lastUsedAt") - delete(additionalProperties, "buildInfo") - delete(additionalProperties, "regionIds") - delete(additionalProperties, "initialRunnerId") - delete(additionalProperties, "ref") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSnapshotDto struct { - value *SnapshotDto - isSet bool -} - -func (v NullableSnapshotDto) Get() *SnapshotDto { - return v.value -} - -func (v *NullableSnapshotDto) Set(val *SnapshotDto) { - v.value = val - v.isSet = true -} - -func (v NullableSnapshotDto) IsSet() bool { - return v.isSet -} - -func (v *NullableSnapshotDto) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSnapshotDto(val *SnapshotDto) *NullableSnapshotDto { - return &NullableSnapshotDto{value: val, isSet: true} -} - -func (v NullableSnapshotDto) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSnapshotDto) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_snapshot_manager_credentials.go b/apps/api-client-go/model_snapshot_manager_credentials.go deleted file mode 100644 index 031e987f2..000000000 --- a/apps/api-client-go/model_snapshot_manager_credentials.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the SnapshotManagerCredentials type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &SnapshotManagerCredentials{} - -// SnapshotManagerCredentials struct for SnapshotManagerCredentials -type SnapshotManagerCredentials struct { - // Snapshot Manager username for the region - Username string `json:"username"` - // Snapshot Manager password for the region - Password string `json:"password"` - AdditionalProperties map[string]interface{} -} - -type _SnapshotManagerCredentials SnapshotManagerCredentials - -// NewSnapshotManagerCredentials instantiates a new SnapshotManagerCredentials object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewSnapshotManagerCredentials(username string, password string) *SnapshotManagerCredentials { - this := SnapshotManagerCredentials{} - this.Username = username - this.Password = password - return &this -} - -// NewSnapshotManagerCredentialsWithDefaults instantiates a new SnapshotManagerCredentials object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewSnapshotManagerCredentialsWithDefaults() *SnapshotManagerCredentials { - this := SnapshotManagerCredentials{} - return &this -} - -// GetUsername returns the Username field value -func (o *SnapshotManagerCredentials) GetUsername() string { - if o == nil { - var ret string - return ret - } - - return o.Username -} - -// GetUsernameOk returns a tuple with the Username field value -// and a boolean to check if the value has been set. -func (o *SnapshotManagerCredentials) GetUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Username, true -} - -// SetUsername sets field value -func (o *SnapshotManagerCredentials) SetUsername(v string) { - o.Username = v -} - -// GetPassword returns the Password field value -func (o *SnapshotManagerCredentials) GetPassword() string { - if o == nil { - var ret string - return ret - } - - return o.Password -} - -// GetPasswordOk returns a tuple with the Password field value -// and a boolean to check if the value has been set. -func (o *SnapshotManagerCredentials) GetPasswordOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Password, true -} - -// SetPassword sets field value -func (o *SnapshotManagerCredentials) SetPassword(v string) { - o.Password = v -} - -func (o SnapshotManagerCredentials) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o SnapshotManagerCredentials) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["username"] = o.Username - toSerialize["password"] = o.Password - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *SnapshotManagerCredentials) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "username", - "password", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varSnapshotManagerCredentials := _SnapshotManagerCredentials{} - - err = json.Unmarshal(data, &varSnapshotManagerCredentials) - - if err != nil { - return err - } - - *o = SnapshotManagerCredentials(varSnapshotManagerCredentials) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "username") - delete(additionalProperties, "password") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableSnapshotManagerCredentials struct { - value *SnapshotManagerCredentials - isSet bool -} - -func (v NullableSnapshotManagerCredentials) Get() *SnapshotManagerCredentials { - return v.value -} - -func (v *NullableSnapshotManagerCredentials) Set(val *SnapshotManagerCredentials) { - v.value = val - v.isSet = true -} - -func (v NullableSnapshotManagerCredentials) IsSet() bool { - return v.isSet -} - -func (v *NullableSnapshotManagerCredentials) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSnapshotManagerCredentials(val *SnapshotManagerCredentials) *NullableSnapshotManagerCredentials { - return &NullableSnapshotManagerCredentials{value: val, isSet: true} -} - -func (v NullableSnapshotManagerCredentials) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSnapshotManagerCredentials) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_snapshot_state.go b/apps/api-client-go/model_snapshot_state.go deleted file mode 100644 index 12b7eaadf..000000000 --- a/apps/api-client-go/model_snapshot_state.go +++ /dev/null @@ -1,123 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// SnapshotState the model 'SnapshotState' -type SnapshotState string - -// List of SnapshotState -const ( - SNAPSHOTSTATE_BUILDING SnapshotState = "building" - SNAPSHOTSTATE_PENDING SnapshotState = "pending" - SNAPSHOTSTATE_PULLING SnapshotState = "pulling" - SNAPSHOTSTATE_ACTIVE SnapshotState = "active" - SNAPSHOTSTATE_INACTIVE SnapshotState = "inactive" - SNAPSHOTSTATE_ERROR SnapshotState = "error" - SNAPSHOTSTATE_BUILD_FAILED SnapshotState = "build_failed" - SNAPSHOTSTATE_REMOVING SnapshotState = "removing" -) - -// All allowed values of SnapshotState enum -var AllowedSnapshotStateEnumValues = []SnapshotState{ - "building", - "pending", - "pulling", - "active", - "inactive", - "error", - "build_failed", - "removing", -} - -func (v *SnapshotState) UnmarshalJSON(src []byte) error { - var value string - err := json.Unmarshal(src, &value) - if err != nil { - return err - } - enumTypeValue := SnapshotState(value) - for _, existing := range AllowedSnapshotStateEnumValues { - if existing == enumTypeValue { - *v = enumTypeValue - return nil - } - } - - return fmt.Errorf("%+v is not a valid SnapshotState", value) -} - -// NewSnapshotStateFromValue returns a pointer to a valid SnapshotState -// for the value passed as argument, or an error if the value passed is not allowed by the enum -func NewSnapshotStateFromValue(v string) (*SnapshotState, error) { - ev := SnapshotState(v) - if ev.IsValid() { - return &ev, nil - } else { - return nil, fmt.Errorf("invalid value '%v' for SnapshotState: valid values are %v", v, AllowedSnapshotStateEnumValues) - } -} - -// IsValid return true if the value is valid for the enum, false otherwise -func (v SnapshotState) IsValid() bool { - for _, existing := range AllowedSnapshotStateEnumValues { - if existing == v { - return true - } - } - return false -} - -// Ptr returns reference to SnapshotState value -func (v SnapshotState) Ptr() *SnapshotState { - return &v -} - -type NullableSnapshotState struct { - value *SnapshotState - isSet bool -} - -func (v NullableSnapshotState) Get() *SnapshotState { - return v.value -} - -func (v *NullableSnapshotState) Set(val *SnapshotState) { - v.value = val - v.isSet = true -} - -func (v NullableSnapshotState) IsSet() bool { - return v.isSet -} - -func (v *NullableSnapshotState) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableSnapshotState(val *SnapshotState) *NullableSnapshotState { - return &NullableSnapshotState{value: val, isSet: true} -} - -func (v NullableSnapshotState) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableSnapshotState) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_ssh_access_dto.go b/apps/api-client-go/model_ssh_access_dto.go index aafe37e5e..d06179c83 100644 --- a/apps/api-client-go/model_ssh_access_dto.go +++ b/apps/api-client-go/model_ssh_access_dto.go @@ -24,8 +24,8 @@ var _ MappedNullable = &SshAccessDto{} type SshAccessDto struct { // Unique identifier for the SSH access Id string `json:"id"` - // ID of the sandbox this SSH access is for - SandboxId string `json:"sandboxId"` + // ID of the box this SSH access is for + BoxId string `json:"boxId"` // SSH access token Token string `json:"token"` // When the SSH access expires @@ -34,7 +34,7 @@ type SshAccessDto struct { CreatedAt time.Time `json:"createdAt"` // When the SSH access was last updated UpdatedAt time.Time `json:"updatedAt"` - // SSH command to connect to the sandbox + // SSH command to connect to the box SshCommand string `json:"sshCommand"` AdditionalProperties map[string]interface{} } @@ -45,10 +45,10 @@ type _SshAccessDto SshAccessDto // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewSshAccessDto(id string, sandboxId string, token string, expiresAt time.Time, createdAt time.Time, updatedAt time.Time, sshCommand string) *SshAccessDto { +func NewSshAccessDto(id string, boxId string, token string, expiresAt time.Time, createdAt time.Time, updatedAt time.Time, sshCommand string) *SshAccessDto { this := SshAccessDto{} this.Id = id - this.SandboxId = sandboxId + this.BoxId = boxId this.Token = token this.ExpiresAt = expiresAt this.CreatedAt = createdAt @@ -89,28 +89,28 @@ func (o *SshAccessDto) SetId(v string) { o.Id = v } -// GetSandboxId returns the SandboxId field value -func (o *SshAccessDto) GetSandboxId() string { +// GetBoxId returns the BoxId field value +func (o *SshAccessDto) GetBoxId() string { if o == nil { var ret string return ret } - return o.SandboxId + return o.BoxId } -// GetSandboxIdOk returns a tuple with the SandboxId field value +// GetBoxIdOk returns a tuple with the BoxId field value // and a boolean to check if the value has been set. -func (o *SshAccessDto) GetSandboxIdOk() (*string, bool) { +func (o *SshAccessDto) GetBoxIdOk() (*string, bool) { if o == nil { return nil, false } - return &o.SandboxId, true + return &o.BoxId, true } -// SetSandboxId sets field value -func (o *SshAccessDto) SetSandboxId(v string) { - o.SandboxId = v +// SetBoxId sets field value +func (o *SshAccessDto) SetBoxId(v string) { + o.BoxId = v } // GetToken returns the Token field value @@ -244,7 +244,7 @@ func (o SshAccessDto) MarshalJSON() ([]byte, error) { func (o SshAccessDto) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} toSerialize["id"] = o.Id - toSerialize["sandboxId"] = o.SandboxId + toSerialize["boxId"] = o.BoxId toSerialize["token"] = o.Token toSerialize["expiresAt"] = o.ExpiresAt toSerialize["createdAt"] = o.CreatedAt @@ -264,7 +264,7 @@ func (o *SshAccessDto) UnmarshalJSON(data []byte) (err error) { // that every required field exists as a key in the generic map. requiredProperties := []string{ "id", - "sandboxId", + "boxId", "token", "expiresAt", "createdAt", @@ -300,7 +300,7 @@ func (o *SshAccessDto) UnmarshalJSON(data []byte) (err error) { if err = json.Unmarshal(data, &additionalProperties); err == nil { delete(additionalProperties, "id") - delete(additionalProperties, "sandboxId") + delete(additionalProperties, "boxId") delete(additionalProperties, "token") delete(additionalProperties, "expiresAt") delete(additionalProperties, "createdAt") @@ -347,3 +347,5 @@ func (v *NullableSshAccessDto) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_ssh_access_validation_dto.go b/apps/api-client-go/model_ssh_access_validation_dto.go index e82de227a..8d460ed33 100644 --- a/apps/api-client-go/model_ssh_access_validation_dto.go +++ b/apps/api-client-go/model_ssh_access_validation_dto.go @@ -23,8 +23,8 @@ var _ MappedNullable = &SshAccessValidationDto{} type SshAccessValidationDto struct { // Whether the SSH access token is valid Valid bool `json:"valid"` - // ID of the sandbox this SSH access is for - SandboxId string `json:"sandboxId"` + // ID of the box this SSH access is for + BoxId string `json:"boxId"` AdditionalProperties map[string]interface{} } @@ -34,10 +34,10 @@ type _SshAccessValidationDto SshAccessValidationDto // This constructor will assign default values to properties that have it defined, // and makes sure properties required by API are set, but the set of arguments // will change when the set of required properties is changed -func NewSshAccessValidationDto(valid bool, sandboxId string) *SshAccessValidationDto { +func NewSshAccessValidationDto(valid bool, boxId string) *SshAccessValidationDto { this := SshAccessValidationDto{} this.Valid = valid - this.SandboxId = sandboxId + this.BoxId = boxId return &this } @@ -73,28 +73,28 @@ func (o *SshAccessValidationDto) SetValid(v bool) { o.Valid = v } -// GetSandboxId returns the SandboxId field value -func (o *SshAccessValidationDto) GetSandboxId() string { +// GetBoxId returns the BoxId field value +func (o *SshAccessValidationDto) GetBoxId() string { if o == nil { var ret string return ret } - return o.SandboxId + return o.BoxId } -// GetSandboxIdOk returns a tuple with the SandboxId field value +// GetBoxIdOk returns a tuple with the BoxId field value // and a boolean to check if the value has been set. -func (o *SshAccessValidationDto) GetSandboxIdOk() (*string, bool) { +func (o *SshAccessValidationDto) GetBoxIdOk() (*string, bool) { if o == nil { return nil, false } - return &o.SandboxId, true + return &o.BoxId, true } -// SetSandboxId sets field value -func (o *SshAccessValidationDto) SetSandboxId(v string) { - o.SandboxId = v +// SetBoxId sets field value +func (o *SshAccessValidationDto) SetBoxId(v string) { + o.BoxId = v } func (o SshAccessValidationDto) MarshalJSON() ([]byte, error) { @@ -108,7 +108,7 @@ func (o SshAccessValidationDto) MarshalJSON() ([]byte, error) { func (o SshAccessValidationDto) ToMap() (map[string]interface{}, error) { toSerialize := map[string]interface{}{} toSerialize["valid"] = o.Valid - toSerialize["sandboxId"] = o.SandboxId + toSerialize["boxId"] = o.BoxId for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -123,7 +123,7 @@ func (o *SshAccessValidationDto) UnmarshalJSON(data []byte) (err error) { // that every required field exists as a key in the generic map. requiredProperties := []string{ "valid", - "sandboxId", + "boxId", } allProperties := make(map[string]interface{}) @@ -154,7 +154,7 @@ func (o *SshAccessValidationDto) UnmarshalJSON(data []byte) (err error) { if err = json.Unmarshal(data, &additionalProperties); err == nil { delete(additionalProperties, "valid") - delete(additionalProperties, "sandboxId") + delete(additionalProperties, "boxId") o.AdditionalProperties = additionalProperties } @@ -196,3 +196,5 @@ func (v *NullableSshAccessValidationDto) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_storage_access_dto.go b/apps/api-client-go/model_storage_access_dto.go index 04f123285..949e0619e 100644 --- a/apps/api-client-go/model_storage_access_dto.go +++ b/apps/api-client-go/model_storage_access_dto.go @@ -316,3 +316,5 @@ func (v *NullableStorageAccessDto) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_system_role.go b/apps/api-client-go/model_system_role.go new file mode 100644 index 000000000..21447bc97 --- /dev/null +++ b/apps/api-client-go/model_system_role.go @@ -0,0 +1,115 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" +) + +// SystemRole the model 'SystemRole' +type SystemRole string + +// List of SystemRole +const ( + SYSTEMROLE_ADMIN SystemRole = "admin" + SYSTEMROLE_USER SystemRole = "user" + SYSTEMROLE_UNKNOWN_DEFAULT_OPEN_API SystemRole = "unknown_default_open_api" +) + +// All allowed values of SystemRole enum +var AllowedSystemRoleEnumValues = []SystemRole{ + "admin", + "user", + "unknown_default_open_api", +} + +func (v *SystemRole) UnmarshalJSON(src []byte) error { + var value string + err := json.Unmarshal(src, &value) + if err != nil { + return err + } + enumTypeValue := SystemRole(value) + for _, existing := range AllowedSystemRoleEnumValues { + if existing == enumTypeValue { + *v = enumTypeValue + return nil + } + } + + *v = SYSTEMROLE_UNKNOWN_DEFAULT_OPEN_API + return nil +} + +// NewSystemRoleFromValue returns a pointer to a valid SystemRole +// for the value passed as argument, or an error if the value passed is not allowed by the enum +func NewSystemRoleFromValue(v string) (*SystemRole, error) { + ev := SystemRole(v) + if ev.IsValid() { + return &ev, nil + } else { + enumValue := SYSTEMROLE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil + } +} + +// IsValid return true if the value is valid for the enum, false otherwise +func (v SystemRole) IsValid() bool { + for _, existing := range AllowedSystemRoleEnumValues { + if existing == v { + return true + } + } + return false +} + +// Ptr returns reference to SystemRole value +func (v SystemRole) Ptr() *SystemRole { + return &v +} + +type NullableSystemRole struct { + value *SystemRole + isSet bool +} + +func (v NullableSystemRole) Get() *SystemRole { + return v.value +} + +func (v *NullableSystemRole) Set(val *SystemRole) { + v.value = val + v.isSet = true +} + +func (v NullableSystemRole) IsSet() bool { + return v.isSet +} + +func (v *NullableSystemRole) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableSystemRole(val *SystemRole) *NullableSystemRole { + return &NullableSystemRole{value: val, isSet: true} +} + +func (v NullableSystemRole) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableSystemRole) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + diff --git a/apps/api-client-go/model_toolbox_proxy_url.go b/apps/api-client-go/model_toolbox_proxy_url.go index 8057e6242..482be0160 100644 --- a/apps/api-client-go/model_toolbox_proxy_url.go +++ b/apps/api-client-go/model_toolbox_proxy_url.go @@ -21,7 +21,7 @@ var _ MappedNullable = &ToolboxProxyUrl{} // ToolboxProxyUrl struct for ToolboxProxyUrl type ToolboxProxyUrl struct { - // The toolbox proxy URL for the sandbox + // The toolbox proxy URL for the box Url string `json:"url"` AdditionalProperties map[string]interface{} } @@ -166,3 +166,5 @@ func (v *NullableToolboxProxyUrl) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_trace_span.go b/apps/api-client-go/model_trace_span.go index 5c0087f00..6c70aa85f 100644 --- a/apps/api-client-go/model_trace_span.go +++ b/apps/api-client-go/model_trace_span.go @@ -29,6 +29,10 @@ type TraceSpan struct { ParentSpanId *string `json:"parentSpanId,omitempty"` // Span name SpanName string `json:"spanName"` + // Emitting service name (e.g. boxlite-api, boxlite-runner, box-) + ServiceName *string `json:"serviceName,omitempty"` + // Resolved emitting layer: api | runner | ec2_host | box + Layer *string `json:"layer,omitempty"` // Span start timestamp Timestamp string `json:"timestamp"` // Span duration in nanoseconds @@ -171,6 +175,70 @@ func (o *TraceSpan) SetSpanName(v string) { o.SpanName = v } +// GetServiceName returns the ServiceName field value if set, zero value otherwise. +func (o *TraceSpan) GetServiceName() string { + if o == nil || IsNil(o.ServiceName) { + var ret string + return ret + } + return *o.ServiceName +} + +// GetServiceNameOk returns a tuple with the ServiceName field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TraceSpan) GetServiceNameOk() (*string, bool) { + if o == nil || IsNil(o.ServiceName) { + return nil, false + } + return o.ServiceName, true +} + +// HasServiceName returns a boolean if a field has been set. +func (o *TraceSpan) HasServiceName() bool { + if o != nil && !IsNil(o.ServiceName) { + return true + } + + return false +} + +// SetServiceName gets a reference to the given string and assigns it to the ServiceName field. +func (o *TraceSpan) SetServiceName(v string) { + o.ServiceName = &v +} + +// GetLayer returns the Layer field value if set, zero value otherwise. +func (o *TraceSpan) GetLayer() string { + if o == nil || IsNil(o.Layer) { + var ret string + return ret + } + return *o.Layer +} + +// GetLayerOk returns a tuple with the Layer field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *TraceSpan) GetLayerOk() (*string, bool) { + if o == nil || IsNil(o.Layer) { + return nil, false + } + return o.Layer, true +} + +// HasLayer returns a boolean if a field has been set. +func (o *TraceSpan) HasLayer() bool { + if o != nil && !IsNil(o.Layer) { + return true + } + + return false +} + +// SetLayer gets a reference to the given string and assigns it to the Layer field. +func (o *TraceSpan) SetLayer(v string) { + o.Layer = &v +} + // GetTimestamp returns the Timestamp field value func (o *TraceSpan) GetTimestamp() string { if o == nil { @@ -323,6 +391,12 @@ func (o TraceSpan) ToMap() (map[string]interface{}, error) { toSerialize["parentSpanId"] = o.ParentSpanId } toSerialize["spanName"] = o.SpanName + if !IsNil(o.ServiceName) { + toSerialize["serviceName"] = o.ServiceName + } + if !IsNil(o.Layer) { + toSerialize["layer"] = o.Layer + } toSerialize["timestamp"] = o.Timestamp toSerialize["durationNs"] = o.DurationNs toSerialize["spanAttributes"] = o.SpanAttributes @@ -384,6 +458,8 @@ func (o *TraceSpan) UnmarshalJSON(data []byte) (err error) { delete(additionalProperties, "spanId") delete(additionalProperties, "parentSpanId") delete(additionalProperties, "spanName") + delete(additionalProperties, "serviceName") + delete(additionalProperties, "layer") delete(additionalProperties, "timestamp") delete(additionalProperties, "durationNs") delete(additionalProperties, "spanAttributes") @@ -430,3 +506,5 @@ func (v *NullableTraceSpan) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_trace_summary.go b/apps/api-client-go/model_trace_summary.go index ade4e45db..d08f168fc 100644 --- a/apps/api-client-go/model_trace_summary.go +++ b/apps/api-client-go/model_trace_summary.go @@ -354,3 +354,5 @@ func (v *NullableTraceSummary) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_box_state_dto.go b/apps/api-client-go/model_update_box_state_dto.go new file mode 100644 index 000000000..3f077af9b --- /dev/null +++ b/apps/api-client-go/model_update_box_state_dto.go @@ -0,0 +1,246 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the UpdateBoxStateDto type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &UpdateBoxStateDto{} + +// UpdateBoxStateDto struct for UpdateBoxStateDto +type UpdateBoxStateDto struct { + // The new state for the box + State string `json:"state"` + // Optional error message when reporting an error state + ErrorReason *string `json:"errorReason,omitempty"` + // Whether the box is recoverable + Recoverable *bool `json:"recoverable,omitempty"` + AdditionalProperties map[string]interface{} +} + +type _UpdateBoxStateDto UpdateBoxStateDto + +// NewUpdateBoxStateDto instantiates a new UpdateBoxStateDto object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewUpdateBoxStateDto(state string) *UpdateBoxStateDto { + this := UpdateBoxStateDto{} + this.State = state + return &this +} + +// NewUpdateBoxStateDtoWithDefaults instantiates a new UpdateBoxStateDto object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewUpdateBoxStateDtoWithDefaults() *UpdateBoxStateDto { + this := UpdateBoxStateDto{} + return &this +} + +// GetState returns the State field value +func (o *UpdateBoxStateDto) GetState() string { + if o == nil { + var ret string + return ret + } + + return o.State +} + +// GetStateOk returns a tuple with the State field value +// and a boolean to check if the value has been set. +func (o *UpdateBoxStateDto) GetStateOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.State, true +} + +// SetState sets field value +func (o *UpdateBoxStateDto) SetState(v string) { + o.State = v +} + +// GetErrorReason returns the ErrorReason field value if set, zero value otherwise. +func (o *UpdateBoxStateDto) GetErrorReason() string { + if o == nil || IsNil(o.ErrorReason) { + var ret string + return ret + } + return *o.ErrorReason +} + +// GetErrorReasonOk returns a tuple with the ErrorReason field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *UpdateBoxStateDto) GetErrorReasonOk() (*string, bool) { + if o == nil || IsNil(o.ErrorReason) { + return nil, false + } + return o.ErrorReason, true +} + +// HasErrorReason returns a boolean if a field has been set. +func (o *UpdateBoxStateDto) HasErrorReason() bool { + if o != nil && !IsNil(o.ErrorReason) { + return true + } + + return false +} + +// SetErrorReason gets a reference to the given string and assigns it to the ErrorReason field. +func (o *UpdateBoxStateDto) SetErrorReason(v string) { + o.ErrorReason = &v +} + +// GetRecoverable returns the Recoverable field value if set, zero value otherwise. +func (o *UpdateBoxStateDto) GetRecoverable() bool { + if o == nil || IsNil(o.Recoverable) { + var ret bool + return ret + } + return *o.Recoverable +} + +// GetRecoverableOk returns a tuple with the Recoverable field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *UpdateBoxStateDto) GetRecoverableOk() (*bool, bool) { + if o == nil || IsNil(o.Recoverable) { + return nil, false + } + return o.Recoverable, true +} + +// HasRecoverable returns a boolean if a field has been set. +func (o *UpdateBoxStateDto) HasRecoverable() bool { + if o != nil && !IsNil(o.Recoverable) { + return true + } + + return false +} + +// SetRecoverable gets a reference to the given bool and assigns it to the Recoverable field. +func (o *UpdateBoxStateDto) SetRecoverable(v bool) { + o.Recoverable = &v +} + +func (o UpdateBoxStateDto) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o UpdateBoxStateDto) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["state"] = o.State + if !IsNil(o.ErrorReason) { + toSerialize["errorReason"] = o.ErrorReason + } + if !IsNil(o.Recoverable) { + toSerialize["recoverable"] = o.Recoverable + } + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *UpdateBoxStateDto) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "state", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varUpdateBoxStateDto := _UpdateBoxStateDto{} + + err = json.Unmarshal(data, &varUpdateBoxStateDto) + + if err != nil { + return err + } + + *o = UpdateBoxStateDto(varUpdateBoxStateDto) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "state") + delete(additionalProperties, "errorReason") + delete(additionalProperties, "recoverable") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableUpdateBoxStateDto struct { + value *UpdateBoxStateDto + isSet bool +} + +func (v NullableUpdateBoxStateDto) Get() *UpdateBoxStateDto { + return v.value +} + +func (v *NullableUpdateBoxStateDto) Set(val *UpdateBoxStateDto) { + v.value = val + v.isSet = true +} + +func (v NullableUpdateBoxStateDto) IsSet() bool { + return v.isSet +} + +func (v *NullableUpdateBoxStateDto) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableUpdateBoxStateDto(val *UpdateBoxStateDto) *NullableUpdateBoxStateDto { + return &NullableUpdateBoxStateDto{value: val, isSet: true} +} + +func (v NullableUpdateBoxStateDto) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableUpdateBoxStateDto) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_update_docker_registry.go b/apps/api-client-go/model_update_docker_registry.go deleted file mode 100644 index 37e47f281..000000000 --- a/apps/api-client-go/model_update_docker_registry.go +++ /dev/null @@ -1,304 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the UpdateDockerRegistry type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &UpdateDockerRegistry{} - -// UpdateDockerRegistry struct for UpdateDockerRegistry -type UpdateDockerRegistry struct { - // Registry name - Name string `json:"name"` - // Registry URL - Url string `json:"url"` - // Registry username - Username string `json:"username"` - // Registry password - Password *string `json:"password,omitempty"` - // Registry project - Project *string `json:"project,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _UpdateDockerRegistry UpdateDockerRegistry - -// NewUpdateDockerRegistry instantiates a new UpdateDockerRegistry object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUpdateDockerRegistry(name string, url string, username string) *UpdateDockerRegistry { - this := UpdateDockerRegistry{} - this.Name = name - this.Url = url - this.Username = username - return &this -} - -// NewUpdateDockerRegistryWithDefaults instantiates a new UpdateDockerRegistry object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUpdateDockerRegistryWithDefaults() *UpdateDockerRegistry { - this := UpdateDockerRegistry{} - return &this -} - -// GetName returns the Name field value -func (o *UpdateDockerRegistry) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *UpdateDockerRegistry) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *UpdateDockerRegistry) SetName(v string) { - o.Name = v -} - -// GetUrl returns the Url field value -func (o *UpdateDockerRegistry) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *UpdateDockerRegistry) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *UpdateDockerRegistry) SetUrl(v string) { - o.Url = v -} - -// GetUsername returns the Username field value -func (o *UpdateDockerRegistry) GetUsername() string { - if o == nil { - var ret string - return ret - } - - return o.Username -} - -// GetUsernameOk returns a tuple with the Username field value -// and a boolean to check if the value has been set. -func (o *UpdateDockerRegistry) GetUsernameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Username, true -} - -// SetUsername sets field value -func (o *UpdateDockerRegistry) SetUsername(v string) { - o.Username = v -} - -// GetPassword returns the Password field value if set, zero value otherwise. -func (o *UpdateDockerRegistry) GetPassword() string { - if o == nil || IsNil(o.Password) { - var ret string - return ret - } - return *o.Password -} - -// GetPasswordOk returns a tuple with the Password field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *UpdateDockerRegistry) GetPasswordOk() (*string, bool) { - if o == nil || IsNil(o.Password) { - return nil, false - } - return o.Password, true -} - -// HasPassword returns a boolean if a field has been set. -func (o *UpdateDockerRegistry) HasPassword() bool { - if o != nil && !IsNil(o.Password) { - return true - } - - return false -} - -// SetPassword gets a reference to the given string and assigns it to the Password field. -func (o *UpdateDockerRegistry) SetPassword(v string) { - o.Password = &v -} - -// GetProject returns the Project field value if set, zero value otherwise. -func (o *UpdateDockerRegistry) GetProject() string { - if o == nil || IsNil(o.Project) { - var ret string - return ret - } - return *o.Project -} - -// GetProjectOk returns a tuple with the Project field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *UpdateDockerRegistry) GetProjectOk() (*string, bool) { - if o == nil || IsNil(o.Project) { - return nil, false - } - return o.Project, true -} - -// HasProject returns a boolean if a field has been set. -func (o *UpdateDockerRegistry) HasProject() bool { - if o != nil && !IsNil(o.Project) { - return true - } - - return false -} - -// SetProject gets a reference to the given string and assigns it to the Project field. -func (o *UpdateDockerRegistry) SetProject(v string) { - o.Project = &v -} - -func (o UpdateDockerRegistry) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o UpdateDockerRegistry) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["name"] = o.Name - toSerialize["url"] = o.Url - toSerialize["username"] = o.Username - if !IsNil(o.Password) { - toSerialize["password"] = o.Password - } - if !IsNil(o.Project) { - toSerialize["project"] = o.Project - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *UpdateDockerRegistry) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "name", - "url", - "username", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varUpdateDockerRegistry := _UpdateDockerRegistry{} - - err = json.Unmarshal(data, &varUpdateDockerRegistry) - - if err != nil { - return err - } - - *o = UpdateDockerRegistry(varUpdateDockerRegistry) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "name") - delete(additionalProperties, "url") - delete(additionalProperties, "username") - delete(additionalProperties, "password") - delete(additionalProperties, "project") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUpdateDockerRegistry struct { - value *UpdateDockerRegistry - isSet bool -} - -func (v NullableUpdateDockerRegistry) Get() *UpdateDockerRegistry { - return v.value -} - -func (v *NullableUpdateDockerRegistry) Set(val *UpdateDockerRegistry) { - v.value = val - v.isSet = true -} - -func (v NullableUpdateDockerRegistry) IsSet() bool { - return v.isSet -} - -func (v *NullableUpdateDockerRegistry) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUpdateDockerRegistry(val *UpdateDockerRegistry) *NullableUpdateDockerRegistry { - return &NullableUpdateDockerRegistry{value: val, isSet: true} -} - -func (v NullableUpdateDockerRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUpdateDockerRegistry) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_update_job_status.go b/apps/api-client-go/model_update_job_status.go index c33e849d8..26c8579e5 100644 --- a/apps/api-client-go/model_update_job_status.go +++ b/apps/api-client-go/model_update_job_status.go @@ -242,3 +242,5 @@ func (v *NullableUpdateJobStatus) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_organization_default_region.go b/apps/api-client-go/model_update_organization_default_region.go index f017f1281..60fd18fad 100644 --- a/apps/api-client-go/model_update_organization_default_region.go +++ b/apps/api-client-go/model_update_organization_default_region.go @@ -166,3 +166,5 @@ func (v *NullableUpdateOrganizationDefaultRegion) UnmarshalJSON(src []byte) erro v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_organization_invitation.go b/apps/api-client-go/model_update_organization_invitation.go index 1490099ea..ec8dcd8e7 100644 --- a/apps/api-client-go/model_update_organization_invitation.go +++ b/apps/api-client-go/model_update_organization_invitation.go @@ -235,3 +235,5 @@ func (v *NullableUpdateOrganizationInvitation) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_organization_member_access.go b/apps/api-client-go/model_update_organization_member_access.go index 85412f025..fd5280dee 100644 --- a/apps/api-client-go/model_update_organization_member_access.go +++ b/apps/api-client-go/model_update_organization_member_access.go @@ -198,3 +198,5 @@ func (v *NullableUpdateOrganizationMemberAccess) UnmarshalJSON(src []byte) error v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_organization_name.go b/apps/api-client-go/model_update_organization_name.go new file mode 100644 index 000000000..c3b464c3d --- /dev/null +++ b/apps/api-client-go/model_update_organization_name.go @@ -0,0 +1,170 @@ +/* +BoxLite + +BoxLite AI platform API Docs + +API version: 1.0 +Contact: support@boxlite.com +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package apiclient + +import ( + "encoding/json" + "fmt" +) + +// checks if the UpdateOrganizationName type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &UpdateOrganizationName{} + +// UpdateOrganizationName struct for UpdateOrganizationName +type UpdateOrganizationName struct { + // The public name of the organization + Name string `json:"name"` + AdditionalProperties map[string]interface{} +} + +type _UpdateOrganizationName UpdateOrganizationName + +// NewUpdateOrganizationName instantiates a new UpdateOrganizationName object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewUpdateOrganizationName(name string) *UpdateOrganizationName { + this := UpdateOrganizationName{} + this.Name = name + return &this +} + +// NewUpdateOrganizationNameWithDefaults instantiates a new UpdateOrganizationName object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewUpdateOrganizationNameWithDefaults() *UpdateOrganizationName { + this := UpdateOrganizationName{} + return &this +} + +// GetName returns the Name field value +func (o *UpdateOrganizationName) GetName() string { + if o == nil { + var ret string + return ret + } + + return o.Name +} + +// GetNameOk returns a tuple with the Name field value +// and a boolean to check if the value has been set. +func (o *UpdateOrganizationName) GetNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Name, true +} + +// SetName sets field value +func (o *UpdateOrganizationName) SetName(v string) { + o.Name = v +} + +func (o UpdateOrganizationName) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o UpdateOrganizationName) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["name"] = o.Name + + for key, value := range o.AdditionalProperties { + toSerialize[key] = value + } + + return toSerialize, nil +} + +func (o *UpdateOrganizationName) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "name", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varUpdateOrganizationName := _UpdateOrganizationName{} + + err = json.Unmarshal(data, &varUpdateOrganizationName) + + if err != nil { + return err + } + + *o = UpdateOrganizationName(varUpdateOrganizationName) + + additionalProperties := make(map[string]interface{}) + + if err = json.Unmarshal(data, &additionalProperties); err == nil { + delete(additionalProperties, "name") + o.AdditionalProperties = additionalProperties + } + + return err +} + +type NullableUpdateOrganizationName struct { + value *UpdateOrganizationName + isSet bool +} + +func (v NullableUpdateOrganizationName) Get() *UpdateOrganizationName { + return v.value +} + +func (v *NullableUpdateOrganizationName) Set(val *UpdateOrganizationName) { + v.value = val + v.isSet = true +} + +func (v NullableUpdateOrganizationName) IsSet() bool { + return v.isSet +} + +func (v *NullableUpdateOrganizationName) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableUpdateOrganizationName(val *UpdateOrganizationName) *NullableUpdateOrganizationName { + return &NullableUpdateOrganizationName{value: val, isSet: true} +} + +func (v NullableUpdateOrganizationName) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableUpdateOrganizationName) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/apps/api-client-go/model_update_organization_quota.go b/apps/api-client-go/model_update_organization_quota.go deleted file mode 100644 index ebdd1528c..000000000 --- a/apps/api-client-go/model_update_organization_quota.go +++ /dev/null @@ -1,542 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the UpdateOrganizationQuota type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &UpdateOrganizationQuota{} - -// UpdateOrganizationQuota struct for UpdateOrganizationQuota -type UpdateOrganizationQuota struct { - MaxCpuPerSandbox NullableFloat32 `json:"maxCpuPerSandbox"` - MaxMemoryPerSandbox NullableFloat32 `json:"maxMemoryPerSandbox"` - MaxDiskPerSandbox NullableFloat32 `json:"maxDiskPerSandbox"` - SnapshotQuota NullableFloat32 `json:"snapshotQuota"` - MaxSnapshotSize NullableFloat32 `json:"maxSnapshotSize"` - VolumeQuota NullableFloat32 `json:"volumeQuota"` - AuthenticatedRateLimit NullableFloat32 `json:"authenticatedRateLimit"` - SandboxCreateRateLimit NullableFloat32 `json:"sandboxCreateRateLimit"` - SandboxLifecycleRateLimit NullableFloat32 `json:"sandboxLifecycleRateLimit"` - AuthenticatedRateLimitTtlSeconds NullableFloat32 `json:"authenticatedRateLimitTtlSeconds"` - SandboxCreateRateLimitTtlSeconds NullableFloat32 `json:"sandboxCreateRateLimitTtlSeconds"` - SandboxLifecycleRateLimitTtlSeconds NullableFloat32 `json:"sandboxLifecycleRateLimitTtlSeconds"` - // Time in minutes before an unused snapshot is deactivated - SnapshotDeactivationTimeoutMinutes NullableFloat32 `json:"snapshotDeactivationTimeoutMinutes"` - AdditionalProperties map[string]interface{} -} - -type _UpdateOrganizationQuota UpdateOrganizationQuota - -// NewUpdateOrganizationQuota instantiates a new UpdateOrganizationQuota object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUpdateOrganizationQuota(maxCpuPerSandbox NullableFloat32, maxMemoryPerSandbox NullableFloat32, maxDiskPerSandbox NullableFloat32, snapshotQuota NullableFloat32, maxSnapshotSize NullableFloat32, volumeQuota NullableFloat32, authenticatedRateLimit NullableFloat32, sandboxCreateRateLimit NullableFloat32, sandboxLifecycleRateLimit NullableFloat32, authenticatedRateLimitTtlSeconds NullableFloat32, sandboxCreateRateLimitTtlSeconds NullableFloat32, sandboxLifecycleRateLimitTtlSeconds NullableFloat32, snapshotDeactivationTimeoutMinutes NullableFloat32) *UpdateOrganizationQuota { - this := UpdateOrganizationQuota{} - this.MaxCpuPerSandbox = maxCpuPerSandbox - this.MaxMemoryPerSandbox = maxMemoryPerSandbox - this.MaxDiskPerSandbox = maxDiskPerSandbox - this.SnapshotQuota = snapshotQuota - this.MaxSnapshotSize = maxSnapshotSize - this.VolumeQuota = volumeQuota - this.AuthenticatedRateLimit = authenticatedRateLimit - this.SandboxCreateRateLimit = sandboxCreateRateLimit - this.SandboxLifecycleRateLimit = sandboxLifecycleRateLimit - this.AuthenticatedRateLimitTtlSeconds = authenticatedRateLimitTtlSeconds - this.SandboxCreateRateLimitTtlSeconds = sandboxCreateRateLimitTtlSeconds - this.SandboxLifecycleRateLimitTtlSeconds = sandboxLifecycleRateLimitTtlSeconds - this.SnapshotDeactivationTimeoutMinutes = snapshotDeactivationTimeoutMinutes - return &this -} - -// NewUpdateOrganizationQuotaWithDefaults instantiates a new UpdateOrganizationQuota object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUpdateOrganizationQuotaWithDefaults() *UpdateOrganizationQuota { - this := UpdateOrganizationQuota{} - return &this -} - -// GetMaxCpuPerSandbox returns the MaxCpuPerSandbox field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetMaxCpuPerSandbox() float32 { - if o == nil || o.MaxCpuPerSandbox.Get() == nil { - var ret float32 - return ret - } - - return *o.MaxCpuPerSandbox.Get() -} - -// GetMaxCpuPerSandboxOk returns a tuple with the MaxCpuPerSandbox field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetMaxCpuPerSandboxOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.MaxCpuPerSandbox.Get(), o.MaxCpuPerSandbox.IsSet() -} - -// SetMaxCpuPerSandbox sets field value -func (o *UpdateOrganizationQuota) SetMaxCpuPerSandbox(v float32) { - o.MaxCpuPerSandbox.Set(&v) -} - -// GetMaxMemoryPerSandbox returns the MaxMemoryPerSandbox field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetMaxMemoryPerSandbox() float32 { - if o == nil || o.MaxMemoryPerSandbox.Get() == nil { - var ret float32 - return ret - } - - return *o.MaxMemoryPerSandbox.Get() -} - -// GetMaxMemoryPerSandboxOk returns a tuple with the MaxMemoryPerSandbox field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetMaxMemoryPerSandboxOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.MaxMemoryPerSandbox.Get(), o.MaxMemoryPerSandbox.IsSet() -} - -// SetMaxMemoryPerSandbox sets field value -func (o *UpdateOrganizationQuota) SetMaxMemoryPerSandbox(v float32) { - o.MaxMemoryPerSandbox.Set(&v) -} - -// GetMaxDiskPerSandbox returns the MaxDiskPerSandbox field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetMaxDiskPerSandbox() float32 { - if o == nil || o.MaxDiskPerSandbox.Get() == nil { - var ret float32 - return ret - } - - return *o.MaxDiskPerSandbox.Get() -} - -// GetMaxDiskPerSandboxOk returns a tuple with the MaxDiskPerSandbox field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetMaxDiskPerSandboxOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.MaxDiskPerSandbox.Get(), o.MaxDiskPerSandbox.IsSet() -} - -// SetMaxDiskPerSandbox sets field value -func (o *UpdateOrganizationQuota) SetMaxDiskPerSandbox(v float32) { - o.MaxDiskPerSandbox.Set(&v) -} - -// GetSnapshotQuota returns the SnapshotQuota field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSnapshotQuota() float32 { - if o == nil || o.SnapshotQuota.Get() == nil { - var ret float32 - return ret - } - - return *o.SnapshotQuota.Get() -} - -// GetSnapshotQuotaOk returns a tuple with the SnapshotQuota field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSnapshotQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SnapshotQuota.Get(), o.SnapshotQuota.IsSet() -} - -// SetSnapshotQuota sets field value -func (o *UpdateOrganizationQuota) SetSnapshotQuota(v float32) { - o.SnapshotQuota.Set(&v) -} - -// GetMaxSnapshotSize returns the MaxSnapshotSize field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetMaxSnapshotSize() float32 { - if o == nil || o.MaxSnapshotSize.Get() == nil { - var ret float32 - return ret - } - - return *o.MaxSnapshotSize.Get() -} - -// GetMaxSnapshotSizeOk returns a tuple with the MaxSnapshotSize field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetMaxSnapshotSizeOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.MaxSnapshotSize.Get(), o.MaxSnapshotSize.IsSet() -} - -// SetMaxSnapshotSize sets field value -func (o *UpdateOrganizationQuota) SetMaxSnapshotSize(v float32) { - o.MaxSnapshotSize.Set(&v) -} - -// GetVolumeQuota returns the VolumeQuota field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetVolumeQuota() float32 { - if o == nil || o.VolumeQuota.Get() == nil { - var ret float32 - return ret - } - - return *o.VolumeQuota.Get() -} - -// GetVolumeQuotaOk returns a tuple with the VolumeQuota field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetVolumeQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.VolumeQuota.Get(), o.VolumeQuota.IsSet() -} - -// SetVolumeQuota sets field value -func (o *UpdateOrganizationQuota) SetVolumeQuota(v float32) { - o.VolumeQuota.Set(&v) -} - -// GetAuthenticatedRateLimit returns the AuthenticatedRateLimit field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetAuthenticatedRateLimit() float32 { - if o == nil || o.AuthenticatedRateLimit.Get() == nil { - var ret float32 - return ret - } - - return *o.AuthenticatedRateLimit.Get() -} - -// GetAuthenticatedRateLimitOk returns a tuple with the AuthenticatedRateLimit field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetAuthenticatedRateLimitOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.AuthenticatedRateLimit.Get(), o.AuthenticatedRateLimit.IsSet() -} - -// SetAuthenticatedRateLimit sets field value -func (o *UpdateOrganizationQuota) SetAuthenticatedRateLimit(v float32) { - o.AuthenticatedRateLimit.Set(&v) -} - -// GetSandboxCreateRateLimit returns the SandboxCreateRateLimit field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSandboxCreateRateLimit() float32 { - if o == nil || o.SandboxCreateRateLimit.Get() == nil { - var ret float32 - return ret - } - - return *o.SandboxCreateRateLimit.Get() -} - -// GetSandboxCreateRateLimitOk returns a tuple with the SandboxCreateRateLimit field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSandboxCreateRateLimitOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SandboxCreateRateLimit.Get(), o.SandboxCreateRateLimit.IsSet() -} - -// SetSandboxCreateRateLimit sets field value -func (o *UpdateOrganizationQuota) SetSandboxCreateRateLimit(v float32) { - o.SandboxCreateRateLimit.Set(&v) -} - -// GetSandboxLifecycleRateLimit returns the SandboxLifecycleRateLimit field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSandboxLifecycleRateLimit() float32 { - if o == nil || o.SandboxLifecycleRateLimit.Get() == nil { - var ret float32 - return ret - } - - return *o.SandboxLifecycleRateLimit.Get() -} - -// GetSandboxLifecycleRateLimitOk returns a tuple with the SandboxLifecycleRateLimit field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSandboxLifecycleRateLimitOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SandboxLifecycleRateLimit.Get(), o.SandboxLifecycleRateLimit.IsSet() -} - -// SetSandboxLifecycleRateLimit sets field value -func (o *UpdateOrganizationQuota) SetSandboxLifecycleRateLimit(v float32) { - o.SandboxLifecycleRateLimit.Set(&v) -} - -// GetAuthenticatedRateLimitTtlSeconds returns the AuthenticatedRateLimitTtlSeconds field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetAuthenticatedRateLimitTtlSeconds() float32 { - if o == nil || o.AuthenticatedRateLimitTtlSeconds.Get() == nil { - var ret float32 - return ret - } - - return *o.AuthenticatedRateLimitTtlSeconds.Get() -} - -// GetAuthenticatedRateLimitTtlSecondsOk returns a tuple with the AuthenticatedRateLimitTtlSeconds field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetAuthenticatedRateLimitTtlSecondsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.AuthenticatedRateLimitTtlSeconds.Get(), o.AuthenticatedRateLimitTtlSeconds.IsSet() -} - -// SetAuthenticatedRateLimitTtlSeconds sets field value -func (o *UpdateOrganizationQuota) SetAuthenticatedRateLimitTtlSeconds(v float32) { - o.AuthenticatedRateLimitTtlSeconds.Set(&v) -} - -// GetSandboxCreateRateLimitTtlSeconds returns the SandboxCreateRateLimitTtlSeconds field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSandboxCreateRateLimitTtlSeconds() float32 { - if o == nil || o.SandboxCreateRateLimitTtlSeconds.Get() == nil { - var ret float32 - return ret - } - - return *o.SandboxCreateRateLimitTtlSeconds.Get() -} - -// GetSandboxCreateRateLimitTtlSecondsOk returns a tuple with the SandboxCreateRateLimitTtlSeconds field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSandboxCreateRateLimitTtlSecondsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SandboxCreateRateLimitTtlSeconds.Get(), o.SandboxCreateRateLimitTtlSeconds.IsSet() -} - -// SetSandboxCreateRateLimitTtlSeconds sets field value -func (o *UpdateOrganizationQuota) SetSandboxCreateRateLimitTtlSeconds(v float32) { - o.SandboxCreateRateLimitTtlSeconds.Set(&v) -} - -// GetSandboxLifecycleRateLimitTtlSeconds returns the SandboxLifecycleRateLimitTtlSeconds field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSandboxLifecycleRateLimitTtlSeconds() float32 { - if o == nil || o.SandboxLifecycleRateLimitTtlSeconds.Get() == nil { - var ret float32 - return ret - } - - return *o.SandboxLifecycleRateLimitTtlSeconds.Get() -} - -// GetSandboxLifecycleRateLimitTtlSecondsOk returns a tuple with the SandboxLifecycleRateLimitTtlSeconds field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSandboxLifecycleRateLimitTtlSecondsOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SandboxLifecycleRateLimitTtlSeconds.Get(), o.SandboxLifecycleRateLimitTtlSeconds.IsSet() -} - -// SetSandboxLifecycleRateLimitTtlSeconds sets field value -func (o *UpdateOrganizationQuota) SetSandboxLifecycleRateLimitTtlSeconds(v float32) { - o.SandboxLifecycleRateLimitTtlSeconds.Set(&v) -} - -// GetSnapshotDeactivationTimeoutMinutes returns the SnapshotDeactivationTimeoutMinutes field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationQuota) GetSnapshotDeactivationTimeoutMinutes() float32 { - if o == nil || o.SnapshotDeactivationTimeoutMinutes.Get() == nil { - var ret float32 - return ret - } - - return *o.SnapshotDeactivationTimeoutMinutes.Get() -} - -// GetSnapshotDeactivationTimeoutMinutesOk returns a tuple with the SnapshotDeactivationTimeoutMinutes field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationQuota) GetSnapshotDeactivationTimeoutMinutesOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.SnapshotDeactivationTimeoutMinutes.Get(), o.SnapshotDeactivationTimeoutMinutes.IsSet() -} - -// SetSnapshotDeactivationTimeoutMinutes sets field value -func (o *UpdateOrganizationQuota) SetSnapshotDeactivationTimeoutMinutes(v float32) { - o.SnapshotDeactivationTimeoutMinutes.Set(&v) -} - -func (o UpdateOrganizationQuota) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o UpdateOrganizationQuota) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["maxCpuPerSandbox"] = o.MaxCpuPerSandbox.Get() - toSerialize["maxMemoryPerSandbox"] = o.MaxMemoryPerSandbox.Get() - toSerialize["maxDiskPerSandbox"] = o.MaxDiskPerSandbox.Get() - toSerialize["snapshotQuota"] = o.SnapshotQuota.Get() - toSerialize["maxSnapshotSize"] = o.MaxSnapshotSize.Get() - toSerialize["volumeQuota"] = o.VolumeQuota.Get() - toSerialize["authenticatedRateLimit"] = o.AuthenticatedRateLimit.Get() - toSerialize["sandboxCreateRateLimit"] = o.SandboxCreateRateLimit.Get() - toSerialize["sandboxLifecycleRateLimit"] = o.SandboxLifecycleRateLimit.Get() - toSerialize["authenticatedRateLimitTtlSeconds"] = o.AuthenticatedRateLimitTtlSeconds.Get() - toSerialize["sandboxCreateRateLimitTtlSeconds"] = o.SandboxCreateRateLimitTtlSeconds.Get() - toSerialize["sandboxLifecycleRateLimitTtlSeconds"] = o.SandboxLifecycleRateLimitTtlSeconds.Get() - toSerialize["snapshotDeactivationTimeoutMinutes"] = o.SnapshotDeactivationTimeoutMinutes.Get() - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *UpdateOrganizationQuota) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "maxCpuPerSandbox", - "maxMemoryPerSandbox", - "maxDiskPerSandbox", - "snapshotQuota", - "maxSnapshotSize", - "volumeQuota", - "authenticatedRateLimit", - "sandboxCreateRateLimit", - "sandboxLifecycleRateLimit", - "authenticatedRateLimitTtlSeconds", - "sandboxCreateRateLimitTtlSeconds", - "sandboxLifecycleRateLimitTtlSeconds", - "snapshotDeactivationTimeoutMinutes", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varUpdateOrganizationQuota := _UpdateOrganizationQuota{} - - err = json.Unmarshal(data, &varUpdateOrganizationQuota) - - if err != nil { - return err - } - - *o = UpdateOrganizationQuota(varUpdateOrganizationQuota) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "maxCpuPerSandbox") - delete(additionalProperties, "maxMemoryPerSandbox") - delete(additionalProperties, "maxDiskPerSandbox") - delete(additionalProperties, "snapshotQuota") - delete(additionalProperties, "maxSnapshotSize") - delete(additionalProperties, "volumeQuota") - delete(additionalProperties, "authenticatedRateLimit") - delete(additionalProperties, "sandboxCreateRateLimit") - delete(additionalProperties, "sandboxLifecycleRateLimit") - delete(additionalProperties, "authenticatedRateLimitTtlSeconds") - delete(additionalProperties, "sandboxCreateRateLimitTtlSeconds") - delete(additionalProperties, "sandboxLifecycleRateLimitTtlSeconds") - delete(additionalProperties, "snapshotDeactivationTimeoutMinutes") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUpdateOrganizationQuota struct { - value *UpdateOrganizationQuota - isSet bool -} - -func (v NullableUpdateOrganizationQuota) Get() *UpdateOrganizationQuota { - return v.value -} - -func (v *NullableUpdateOrganizationQuota) Set(val *UpdateOrganizationQuota) { - v.value = val - v.isSet = true -} - -func (v NullableUpdateOrganizationQuota) IsSet() bool { - return v.isSet -} - -func (v *NullableUpdateOrganizationQuota) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUpdateOrganizationQuota(val *UpdateOrganizationQuota) *NullableUpdateOrganizationQuota { - return &NullableUpdateOrganizationQuota{value: val, isSet: true} -} - -func (v NullableUpdateOrganizationQuota) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUpdateOrganizationQuota) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_update_organization_region_quota.go b/apps/api-client-go/model_update_organization_region_quota.go deleted file mode 100644 index 93d935009..000000000 --- a/apps/api-client-go/model_update_organization_region_quota.go +++ /dev/null @@ -1,231 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the UpdateOrganizationRegionQuota type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &UpdateOrganizationRegionQuota{} - -// UpdateOrganizationRegionQuota struct for UpdateOrganizationRegionQuota -type UpdateOrganizationRegionQuota struct { - TotalCpuQuota NullableFloat32 `json:"totalCpuQuota"` - TotalMemoryQuota NullableFloat32 `json:"totalMemoryQuota"` - TotalDiskQuota NullableFloat32 `json:"totalDiskQuota"` - AdditionalProperties map[string]interface{} -} - -type _UpdateOrganizationRegionQuota UpdateOrganizationRegionQuota - -// NewUpdateOrganizationRegionQuota instantiates a new UpdateOrganizationRegionQuota object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUpdateOrganizationRegionQuota(totalCpuQuota NullableFloat32, totalMemoryQuota NullableFloat32, totalDiskQuota NullableFloat32) *UpdateOrganizationRegionQuota { - this := UpdateOrganizationRegionQuota{} - this.TotalCpuQuota = totalCpuQuota - this.TotalMemoryQuota = totalMemoryQuota - this.TotalDiskQuota = totalDiskQuota - return &this -} - -// NewUpdateOrganizationRegionQuotaWithDefaults instantiates a new UpdateOrganizationRegionQuota object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUpdateOrganizationRegionQuotaWithDefaults() *UpdateOrganizationRegionQuota { - this := UpdateOrganizationRegionQuota{} - return &this -} - -// GetTotalCpuQuota returns the TotalCpuQuota field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalCpuQuota() float32 { - if o == nil || o.TotalCpuQuota.Get() == nil { - var ret float32 - return ret - } - - return *o.TotalCpuQuota.Get() -} - -// GetTotalCpuQuotaOk returns a tuple with the TotalCpuQuota field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalCpuQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.TotalCpuQuota.Get(), o.TotalCpuQuota.IsSet() -} - -// SetTotalCpuQuota sets field value -func (o *UpdateOrganizationRegionQuota) SetTotalCpuQuota(v float32) { - o.TotalCpuQuota.Set(&v) -} - -// GetTotalMemoryQuota returns the TotalMemoryQuota field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalMemoryQuota() float32 { - if o == nil || o.TotalMemoryQuota.Get() == nil { - var ret float32 - return ret - } - - return *o.TotalMemoryQuota.Get() -} - -// GetTotalMemoryQuotaOk returns a tuple with the TotalMemoryQuota field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalMemoryQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.TotalMemoryQuota.Get(), o.TotalMemoryQuota.IsSet() -} - -// SetTotalMemoryQuota sets field value -func (o *UpdateOrganizationRegionQuota) SetTotalMemoryQuota(v float32) { - o.TotalMemoryQuota.Set(&v) -} - -// GetTotalDiskQuota returns the TotalDiskQuota field value -// If the value is explicit nil, the zero value for float32 will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalDiskQuota() float32 { - if o == nil || o.TotalDiskQuota.Get() == nil { - var ret float32 - return ret - } - - return *o.TotalDiskQuota.Get() -} - -// GetTotalDiskQuotaOk returns a tuple with the TotalDiskQuota field value -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateOrganizationRegionQuota) GetTotalDiskQuotaOk() (*float32, bool) { - if o == nil { - return nil, false - } - return o.TotalDiskQuota.Get(), o.TotalDiskQuota.IsSet() -} - -// SetTotalDiskQuota sets field value -func (o *UpdateOrganizationRegionQuota) SetTotalDiskQuota(v float32) { - o.TotalDiskQuota.Set(&v) -} - -func (o UpdateOrganizationRegionQuota) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o UpdateOrganizationRegionQuota) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["totalCpuQuota"] = o.TotalCpuQuota.Get() - toSerialize["totalMemoryQuota"] = o.TotalMemoryQuota.Get() - toSerialize["totalDiskQuota"] = o.TotalDiskQuota.Get() - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *UpdateOrganizationRegionQuota) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "totalCpuQuota", - "totalMemoryQuota", - "totalDiskQuota", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varUpdateOrganizationRegionQuota := _UpdateOrganizationRegionQuota{} - - err = json.Unmarshal(data, &varUpdateOrganizationRegionQuota) - - if err != nil { - return err - } - - *o = UpdateOrganizationRegionQuota(varUpdateOrganizationRegionQuota) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "totalCpuQuota") - delete(additionalProperties, "totalMemoryQuota") - delete(additionalProperties, "totalDiskQuota") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUpdateOrganizationRegionQuota struct { - value *UpdateOrganizationRegionQuota - isSet bool -} - -func (v NullableUpdateOrganizationRegionQuota) Get() *UpdateOrganizationRegionQuota { - return v.value -} - -func (v *NullableUpdateOrganizationRegionQuota) Set(val *UpdateOrganizationRegionQuota) { - v.value = val - v.isSet = true -} - -func (v NullableUpdateOrganizationRegionQuota) IsSet() bool { - return v.isSet -} - -func (v *NullableUpdateOrganizationRegionQuota) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUpdateOrganizationRegionQuota(val *UpdateOrganizationRegionQuota) *NullableUpdateOrganizationRegionQuota { - return &NullableUpdateOrganizationRegionQuota{value: val, isSet: true} -} - -func (v NullableUpdateOrganizationRegionQuota) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUpdateOrganizationRegionQuota) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_update_organization_role.go b/apps/api-client-go/model_update_organization_role.go index 70456e03e..b313e6c18 100644 --- a/apps/api-client-go/model_update_organization_role.go +++ b/apps/api-client-go/model_update_organization_role.go @@ -226,3 +226,5 @@ func (v *NullableUpdateOrganizationRole) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_region.go b/apps/api-client-go/model_update_region.go index 7e8a87c5e..dc9f25ac0 100644 --- a/apps/api-client-go/model_update_region.go +++ b/apps/api-client-go/model_update_region.go @@ -24,8 +24,6 @@ type UpdateRegion struct { ProxyUrl NullableString `json:"proxyUrl,omitempty"` // SSH Gateway URL for the region SshGatewayUrl NullableString `json:"sshGatewayUrl,omitempty"` - // Snapshot Manager URL for the region - SnapshotManagerUrl NullableString `json:"snapshotManagerUrl,omitempty"` AdditionalProperties map[string]interface{} } @@ -132,48 +130,6 @@ func (o *UpdateRegion) UnsetSshGatewayUrl() { o.SshGatewayUrl.Unset() } -// GetSnapshotManagerUrl returns the SnapshotManagerUrl field value if set, zero value otherwise (both if not set or set to explicit null). -func (o *UpdateRegion) GetSnapshotManagerUrl() string { - if o == nil || IsNil(o.SnapshotManagerUrl.Get()) { - var ret string - return ret - } - return *o.SnapshotManagerUrl.Get() -} - -// GetSnapshotManagerUrlOk returns a tuple with the SnapshotManagerUrl field value if set, nil otherwise -// and a boolean to check if the value has been set. -// NOTE: If the value is an explicit nil, `nil, true` will be returned -func (o *UpdateRegion) GetSnapshotManagerUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return o.SnapshotManagerUrl.Get(), o.SnapshotManagerUrl.IsSet() -} - -// HasSnapshotManagerUrl returns a boolean if a field has been set. -func (o *UpdateRegion) HasSnapshotManagerUrl() bool { - if o != nil && o.SnapshotManagerUrl.IsSet() { - return true - } - - return false -} - -// SetSnapshotManagerUrl gets a reference to the given NullableString and assigns it to the SnapshotManagerUrl field. -func (o *UpdateRegion) SetSnapshotManagerUrl(v string) { - o.SnapshotManagerUrl.Set(&v) -} -// SetSnapshotManagerUrlNil sets the value for SnapshotManagerUrl to be an explicit nil -func (o *UpdateRegion) SetSnapshotManagerUrlNil() { - o.SnapshotManagerUrl.Set(nil) -} - -// UnsetSnapshotManagerUrl ensures that no value is present for SnapshotManagerUrl, not even an explicit nil -func (o *UpdateRegion) UnsetSnapshotManagerUrl() { - o.SnapshotManagerUrl.Unset() -} - func (o UpdateRegion) MarshalJSON() ([]byte, error) { toSerialize,err := o.ToMap() if err != nil { @@ -190,9 +146,6 @@ func (o UpdateRegion) ToMap() (map[string]interface{}, error) { if o.SshGatewayUrl.IsSet() { toSerialize["sshGatewayUrl"] = o.SshGatewayUrl.Get() } - if o.SnapshotManagerUrl.IsSet() { - toSerialize["snapshotManagerUrl"] = o.SnapshotManagerUrl.Get() - } for key, value := range o.AdditionalProperties { toSerialize[key] = value @@ -217,7 +170,6 @@ func (o *UpdateRegion) UnmarshalJSON(data []byte) (err error) { if err = json.Unmarshal(data, &additionalProperties); err == nil { delete(additionalProperties, "proxyUrl") delete(additionalProperties, "sshGatewayUrl") - delete(additionalProperties, "snapshotManagerUrl") o.AdditionalProperties = additionalProperties } @@ -259,3 +211,5 @@ func (v *NullableUpdateRegion) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_update_sandbox_state_dto.go b/apps/api-client-go/model_update_sandbox_state_dto.go deleted file mode 100644 index 4f767b999..000000000 --- a/apps/api-client-go/model_update_sandbox_state_dto.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the UpdateSandboxStateDto type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &UpdateSandboxStateDto{} - -// UpdateSandboxStateDto struct for UpdateSandboxStateDto -type UpdateSandboxStateDto struct { - // The new state for the sandbox - State string `json:"state"` - // Optional error message when reporting an error state - ErrorReason *string `json:"errorReason,omitempty"` - // Whether the sandbox is recoverable - Recoverable *bool `json:"recoverable,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _UpdateSandboxStateDto UpdateSandboxStateDto - -// NewUpdateSandboxStateDto instantiates a new UpdateSandboxStateDto object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUpdateSandboxStateDto(state string) *UpdateSandboxStateDto { - this := UpdateSandboxStateDto{} - this.State = state - return &this -} - -// NewUpdateSandboxStateDtoWithDefaults instantiates a new UpdateSandboxStateDto object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUpdateSandboxStateDtoWithDefaults() *UpdateSandboxStateDto { - this := UpdateSandboxStateDto{} - return &this -} - -// GetState returns the State field value -func (o *UpdateSandboxStateDto) GetState() string { - if o == nil { - var ret string - return ret - } - - return o.State -} - -// GetStateOk returns a tuple with the State field value -// and a boolean to check if the value has been set. -func (o *UpdateSandboxStateDto) GetStateOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.State, true -} - -// SetState sets field value -func (o *UpdateSandboxStateDto) SetState(v string) { - o.State = v -} - -// GetErrorReason returns the ErrorReason field value if set, zero value otherwise. -func (o *UpdateSandboxStateDto) GetErrorReason() string { - if o == nil || IsNil(o.ErrorReason) { - var ret string - return ret - } - return *o.ErrorReason -} - -// GetErrorReasonOk returns a tuple with the ErrorReason field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *UpdateSandboxStateDto) GetErrorReasonOk() (*string, bool) { - if o == nil || IsNil(o.ErrorReason) { - return nil, false - } - return o.ErrorReason, true -} - -// HasErrorReason returns a boolean if a field has been set. -func (o *UpdateSandboxStateDto) HasErrorReason() bool { - if o != nil && !IsNil(o.ErrorReason) { - return true - } - - return false -} - -// SetErrorReason gets a reference to the given string and assigns it to the ErrorReason field. -func (o *UpdateSandboxStateDto) SetErrorReason(v string) { - o.ErrorReason = &v -} - -// GetRecoverable returns the Recoverable field value if set, zero value otherwise. -func (o *UpdateSandboxStateDto) GetRecoverable() bool { - if o == nil || IsNil(o.Recoverable) { - var ret bool - return ret - } - return *o.Recoverable -} - -// GetRecoverableOk returns a tuple with the Recoverable field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *UpdateSandboxStateDto) GetRecoverableOk() (*bool, bool) { - if o == nil || IsNil(o.Recoverable) { - return nil, false - } - return o.Recoverable, true -} - -// HasRecoverable returns a boolean if a field has been set. -func (o *UpdateSandboxStateDto) HasRecoverable() bool { - if o != nil && !IsNil(o.Recoverable) { - return true - } - - return false -} - -// SetRecoverable gets a reference to the given bool and assigns it to the Recoverable field. -func (o *UpdateSandboxStateDto) SetRecoverable(v bool) { - o.Recoverable = &v -} - -func (o UpdateSandboxStateDto) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o UpdateSandboxStateDto) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["state"] = o.State - if !IsNil(o.ErrorReason) { - toSerialize["errorReason"] = o.ErrorReason - } - if !IsNil(o.Recoverable) { - toSerialize["recoverable"] = o.Recoverable - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *UpdateSandboxStateDto) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "state", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varUpdateSandboxStateDto := _UpdateSandboxStateDto{} - - err = json.Unmarshal(data, &varUpdateSandboxStateDto) - - if err != nil { - return err - } - - *o = UpdateSandboxStateDto(varUpdateSandboxStateDto) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "state") - delete(additionalProperties, "errorReason") - delete(additionalProperties, "recoverable") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUpdateSandboxStateDto struct { - value *UpdateSandboxStateDto - isSet bool -} - -func (v NullableUpdateSandboxStateDto) Get() *UpdateSandboxStateDto { - return v.value -} - -func (v *NullableUpdateSandboxStateDto) Set(val *UpdateSandboxStateDto) { - v.value = val - v.isSet = true -} - -func (v NullableUpdateSandboxStateDto) IsSet() bool { - return v.isSet -} - -func (v *NullableUpdateSandboxStateDto) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUpdateSandboxStateDto(val *UpdateSandboxStateDto) *NullableUpdateSandboxStateDto { - return &NullableUpdateSandboxStateDto{value: val, isSet: true} -} - -func (v NullableUpdateSandboxStateDto) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUpdateSandboxStateDto) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_url.go b/apps/api-client-go/model_url.go deleted file mode 100644 index 4f0de7e65..000000000 --- a/apps/api-client-go/model_url.go +++ /dev/null @@ -1,168 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Url type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Url{} - -// Url struct for Url -type Url struct { - // URL response - Url string `json:"url"` - AdditionalProperties map[string]interface{} -} - -type _Url Url - -// NewUrl instantiates a new Url object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUrl(url string) *Url { - this := Url{} - this.Url = url - return &this -} - -// NewUrlWithDefaults instantiates a new Url object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUrlWithDefaults() *Url { - this := Url{} - return &this -} - -// GetUrl returns the Url field value -func (o *Url) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *Url) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *Url) SetUrl(v string) { - o.Url = v -} - -func (o Url) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Url) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["url"] = o.Url - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Url) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "url", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varUrl := _Url{} - - err = json.Unmarshal(data, &varUrl) - - if err != nil { - return err - } - - *o = Url(varUrl) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "url") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUrl struct { - value *Url - isSet bool -} - -func (v NullableUrl) Get() *Url { - return v.value -} - -func (v *NullableUrl) Set(val *Url) { - v.value = val - v.isSet = true -} - -func (v NullableUrl) IsSet() bool { - return v.isSet -} - -func (v *NullableUrl) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUrl(val *Url) *NullableUrl { - return &NullableUrl{value: val, isSet: true} -} - -func (v NullableUrl) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUrl) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_user.go b/apps/api-client-go/model_user.go index 53415b2bd..d2e3450fb 100644 --- a/apps/api-client-go/model_user.go +++ b/apps/api-client-go/model_user.go @@ -287,3 +287,5 @@ func (v *NullableUser) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_user_home_dir_response.go b/apps/api-client-go/model_user_home_dir_response.go deleted file mode 100644 index 34cd76c58..000000000 --- a/apps/api-client-go/model_user_home_dir_response.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the UserHomeDirResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &UserHomeDirResponse{} - -// UserHomeDirResponse struct for UserHomeDirResponse -type UserHomeDirResponse struct { - Dir *string `json:"dir,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _UserHomeDirResponse UserHomeDirResponse - -// NewUserHomeDirResponse instantiates a new UserHomeDirResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewUserHomeDirResponse() *UserHomeDirResponse { - this := UserHomeDirResponse{} - return &this -} - -// NewUserHomeDirResponseWithDefaults instantiates a new UserHomeDirResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewUserHomeDirResponseWithDefaults() *UserHomeDirResponse { - this := UserHomeDirResponse{} - return &this -} - -// GetDir returns the Dir field value if set, zero value otherwise. -func (o *UserHomeDirResponse) GetDir() string { - if o == nil || IsNil(o.Dir) { - var ret string - return ret - } - return *o.Dir -} - -// GetDirOk returns a tuple with the Dir field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *UserHomeDirResponse) GetDirOk() (*string, bool) { - if o == nil || IsNil(o.Dir) { - return nil, false - } - return o.Dir, true -} - -// HasDir returns a boolean if a field has been set. -func (o *UserHomeDirResponse) HasDir() bool { - if o != nil && !IsNil(o.Dir) { - return true - } - - return false -} - -// SetDir gets a reference to the given string and assigns it to the Dir field. -func (o *UserHomeDirResponse) SetDir(v string) { - o.Dir = &v -} - -func (o UserHomeDirResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o UserHomeDirResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Dir) { - toSerialize["dir"] = o.Dir - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *UserHomeDirResponse) UnmarshalJSON(data []byte) (err error) { - varUserHomeDirResponse := _UserHomeDirResponse{} - - err = json.Unmarshal(data, &varUserHomeDirResponse) - - if err != nil { - return err - } - - *o = UserHomeDirResponse(varUserHomeDirResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "dir") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableUserHomeDirResponse struct { - value *UserHomeDirResponse - isSet bool -} - -func (v NullableUserHomeDirResponse) Get() *UserHomeDirResponse { - return v.value -} - -func (v *NullableUserHomeDirResponse) Set(val *UserHomeDirResponse) { - v.value = val - v.isSet = true -} - -func (v NullableUserHomeDirResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableUserHomeDirResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableUserHomeDirResponse(val *UserHomeDirResponse) *NullableUserHomeDirResponse { - return &NullableUserHomeDirResponse{value: val, isSet: true} -} - -func (v NullableUserHomeDirResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableUserHomeDirResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_user_public_key.go b/apps/api-client-go/model_user_public_key.go index 82600625b..c346c2b73 100644 --- a/apps/api-client-go/model_user_public_key.go +++ b/apps/api-client-go/model_user_public_key.go @@ -196,3 +196,5 @@ func (v *NullableUserPublicKey) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_volume_dto.go b/apps/api-client-go/model_volume_dto.go index 4502edc04..ea2e09e63 100644 --- a/apps/api-client-go/model_volume_dto.go +++ b/apps/api-client-go/model_volume_dto.go @@ -396,3 +396,5 @@ func (v *NullableVolumeDto) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_volume_state.go b/apps/api-client-go/model_volume_state.go index 68099485b..ac502201a 100644 --- a/apps/api-client-go/model_volume_state.go +++ b/apps/api-client-go/model_volume_state.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // VolumeState Volume state @@ -28,6 +27,7 @@ const ( VOLUMESTATE_DELETING VolumeState = "deleting" VOLUMESTATE_DELETED VolumeState = "deleted" VOLUMESTATE_ERROR VolumeState = "error" + VOLUMESTATE_UNKNOWN_DEFAULT_OPEN_API VolumeState = "unknown_default_open_api" ) // All allowed values of VolumeState enum @@ -39,6 +39,7 @@ var AllowedVolumeStateEnumValues = []VolumeState{ "deleting", "deleted", "error", + "unknown_default_open_api", } func (v *VolumeState) UnmarshalJSON(src []byte) error { @@ -55,7 +56,8 @@ func (v *VolumeState) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid VolumeState", value) + *v = VOLUMESTATE_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewVolumeStateFromValue returns a pointer to a valid VolumeState @@ -65,7 +67,8 @@ func NewVolumeStateFromValue(v string) (*VolumeState, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for VolumeState: valid values are %v", v, AllowedVolumeStateEnumValues) + enumValue := VOLUMESTATE_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -119,3 +122,4 @@ func (v *NullableVolumeState) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_webhook_app_portal_access.go b/apps/api-client-go/model_webhook_app_portal_access.go index 002f34de4..93cb16c46 100644 --- a/apps/api-client-go/model_webhook_app_portal_access.go +++ b/apps/api-client-go/model_webhook_app_portal_access.go @@ -196,3 +196,5 @@ func (v *NullableWebhookAppPortalAccess) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_webhook_controller_get_status_200_response.go b/apps/api-client-go/model_webhook_controller_get_status_200_response.go index 8d9c5a950..aada6c52c 100644 --- a/apps/api-client-go/model_webhook_controller_get_status_200_response.go +++ b/apps/api-client-go/model_webhook_controller_get_status_200_response.go @@ -152,3 +152,5 @@ func (v *NullableWebhookControllerGetStatus200Response) UnmarshalJSON(src []byte v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_webhook_event.go b/apps/api-client-go/model_webhook_event.go index 97bd4e9d9..03c638372 100644 --- a/apps/api-client-go/model_webhook_event.go +++ b/apps/api-client-go/model_webhook_event.go @@ -13,7 +13,6 @@ package apiclient import ( "encoding/json" - "fmt" ) // WebhookEvent The type of event being sent @@ -21,24 +20,26 @@ type WebhookEvent string // List of WebhookEvent const ( - WEBHOOKEVENT_SANDBOX_CREATED WebhookEvent = "sandbox.created" - WEBHOOKEVENT_SANDBOX_STATE_UPDATED WebhookEvent = "sandbox.state.updated" - WEBHOOKEVENT_SNAPSHOT_CREATED WebhookEvent = "snapshot.created" - WEBHOOKEVENT_SNAPSHOT_STATE_UPDATED WebhookEvent = "snapshot.state.updated" - WEBHOOKEVENT_SNAPSHOT_REMOVED WebhookEvent = "snapshot.removed" + WEBHOOKEVENT_BOX_CREATED WebhookEvent = "box.created" + WEBHOOKEVENT_BOX_STATE_UPDATED WebhookEvent = "box.state.updated" + WEBHOOKEVENT_TEMPLATE_CREATED WebhookEvent = "template.created" + WEBHOOKEVENT_TEMPLATE_STATE_UPDATED WebhookEvent = "template.state.updated" + WEBHOOKEVENT_TEMPLATE_REMOVED WebhookEvent = "template.removed" WEBHOOKEVENT_VOLUME_CREATED WebhookEvent = "volume.created" WEBHOOKEVENT_VOLUME_STATE_UPDATED WebhookEvent = "volume.state.updated" + WEBHOOKEVENT_UNKNOWN_DEFAULT_OPEN_API WebhookEvent = "unknown_default_open_api" ) // All allowed values of WebhookEvent enum var AllowedWebhookEventEnumValues = []WebhookEvent{ - "sandbox.created", - "sandbox.state.updated", - "snapshot.created", - "snapshot.state.updated", - "snapshot.removed", + "box.created", + "box.state.updated", + "template.created", + "template.state.updated", + "template.removed", "volume.created", "volume.state.updated", + "unknown_default_open_api", } func (v *WebhookEvent) UnmarshalJSON(src []byte) error { @@ -55,7 +56,8 @@ func (v *WebhookEvent) UnmarshalJSON(src []byte) error { } } - return fmt.Errorf("%+v is not a valid WebhookEvent", value) + *v = WEBHOOKEVENT_UNKNOWN_DEFAULT_OPEN_API + return nil } // NewWebhookEventFromValue returns a pointer to a valid WebhookEvent @@ -65,7 +67,8 @@ func NewWebhookEventFromValue(v string) (*WebhookEvent, error) { if ev.IsValid() { return &ev, nil } else { - return nil, fmt.Errorf("invalid value '%v' for WebhookEvent: valid values are %v", v, AllowedWebhookEventEnumValues) + enumValue := WEBHOOKEVENT_UNKNOWN_DEFAULT_OPEN_API + return &enumValue, nil } } @@ -119,3 +122,4 @@ func (v *NullableWebhookEvent) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + diff --git a/apps/api-client-go/model_webhook_initialization_status.go b/apps/api-client-go/model_webhook_initialization_status.go index 150dd4165..6d8f73d8b 100644 --- a/apps/api-client-go/model_webhook_initialization_status.go +++ b/apps/api-client-go/model_webhook_initialization_status.go @@ -320,3 +320,5 @@ func (v *NullableWebhookInitializationStatus) UnmarshalJSON(src []byte) error { v.isSet = true return json.Unmarshal(src, &v.value) } + + diff --git a/apps/api-client-go/model_windows_response.go b/apps/api-client-go/model_windows_response.go deleted file mode 100644 index c7286d492..000000000 --- a/apps/api-client-go/model_windows_response.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the WindowsResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &WindowsResponse{} - -// WindowsResponse struct for WindowsResponse -type WindowsResponse struct { - // Array of window information for all visible windows - Windows []map[string]interface{} `json:"windows"` - // The total number of windows found - Count float32 `json:"count"` - AdditionalProperties map[string]interface{} -} - -type _WindowsResponse WindowsResponse - -// NewWindowsResponse instantiates a new WindowsResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewWindowsResponse(windows []map[string]interface{}, count float32) *WindowsResponse { - this := WindowsResponse{} - this.Windows = windows - this.Count = count - return &this -} - -// NewWindowsResponseWithDefaults instantiates a new WindowsResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewWindowsResponseWithDefaults() *WindowsResponse { - this := WindowsResponse{} - return &this -} - -// GetWindows returns the Windows field value -func (o *WindowsResponse) GetWindows() []map[string]interface{} { - if o == nil { - var ret []map[string]interface{} - return ret - } - - return o.Windows -} - -// GetWindowsOk returns a tuple with the Windows field value -// and a boolean to check if the value has been set. -func (o *WindowsResponse) GetWindowsOk() ([]map[string]interface{}, bool) { - if o == nil { - return nil, false - } - return o.Windows, true -} - -// SetWindows sets field value -func (o *WindowsResponse) SetWindows(v []map[string]interface{}) { - o.Windows = v -} - -// GetCount returns the Count field value -func (o *WindowsResponse) GetCount() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Count -} - -// GetCountOk returns a tuple with the Count field value -// and a boolean to check if the value has been set. -func (o *WindowsResponse) GetCountOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Count, true -} - -// SetCount sets field value -func (o *WindowsResponse) SetCount(v float32) { - o.Count = v -} - -func (o WindowsResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o WindowsResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["windows"] = o.Windows - toSerialize["count"] = o.Count - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *WindowsResponse) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "windows", - "count", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varWindowsResponse := _WindowsResponse{} - - err = json.Unmarshal(data, &varWindowsResponse) - - if err != nil { - return err - } - - *o = WindowsResponse(varWindowsResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "windows") - delete(additionalProperties, "count") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableWindowsResponse struct { - value *WindowsResponse - isSet bool -} - -func (v NullableWindowsResponse) Get() *WindowsResponse { - return v.value -} - -func (v *NullableWindowsResponse) Set(val *WindowsResponse) { - v.value = val - v.isSet = true -} - -func (v NullableWindowsResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableWindowsResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableWindowsResponse(val *WindowsResponse) *NullableWindowsResponse { - return &NullableWindowsResponse{value: val, isSet: true} -} - -func (v NullableWindowsResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableWindowsResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_work_dir_response.go b/apps/api-client-go/model_work_dir_response.go deleted file mode 100644 index 46c98a2bf..000000000 --- a/apps/api-client-go/model_work_dir_response.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" -) - -// checks if the WorkDirResponse type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &WorkDirResponse{} - -// WorkDirResponse struct for WorkDirResponse -type WorkDirResponse struct { - Dir *string `json:"dir,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _WorkDirResponse WorkDirResponse - -// NewWorkDirResponse instantiates a new WorkDirResponse object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewWorkDirResponse() *WorkDirResponse { - this := WorkDirResponse{} - return &this -} - -// NewWorkDirResponseWithDefaults instantiates a new WorkDirResponse object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewWorkDirResponseWithDefaults() *WorkDirResponse { - this := WorkDirResponse{} - return &this -} - -// GetDir returns the Dir field value if set, zero value otherwise. -func (o *WorkDirResponse) GetDir() string { - if o == nil || IsNil(o.Dir) { - var ret string - return ret - } - return *o.Dir -} - -// GetDirOk returns a tuple with the Dir field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *WorkDirResponse) GetDirOk() (*string, bool) { - if o == nil || IsNil(o.Dir) { - return nil, false - } - return o.Dir, true -} - -// HasDir returns a boolean if a field has been set. -func (o *WorkDirResponse) HasDir() bool { - if o != nil && !IsNil(o.Dir) { - return true - } - - return false -} - -// SetDir gets a reference to the given string and assigns it to the Dir field. -func (o *WorkDirResponse) SetDir(v string) { - o.Dir = &v -} - -func (o WorkDirResponse) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o WorkDirResponse) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - if !IsNil(o.Dir) { - toSerialize["dir"] = o.Dir - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *WorkDirResponse) UnmarshalJSON(data []byte) (err error) { - varWorkDirResponse := _WorkDirResponse{} - - err = json.Unmarshal(data, &varWorkDirResponse) - - if err != nil { - return err - } - - *o = WorkDirResponse(varWorkDirResponse) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "dir") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableWorkDirResponse struct { - value *WorkDirResponse - isSet bool -} - -func (v NullableWorkDirResponse) Get() *WorkDirResponse { - return v.value -} - -func (v *NullableWorkDirResponse) Set(val *WorkDirResponse) { - v.value = val - v.isSet = true -} - -func (v NullableWorkDirResponse) IsSet() bool { - return v.isSet -} - -func (v *NullableWorkDirResponse) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableWorkDirResponse(val *WorkDirResponse) *NullableWorkDirResponse { - return &NullableWorkDirResponse{value: val, isSet: true} -} - -func (v NullableWorkDirResponse) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableWorkDirResponse) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_workspace.go b/apps/api-client-go/model_workspace.go deleted file mode 100644 index 4664b980c..000000000 --- a/apps/api-client-go/model_workspace.go +++ /dev/null @@ -1,1398 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the Workspace type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &Workspace{} - -// Workspace struct for Workspace -type Workspace struct { - // The ID of the sandbox - Id string `json:"id"` - // The organization ID of the sandbox - OrganizationId string `json:"organizationId"` - // The name of the sandbox - Name string `json:"name"` - // The snapshot used for the sandbox - Snapshot *string `json:"snapshot,omitempty"` - // The user associated with the project - User string `json:"user"` - // Environment variables for the sandbox - Env map[string]string `json:"env"` - // Labels for the sandbox - Labels map[string]string `json:"labels"` - // Whether the sandbox http preview is public - Public bool `json:"public"` - // Whether to block all network access for the sandbox - NetworkBlockAll bool `json:"networkBlockAll"` - // Comma-separated list of allowed CIDR network addresses for the sandbox - NetworkAllowList *string `json:"networkAllowList,omitempty"` - // The target environment for the sandbox - Target string `json:"target"` - // The CPU quota for the sandbox - Cpu float32 `json:"cpu"` - // The GPU quota for the sandbox - Gpu float32 `json:"gpu"` - // The memory quota for the sandbox - Memory float32 `json:"memory"` - // The disk quota for the sandbox - Disk float32 `json:"disk"` - // The state of the sandbox - State *SandboxState `json:"state,omitempty"` - // The desired state of the sandbox - DesiredState *SandboxDesiredState `json:"desiredState,omitempty"` - // The error reason of the sandbox - ErrorReason *string `json:"errorReason,omitempty"` - // Whether the sandbox error is recoverable. - Recoverable *bool `json:"recoverable,omitempty"` - // The state of the backup - BackupState *string `json:"backupState,omitempty"` - // The creation timestamp of the last backup - BackupCreatedAt *string `json:"backupCreatedAt,omitempty"` - // Auto-stop interval in minutes (0 means disabled) - AutoStopInterval *float32 `json:"autoStopInterval,omitempty"` - // Auto-archive interval in minutes - AutoArchiveInterval *float32 `json:"autoArchiveInterval,omitempty"` - // Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - AutoDeleteInterval *float32 `json:"autoDeleteInterval,omitempty"` - // Array of volumes attached to the sandbox - Volumes []SandboxVolume `json:"volumes,omitempty"` - // Build information for the sandbox - BuildInfo *BuildInfo `json:"buildInfo,omitempty"` - // The creation timestamp of the sandbox - CreatedAt *string `json:"createdAt,omitempty"` - // The last update timestamp of the sandbox - UpdatedAt *string `json:"updatedAt,omitempty"` - // The class of the sandbox - // Deprecated - Class *string `json:"class,omitempty"` - // The version of the daemon running in the sandbox - DaemonVersion *string `json:"daemonVersion,omitempty"` - // The runner ID of the sandbox - RunnerId *string `json:"runnerId,omitempty"` - // The toolbox proxy URL for the sandbox - ToolboxProxyUrl string `json:"toolboxProxyUrl"` - // The image used for the workspace - Image *string `json:"image,omitempty"` - // The state of the snapshot - SnapshotState *string `json:"snapshotState,omitempty"` - // The creation timestamp of the last snapshot - SnapshotCreatedAt *string `json:"snapshotCreatedAt,omitempty"` - // Additional information about the sandbox - Info *SandboxInfo `json:"info,omitempty"` - AdditionalProperties map[string]interface{} -} - -type _Workspace Workspace - -// NewWorkspace instantiates a new Workspace object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewWorkspace(id string, organizationId string, name string, user string, env map[string]string, labels map[string]string, public bool, networkBlockAll bool, target string, cpu float32, gpu float32, memory float32, disk float32, toolboxProxyUrl string) *Workspace { - this := Workspace{} - this.Id = id - this.OrganizationId = organizationId - this.Name = name - this.User = user - this.Env = env - this.Labels = labels - this.Public = public - this.NetworkBlockAll = networkBlockAll - this.Target = target - this.Cpu = cpu - this.Gpu = gpu - this.Memory = memory - this.Disk = disk - this.ToolboxProxyUrl = toolboxProxyUrl - return &this -} - -// NewWorkspaceWithDefaults instantiates a new Workspace object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewWorkspaceWithDefaults() *Workspace { - this := Workspace{} - return &this -} - -// GetId returns the Id field value -func (o *Workspace) GetId() string { - if o == nil { - var ret string - return ret - } - - return o.Id -} - -// GetIdOk returns a tuple with the Id field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Id, true -} - -// SetId sets field value -func (o *Workspace) SetId(v string) { - o.Id = v -} - -// GetOrganizationId returns the OrganizationId field value -func (o *Workspace) GetOrganizationId() string { - if o == nil { - var ret string - return ret - } - - return o.OrganizationId -} - -// GetOrganizationIdOk returns a tuple with the OrganizationId field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetOrganizationIdOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.OrganizationId, true -} - -// SetOrganizationId sets field value -func (o *Workspace) SetOrganizationId(v string) { - o.OrganizationId = v -} - -// GetName returns the Name field value -func (o *Workspace) GetName() string { - if o == nil { - var ret string - return ret - } - - return o.Name -} - -// GetNameOk returns a tuple with the Name field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetNameOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Name, true -} - -// SetName sets field value -func (o *Workspace) SetName(v string) { - o.Name = v -} - -// GetSnapshot returns the Snapshot field value if set, zero value otherwise. -func (o *Workspace) GetSnapshot() string { - if o == nil || IsNil(o.Snapshot) { - var ret string - return ret - } - return *o.Snapshot -} - -// GetSnapshotOk returns a tuple with the Snapshot field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetSnapshotOk() (*string, bool) { - if o == nil || IsNil(o.Snapshot) { - return nil, false - } - return o.Snapshot, true -} - -// HasSnapshot returns a boolean if a field has been set. -func (o *Workspace) HasSnapshot() bool { - if o != nil && !IsNil(o.Snapshot) { - return true - } - - return false -} - -// SetSnapshot gets a reference to the given string and assigns it to the Snapshot field. -func (o *Workspace) SetSnapshot(v string) { - o.Snapshot = &v -} - -// GetUser returns the User field value -func (o *Workspace) GetUser() string { - if o == nil { - var ret string - return ret - } - - return o.User -} - -// GetUserOk returns a tuple with the User field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetUserOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.User, true -} - -// SetUser sets field value -func (o *Workspace) SetUser(v string) { - o.User = v -} - -// GetEnv returns the Env field value -func (o *Workspace) GetEnv() map[string]string { - if o == nil { - var ret map[string]string - return ret - } - - return o.Env -} - -// GetEnvOk returns a tuple with the Env field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetEnvOk() (*map[string]string, bool) { - if o == nil { - return nil, false - } - return &o.Env, true -} - -// SetEnv sets field value -func (o *Workspace) SetEnv(v map[string]string) { - o.Env = v -} - -// GetLabels returns the Labels field value -func (o *Workspace) GetLabels() map[string]string { - if o == nil { - var ret map[string]string - return ret - } - - return o.Labels -} - -// GetLabelsOk returns a tuple with the Labels field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetLabelsOk() (*map[string]string, bool) { - if o == nil { - return nil, false - } - return &o.Labels, true -} - -// SetLabels sets field value -func (o *Workspace) SetLabels(v map[string]string) { - o.Labels = v -} - -// GetPublic returns the Public field value -func (o *Workspace) GetPublic() bool { - if o == nil { - var ret bool - return ret - } - - return o.Public -} - -// GetPublicOk returns a tuple with the Public field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetPublicOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.Public, true -} - -// SetPublic sets field value -func (o *Workspace) SetPublic(v bool) { - o.Public = v -} - -// GetNetworkBlockAll returns the NetworkBlockAll field value -func (o *Workspace) GetNetworkBlockAll() bool { - if o == nil { - var ret bool - return ret - } - - return o.NetworkBlockAll -} - -// GetNetworkBlockAllOk returns a tuple with the NetworkBlockAll field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetNetworkBlockAllOk() (*bool, bool) { - if o == nil { - return nil, false - } - return &o.NetworkBlockAll, true -} - -// SetNetworkBlockAll sets field value -func (o *Workspace) SetNetworkBlockAll(v bool) { - o.NetworkBlockAll = v -} - -// GetNetworkAllowList returns the NetworkAllowList field value if set, zero value otherwise. -func (o *Workspace) GetNetworkAllowList() string { - if o == nil || IsNil(o.NetworkAllowList) { - var ret string - return ret - } - return *o.NetworkAllowList -} - -// GetNetworkAllowListOk returns a tuple with the NetworkAllowList field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetNetworkAllowListOk() (*string, bool) { - if o == nil || IsNil(o.NetworkAllowList) { - return nil, false - } - return o.NetworkAllowList, true -} - -// HasNetworkAllowList returns a boolean if a field has been set. -func (o *Workspace) HasNetworkAllowList() bool { - if o != nil && !IsNil(o.NetworkAllowList) { - return true - } - - return false -} - -// SetNetworkAllowList gets a reference to the given string and assigns it to the NetworkAllowList field. -func (o *Workspace) SetNetworkAllowList(v string) { - o.NetworkAllowList = &v -} - -// GetTarget returns the Target field value -func (o *Workspace) GetTarget() string { - if o == nil { - var ret string - return ret - } - - return o.Target -} - -// GetTargetOk returns a tuple with the Target field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetTargetOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Target, true -} - -// SetTarget sets field value -func (o *Workspace) SetTarget(v string) { - o.Target = v -} - -// GetCpu returns the Cpu field value -func (o *Workspace) GetCpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Cpu -} - -// GetCpuOk returns a tuple with the Cpu field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetCpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Cpu, true -} - -// SetCpu sets field value -func (o *Workspace) SetCpu(v float32) { - o.Cpu = v -} - -// GetGpu returns the Gpu field value -func (o *Workspace) GetGpu() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Gpu -} - -// GetGpuOk returns a tuple with the Gpu field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetGpuOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Gpu, true -} - -// SetGpu sets field value -func (o *Workspace) SetGpu(v float32) { - o.Gpu = v -} - -// GetMemory returns the Memory field value -func (o *Workspace) GetMemory() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Memory -} - -// GetMemoryOk returns a tuple with the Memory field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetMemoryOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Memory, true -} - -// SetMemory sets field value -func (o *Workspace) SetMemory(v float32) { - o.Memory = v -} - -// GetDisk returns the Disk field value -func (o *Workspace) GetDisk() float32 { - if o == nil { - var ret float32 - return ret - } - - return o.Disk -} - -// GetDiskOk returns a tuple with the Disk field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetDiskOk() (*float32, bool) { - if o == nil { - return nil, false - } - return &o.Disk, true -} - -// SetDisk sets field value -func (o *Workspace) SetDisk(v float32) { - o.Disk = v -} - -// GetState returns the State field value if set, zero value otherwise. -func (o *Workspace) GetState() SandboxState { - if o == nil || IsNil(o.State) { - var ret SandboxState - return ret - } - return *o.State -} - -// GetStateOk returns a tuple with the State field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetStateOk() (*SandboxState, bool) { - if o == nil || IsNil(o.State) { - return nil, false - } - return o.State, true -} - -// HasState returns a boolean if a field has been set. -func (o *Workspace) HasState() bool { - if o != nil && !IsNil(o.State) { - return true - } - - return false -} - -// SetState gets a reference to the given SandboxState and assigns it to the State field. -func (o *Workspace) SetState(v SandboxState) { - o.State = &v -} - -// GetDesiredState returns the DesiredState field value if set, zero value otherwise. -func (o *Workspace) GetDesiredState() SandboxDesiredState { - if o == nil || IsNil(o.DesiredState) { - var ret SandboxDesiredState - return ret - } - return *o.DesiredState -} - -// GetDesiredStateOk returns a tuple with the DesiredState field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetDesiredStateOk() (*SandboxDesiredState, bool) { - if o == nil || IsNil(o.DesiredState) { - return nil, false - } - return o.DesiredState, true -} - -// HasDesiredState returns a boolean if a field has been set. -func (o *Workspace) HasDesiredState() bool { - if o != nil && !IsNil(o.DesiredState) { - return true - } - - return false -} - -// SetDesiredState gets a reference to the given SandboxDesiredState and assigns it to the DesiredState field. -func (o *Workspace) SetDesiredState(v SandboxDesiredState) { - o.DesiredState = &v -} - -// GetErrorReason returns the ErrorReason field value if set, zero value otherwise. -func (o *Workspace) GetErrorReason() string { - if o == nil || IsNil(o.ErrorReason) { - var ret string - return ret - } - return *o.ErrorReason -} - -// GetErrorReasonOk returns a tuple with the ErrorReason field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetErrorReasonOk() (*string, bool) { - if o == nil || IsNil(o.ErrorReason) { - return nil, false - } - return o.ErrorReason, true -} - -// HasErrorReason returns a boolean if a field has been set. -func (o *Workspace) HasErrorReason() bool { - if o != nil && !IsNil(o.ErrorReason) { - return true - } - - return false -} - -// SetErrorReason gets a reference to the given string and assigns it to the ErrorReason field. -func (o *Workspace) SetErrorReason(v string) { - o.ErrorReason = &v -} - -// GetRecoverable returns the Recoverable field value if set, zero value otherwise. -func (o *Workspace) GetRecoverable() bool { - if o == nil || IsNil(o.Recoverable) { - var ret bool - return ret - } - return *o.Recoverable -} - -// GetRecoverableOk returns a tuple with the Recoverable field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetRecoverableOk() (*bool, bool) { - if o == nil || IsNil(o.Recoverable) { - return nil, false - } - return o.Recoverable, true -} - -// HasRecoverable returns a boolean if a field has been set. -func (o *Workspace) HasRecoverable() bool { - if o != nil && !IsNil(o.Recoverable) { - return true - } - - return false -} - -// SetRecoverable gets a reference to the given bool and assigns it to the Recoverable field. -func (o *Workspace) SetRecoverable(v bool) { - o.Recoverable = &v -} - -// GetBackupState returns the BackupState field value if set, zero value otherwise. -func (o *Workspace) GetBackupState() string { - if o == nil || IsNil(o.BackupState) { - var ret string - return ret - } - return *o.BackupState -} - -// GetBackupStateOk returns a tuple with the BackupState field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetBackupStateOk() (*string, bool) { - if o == nil || IsNil(o.BackupState) { - return nil, false - } - return o.BackupState, true -} - -// HasBackupState returns a boolean if a field has been set. -func (o *Workspace) HasBackupState() bool { - if o != nil && !IsNil(o.BackupState) { - return true - } - - return false -} - -// SetBackupState gets a reference to the given string and assigns it to the BackupState field. -func (o *Workspace) SetBackupState(v string) { - o.BackupState = &v -} - -// GetBackupCreatedAt returns the BackupCreatedAt field value if set, zero value otherwise. -func (o *Workspace) GetBackupCreatedAt() string { - if o == nil || IsNil(o.BackupCreatedAt) { - var ret string - return ret - } - return *o.BackupCreatedAt -} - -// GetBackupCreatedAtOk returns a tuple with the BackupCreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetBackupCreatedAtOk() (*string, bool) { - if o == nil || IsNil(o.BackupCreatedAt) { - return nil, false - } - return o.BackupCreatedAt, true -} - -// HasBackupCreatedAt returns a boolean if a field has been set. -func (o *Workspace) HasBackupCreatedAt() bool { - if o != nil && !IsNil(o.BackupCreatedAt) { - return true - } - - return false -} - -// SetBackupCreatedAt gets a reference to the given string and assigns it to the BackupCreatedAt field. -func (o *Workspace) SetBackupCreatedAt(v string) { - o.BackupCreatedAt = &v -} - -// GetAutoStopInterval returns the AutoStopInterval field value if set, zero value otherwise. -func (o *Workspace) GetAutoStopInterval() float32 { - if o == nil || IsNil(o.AutoStopInterval) { - var ret float32 - return ret - } - return *o.AutoStopInterval -} - -// GetAutoStopIntervalOk returns a tuple with the AutoStopInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetAutoStopIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoStopInterval) { - return nil, false - } - return o.AutoStopInterval, true -} - -// HasAutoStopInterval returns a boolean if a field has been set. -func (o *Workspace) HasAutoStopInterval() bool { - if o != nil && !IsNil(o.AutoStopInterval) { - return true - } - - return false -} - -// SetAutoStopInterval gets a reference to the given float32 and assigns it to the AutoStopInterval field. -func (o *Workspace) SetAutoStopInterval(v float32) { - o.AutoStopInterval = &v -} - -// GetAutoArchiveInterval returns the AutoArchiveInterval field value if set, zero value otherwise. -func (o *Workspace) GetAutoArchiveInterval() float32 { - if o == nil || IsNil(o.AutoArchiveInterval) { - var ret float32 - return ret - } - return *o.AutoArchiveInterval -} - -// GetAutoArchiveIntervalOk returns a tuple with the AutoArchiveInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetAutoArchiveIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoArchiveInterval) { - return nil, false - } - return o.AutoArchiveInterval, true -} - -// HasAutoArchiveInterval returns a boolean if a field has been set. -func (o *Workspace) HasAutoArchiveInterval() bool { - if o != nil && !IsNil(o.AutoArchiveInterval) { - return true - } - - return false -} - -// SetAutoArchiveInterval gets a reference to the given float32 and assigns it to the AutoArchiveInterval field. -func (o *Workspace) SetAutoArchiveInterval(v float32) { - o.AutoArchiveInterval = &v -} - -// GetAutoDeleteInterval returns the AutoDeleteInterval field value if set, zero value otherwise. -func (o *Workspace) GetAutoDeleteInterval() float32 { - if o == nil || IsNil(o.AutoDeleteInterval) { - var ret float32 - return ret - } - return *o.AutoDeleteInterval -} - -// GetAutoDeleteIntervalOk returns a tuple with the AutoDeleteInterval field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetAutoDeleteIntervalOk() (*float32, bool) { - if o == nil || IsNil(o.AutoDeleteInterval) { - return nil, false - } - return o.AutoDeleteInterval, true -} - -// HasAutoDeleteInterval returns a boolean if a field has been set. -func (o *Workspace) HasAutoDeleteInterval() bool { - if o != nil && !IsNil(o.AutoDeleteInterval) { - return true - } - - return false -} - -// SetAutoDeleteInterval gets a reference to the given float32 and assigns it to the AutoDeleteInterval field. -func (o *Workspace) SetAutoDeleteInterval(v float32) { - o.AutoDeleteInterval = &v -} - -// GetVolumes returns the Volumes field value if set, zero value otherwise. -func (o *Workspace) GetVolumes() []SandboxVolume { - if o == nil || IsNil(o.Volumes) { - var ret []SandboxVolume - return ret - } - return o.Volumes -} - -// GetVolumesOk returns a tuple with the Volumes field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetVolumesOk() ([]SandboxVolume, bool) { - if o == nil || IsNil(o.Volumes) { - return nil, false - } - return o.Volumes, true -} - -// HasVolumes returns a boolean if a field has been set. -func (o *Workspace) HasVolumes() bool { - if o != nil && !IsNil(o.Volumes) { - return true - } - - return false -} - -// SetVolumes gets a reference to the given []SandboxVolume and assigns it to the Volumes field. -func (o *Workspace) SetVolumes(v []SandboxVolume) { - o.Volumes = v -} - -// GetBuildInfo returns the BuildInfo field value if set, zero value otherwise. -func (o *Workspace) GetBuildInfo() BuildInfo { - if o == nil || IsNil(o.BuildInfo) { - var ret BuildInfo - return ret - } - return *o.BuildInfo -} - -// GetBuildInfoOk returns a tuple with the BuildInfo field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetBuildInfoOk() (*BuildInfo, bool) { - if o == nil || IsNil(o.BuildInfo) { - return nil, false - } - return o.BuildInfo, true -} - -// HasBuildInfo returns a boolean if a field has been set. -func (o *Workspace) HasBuildInfo() bool { - if o != nil && !IsNil(o.BuildInfo) { - return true - } - - return false -} - -// SetBuildInfo gets a reference to the given BuildInfo and assigns it to the BuildInfo field. -func (o *Workspace) SetBuildInfo(v BuildInfo) { - o.BuildInfo = &v -} - -// GetCreatedAt returns the CreatedAt field value if set, zero value otherwise. -func (o *Workspace) GetCreatedAt() string { - if o == nil || IsNil(o.CreatedAt) { - var ret string - return ret - } - return *o.CreatedAt -} - -// GetCreatedAtOk returns a tuple with the CreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetCreatedAtOk() (*string, bool) { - if o == nil || IsNil(o.CreatedAt) { - return nil, false - } - return o.CreatedAt, true -} - -// HasCreatedAt returns a boolean if a field has been set. -func (o *Workspace) HasCreatedAt() bool { - if o != nil && !IsNil(o.CreatedAt) { - return true - } - - return false -} - -// SetCreatedAt gets a reference to the given string and assigns it to the CreatedAt field. -func (o *Workspace) SetCreatedAt(v string) { - o.CreatedAt = &v -} - -// GetUpdatedAt returns the UpdatedAt field value if set, zero value otherwise. -func (o *Workspace) GetUpdatedAt() string { - if o == nil || IsNil(o.UpdatedAt) { - var ret string - return ret - } - return *o.UpdatedAt -} - -// GetUpdatedAtOk returns a tuple with the UpdatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetUpdatedAtOk() (*string, bool) { - if o == nil || IsNil(o.UpdatedAt) { - return nil, false - } - return o.UpdatedAt, true -} - -// HasUpdatedAt returns a boolean if a field has been set. -func (o *Workspace) HasUpdatedAt() bool { - if o != nil && !IsNil(o.UpdatedAt) { - return true - } - - return false -} - -// SetUpdatedAt gets a reference to the given string and assigns it to the UpdatedAt field. -func (o *Workspace) SetUpdatedAt(v string) { - o.UpdatedAt = &v -} - -// GetClass returns the Class field value if set, zero value otherwise. -// Deprecated -func (o *Workspace) GetClass() string { - if o == nil || IsNil(o.Class) { - var ret string - return ret - } - return *o.Class -} - -// GetClassOk returns a tuple with the Class field value if set, nil otherwise -// and a boolean to check if the value has been set. -// Deprecated -func (o *Workspace) GetClassOk() (*string, bool) { - if o == nil || IsNil(o.Class) { - return nil, false - } - return o.Class, true -} - -// HasClass returns a boolean if a field has been set. -func (o *Workspace) HasClass() bool { - if o != nil && !IsNil(o.Class) { - return true - } - - return false -} - -// SetClass gets a reference to the given string and assigns it to the Class field. -// Deprecated -func (o *Workspace) SetClass(v string) { - o.Class = &v -} - -// GetDaemonVersion returns the DaemonVersion field value if set, zero value otherwise. -func (o *Workspace) GetDaemonVersion() string { - if o == nil || IsNil(o.DaemonVersion) { - var ret string - return ret - } - return *o.DaemonVersion -} - -// GetDaemonVersionOk returns a tuple with the DaemonVersion field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetDaemonVersionOk() (*string, bool) { - if o == nil || IsNil(o.DaemonVersion) { - return nil, false - } - return o.DaemonVersion, true -} - -// HasDaemonVersion returns a boolean if a field has been set. -func (o *Workspace) HasDaemonVersion() bool { - if o != nil && !IsNil(o.DaemonVersion) { - return true - } - - return false -} - -// SetDaemonVersion gets a reference to the given string and assigns it to the DaemonVersion field. -func (o *Workspace) SetDaemonVersion(v string) { - o.DaemonVersion = &v -} - -// GetRunnerId returns the RunnerId field value if set, zero value otherwise. -func (o *Workspace) GetRunnerId() string { - if o == nil || IsNil(o.RunnerId) { - var ret string - return ret - } - return *o.RunnerId -} - -// GetRunnerIdOk returns a tuple with the RunnerId field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetRunnerIdOk() (*string, bool) { - if o == nil || IsNil(o.RunnerId) { - return nil, false - } - return o.RunnerId, true -} - -// HasRunnerId returns a boolean if a field has been set. -func (o *Workspace) HasRunnerId() bool { - if o != nil && !IsNil(o.RunnerId) { - return true - } - - return false -} - -// SetRunnerId gets a reference to the given string and assigns it to the RunnerId field. -func (o *Workspace) SetRunnerId(v string) { - o.RunnerId = &v -} - -// GetToolboxProxyUrl returns the ToolboxProxyUrl field value -func (o *Workspace) GetToolboxProxyUrl() string { - if o == nil { - var ret string - return ret - } - - return o.ToolboxProxyUrl -} - -// GetToolboxProxyUrlOk returns a tuple with the ToolboxProxyUrl field value -// and a boolean to check if the value has been set. -func (o *Workspace) GetToolboxProxyUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.ToolboxProxyUrl, true -} - -// SetToolboxProxyUrl sets field value -func (o *Workspace) SetToolboxProxyUrl(v string) { - o.ToolboxProxyUrl = v -} - -// GetImage returns the Image field value if set, zero value otherwise. -func (o *Workspace) GetImage() string { - if o == nil || IsNil(o.Image) { - var ret string - return ret - } - return *o.Image -} - -// GetImageOk returns a tuple with the Image field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetImageOk() (*string, bool) { - if o == nil || IsNil(o.Image) { - return nil, false - } - return o.Image, true -} - -// HasImage returns a boolean if a field has been set. -func (o *Workspace) HasImage() bool { - if o != nil && !IsNil(o.Image) { - return true - } - - return false -} - -// SetImage gets a reference to the given string and assigns it to the Image field. -func (o *Workspace) SetImage(v string) { - o.Image = &v -} - -// GetSnapshotState returns the SnapshotState field value if set, zero value otherwise. -func (o *Workspace) GetSnapshotState() string { - if o == nil || IsNil(o.SnapshotState) { - var ret string - return ret - } - return *o.SnapshotState -} - -// GetSnapshotStateOk returns a tuple with the SnapshotState field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetSnapshotStateOk() (*string, bool) { - if o == nil || IsNil(o.SnapshotState) { - return nil, false - } - return o.SnapshotState, true -} - -// HasSnapshotState returns a boolean if a field has been set. -func (o *Workspace) HasSnapshotState() bool { - if o != nil && !IsNil(o.SnapshotState) { - return true - } - - return false -} - -// SetSnapshotState gets a reference to the given string and assigns it to the SnapshotState field. -func (o *Workspace) SetSnapshotState(v string) { - o.SnapshotState = &v -} - -// GetSnapshotCreatedAt returns the SnapshotCreatedAt field value if set, zero value otherwise. -func (o *Workspace) GetSnapshotCreatedAt() string { - if o == nil || IsNil(o.SnapshotCreatedAt) { - var ret string - return ret - } - return *o.SnapshotCreatedAt -} - -// GetSnapshotCreatedAtOk returns a tuple with the SnapshotCreatedAt field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetSnapshotCreatedAtOk() (*string, bool) { - if o == nil || IsNil(o.SnapshotCreatedAt) { - return nil, false - } - return o.SnapshotCreatedAt, true -} - -// HasSnapshotCreatedAt returns a boolean if a field has been set. -func (o *Workspace) HasSnapshotCreatedAt() bool { - if o != nil && !IsNil(o.SnapshotCreatedAt) { - return true - } - - return false -} - -// SetSnapshotCreatedAt gets a reference to the given string and assigns it to the SnapshotCreatedAt field. -func (o *Workspace) SetSnapshotCreatedAt(v string) { - o.SnapshotCreatedAt = &v -} - -// GetInfo returns the Info field value if set, zero value otherwise. -func (o *Workspace) GetInfo() SandboxInfo { - if o == nil || IsNil(o.Info) { - var ret SandboxInfo - return ret - } - return *o.Info -} - -// GetInfoOk returns a tuple with the Info field value if set, nil otherwise -// and a boolean to check if the value has been set. -func (o *Workspace) GetInfoOk() (*SandboxInfo, bool) { - if o == nil || IsNil(o.Info) { - return nil, false - } - return o.Info, true -} - -// HasInfo returns a boolean if a field has been set. -func (o *Workspace) HasInfo() bool { - if o != nil && !IsNil(o.Info) { - return true - } - - return false -} - -// SetInfo gets a reference to the given SandboxInfo and assigns it to the Info field. -func (o *Workspace) SetInfo(v SandboxInfo) { - o.Info = &v -} - -func (o Workspace) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o Workspace) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["id"] = o.Id - toSerialize["organizationId"] = o.OrganizationId - toSerialize["name"] = o.Name - if !IsNil(o.Snapshot) { - toSerialize["snapshot"] = o.Snapshot - } - toSerialize["user"] = o.User - toSerialize["env"] = o.Env - toSerialize["labels"] = o.Labels - toSerialize["public"] = o.Public - toSerialize["networkBlockAll"] = o.NetworkBlockAll - if !IsNil(o.NetworkAllowList) { - toSerialize["networkAllowList"] = o.NetworkAllowList - } - toSerialize["target"] = o.Target - toSerialize["cpu"] = o.Cpu - toSerialize["gpu"] = o.Gpu - toSerialize["memory"] = o.Memory - toSerialize["disk"] = o.Disk - if !IsNil(o.State) { - toSerialize["state"] = o.State - } - if !IsNil(o.DesiredState) { - toSerialize["desiredState"] = o.DesiredState - } - if !IsNil(o.ErrorReason) { - toSerialize["errorReason"] = o.ErrorReason - } - if !IsNil(o.Recoverable) { - toSerialize["recoverable"] = o.Recoverable - } - if !IsNil(o.BackupState) { - toSerialize["backupState"] = o.BackupState - } - if !IsNil(o.BackupCreatedAt) { - toSerialize["backupCreatedAt"] = o.BackupCreatedAt - } - if !IsNil(o.AutoStopInterval) { - toSerialize["autoStopInterval"] = o.AutoStopInterval - } - if !IsNil(o.AutoArchiveInterval) { - toSerialize["autoArchiveInterval"] = o.AutoArchiveInterval - } - if !IsNil(o.AutoDeleteInterval) { - toSerialize["autoDeleteInterval"] = o.AutoDeleteInterval - } - if !IsNil(o.Volumes) { - toSerialize["volumes"] = o.Volumes - } - if !IsNil(o.BuildInfo) { - toSerialize["buildInfo"] = o.BuildInfo - } - if !IsNil(o.CreatedAt) { - toSerialize["createdAt"] = o.CreatedAt - } - if !IsNil(o.UpdatedAt) { - toSerialize["updatedAt"] = o.UpdatedAt - } - if !IsNil(o.Class) { - toSerialize["class"] = o.Class - } - if !IsNil(o.DaemonVersion) { - toSerialize["daemonVersion"] = o.DaemonVersion - } - if !IsNil(o.RunnerId) { - toSerialize["runnerId"] = o.RunnerId - } - toSerialize["toolboxProxyUrl"] = o.ToolboxProxyUrl - if !IsNil(o.Image) { - toSerialize["image"] = o.Image - } - if !IsNil(o.SnapshotState) { - toSerialize["snapshotState"] = o.SnapshotState - } - if !IsNil(o.SnapshotCreatedAt) { - toSerialize["snapshotCreatedAt"] = o.SnapshotCreatedAt - } - if !IsNil(o.Info) { - toSerialize["info"] = o.Info - } - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *Workspace) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "id", - "organizationId", - "name", - "user", - "env", - "labels", - "public", - "networkBlockAll", - "target", - "cpu", - "gpu", - "memory", - "disk", - "toolboxProxyUrl", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varWorkspace := _Workspace{} - - err = json.Unmarshal(data, &varWorkspace) - - if err != nil { - return err - } - - *o = Workspace(varWorkspace) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "id") - delete(additionalProperties, "organizationId") - delete(additionalProperties, "name") - delete(additionalProperties, "snapshot") - delete(additionalProperties, "user") - delete(additionalProperties, "env") - delete(additionalProperties, "labels") - delete(additionalProperties, "public") - delete(additionalProperties, "networkBlockAll") - delete(additionalProperties, "networkAllowList") - delete(additionalProperties, "target") - delete(additionalProperties, "cpu") - delete(additionalProperties, "gpu") - delete(additionalProperties, "memory") - delete(additionalProperties, "disk") - delete(additionalProperties, "state") - delete(additionalProperties, "desiredState") - delete(additionalProperties, "errorReason") - delete(additionalProperties, "recoverable") - delete(additionalProperties, "backupState") - delete(additionalProperties, "backupCreatedAt") - delete(additionalProperties, "autoStopInterval") - delete(additionalProperties, "autoArchiveInterval") - delete(additionalProperties, "autoDeleteInterval") - delete(additionalProperties, "volumes") - delete(additionalProperties, "buildInfo") - delete(additionalProperties, "createdAt") - delete(additionalProperties, "updatedAt") - delete(additionalProperties, "class") - delete(additionalProperties, "daemonVersion") - delete(additionalProperties, "runnerId") - delete(additionalProperties, "toolboxProxyUrl") - delete(additionalProperties, "image") - delete(additionalProperties, "snapshotState") - delete(additionalProperties, "snapshotCreatedAt") - delete(additionalProperties, "info") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableWorkspace struct { - value *Workspace - isSet bool -} - -func (v NullableWorkspace) Get() *Workspace { - return v.value -} - -func (v *NullableWorkspace) Set(val *Workspace) { - v.value = val - v.isSet = true -} - -func (v NullableWorkspace) IsSet() bool { - return v.isSet -} - -func (v *NullableWorkspace) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableWorkspace(val *Workspace) *NullableWorkspace { - return &NullableWorkspace{value: val, isSet: true} -} - -func (v NullableWorkspace) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableWorkspace) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/model_workspace_port_preview_url.go b/apps/api-client-go/model_workspace_port_preview_url.go deleted file mode 100644 index 1f416ebec..000000000 --- a/apps/api-client-go/model_workspace_port_preview_url.go +++ /dev/null @@ -1,198 +0,0 @@ -/* -BoxLite - -BoxLite AI platform API Docs - -API version: 1.0 -Contact: support@boxlite.com -*/ - -// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. - -package apiclient - -import ( - "encoding/json" - "fmt" -) - -// checks if the WorkspacePortPreviewUrl type satisfies the MappedNullable interface at compile time -var _ MappedNullable = &WorkspacePortPreviewUrl{} - -// WorkspacePortPreviewUrl struct for WorkspacePortPreviewUrl -type WorkspacePortPreviewUrl struct { - // Preview url - Url string `json:"url"` - // Access token - Token string `json:"token"` - AdditionalProperties map[string]interface{} -} - -type _WorkspacePortPreviewUrl WorkspacePortPreviewUrl - -// NewWorkspacePortPreviewUrl instantiates a new WorkspacePortPreviewUrl object -// This constructor will assign default values to properties that have it defined, -// and makes sure properties required by API are set, but the set of arguments -// will change when the set of required properties is changed -func NewWorkspacePortPreviewUrl(url string, token string) *WorkspacePortPreviewUrl { - this := WorkspacePortPreviewUrl{} - this.Url = url - this.Token = token - return &this -} - -// NewWorkspacePortPreviewUrlWithDefaults instantiates a new WorkspacePortPreviewUrl object -// This constructor will only assign default values to properties that have it defined, -// but it doesn't guarantee that properties required by API are set -func NewWorkspacePortPreviewUrlWithDefaults() *WorkspacePortPreviewUrl { - this := WorkspacePortPreviewUrl{} - return &this -} - -// GetUrl returns the Url field value -func (o *WorkspacePortPreviewUrl) GetUrl() string { - if o == nil { - var ret string - return ret - } - - return o.Url -} - -// GetUrlOk returns a tuple with the Url field value -// and a boolean to check if the value has been set. -func (o *WorkspacePortPreviewUrl) GetUrlOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Url, true -} - -// SetUrl sets field value -func (o *WorkspacePortPreviewUrl) SetUrl(v string) { - o.Url = v -} - -// GetToken returns the Token field value -func (o *WorkspacePortPreviewUrl) GetToken() string { - if o == nil { - var ret string - return ret - } - - return o.Token -} - -// GetTokenOk returns a tuple with the Token field value -// and a boolean to check if the value has been set. -func (o *WorkspacePortPreviewUrl) GetTokenOk() (*string, bool) { - if o == nil { - return nil, false - } - return &o.Token, true -} - -// SetToken sets field value -func (o *WorkspacePortPreviewUrl) SetToken(v string) { - o.Token = v -} - -func (o WorkspacePortPreviewUrl) MarshalJSON() ([]byte, error) { - toSerialize,err := o.ToMap() - if err != nil { - return []byte{}, err - } - return json.Marshal(toSerialize) -} - -func (o WorkspacePortPreviewUrl) ToMap() (map[string]interface{}, error) { - toSerialize := map[string]interface{}{} - toSerialize["url"] = o.Url - toSerialize["token"] = o.Token - - for key, value := range o.AdditionalProperties { - toSerialize[key] = value - } - - return toSerialize, nil -} - -func (o *WorkspacePortPreviewUrl) UnmarshalJSON(data []byte) (err error) { - // This validates that all required properties are included in the JSON object - // by unmarshalling the object into a generic map with string keys and checking - // that every required field exists as a key in the generic map. - requiredProperties := []string{ - "url", - "token", - } - - allProperties := make(map[string]interface{}) - - err = json.Unmarshal(data, &allProperties) - - if err != nil { - return err; - } - - for _, requiredProperty := range(requiredProperties) { - if _, exists := allProperties[requiredProperty]; !exists { - return fmt.Errorf("no value given for required property %v", requiredProperty) - } - } - - varWorkspacePortPreviewUrl := _WorkspacePortPreviewUrl{} - - err = json.Unmarshal(data, &varWorkspacePortPreviewUrl) - - if err != nil { - return err - } - - *o = WorkspacePortPreviewUrl(varWorkspacePortPreviewUrl) - - additionalProperties := make(map[string]interface{}) - - if err = json.Unmarshal(data, &additionalProperties); err == nil { - delete(additionalProperties, "url") - delete(additionalProperties, "token") - o.AdditionalProperties = additionalProperties - } - - return err -} - -type NullableWorkspacePortPreviewUrl struct { - value *WorkspacePortPreviewUrl - isSet bool -} - -func (v NullableWorkspacePortPreviewUrl) Get() *WorkspacePortPreviewUrl { - return v.value -} - -func (v *NullableWorkspacePortPreviewUrl) Set(val *WorkspacePortPreviewUrl) { - v.value = val - v.isSet = true -} - -func (v NullableWorkspacePortPreviewUrl) IsSet() bool { - return v.isSet -} - -func (v *NullableWorkspacePortPreviewUrl) Unset() { - v.value = nil - v.isSet = false -} - -func NewNullableWorkspacePortPreviewUrl(val *WorkspacePortPreviewUrl) *NullableWorkspacePortPreviewUrl { - return &NullableWorkspacePortPreviewUrl{value: val, isSet: true} -} - -func (v NullableWorkspacePortPreviewUrl) MarshalJSON() ([]byte, error) { - return json.Marshal(v.value) -} - -func (v *NullableWorkspacePortPreviewUrl) UnmarshalJSON(src []byte) error { - v.isSet = true - return json.Unmarshal(src, &v.value) -} diff --git a/apps/api-client-go/project.json b/apps/api-client-go/project.json index 56064200c..b6abf19bf 100644 --- a/apps/api-client-go/project.json +++ b/apps/api-client-go/project.json @@ -32,8 +32,8 @@ "options": { "commands": [ "rm -f {projectRoot}/*.go", - "yarn run openapi-generator-cli generate --git-repo-id=boxlite/libs/api-client-go --git-user-id=boxlite-ai -i dist/apps/api/openapi.json -g go --additional-properties=disallowAdditionalPropertiesIfNotPresent=false,packageName=apiclient,moduleVersion=$DEFAULT_PACKAGE_VERSION,generateInterfaces=true,enumClassPrefix=true,structPrefix=true -o libs/api-client-go", - "bash hack/go-client/postprocess.sh libs/api-client-go apiclient api-client-go" + "yarn run openapi-generator-cli generate --git-repo-id=boxlite/libs/api-client-go --git-user-id=boxlite-ai -i dist/apps/api/openapi.json -g go -t hack/go-client/openapi-templates --additional-properties=disallowAdditionalPropertiesIfNotPresent=false,packageName=apiclient,moduleVersion=$DEFAULT_PACKAGE_VERSION,generateInterfaces=true,enumClassPrefix=true,structPrefix=true,enumUnknownDefaultCase=true -o api-client-go", + "bash hack/go-client/postprocess.sh api-client-go apiclient api-client-go" ], "parallel": false }, @@ -52,16 +52,14 @@ "executor": "nx:run-commands", "cache": true, "inputs": [ - "{workspaceRoot}/apps/cli/go.mod", "{workspaceRoot}/libs/sdk-go/go.mod", { "env": "VERSION" }, { "dependentTasksOutputFiles": "**/*", "transitive": true } ], - "outputs": ["{workspaceRoot}/apps/cli/go.mod", "{workspaceRoot}/libs/sdk-go/go.mod", "{projectRoot}/VERSION"], + "outputs": ["{workspaceRoot}/libs/sdk-go/go.mod", "{projectRoot}/VERSION"], "options": { "cwd": "{workspaceRoot}", "commands": [ - "cd apps/cli && go mod edit -require=github.com/boxlite-ai/boxlite/libs/api-client-go@$VERSION", "cd libs/sdk-go && go mod edit -require=github.com/boxlite-ai/boxlite/libs/api-client-go@$VERSION", "cd apps/otel-collector/exporter && go mod edit -require=github.com/boxlite-ai/boxlite/libs/api-client-go@$VERSION", "echo \"$VERSION\" > libs/api-client-go/VERSION" diff --git a/apps/api/.env.example b/apps/api/.env.example new file mode 100644 index 000000000..4aa0d6cce --- /dev/null +++ b/apps/api/.env.example @@ -0,0 +1,120 @@ +PORT=3001 + +DB_HOST=db +DB_PORT=5432 +DB_USERNAME=user +DB_PASSWORD=pass +DB_DATABASE=application_ctx + +REDIS_HOST=redis +REDIS_PORT=6379 + +OIDC_CLIENT_ID=boxlite +OIDC_ISSUER_BASE_URL=http://localhost:5556/dex +OIDC_AUDIENCE=boxlite + +OIDC_MANAGEMENT_API_ENABLED= +OIDC_MANAGEMENT_API_CLIENT_ID= +OIDC_MANAGEMENT_API_CLIENT_SECRET= +OIDC_MANAGEMENT_API_AUDIENCE= + +DEFAULT_SNAPSHOT=ubuntu:22.04 +DASHBOARD_URL=http://localhost:3000/dashboard +DASHBOARD_BASE_API_URL=http://localhost:3000 + +POSTHOG_API_KEY= +POSTHOG_HOST= + +TRANSIENT_REGISTRY_URL=http://registry:5000 +TRANSIENT_REGISTRY_ADMIN=admin +TRANSIENT_REGISTRY_PASSWORD=password +TRANSIENT_REGISTRY_PROJECT_ID=boxlite + +INTERNAL_REGISTRY_URL=http://registry:5000 +INTERNAL_REGISTRY_ADMIN=admin +INTERNAL_REGISTRY_PASSWORD=password +INTERNAL_REGISTRY_PROJECT_ID=boxlite + +SMTP_HOST=maildev +SMTP_PORT=1025 +SMTP_USER= +SMTP_PASSWORD= +SMTP_SECURE= +SMTP_EMAIL_FROM="BoxLite Team " + +S3_ENDPOINT=http://minio:9000 +S3_STS_ENDPOINT=http://minio:9000/minio/v1/assume-role +S3_REGION=us-east-1 +S3_ACCESS_KEY=minioadmin +S3_SECRET_KEY=minioadmin +S3_DEFAULT_BUCKET=boxlite +S3_ACCOUNT_ID=/ +S3_ROLE_NAME=/ + +ENVIRONMENT=dev + +MAX_AUTO_ARCHIVE_INTERVAL=43200 + +OTEL_ENABLED=true +OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318 + +MAINTENANCE_MODE=false + +PROXY_DOMAIN=proxy.localhost:4000 +PROXY_PROTOCOL=http +PROXY_API_KEY=super_secret_key + +AUDIT_TOOLBOX_REQUESTS_ENABLED=false +AUDIT_LOG_RETENTION_DAYS= +AUDIT_CONSOLE_LOG_ENABLED= + +# see https://www.iana.org/time-zones +CRON_TIMEZONE= + +SSH_GATEWAY_API_KEY=ssh_secret_api_token +SSH_GATEWAY_COMMAND="ssh -p 2222 {{TOKEN}}@localhost" +SSH_GATEWAY_PUBLIC_KEY= +SSH_GATEWAY_URL="localhost:2222" + +PROXY_TEMPLATE_URL=http://{{PORT}}-{{boxId}}.proxy.localhost:4000 +PROXY_TOOLBOX_BASE_URL=http://proxy.localhost:4000 + +PYLON_APP_ID= + +BILLING_API_URL= + +DEFAULT_RUNNER_DOMAIN=localhost:3003 +DEFAULT_RUNNER_API_URL=http://localhost:3003 +DEFAULT_RUNNER_PROXY_URL=http://localhost:3003 +DEFAULT_RUNNER_API_KEY=secret_api_token +DEFAULT_RUNNER_CPU=4 +DEFAULT_RUNNER_MEMORY=8 +DEFAULT_RUNNER_DISK=50 +DEFAULT_RUNNER_NAME=default + +API_KEY_VALIDATION_CACHE_TTL_SECONDS=10 +API_KEY_USER_CACHE_TTL_SECONDS=60 + +ADMIN_API_KEY=supersecret +ADMIN_TOTAL_CPU_QUOTA=10 +ADMIN_TOTAL_MEMORY_QUOTA=10 +ADMIN_TOTAL_DISK_QUOTA=10 +ADMIN_MAX_CPU_PER_BOX=4 +ADMIN_MAX_MEMORY_PER_BOX=8 +ADMIN_MAX_DISK_PER_BOX=10 +ADMIN_SNAPSHOT_QUOTA=100 +ADMIN_MAX_SNAPSHOT_SIZE=100 +ADMIN_VOLUME_QUOTA=100 + +SKIP_USER_EMAIL_VERIFICATION=true + +OTEL_COLLECTOR_API_KEY=otel_collector_api_key + +BOX_OTEL_ENDPOINT_URL=http://host.docker.internal:4318 + +ENCRYPTION_KEY=supersecretkey +ENCRYPTION_SALT=supersecretsalt + +HEALTH_CHECK_API_KEY=supersecretkey + + diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index bc846e762..6ae897f96 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,38 +1,61 @@ -FROM node:24-slim AS boxlite -# build: 2026-04-28-v2 +# syntax=docker/dockerfile:1 + +FROM node:24-slim AS base ENV CI=true RUN apt-get update && apt-get install -y --no-install-recommends bash curl && \ rm -rf /var/lib/apt/lists/* RUN npm install -g corepack && corepack enable -WORKDIR /boxlite +WORKDIR /boxlite/apps +FROM base AS deps COPY apps/package.json apps/yarn.lock apps/.yarnrc.yml ./ RUN yarn install --immutable -COPY apps/nx.json apps/tsconfig.base.json ./ +FROM base AS runtime-deps +ENV NODE_ENV=production +COPY apps/package.json apps/yarn.lock apps/.yarnrc.yml ./ +RUN yarn workspaces focus --all --production + +FROM deps AS build +COPY apps/nx.json apps/tsconfig.base.json apps/NOTICE ./ -ARG CACHE_BUST=1 ENV NX_DAEMON=false ENV NX_SKIP_NX_CACHE=true -COPY apps/api/ apps/api/ -COPY apps/dashboard/ apps/dashboard/ +COPY apps/api/ api/ +COPY apps/dashboard/ dashboard/ COPY apps/libs/runner-api-client/ libs/runner-api-client/ COPY apps/libs/api-client/ libs/api-client/ COPY apps/libs/analytics-api-client/ libs/analytics-api-client/ -COPY apps/libs/toolbox-api-client/ libs/toolbox-api-client/ -COPY apps/libs/sdk-typescript/ libs/sdk-typescript/ -RUN echo 1777389170 && yarn nx build api --configuration=production --nxBail=true +RUN yarn nx build api --configuration=production --nxBail=true --output-style=stream && node --check dist/apps/api/main.js RUN VITE_BASE_API_URL=%BOXLITE_BASE_API_URL% yarn nx build dashboard --configuration=production --nxBail=true --output-style=stream +FROM node:24-slim AS boxlite +ENV CI=true +ENV NODE_ENV=production + +RUN apt-get update && apt-get install -y --no-install-recommends bash curl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /boxlite/apps + +COPY --from=runtime-deps --chown=node:node /boxlite/apps/package.json ./package.json +COPY --from=runtime-deps --chown=node:node /boxlite/apps/node_modules ./node_modules +COPY --from=build --chown=node:node /boxlite/apps/NOTICE ./NOTICE +COPY --from=build --chown=node:node /boxlite/apps/dist/apps/api ./dist/apps/api +COPY --from=build --chown=node:node /boxlite/apps/dist/apps/dashboard ./dist/apps/dashboard + ARG VERSION=0.0.1 ENV VERSION=${VERSION} +EXPOSE 3000 HEALTHCHECK CMD [ "curl", "-f", "http://localhost:3000/api/config" ] +USER node + ENTRYPOINT ["node", "dist/apps/api/main.js"] diff --git a/apps/api/eslint.config.mjs b/apps/api/eslint.config.mjs index 316a00478..8fc224c72 100644 --- a/apps/api/eslint.config.mjs +++ b/apps/api/eslint.config.mjs @@ -1,4 +1,4 @@ -import baseConfig from '../../eslint.config.mjs' +import baseConfig from '../eslint.config.mjs' export default [ ...baseConfig, @@ -8,9 +8,8 @@ export default [ 'no-restricted-syntax': [ 'error', { - selector: - 'Decorator[expression.callee.name="InjectRepository"] > CallExpression > Identifier[name="Sandbox"]', - message: 'Do not use @InjectRepository(Sandbox). Use the custom SandboxRepository instead.', + selector: 'Decorator[expression.callee.name="InjectRepository"] > CallExpression > Identifier[name="Box"]', + message: 'Do not use @InjectRepository(Box). Use the custom BoxRepository instead.', }, ], }, diff --git a/apps/api/jest.config.ts b/apps/api/jest.config.ts index 907e2772b..bb4be2f03 100644 --- a/apps/api/jest.config.ts +++ b/apps/api/jest.config.ts @@ -6,11 +6,14 @@ export default { displayName: 'boxlite', - preset: '../../jest.preset.js', + preset: '../jest.preset.js', testEnvironment: 'node', transform: { '^.+\\.[tj]s$': ['ts-jest', { tsconfig: '/tsconfig.spec.json' }], }, + // uuid v14 and nanoid v5 ship ESM-only. The nx preset ignores node_modules + // from transformation, so let ts-jest down-level them. + transformIgnorePatterns: ['/node_modules/(?!(?:uuid|nanoid)/)'], moduleFileExtensions: ['ts', 'js', 'html'], coverageDirectory: '../../coverage/apps/boxlite', } diff --git a/apps/api/project.json b/apps/api/project.json index 0581cada9..41e65a60e 100644 --- a/apps/api/project.json +++ b/apps/api/project.json @@ -1,7 +1,7 @@ { "name": "api", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/api/src", + "sourceRoot": "api/src", "projectType": "application", "tags": [], "targets": { @@ -10,22 +10,25 @@ "options": { "outputPath": "dist/apps/api", "deleteOutputPath": false, - "main": "apps/api/src/main.ts", - "tsConfig": "apps/api/tsconfig.app.json", - "generatePackageJson": true, + "main": "api/src/main.ts", + "tsConfig": "api/tsconfig.app.json", + "generatePackageJson": false, "target": "node", "compiler": "tsc", "sourceMap": true, - "webpackConfig": "apps/api/webpack.config.js", + "webpackConfig": "api/webpack.config.js", "assets": [ { - "input": "apps/api/src/assets", + "input": "api/src/assets", "glob": "**/*", "output": "./assets/" } ] }, "configurations": { + "development": { + "skipTypeChecking": true + }, "production": { "optimization": true, "extractLicenses": true, @@ -49,11 +52,12 @@ "options": { "commands": [ "mkdir -p dist/apps/api", - "yarn ts-node apps/api/src/generate-openapi.ts -o dist/apps/api/openapi.json" + "yarn ts-node api/src/generate-openapi.ts -o dist/apps/api/openapi.json" ], "parallel": false, "env": { - "TS_NODE_PROJECT": "apps/api/tsconfig.app.json", + "TS_NODE_PROJECT": "api/tsconfig.app.json", + "TS_NODE_TRANSPILE_ONLY": "true", "NODE_OPTIONS": "--require tsconfig-paths/register", "SKIP_CONNECTIONS": "true" } @@ -62,7 +66,6 @@ "serve": { "executor": "@nx/js:node", "defaultConfiguration": "development", - "dependsOn": ["build"], "options": { "buildTarget": "api:build", "runBuildTargetDependencies": false, @@ -91,20 +94,16 @@ "check-version-env": {}, "docker": { "options": { - "target": "boxlite" - }, - "dependsOn": [ - { - "target": "build-amd64", - "projects": "runner" - } - ] + "target": "boxlite", + "context": "..", + "file": "apps/api/Dockerfile" + } }, "push-manifest": {}, "lint": { "executor": "nx:run-commands", "options": { - "command": "bash apps/api/scripts/validate-migration-paths.sh $(find apps/api/src/migrations -name '*-migration.ts' -type f)" + "command": "bash api/scripts/validate-migration-paths.sh $(find api/src/migrations -name '*-migration.ts' -type f)" } } } diff --git a/apps/api/scripts/validate-migration-paths.sh b/apps/api/scripts/validate-migration-paths.sh index e5815434b..7f2efd1d3 100755 --- a/apps/api/scripts/validate-migration-paths.sh +++ b/apps/api/scripts/validate-migration-paths.sh @@ -15,17 +15,23 @@ LEGACY_CUTOFF=1770880371265 forbidden=() for f in "$@"; do - rel="$(realpath --relative-to="$PWD" "$f")" - - case "$rel" in - apps/api/src/migrations/pre-deploy/*-migration.ts) ;; - apps/api/src/migrations/post-deploy/*-migration.ts) ;; - apps/api/src/migrations/*/*-migration.ts) + rel="${f#"$PWD"/}" + rel="${rel#./}" + migration_path="${rel#*src/migrations/}" + + if [ "$migration_path" = "$rel" ]; then + continue + fi + + case "$migration_path" in + pre-deploy/*-migration.ts) ;; + post-deploy/*-migration.ts) ;; + */*-migration.ts) forbidden+=("$rel") ;; - apps/api/src/migrations/*-migration.ts) - timestamp=$(basename "$rel" | grep -oP '^\d+') - if [ -n "$timestamp" ] && [ "$timestamp" -le "$LEGACY_CUTOFF" ]; then + *-migration.ts) + timestamp="${migration_path%%-*}" + if [[ "$timestamp" =~ ^[0-9]+$ ]] && [ "$timestamp" -le "$LEGACY_CUTOFF" ]; then continue fi forbidden+=("$rel") diff --git a/apps/api/src/admin/admin.module.ts b/apps/api/src/admin/admin.module.ts index 4b7d718fd..e7cd92d31 100644 --- a/apps/api/src/admin/admin.module.ts +++ b/apps/api/src/admin/admin.module.ts @@ -5,14 +5,39 @@ */ import { Module } from '@nestjs/common' +import { AdminObservabilityController } from './controllers/observability.controller' +import { AdminOverviewController } from './controllers/overview.controller' import { AdminRunnerController } from './controllers/runner.controller' -import { AdminSandboxController } from './controllers/sandbox.controller' -import { SandboxModule } from '../sandbox/sandbox.module' +import { AdminBoxController } from './controllers/box.controller' +import { + ADMIN_AUDIT_LOG_READER, + ADMIN_CLOUDWATCH_LOG_READER, + ADMIN_PLATFORM_STATE_READER, + ADMIN_S3_OBJECT_READER, + AdminObservabilityService, +} from './services/observability.service' +import { AdminCloudWatchLogReader } from './services/observability-cloudwatch.reader' +import { AdminOverviewService } from './services/overview.service' +import { AdminS3ObjectReader } from './services/observability-s3.reader' +import { BoxModule } from '../box/box.module' import { RegionModule } from '../region/region.module' import { OrganizationModule } from '../organization/organization.module' +import { UserModule } from '../user/user.module' +import { AuditModule } from '../audit/audit.module' +import { AuditService } from '../audit/services/audit.service' @Module({ - imports: [SandboxModule, RegionModule, OrganizationModule], - controllers: [AdminRunnerController, AdminSandboxController], + imports: [BoxModule, RegionModule, OrganizationModule, UserModule, AuditModule], + controllers: [AdminRunnerController, AdminBoxController, AdminOverviewController, AdminObservabilityController], + providers: [ + AdminOverviewService, + AdminObservabilityService, + AdminCloudWatchLogReader, + AdminS3ObjectReader, + { provide: ADMIN_PLATFORM_STATE_READER, useExisting: AdminOverviewService }, + { provide: ADMIN_AUDIT_LOG_READER, useExisting: AuditService }, + { provide: ADMIN_CLOUDWATCH_LOG_READER, useExisting: AdminCloudWatchLogReader }, + { provide: ADMIN_S3_OBJECT_READER, useExisting: AdminS3ObjectReader }, + ], }) export class AdminModule {} diff --git a/apps/api/src/admin/controllers/box.controller.ts b/apps/api/src/admin/controllers/box.controller.ts new file mode 100644 index 000000000..a1efe5b2d --- /dev/null +++ b/apps/api/src/admin/controllers/box.controller.ts @@ -0,0 +1,61 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, HttpCode, NotFoundException, Param, Post, UseGuards } from '@nestjs/common' +import { ApiBearerAuth, ApiOAuth2, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger' +import { Audit } from '../../audit/decorators/audit.decorator' +import { AuditAction } from '../../audit/enums/audit-action.enum' +import { AuditTarget } from '../../audit/enums/audit-target.enum' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { SystemActionGuard } from '../../auth/system-action.guard' +import { RequiredApiRole } from '../../common/decorators/required-role.decorator' +import { OrganizationService } from '../../organization/services/organization.service' +import { BoxDto } from '../../box/dto/box.dto' +import { BoxService } from '../../box/services/box.service' +import { SystemRole } from '../../user/enums/system-role.enum' + +@ApiTags('admin') +@Controller('admin/box') +@UseGuards(CombinedAuthGuard, SystemActionGuard) +@RequiredApiRole([SystemRole.ADMIN]) +@ApiOAuth2(['openid', 'profile', 'email']) +@ApiBearerAuth() +export class AdminBoxController { + constructor( + private readonly boxService: BoxService, + private readonly organizationService: OrganizationService, + ) {} + + @Post(':boxId/recover') + @HttpCode(200) + @ApiOperation({ + summary: 'Recover box from error state as an admin', + operationId: 'adminRecoverBox', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Recovery initiated', + type: BoxDto, + }) + @Audit({ + action: AuditAction.RECOVER, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxId, + targetIdFromResult: (result: BoxDto) => result?.id, + }) + async recoverBox(@Param('boxId') boxId: string): Promise { + const organization = await this.organizationService.findByBoxId(boxId) + if (!organization) { + throw new NotFoundException('Box not found') + } + const recoveredBox = await this.boxService.recover(boxId, organization) + return this.boxService.toBoxDto(recoveredBox) + } +} diff --git a/apps/api/src/admin/controllers/observability.controller.spec.ts b/apps/api/src/admin/controllers/observability.controller.spec.ts new file mode 100644 index 000000000..6db3d671f --- /dev/null +++ b/apps/api/src/admin/controllers/observability.controller.spec.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Request } from 'express' +import { GUARDS_METADATA } from '@nestjs/common/constants' +import { AUDIT_CONTEXT_KEY, AuditContext } from '../../audit/decorators/audit.decorator' +import { AuditAction } from '../../audit/enums/audit-action.enum' +import { AuditTarget } from '../../audit/enums/audit-target.enum' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { SystemActionGuard } from '../../auth/system-action.guard' +import { RequiredApiRole } from '../../common/decorators/required-role.decorator' +import { SystemRole } from '../../user/enums/system-role.enum' +import { AdminObservabilityController } from './observability.controller' + +describe('AdminObservabilityController audit metadata', () => { + function auditContext(methodName: keyof AdminObservabilityController): AuditContext { + return Reflect.getMetadata(AUDIT_CONTEXT_KEY, AdminObservabilityController.prototype[methodName]) + } + + it('audits investigate with a scoped target id and sanitized query metadata', () => { + const context = auditContext('investigate') + const request = { + query: { + traceId: 'trace-1', + boxId: 'box-1', + runnerId: 'runner-1', + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + search: 'secret token', + }, + } as unknown as Request + + expect(context).toMatchObject({ + action: AuditAction.READ, + targetType: AuditTarget.OBSERVABILITY, + }) + expect(context.targetIdFromRequest?.(request)).toBe('traceId:trace-1') + expect(context.requestMetadata?.surface(request)).toBe('admin_observability') + expect(context.requestMetadata?.query(request)).toEqual({ + traceId: 'trace-1', + boxId: 'box-1', + runnerId: 'runner-1', + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + search: { present: true, length: 12 }, + }) + }) + + it('audits trace span detail reads with the path trace id', () => { + const context = auditContext('getTraceSpans') + const request = { + params: { traceId: 'trace-path-1' }, + query: { boxId: 'box-1' }, + } as unknown as Request + + expect(context.targetIdFromRequest?.(request)).toBe('traceId:trace-path-1') + expect(context.requestMetadata?.query(request)).toEqual({ boxId: 'box-1' }) + }) +}) + +describe('AdminObservabilityController access control', () => { + it('requires authenticated Admin API access for all observability endpoints', () => { + const guards = Reflect.getMetadata(GUARDS_METADATA, AdminObservabilityController) + const requiredApiRoles = Reflect.getMetadata(RequiredApiRole.KEY, AdminObservabilityController) + + expect(guards).toEqual([CombinedAuthGuard, SystemActionGuard]) + expect(requiredApiRoles).toEqual([SystemRole.ADMIN]) + }) +}) diff --git a/apps/api/src/admin/controllers/observability.controller.ts b/apps/api/src/admin/controllers/observability.controller.ts new file mode 100644 index 000000000..43e78a5f4 --- /dev/null +++ b/apps/api/src/admin/controllers/observability.controller.ts @@ -0,0 +1,206 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, Get, HttpCode, Param, Query, UseGuards } from '@nestjs/common' +import { ApiBearerAuth, ApiOAuth2, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger' +import { Request } from 'express' +import { Audit } from '../../audit/decorators/audit.decorator' +import { AuditAction } from '../../audit/enums/audit-action.enum' +import { AuditTarget } from '../../audit/enums/audit-target.enum' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { SystemActionGuard } from '../../auth/system-action.guard' +import { RequiredApiRole } from '../../common/decorators/required-role.decorator' +import { MetricsResponseDto } from '../../box-telemetry/dto/metrics-response.dto' +import { PaginatedLogsDto } from '../../box-telemetry/dto/paginated-logs.dto' +import { PaginatedTracesDto } from '../../box-telemetry/dto/paginated-traces.dto' +import { TraceSpanDto } from '../../box-telemetry/dto/trace-span.dto' +import { SystemRole } from '../../user/enums/system-role.enum' +import { + AdminObservabilityLogsQueryParamsDto, + AdminObservabilityMetricsQueryParamsDto, + AdminObservabilityQueryParamsDto, +} from '../dto/observability-query.dto' +import { + AdminObservabilityInvestigateQueryParamsDto, + AdminObservabilityInvestigateResponseDto, +} from '../dto/observability-investigate.dto' +import { AdminObservabilityStatusDto } from '../dto/observability-status.dto' +import { AdminObservabilityService } from '../services/observability.service' + +const OBSERVABILITY_AUDIT_QUERY_KEYS = [ + 'from', + 'to', + 'page', + 'limit', + 'layer', + 'serviceName', + 'orgId', + 'userId', + 'boxId', + 'runnerId', + 'machineId', + 'traceId', + 'requestId', + 'operationId', + 'executionId', + 'jobId', + 'severities', + 'metricNames', +] as const + +const OBSERVABILITY_TARGET_ID_KEYS = [ + 'traceId', + 'userId', + 'boxId', + 'runnerId', + 'machineId', + 'executionId', + 'jobId', + 'requestId', + 'operationId', +] as const + +function firstQueryValue(value: unknown): string | undefined { + const resolvedValue = Array.isArray(value) ? value[0] : value + return typeof resolvedValue === 'string' && resolvedValue.length > 0 ? resolvedValue : undefined +} + +function resolveObservabilityTargetId(req: Request): string | undefined { + for (const key of OBSERVABILITY_TARGET_ID_KEYS) { + const value = firstQueryValue(req.query[key]) + if (value) { + return `${key}:${value}` + } + } + + return undefined +} + +function buildObservabilityAuditQuery(req: Request): Record { + const metadata: Record = {} + for (const key of OBSERVABILITY_AUDIT_QUERY_KEYS) { + const value = req.query[key] + if (value !== undefined) { + metadata[key] = value + } + } + + const search = firstQueryValue(req.query.search) + if (search) { + metadata.search = { present: true, length: search.length } + } + + return metadata +} + +const OBSERVABILITY_READ_AUDIT = { + action: AuditAction.READ, + targetType: AuditTarget.OBSERVABILITY, + targetIdFromRequest: resolveObservabilityTargetId, + requestMetadata: { + surface: () => 'admin_observability', + query: buildObservabilityAuditQuery, + }, +} + +@ApiTags('admin') +@Controller('admin/observability') +@UseGuards(CombinedAuthGuard, SystemActionGuard) +@RequiredApiRole([SystemRole.ADMIN]) +@ApiOAuth2(['openid', 'profile', 'email']) +@ApiBearerAuth() +export class AdminObservabilityController { + constructor(private readonly observabilityService: AdminObservabilityService) {} + + @Get('status') + @HttpCode(200) + @ApiOperation({ + summary: 'Get admin observability backend and layer status', + operationId: 'adminGetObservabilityStatus', + }) + @ApiResponse({ status: 200, type: AdminObservabilityStatusDto }) + @Audit(OBSERVABILITY_READ_AUDIT) + async getStatus(): Promise { + return this.observabilityService.getStatus() + } + + @Get('logs') + @HttpCode(200) + @ApiOperation({ + summary: 'Get admin-scoped logs', + operationId: 'adminGetObservabilityLogs', + }) + @ApiResponse({ status: 200, type: PaginatedLogsDto }) + @Audit(OBSERVABILITY_READ_AUDIT) + async getLogs(@Query() queryParams: AdminObservabilityLogsQueryParamsDto): Promise { + return this.observabilityService.getLogs(queryParams) + } + + @Get('traces') + @HttpCode(200) + @ApiOperation({ + summary: 'Get admin-scoped traces', + operationId: 'adminGetObservabilityTraces', + }) + @ApiResponse({ status: 200, type: PaginatedTracesDto }) + @Audit(OBSERVABILITY_READ_AUDIT) + async getTraces(@Query() queryParams: AdminObservabilityQueryParamsDto): Promise { + return this.observabilityService.getTraces({ + ...queryParams, + page: queryParams.page ?? 1, + limit: queryParams.limit ?? 100, + }) + } + + @Get('traces/:traceId') + @HttpCode(200) + @ApiOperation({ + summary: 'Get admin-scoped trace spans', + operationId: 'adminGetObservabilityTraceSpans', + }) + @ApiParam({ name: 'traceId', type: 'string' }) + @ApiResponse({ status: 200, type: [TraceSpanDto] }) + @Audit({ + ...OBSERVABILITY_READ_AUDIT, + targetIdFromRequest: (req) => `traceId:${req.params.traceId}`, + }) + async getTraceSpans( + @Param('traceId') traceId: string, + @Query() queryParams: AdminObservabilityQueryParamsDto, + ): Promise { + return this.observabilityService.getTraceSpans(traceId, queryParams) + } + + @Get('metrics') + @HttpCode(200) + @ApiOperation({ + summary: 'Get admin-scoped metrics', + operationId: 'adminGetObservabilityMetrics', + }) + @ApiResponse({ status: 200, type: MetricsResponseDto }) + @Audit(OBSERVABILITY_READ_AUDIT) + async getMetrics(@Query() queryParams: AdminObservabilityMetricsQueryParamsDto): Promise { + return this.observabilityService.getMetrics(queryParams) + } + + @Get('investigate') + @HttpCode(200) + @ApiOperation({ + summary: 'Investigate related observability and platform state from trace or resource identifiers', + operationId: 'adminInvestigateObservability', + }) + @ApiResponse({ status: 200, type: AdminObservabilityInvestigateResponseDto }) + @Audit(OBSERVABILITY_READ_AUDIT) + async investigate( + @Query() queryParams: AdminObservabilityInvestigateQueryParamsDto, + ): Promise { + return this.observabilityService.investigate({ + ...queryParams, + page: queryParams.page ?? 1, + limit: queryParams.limit ?? 100, + }) + } +} diff --git a/apps/api/src/admin/controllers/overview.controller.ts b/apps/api/src/admin/controllers/overview.controller.ts new file mode 100644 index 000000000..80149197d --- /dev/null +++ b/apps/api/src/admin/controllers/overview.controller.ts @@ -0,0 +1,100 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, Get, HttpCode, UseGuards } from '@nestjs/common' +import { ApiBearerAuth, ApiOAuth2, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { SystemActionGuard } from '../../auth/system-action.guard' +import { RequiredApiRole } from '../../common/decorators/required-role.decorator' +import { SystemRole } from '../../user/enums/system-role.enum' +import { AdminOverviewService } from '../services/overview.service' +import { + AdminBoxItemDto, + AdminMachineItemDto, + AdminOverviewDto, + AdminRunnerItemDto, + AdminUserItemDto, +} from '../dto/admin-overview.dto' + +@ApiTags('admin') +@Controller('admin/overview') +@UseGuards(CombinedAuthGuard, SystemActionGuard) +@RequiredApiRole([SystemRole.ADMIN]) +@ApiOAuth2(['openid', 'profile', 'email']) +@ApiBearerAuth() +export class AdminOverviewController { + constructor(private readonly adminOverviewService: AdminOverviewService) {} + + @Get() + @HttpCode(200) + @ApiOperation({ + summary: 'Admin KPI summary', + operationId: 'adminGetOverview', + }) + @ApiResponse({ + status: 200, + type: AdminOverviewDto, + }) + async getOverview(): Promise { + return this.adminOverviewService.getOverview() + } + + @Get('users') + @HttpCode(200) + @ApiOperation({ + summary: 'List all users (cross-org)', + operationId: 'adminListUsers', + }) + @ApiResponse({ + status: 200, + type: [AdminUserItemDto], + }) + async listUsers(): Promise { + return this.adminOverviewService.listUsers() + } + + @Get('boxes') + @HttpCode(200) + @ApiOperation({ + summary: 'List all boxes (cross-org)', + operationId: 'adminListBoxes', + }) + @ApiResponse({ + status: 200, + type: [AdminBoxItemDto], + }) + async listBoxes(): Promise { + return this.adminOverviewService.listBoxes() + } + + @Get('runners') + @HttpCode(200) + @ApiOperation({ + summary: 'List all runners with full details', + operationId: 'adminListRunnersOverview', + }) + @ApiResponse({ + status: 200, + type: [AdminRunnerItemDto], + }) + async listRunners(): Promise { + return this.adminOverviewService.listRunners() + } + + @Get('machines') + @HttpCode(200) + @ApiOperation({ + summary: 'Runner-as-machine resource view', + operationId: 'adminListMachines', + }) + @ApiResponse({ + status: 200, + type: [AdminMachineItemDto], + }) + async listMachines(): Promise { + return this.adminOverviewService.listMachines() + } +} diff --git a/apps/api/src/admin/controllers/runner.controller.spec.ts b/apps/api/src/admin/controllers/runner.controller.spec.ts new file mode 100644 index 000000000..3b319c18f --- /dev/null +++ b/apps/api/src/admin/controllers/runner.controller.spec.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { AdminRunnerController } from './runner.controller' + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'uuid-test'), + validate: jest.fn(() => true), +})) + +describe('AdminRunnerController', () => { + function buildController() { + const runnerService = { + findOneFullOrFail: jest.fn().mockResolvedValue({ + id: 'runner-1', + state: 'ready', + apiKey: 'runner-secret-key', + }), + findAllFull: jest.fn().mockResolvedValue([ + { + id: 'runner-1', + state: 'ready', + apiKey: 'runner-secret-key', + }, + ]), + findAllByRegionFull: jest.fn().mockResolvedValue([ + { + id: 'runner-1', + state: 'ready', + apiKey: 'runner-secret-key', + }, + ]), + } + const regionService = { + findOne: jest.fn(), + } + + return { + runnerService, + controller: new AdminRunnerController(runnerService as any, regionService as any), + } + } + + it('redacts runner api keys from list and detail responses', async () => { + const { controller } = buildController() + + const detail = await controller.getRunnerById('00000000-0000-4000-8000-000000000000') + const list = await controller.findAll() + const regionalList = await controller.findAll('us') + + expect(detail).not.toHaveProperty('apiKey') + expect(list[0]).not.toHaveProperty('apiKey') + expect(regionalList[0]).not.toHaveProperty('apiKey') + expect(JSON.stringify({ detail, list, regionalList })).not.toContain('runner-secret-key') + }) +}) diff --git a/apps/api/src/admin/controllers/runner.controller.ts b/apps/api/src/admin/controllers/runner.controller.ts index d7ff299d3..0d4986333 100644 --- a/apps/api/src/admin/controllers/runner.controller.ts +++ b/apps/api/src/admin/controllers/runner.controller.ts @@ -20,6 +20,7 @@ import { } from '@nestjs/common' import { ApiBearerAuth, ApiOAuth2, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger' import { AdminCreateRunnerDto } from '../dto/create-runner.dto' +import { AdminRunnerDto } from '../dto/admin-overview.dto' import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../../audit/decorators/audit.decorator' import { AuditAction } from '../../audit/enums/audit-action.enum' import { AuditTarget } from '../../audit/enums/audit-target.enum' @@ -27,10 +28,9 @@ import { CombinedAuthGuard } from '../../auth/combined-auth.guard' import { SystemActionGuard } from '../../auth/system-action.guard' import { RequiredApiRole } from '../../common/decorators/required-role.decorator' import { RegionService } from '../../region/services/region.service' -import { CreateRunnerResponseDto } from '../../sandbox/dto/create-runner-response.dto' -import { RunnerFullDto } from '../../sandbox/dto/runner-full.dto' -import { RunnerDto } from '../../sandbox/dto/runner.dto' -import { RunnerService } from '../../sandbox/services/runner.service' +import { CreateRunnerResponseDto } from '../../box/dto/create-runner-response.dto' +import { RunnerDto } from '../../box/dto/runner.dto' +import { RunnerService } from '../../box/services/runner.service' import { SystemRole } from '../../user/enums/system-role.enum' @ApiTags('admin') @@ -102,15 +102,15 @@ export class AdminRunnerController { }) @ApiResponse({ status: 200, - type: RunnerFullDto, + type: AdminRunnerDto, }) @ApiParam({ name: 'id', description: 'Runner ID', type: String, }) - async getRunnerById(@Param('id', ParseUUIDPipe) id: string): Promise { - return this.runnerService.findOneFullOrFail(id) + async getRunnerById(@Param('id', ParseUUIDPipe) id: string): Promise { + return this.toAdminRunnerDto(await this.runnerService.findOneFullOrFail(id)) } @Get() @@ -121,7 +121,7 @@ export class AdminRunnerController { }) @ApiResponse({ status: 200, - type: [RunnerFullDto], + type: [AdminRunnerDto], }) @ApiQuery({ name: 'regionId', @@ -129,11 +129,11 @@ export class AdminRunnerController { type: String, required: false, }) - async findAll(@Query('regionId') regionId?: string): Promise { + async findAll(@Query('regionId') regionId?: string): Promise { if (regionId) { - return this.runnerService.findAllByRegionFull(regionId) + return (await this.runnerService.findAllByRegionFull(regionId)).map((runner) => this.toAdminRunnerDto(runner)) } - return this.runnerService.findAllFull() + return (await this.runnerService.findAllFull()).map((runner) => this.toAdminRunnerDto(runner)) } @Patch(':id/scheduling') @@ -184,4 +184,9 @@ export class AdminRunnerController { async delete(@Param('id', ParseUUIDPipe) id: string): Promise { return this.runnerService.remove(id) } + + private toAdminRunnerDto(runner: AdminRunnerDto & { apiKey?: string }): AdminRunnerDto { + const { apiKey: _apiKey, ...safeRunner } = runner + return safeRunner + } } diff --git a/apps/api/src/admin/controllers/sandbox.controller.ts b/apps/api/src/admin/controllers/sandbox.controller.ts deleted file mode 100644 index ee0a0161c..000000000 --- a/apps/api/src/admin/controllers/sandbox.controller.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Controller, HttpCode, NotFoundException, Param, Post, UseGuards } from '@nestjs/common' -import { ApiBearerAuth, ApiOAuth2, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger' -import { Audit } from '../../audit/decorators/audit.decorator' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { SystemActionGuard } from '../../auth/system-action.guard' -import { RequiredApiRole } from '../../common/decorators/required-role.decorator' -import { OrganizationService } from '../../organization/services/organization.service' -import { SandboxDto } from '../../sandbox/dto/sandbox.dto' -import { SandboxService } from '../../sandbox/services/sandbox.service' -import { SystemRole } from '../../user/enums/system-role.enum' - -@ApiTags('admin') -@Controller('admin/sandbox') -@UseGuards(CombinedAuthGuard, SystemActionGuard) -@RequiredApiRole([SystemRole.ADMIN]) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class AdminSandboxController { - constructor( - private readonly sandboxService: SandboxService, - private readonly organizationService: OrganizationService, - ) {} - - @Post(':sandboxId/recover') - @HttpCode(200) - @ApiOperation({ - summary: 'Recover sandbox from error state as an admin', - operationId: 'adminRecoverSandbox', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Recovery initiated', - type: SandboxDto, - }) - @Audit({ - action: AuditAction.RECOVER, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async recoverSandbox(@Param('sandboxId') sandboxId: string): Promise { - const organization = await this.organizationService.findBySandboxId(sandboxId) - if (!organization) { - throw new NotFoundException('Sandbox not found') - } - const recoveredSandbox = await this.sandboxService.recover(sandboxId, organization) - return this.sandboxService.toSandboxDto(recoveredSandbox) - } -} diff --git a/apps/api/src/admin/dto/admin-overview.dto.ts b/apps/api/src/admin/dto/admin-overview.dto.ts new file mode 100644 index 000000000..f985316f9 --- /dev/null +++ b/apps/api/src/admin/dto/admin-overview.dto.ts @@ -0,0 +1,174 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' +import { RegionType } from '../../region/enums/region-type.enum' +import { RunnerDto } from '../../box/dto/runner.dto' +import { SystemRole } from '../../user/enums/system-role.enum' +import { BoxState } from '../../box/enums/box-state.enum' + +// ─── KPI summary ──────────────────────────────────────────────────────────── + +@ApiSchema({ name: 'AdminOverviewRunners' }) +export class AdminOverviewRunnersDto { + @ApiProperty({ description: 'Number of online (ready) runners', example: 3 }) + online: number + + @ApiProperty({ description: 'Total number of runners', example: 5 }) + total: number + + @ApiProperty({ description: 'Number of draining runners', example: 1 }) + draining: number +} + +@ApiSchema({ name: 'AdminOverviewCluster' }) +export class AdminOverviewClusterDto { + @ApiProperty({ description: 'Average CPU utilisation across all runners (0–1)', example: 0.42 }) + cpuUtil: number + + @ApiProperty({ description: 'Average CPU oversell ratio (allocated / capacity); 0 when no capacity', example: 1.1 }) + oversell: number +} + +@ApiSchema({ name: 'AdminOverviewBoxes' }) +export class AdminOverviewBoxesDto { + @ApiProperty({ description: 'Total number of boxes across all states', example: 80 }) + total: number + + @ApiProperty({ + description: 'Box counts keyed by BoxState', + example: { started: 15, error: 2, build_failed: 1, stopped: 62 }, + additionalProperties: { type: 'number' }, + }) + byState: Record +} + +@ApiSchema({ name: 'AdminOverview' }) +export class AdminOverviewDto { + @ApiProperty({ description: 'Total number of users', example: 120 }) + users: number + + @ApiProperty({ description: 'Number of active (started) boxes', example: 47 }) + activeBoxes: number + + @ApiProperty({ type: AdminOverviewBoxesDto }) + boxes: AdminOverviewBoxesDto + + @ApiProperty({ type: AdminOverviewRunnersDto }) + runners: AdminOverviewRunnersDto + + @ApiProperty({ type: AdminOverviewClusterDto }) + cluster: AdminOverviewClusterDto +} + +// ─── User list ─────────────────────────────────────────────────────────────── + +@ApiSchema({ name: 'AdminUserItem' }) +export class AdminUserItemDto { + @ApiProperty({ description: 'User ID', example: 'usr_abc123' }) + id: string + + @ApiProperty({ description: 'User email', example: 'alice@example.com' }) + email: string + + @ApiProperty({ description: 'Display name', example: 'Alice Smith' }) + name: string + + @ApiProperty({ enum: SystemRole, enumName: 'SystemRole', example: SystemRole.USER }) + role: SystemRole +} + +// ─── Box list ──────────────────────────────────────────────────────────────── + +@ApiSchema({ name: 'AdminBoxOwner' }) +export class AdminBoxOwnerDto { + @ApiPropertyOptional({ description: 'Creator/user ID behind this owner group when available', example: 'usr_abc123' }) + userId?: string + + @ApiProperty({ description: 'Display name for the owner group', example: 'Alice Smith' }) + name: string + + @ApiProperty({ + description: 'Owner email for personal organizations, blank when unavailable', + example: 'alice@example.com', + }) + email: string + + @ApiProperty({ description: 'Organization name backing this box', example: 'Alice Personal' }) + orgName: string + + @ApiProperty({ description: 'Whether this is a personal organization', example: true }) + personal: boolean +} + +@ApiSchema({ name: 'AdminBoxItem' }) +export class AdminBoxItemDto { + @ApiProperty({ description: 'Box ID', example: 'box_abc123' }) + id: string + + @ApiProperty({ description: 'Organization ID', example: 'org_xyz' }) + organizationId: string + + @ApiProperty({ enum: BoxState, enumName: 'BoxState', example: BoxState.STARTED }) + state: BoxState + + @ApiPropertyOptional({ description: 'Runner ID the box is assigned to', example: 'runner-uuid' }) + runnerId?: string + + @ApiProperty({ description: 'Allocated CPU (vCPUs)', example: 2 }) + cpu: number + + @ApiPropertyOptional({ description: 'Allocated memory in GiB', example: 4 }) + memoryGiB?: number + + @ApiProperty({ description: 'Creation timestamp', example: '2024-01-01T00:00:00Z' }) + createdAt: string + + @ApiProperty({ type: AdminBoxOwnerDto }) + owner: AdminBoxOwnerDto +} + +// ─── Runner admin item (safe RunnerDto + draining flag) ────────────────────── + +@ApiSchema({ name: 'AdminRunner' }) +export class AdminRunnerDto extends RunnerDto { + @ApiPropertyOptional({ + description: 'The region type of the runner', + enum: RegionType, + enumName: 'RegionType', + example: Object.values(RegionType)[0], + }) + regionType?: RegionType +} + +@ApiSchema({ name: 'AdminRunnerItem' }) +export class AdminRunnerItemDto extends AdminRunnerDto { + @ApiProperty({ description: 'Whether the runner is currently draining', example: false }) + draining: boolean +} + +// ─── Machine (runner-as-machine) view ──────────────────────────────────────── + +@ApiSchema({ name: 'AdminMachineItem' }) +export class AdminMachineItemDto { + @ApiProperty({ description: 'Runner / host ID', example: 'runner-uuid' }) + host: string + + @ApiProperty({ description: 'Region ID', example: 'us-east-1' }) + region: string + + @ApiProperty({ description: 'CPU oversell ratio (allocatedCpu / totalCpu); 0 when capacity is 0', example: 0.75 }) + oversellCpu: number + + @ApiProperty({ description: 'CPU utilisation waterline (0–100)', example: 45.6 }) + cpuWaterline: number + + @ApiProperty({ description: 'Memory utilisation waterline (0–100)', example: 68.2 }) + memWaterline: number + + @ApiProperty({ description: 'Number of currently started boxes on this runner', example: 5 }) + boxes: number +} diff --git a/apps/api/src/admin/dto/create-runner.dto.ts b/apps/api/src/admin/dto/create-runner.dto.ts index 80cf43bb6..dbb0b8699 100644 --- a/apps/api/src/admin/dto/create-runner.dto.ts +++ b/apps/api/src/admin/dto/create-runner.dto.ts @@ -6,7 +6,7 @@ import { IsNumber, IsOptional, IsString } from 'class-validator' import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { CreateRunnerDto } from '../../sandbox/dto/create-runner.dto' +import { CreateRunnerDto } from '../../box/dto/create-runner.dto' @ApiSchema({ name: 'AdminCreateRunner' }) export class AdminCreateRunnerDto extends CreateRunnerDto { diff --git a/apps/api/src/admin/dto/observability-investigate.dto.ts b/apps/api/src/admin/dto/observability-investigate.dto.ts new file mode 100644 index 000000000..f62df7c97 --- /dev/null +++ b/apps/api/src/admin/dto/observability-investigate.dto.ts @@ -0,0 +1,392 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' +import { LogEntryDto } from '../../box-telemetry/dto/log-entry.dto' +import { MetricsResponseDto } from '../../box-telemetry/dto/metrics-response.dto' +import { TraceSpanDto } from '../../box-telemetry/dto/trace-span.dto' +import { AdminBoxItemDto, AdminMachineItemDto, AdminRunnerItemDto } from './admin-overview.dto' +import { AdminObservabilityQueryParamsDto } from './observability-query.dto' + +export const ADMIN_OBSERVABILITY_SOURCES = [ + 'clickhouse', + 'clickstack', + 'postgres', + 'audit', + 'cloudwatch', + 's3', + 'xlog', +] as const +export type AdminObservabilitySource = (typeof ADMIN_OBSERVABILITY_SOURCES)[number] + +export const ADMIN_OBSERVABILITY_SOURCE_STATES = ['available', 'missing', 'stale', 'not_configured', 'error'] as const +export type AdminObservabilitySourceState = (typeof ADMIN_OBSERVABILITY_SOURCE_STATES)[number] + +@ApiSchema({ name: 'AdminObservabilityInvestigateQuery' }) +export class AdminObservabilityInvestigateQueryParamsDto extends AdminObservabilityQueryParamsDto {} + +@ApiSchema({ name: 'AdminObservabilityCorrelation' }) +export class AdminObservabilityCorrelationDto { + @ApiProperty({ type: [String] }) + traceIds: string[] + + @ApiProperty({ type: [String] }) + orgIds: string[] + + @ApiProperty({ type: [String] }) + userIds: string[] + + @ApiProperty({ type: [String] }) + boxIds: string[] + + @ApiProperty({ type: [String] }) + runnerIds: string[] + + @ApiProperty({ type: [String] }) + machineIds: string[] + + @ApiProperty({ type: [String] }) + requestIds: string[] + + @ApiProperty({ type: [String] }) + operationIds: string[] + + @ApiProperty({ type: [String] }) + executionIds: string[] + + @ApiProperty({ type: [String] }) + jobIds: string[] + + @ApiProperty({ type: [String] }) + serviceNames: string[] +} + +@ApiSchema({ name: 'AdminObservabilitySourceStatus' }) +export class AdminObservabilitySourceStatusDto { + @ApiProperty({ enum: ADMIN_OBSERVABILITY_SOURCES }) + source: AdminObservabilitySource + + @ApiProperty({ enum: ADMIN_OBSERVABILITY_SOURCE_STATES }) + state: AdminObservabilitySourceState + + @ApiPropertyOptional() + message?: string + + @ApiPropertyOptional() + count?: number +} + +@ApiSchema({ name: 'AdminObservabilityResourceSummary' }) +export class AdminObservabilityResourceSummaryDto { + @ApiProperty({ + enum: ['box', 'runner', 'machine', 'trace', 'request', 'operation', 'execution', 'job', 'org', 'user', 'unknown'], + }) + type: + | 'box' + | 'runner' + | 'machine' + | 'trace' + | 'request' + | 'operation' + | 'execution' + | 'job' + | 'org' + | 'user' + | 'unknown' + + @ApiProperty() + title: string + + @ApiPropertyOptional() + subtitle?: string + + @ApiPropertyOptional() + state?: string + + @ApiPropertyOptional() + owner?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + identifiers?: Record + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + timeRange?: { from?: string; to?: string } +} + +@ApiSchema({ name: 'AdminObservabilityTimelineEvent' }) +export class AdminObservabilityTimelineEventDto { + @ApiProperty() + timestamp: string + + @ApiProperty() + source: string + + @ApiProperty() + title: string + + @ApiPropertyOptional() + detail?: string + + @ApiPropertyOptional() + severity?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + identifiers?: Record +} + +@ApiSchema({ name: 'AdminObservabilityOperation' }) +export class AdminObservabilityOperationDto { + @ApiProperty() + id: string + + @ApiProperty() + label: string + + @ApiProperty({ + enum: ['enabled', 'disabled', 'request_only'], + }) + state: 'enabled' | 'disabled' | 'request_only' + + @ApiProperty() + method: string + + @ApiProperty() + path: string + + @ApiProperty() + reason: string + + @ApiPropertyOptional() + targetId?: string +} + +@ApiSchema({ name: 'AdminObservabilityCommands' }) +export class AdminObservabilityCommandsDto { + @ApiProperty() + api: string + + @ApiProperty() + aiAgentPrompt: string +} + +@ApiSchema({ name: 'AdminObservabilityClickStackSourceSetup' }) +export class AdminObservabilityClickStackSourceSetupDto { + @ApiProperty({ enum: ['logs', 'traces', 'metrics'] }) + kind: 'logs' | 'traces' | 'metrics' + + @ApiProperty() + envVar: string + + @ApiProperty() + name: string + + @ApiProperty() + dataType: string + + @ApiProperty() + database: string + + @ApiPropertyOptional() + table?: string + + @ApiProperty() + timestampColumn: string + + @ApiPropertyOptional() + defaultSelect?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + fields?: Record + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + metricTables?: Record +} + +@ApiSchema({ name: 'AdminObservabilityClickStackLinks' }) +export class AdminObservabilityClickStackLinksDto { + @ApiProperty() + configured: boolean + + @ApiPropertyOptional() + message?: string + + @ApiPropertyOptional({ type: [String] }) + missingSources?: string[] + + @ApiPropertyOptional({ type: [AdminObservabilityClickStackSourceSetupDto] }) + sourceSetup?: AdminObservabilityClickStackSourceSetupDto[] + + @ApiPropertyOptional() + logsUrl?: string + + @ApiPropertyOptional() + dashboardUrl?: string + + @ApiPropertyOptional() + tracesUrl?: string + + @ApiPropertyOptional() + metricsUrl?: string + + @ApiPropertyOptional() + query?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + queryContext?: Record +} + +@ApiSchema({ name: 'AdminObservabilityExternalLinks' }) +export class AdminObservabilityExternalLinksDto { + @ApiProperty({ type: AdminObservabilityClickStackLinksDto }) + clickstack: AdminObservabilityClickStackLinksDto +} + +@ApiSchema({ name: 'AdminObservabilityAuditLog' }) +export class AdminObservabilityAuditLogDto { + @ApiProperty() + id: string + + @ApiProperty() + actorId: string + + @ApiProperty() + actorEmail: string + + @ApiPropertyOptional() + organizationId?: string + + @ApiProperty() + action: string + + @ApiPropertyOptional() + targetType?: string + + @ApiPropertyOptional() + targetId?: string + + @ApiPropertyOptional() + statusCode?: number + + @ApiPropertyOptional() + errorMessage?: string + + @ApiPropertyOptional() + source?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + metadata?: Record + + @ApiProperty() + createdAt: Date +} + +@ApiSchema({ name: 'AdminObservabilityXLog' }) +export class AdminObservabilityXLogDto { + @ApiProperty() + source: string + + @ApiProperty() + timestamp: string + + @ApiProperty() + serviceName: string + + @ApiProperty() + body: string + + @ApiPropertyOptional() + severityText?: string + + @ApiPropertyOptional() + traceId?: string + + @ApiPropertyOptional() + spanId?: string + + @ApiPropertyOptional() + executionId?: string + + @ApiPropertyOptional() + jobId?: string + + @ApiPropertyOptional() + stream?: string + + @ApiPropertyOptional({ type: 'object', additionalProperties: true }) + attributes?: Record +} + +@ApiSchema({ name: 'AdminObservabilityS3Object' }) +export class AdminObservabilityS3ObjectDto { + @ApiProperty() + bucket: string + + @ApiProperty() + key: string + + @ApiPropertyOptional() + size?: number + + @ApiPropertyOptional() + lastModified?: Date + + @ApiPropertyOptional() + etag?: string + + @ApiPropertyOptional() + matchedBy?: string +} + +@ApiSchema({ name: 'AdminObservabilityInvestigateResponse' }) +export class AdminObservabilityInvestigateResponseDto { + @ApiProperty({ type: AdminObservabilityResourceSummaryDto }) + resource: AdminObservabilityResourceSummaryDto + + @ApiProperty({ type: AdminObservabilityCorrelationDto }) + correlation: AdminObservabilityCorrelationDto + + @ApiProperty({ type: [AdminObservabilitySourceStatusDto] }) + sources: AdminObservabilitySourceStatusDto[] + + @ApiProperty({ type: [TraceSpanDto] }) + traceSpans: TraceSpanDto[] + + @ApiProperty({ type: [LogEntryDto] }) + logs: LogEntryDto[] + + @ApiProperty({ type: MetricsResponseDto }) + metrics: MetricsResponseDto + + @ApiProperty({ type: [AdminBoxItemDto] }) + boxes: AdminBoxItemDto[] + + @ApiProperty({ type: [AdminRunnerItemDto] }) + runners: AdminRunnerItemDto[] + + @ApiProperty({ type: [AdminMachineItemDto] }) + machines: AdminMachineItemDto[] + + @ApiProperty({ type: [AdminObservabilityAuditLogDto] }) + auditLogs: AdminObservabilityAuditLogDto[] + + @ApiProperty({ type: [AdminObservabilityXLogDto] }) + xlogs: AdminObservabilityXLogDto[] + + @ApiProperty({ type: [AdminObservabilityS3ObjectDto] }) + s3Objects: AdminObservabilityS3ObjectDto[] + + @ApiProperty({ type: [AdminObservabilityTimelineEventDto] }) + timeline: AdminObservabilityTimelineEventDto[] + + @ApiProperty({ type: [AdminObservabilityOperationDto] }) + operations: AdminObservabilityOperationDto[] + + @ApiProperty({ type: AdminObservabilityCommandsDto }) + commands: AdminObservabilityCommandsDto + + @ApiProperty({ type: AdminObservabilityExternalLinksDto }) + externalLinks: AdminObservabilityExternalLinksDto +} diff --git a/apps/api/src/admin/dto/observability-query.dto.spec.ts b/apps/api/src/admin/dto/observability-query.dto.spec.ts new file mode 100644 index 000000000..b85382ade --- /dev/null +++ b/apps/api/src/admin/dto/observability-query.dto.spec.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { validate } from 'class-validator' +import { AdminObservabilityLogsQueryParamsDto } from './observability-query.dto' + +describe('AdminObservabilityQueryParamsDto', () => { + it('allows callers to omit from/to so the API can apply the default lookback window', async () => { + const query = new AdminObservabilityLogsQueryParamsDto() + query.limit = 5 + + const errors = await validate(query) + + expect(errors.map((error) => error.property)).not.toContain('from') + expect(errors.map((error) => error.property)).not.toContain('to') + }) + + it('accepts userId as an admin observability correlation filter', async () => { + const query = new AdminObservabilityLogsQueryParamsDto() + query.userId = 'user-1' + + const errors = await validate(query) + + expect(errors.map((error) => error.property)).not.toContain('userId') + }) +}) diff --git a/apps/api/src/admin/dto/observability-query.dto.ts b/apps/api/src/admin/dto/observability-query.dto.ts new file mode 100644 index 000000000..b461ef88a --- /dev/null +++ b/apps/api/src/admin/dto/observability-query.dto.ts @@ -0,0 +1,127 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiPropertyOptional } from '@nestjs/swagger' +import { Transform, Type } from 'class-transformer' +import { IsArray, IsDateString, IsIn, IsNumber, IsOptional, IsString, Min } from 'class-validator' + +export const OBSERVABILITY_LAYERS = ['api', 'runner', 'ec2_host', 'box'] as const +export type ObservabilityLayer = (typeof OBSERVABILITY_LAYERS)[number] + +export class AdminObservabilityQueryParamsDto { + @ApiPropertyOptional({ type: String, format: 'date-time', description: 'Start of time range (ISO 8601)' }) + @IsOptional() + @IsDateString() + from?: string + + @ApiPropertyOptional({ type: String, format: 'date-time', description: 'End of time range (ISO 8601)' }) + @IsOptional() + @IsDateString() + to?: string + + @ApiPropertyOptional({ type: Number, default: 1, description: 'Page number (1-indexed)' }) + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + page?: number = 1 + + @ApiPropertyOptional({ type: Number, default: 100, description: 'Number of items per page' }) + @IsOptional() + @Type(() => Number) + @IsNumber() + @Min(1) + limit?: number = 100 + + @ApiPropertyOptional({ enum: OBSERVABILITY_LAYERS, description: 'Telemetry producer layer' }) + @IsOptional() + @IsIn(OBSERVABILITY_LAYERS) + layer?: ObservabilityLayer + + @ApiPropertyOptional({ type: String, description: 'OpenTelemetry service.name filter' }) + @IsOptional() + @IsString() + serviceName?: string + + @ApiPropertyOptional({ type: String, description: 'Organization ID filter' }) + @IsOptional() + @IsString() + orgId?: string + + @ApiPropertyOptional({ type: String, description: 'User ID filter' }) + @IsOptional() + @IsString() + userId?: string + + @ApiPropertyOptional({ type: String, description: 'Box ID filter' }) + @IsOptional() + @IsString() + boxId?: string + + @ApiPropertyOptional({ type: String, description: 'Runner ID filter' }) + @IsOptional() + @IsString() + runnerId?: string + + @ApiPropertyOptional({ type: String, description: 'Machine or host ID filter' }) + @IsOptional() + @IsString() + machineId?: string + + @ApiPropertyOptional({ type: String, description: 'Trace ID filter' }) + @IsOptional() + @IsString() + traceId?: string + + @ApiPropertyOptional({ type: String, description: 'Request ID filter' }) + @IsOptional() + @IsString() + requestId?: string + + @ApiPropertyOptional({ type: String, description: 'Operation ID filter' }) + @IsOptional() + @IsString() + operationId?: string + + @ApiPropertyOptional({ type: String, description: 'Execution ID filter' }) + @IsOptional() + @IsString() + executionId?: string + + @ApiPropertyOptional({ type: String, description: 'Job ID filter' }) + @IsOptional() + @IsString() + jobId?: string +} + +export class AdminObservabilityLogsQueryParamsDto extends AdminObservabilityQueryParamsDto { + @ApiPropertyOptional({ + type: [String], + description: 'Filter by severity levels (DEBUG, INFO, WARN, ERROR)', + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) + @Transform(({ value }) => (Array.isArray(value) ? value : [value])) + severities?: string[] + + @ApiPropertyOptional({ type: String, description: 'Search in log body' }) + @IsOptional() + @IsString() + search?: string +} + +export class AdminObservabilityMetricsQueryParamsDto extends AdminObservabilityQueryParamsDto { + @ApiPropertyOptional({ + type: [String], + description: 'Filter by metric names', + }) + @IsOptional() + @IsArray() + @IsString({ each: true }) + @Transform(({ value }) => (Array.isArray(value) ? value : [value])) + metricNames?: string[] +} diff --git a/apps/api/src/admin/dto/observability-status.dto.ts b/apps/api/src/admin/dto/observability-status.dto.ts new file mode 100644 index 000000000..b811826f4 --- /dev/null +++ b/apps/api/src/admin/dto/observability-status.dto.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty } from '@nestjs/swagger' +import { OBSERVABILITY_LAYERS, ObservabilityLayer } from './observability-query.dto' + +export const OBSERVABILITY_STATES = ['missing', 'configured', 'receiving', 'stale', 'error'] as const +export type ObservabilityState = (typeof OBSERVABILITY_STATES)[number] + +export class AdminObservabilityBackendStatusDto { + @ApiProperty({ description: 'Whether ClickHouse/ClickStack query configuration is present' }) + configured: boolean + + @ApiProperty({ enum: OBSERVABILITY_STATES }) + state: ObservabilityState + + @ApiProperty({ required: false }) + message?: string +} + +export class AdminObservabilityLayerSignalsDto { + @ApiProperty({ enum: OBSERVABILITY_STATES }) + logs: ObservabilityState + + @ApiProperty({ enum: OBSERVABILITY_STATES }) + traces: ObservabilityState + + @ApiProperty({ enum: OBSERVABILITY_STATES }) + metrics: ObservabilityState +} + +export class AdminObservabilityLayerStatusDto { + @ApiProperty({ enum: OBSERVABILITY_LAYERS }) + layer: ObservabilityLayer + + @ApiProperty({ enum: OBSERVABILITY_STATES }) + state: ObservabilityState + + @ApiProperty({ type: AdminObservabilityLayerSignalsDto }) + signals: AdminObservabilityLayerSignalsDto + + @ApiProperty({ required: false }) + lastSeen?: string +} + +export class AdminObservabilityStatusDto { + @ApiProperty({ type: AdminObservabilityBackendStatusDto }) + backend: AdminObservabilityBackendStatusDto + + @ApiProperty({ type: [AdminObservabilityLayerStatusDto] }) + layers: AdminObservabilityLayerStatusDto[] +} diff --git a/apps/api/src/admin/services/observability-cloudwatch.reader.ts b/apps/api/src/admin/services/observability-cloudwatch.reader.ts new file mode 100644 index 000000000..9a03c6d28 --- /dev/null +++ b/apps/api/src/admin/services/observability-cloudwatch.reader.ts @@ -0,0 +1,298 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable } from '@nestjs/common' +import { CloudWatchLogsClient, DescribeLogGroupsCommand, FilterLogEventsCommand } from '@aws-sdk/client-cloudwatch-logs' +import { TypedConfigService } from '../../config/typed-config.service' +import { LogEntryDto } from '../../box-telemetry/dto/log-entry.dto' +import { + AdminObservabilityCorrelationDto, + AdminObservabilityInvestigateQueryParamsDto, + AdminObservabilitySourceStatusDto, +} from '../dto/observability-investigate.dto' + +interface CloudWatchFilterResponse { + events?: Array<{ + eventId?: string + ingestionTime?: number + logStreamName?: string + message?: string + timestamp?: number + }> +} + +@Injectable() +export class AdminCloudWatchLogReader { + private readonly clients = new Map() + + constructor(private readonly configService: TypedConfigService) {} + + async getRelatedLogs( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ logs: LogEntryDto[]; status: AdminObservabilitySourceStatusDto }> { + const logGroups = this.configService.get('adminObservability.cloudwatch.logGroups') + const logGroupPrefix = this.configService.get('adminObservability.cloudwatch.logGroupPrefix') + const maxLogGroups = this.configService.get('adminObservability.cloudwatch.maxLogGroups') || 20 + const region = this.configService.get('adminObservability.cloudwatch.region') + const limitPerGroup = this.configService.get('adminObservability.cloudwatch.limitPerGroup') || 25 + + if (!region || (logGroups.length === 0 && !logGroupPrefix)) { + return { + logs: [], + status: { + source: 'cloudwatch', + state: 'not_configured', + message: + 'ADMIN_OBSERVABILITY_CLOUDWATCH_LOG_GROUPS or ADMIN_OBSERVABILITY_CLOUDWATCH_LOG_GROUP_PREFIX is not configured', + count: 0, + }, + } + } + + const terms = this.buildSearchTerms(query, correlation) + if (terms.length === 0) { + return { + logs: [], + status: { + source: 'cloudwatch', + state: 'available', + message: 'CloudWatch is configured, but no correlation identifiers were available for fallback search', + count: 0, + }, + } + } + + const { from, to } = this.buildTimeRange(query) + const events = new Map() + const resolvedLogGroups = + logGroups.length > 0 || !logGroupPrefix + ? logGroups + : await this.describeLogGroups(region, logGroupPrefix, maxLogGroups) + + if (resolvedLogGroups.length === 0) { + return { + logs: [], + status: { + source: 'cloudwatch', + state: 'available', + message: 'CloudWatch is configured, but no log groups matched the configured prefix', + count: 0, + }, + } + } + + for (const logGroupName of resolvedLogGroups) { + for (const term of terms.slice(0, 6)) { + const response = await this.filterLogEvents(region, { + logGroupName, + filterPattern: this.quoteFilterTerm(term), + startTime: from.getTime(), + endTime: to.getTime(), + limit: Math.max(1, Math.min(limitPerGroup, 100)), + }) + + for (const event of response.events ?? []) { + const key = `${logGroupName}:${event.logStreamName ?? ''}:${event.timestamp ?? ''}:${event.eventId ?? ''}` + if (!events.has(key)) { + events.set(key, this.toLogEntry(logGroupName, term, event)) + } + } + } + } + + const logs = Array.from(events.values()).sort((a, b) => b.timestamp.localeCompare(a.timestamp)) + return { + logs, + status: { + source: 'cloudwatch', + state: 'available', + count: logs.length, + ...(logs.length === 0 ? { message: 'No CloudWatch stdout entries matched the current correlation' } : {}), + }, + } + } + + private async filterLogEvents( + region: string, + body: { + logGroupName: string + filterPattern: string + startTime: number + endTime: number + limit: number + }, + ): Promise { + return this.client(region).send(new FilterLogEventsCommand(body)) + } + + private async describeLogGroups(region: string, prefix: string, maxLogGroups: number): Promise { + const response = await this.client(region).send( + new DescribeLogGroupsCommand({ + logGroupNamePrefix: prefix, + limit: Math.max(1, Math.min(maxLogGroups, 50)), + }), + ) + return (response.logGroups ?? []) + .map((group) => group.logGroupName) + .filter((name): name is string => Boolean(name)) + .slice(0, maxLogGroups) + } + + private client(region: string): CloudWatchLogsClient { + let client = this.clients.get(region) + if (!client) { + client = new CloudWatchLogsClient({ region }) + this.clients.set(region, client) + } + return client + } + + private toLogEntry( + logGroupName: string, + matchedBy: string, + event: NonNullable[number], + ): LogEntryDto { + const parsed = this.tryParseMessage(event.message ?? '') + const body = parsed.body ?? event.message ?? '' + const resourceAttributes = { + 'boxlite.source': 'cloudwatch', + 'boxlite.layer': this.inferLayer(logGroupName), + 'aws.log_group': logGroupName, + 'aws.log_stream': event.logStreamName ?? '', + } + const logAttributes = { + 'cloudwatch.event_id': event.eventId ?? '', + 'cloudwatch.ingestion_time': event.ingestionTime ? new Date(event.ingestionTime).toISOString() : '', + 'cloudwatch.matched_by': matchedBy, + ...parsed.attributes, + } + + return { + timestamp: event.timestamp ? new Date(event.timestamp).toISOString() : new Date().toISOString(), + body, + severityText: parsed.severityText ?? this.inferSeverity(body), + serviceName: parsed.serviceName ?? `cloudwatch:${logGroupName.split('/').filter(Boolean).pop() ?? 'stdout'}`, + resourceAttributes, + logAttributes, + traceId: parsed.traceId ?? this.extractTraceId(body), + spanId: parsed.spanId, + } + } + + private tryParseMessage(message: string): { + body?: string + severityText?: string + serviceName?: string + traceId?: string + spanId?: string + attributes: Record + } { + try { + const payload = JSON.parse(message) as Record + const attributes: Record = {} + for (const [key, value] of Object.entries(payload)) { + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { + attributes[key] = String(value) + } + } + return { + body: this.readString(payload, ['msg', 'message', 'body']), + severityText: this.normalizeSeverity(this.readString(payload, ['level', 'severity', 'severityText'])), + serviceName: this.readString(payload, ['serviceName', 'service_name']), + traceId: this.readString(payload, ['traceId', 'trace_id']), + spanId: this.readString(payload, ['spanId', 'span_id']), + attributes, + } + } catch { + return { attributes: {} } + } + } + + private readString(payload: Record, keys: string[]): string | undefined { + for (const key of keys) { + const value = payload[key] + if (typeof value === 'string' && value.trim()) { + return value.trim() + } + if (typeof value === 'number') { + return String(value) + } + } + return undefined + } + + private inferLayer(logGroupName: string): string { + if (logGroupName.includes('/Api')) return 'api' + if (logGroupName.includes('/SshGateway') || logGroupName.includes('/Proxy')) return 'runner' + if (logGroupName.includes('/OtelCollector')) return 'ec2_host' + return '' + } + + private inferSeverity(message: string): string { + const normalized = message.toLowerCase() + if (normalized.includes('error') || normalized.includes('exception')) return 'ERROR' + if (normalized.includes('warn')) return 'WARN' + if (normalized.includes('debug')) return 'DEBUG' + return 'INFO' + } + + private normalizeSeverity(value?: string): string | undefined { + if (!value) return undefined + const normalized = value.toUpperCase() + if (normalized === '10') return 'TRACE' + if (normalized === '20') return 'DEBUG' + if (normalized === '30') return 'INFO' + if (normalized === '40') return 'WARN' + if (normalized === '50') return 'ERROR' + if (normalized === '60') return 'FATAL' + return normalized + } + + private extractTraceId(message: string): string | undefined { + return message.match(/\b[0-9a-f]{32}\b/i)?.[0] + } + + private buildSearchTerms( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): string[] { + return Array.from( + new Set( + [ + query.traceId, + query.requestId, + query.operationId, + query.executionId, + query.jobId, + query.boxId, + query.runnerId, + query.machineId, + query.serviceName, + ...correlation.traceIds, + ...correlation.requestIds, + ...correlation.operationIds, + ...correlation.executionIds, + ...correlation.jobIds, + ...correlation.boxIds, + ...correlation.runnerIds, + ...correlation.machineIds, + ...correlation.serviceNames, + ].filter((value): value is string => typeof value === 'string' && value.trim().length >= 3), + ), + ) + } + + private quoteFilterTerm(term: string): string { + return `"${term.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"` + } + + private buildTimeRange(query: AdminObservabilityInvestigateQueryParamsDto): { from: Date; to: Date } { + const to = query.to ? new Date(query.to) : new Date() + const from = query.from ? new Date(query.from) : new Date(to.getTime() - 60 * 60 * 1000) + return { from, to } + } +} diff --git a/apps/api/src/admin/services/observability-s3.reader.spec.ts b/apps/api/src/admin/services/observability-s3.reader.spec.ts new file mode 100644 index 000000000..4151df418 --- /dev/null +++ b/apps/api/src/admin/services/observability-s3.reader.spec.ts @@ -0,0 +1,137 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' +import { AdminObservabilityCorrelationDto } from '../dto/observability-investigate.dto' +import { AdminS3ObjectReader } from './observability-s3.reader' + +const mockSend = jest.fn() + +jest.mock('@aws-sdk/client-s3', () => ({ + S3Client: jest.fn().mockImplementation(() => ({ send: mockSend })), + ListObjectsV2Command: jest.fn().mockImplementation((input) => ({ input })), +})) + +describe('AdminS3ObjectReader', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + function buildReader(values: Record) { + const configService = { + get: jest.fn((key: string) => values[key]), + } + return { + configService, + reader: new AdminS3ObjectReader(configService as any), + } + } + + function buildCorrelation( + overrides: Partial = {}, + ): AdminObservabilityCorrelationDto { + return { + traceIds: [], + orgIds: [], + userIds: [], + boxIds: [], + runnerIds: [], + machineIds: [], + requestIds: [], + operationIds: [], + executionIds: [], + jobIds: [], + serviceNames: [], + ...overrides, + } + } + + it('allows AWS task role credentials when region and buckets are configured', async () => { + mockSend.mockResolvedValue({ + Contents: [ + { + Key: 'box-1/xlog/output.txt', + Size: 42, + LastModified: new Date('2026-06-07T00:00:00.000Z'), + ETag: '"etag-1"', + }, + ], + }) + const { reader } = buildReader({ + 'adminObservability.s3.buckets': ['boxlite-dev-storage'], + 'adminObservability.s3.region': 'ap-southeast-1', + 'adminObservability.s3.endpoint': undefined, + 'adminObservability.s3.accessKey': undefined, + 'adminObservability.s3.secretKey': undefined, + 'adminObservability.s3.maxObjects': 25, + 'adminObservability.s3.prefixes': [], + }) + + const result = await reader.listRelatedObjects(buildCorrelation({ boxIds: ['box-1'] })) + + expect(S3Client).toHaveBeenCalledWith({ region: 'ap-southeast-1' }) + expect(ListObjectsV2Command).toHaveBeenCalledWith({ + Bucket: 'boxlite-dev-storage', + Prefix: 'box-1/', + MaxKeys: 25, + }) + expect(result.status).toMatchObject({ source: 's3', state: 'available', count: 1 }) + expect(result.objects).toEqual([ + { + bucket: 'boxlite-dev-storage', + key: 'box-1/xlog/output.txt', + size: 42, + lastModified: new Date('2026-06-07T00:00:00.000Z'), + etag: '"etag-1"', + matchedBy: 'box:box-1', + }, + ]) + }) + + it('still supports explicit credentials for S3-compatible endpoints', async () => { + mockSend.mockResolvedValue({ Contents: [] }) + const { reader } = buildReader({ + 'adminObservability.s3.buckets': ['local-bucket'], + 'adminObservability.s3.region': 'us-east-1', + 'adminObservability.s3.endpoint': 'localhost:9000', + 'adminObservability.s3.accessKey': 'access', + 'adminObservability.s3.secretKey': 'secret', + 'adminObservability.s3.maxObjects': 5, + 'adminObservability.s3.prefixes': [], + }) + + await reader.listRelatedObjects(buildCorrelation({ traceIds: ['trace-1'] })) + + expect(S3Client).toHaveBeenCalledWith({ + endpoint: 'http://localhost:9000', + forcePathStyle: true, + region: 'us-east-1', + credentials: { accessKeyId: 'access', secretAccessKey: 'secret' }, + }) + }) + + it('rejects partial static credentials instead of falling through to AWS credentials', async () => { + const { reader } = buildReader({ + 'adminObservability.s3.buckets': ['boxlite-dev-storage'], + 'adminObservability.s3.region': 'ap-southeast-1', + 'adminObservability.s3.endpoint': undefined, + 'adminObservability.s3.accessKey': 'access', + 'adminObservability.s3.secretKey': undefined, + 'adminObservability.s3.maxObjects': 25, + 'adminObservability.s3.prefixes': [], + }) + + const result = await reader.listRelatedObjects(buildCorrelation({ boxIds: ['box-1'] })) + + expect(result.status).toMatchObject({ + source: 's3', + state: 'not_configured', + message: 'Admin S3 lookup static credentials require both access key and secret key', + count: 0, + }) + expect(S3Client).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/admin/services/observability-s3.reader.ts b/apps/api/src/admin/services/observability-s3.reader.ts new file mode 100644 index 000000000..0d5e92063 --- /dev/null +++ b/apps/api/src/admin/services/observability-s3.reader.ts @@ -0,0 +1,139 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable } from '@nestjs/common' +import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' +import { TypedConfigService } from '../../config/typed-config.service' +import { + AdminObservabilityCorrelationDto, + AdminObservabilityS3ObjectDto, + AdminObservabilitySourceStatusDto, +} from '../dto/observability-investigate.dto' + +@Injectable() +export class AdminS3ObjectReader { + constructor(private readonly configService: TypedConfigService) {} + + async listRelatedObjects( + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ objects: AdminObservabilityS3ObjectDto[]; status: AdminObservabilitySourceStatusDto }> { + const buckets = this.configService.get('adminObservability.s3.buckets') + const region = this.configService.get('adminObservability.s3.region') + const endpoint = this.configService.get('adminObservability.s3.endpoint') + const accessKey = this.configService.get('adminObservability.s3.accessKey') + const secretKey = this.configService.get('adminObservability.s3.secretKey') + const maxObjects = this.configService.get('adminObservability.s3.maxObjects') || 25 + + if (!region || buckets.length === 0) { + return { + objects: [], + status: { + source: 's3', + state: 'not_configured', + message: 'Admin S3 lookup requires region and at least one bucket', + count: 0, + }, + } + } + + if ((accessKey && !secretKey) || (!accessKey && secretKey)) { + return { + objects: [], + status: { + source: 's3', + state: 'not_configured', + message: 'Admin S3 lookup static credentials require both access key and secret key', + count: 0, + }, + } + } + + const prefixes = this.buildPrefixes(correlation) + if (prefixes.length === 0) { + return { + objects: [], + status: { + source: 's3', + state: 'available', + message: 'S3 lookup is configured, but no correlation identifiers were available for prefix search', + count: 0, + }, + } + } + + const clientConfig = { + region, + ...(endpoint + ? { endpoint: endpoint.startsWith('http') ? endpoint : `http://${endpoint}`, forcePathStyle: true } + : {}), + ...(accessKey && secretKey ? { credentials: { accessKeyId: accessKey, secretAccessKey: secretKey } } : {}), + } + const client = new S3Client(clientConfig) + const objects = new Map() + + for (const bucket of buckets) { + for (const { prefix, matchedBy } of prefixes.slice(0, 12)) { + const response = await client.send( + new ListObjectsV2Command({ + Bucket: bucket, + Prefix: prefix, + MaxKeys: Math.max(1, Math.min(maxObjects, 100)), + }), + ) + for (const object of response.Contents ?? []) { + if (!object.Key) { + continue + } + const key = `${bucket}:${object.Key}` + objects.set(key, { + bucket, + key: object.Key, + size: object.Size, + lastModified: object.LastModified, + etag: object.ETag, + matchedBy, + }) + } + } + } + + const values = Array.from(objects.values()).slice(0, maxObjects) + return { + objects: values, + status: { + source: 's3', + state: 'available', + count: values.length, + ...(values.length === 0 ? { message: 'No S3 objects matched the current correlation prefixes' } : {}), + }, + } + } + + private buildPrefixes(correlation: AdminObservabilityCorrelationDto): Array<{ prefix: string; matchedBy: string }> { + const configured = this.configService + .get('adminObservability.s3.prefixes') + .map((prefix) => ({ prefix: this.normalizePrefix(prefix), matchedBy: 'configured-prefix' })) + const correlated = [ + ...correlation.orgIds.map((id) => ({ prefix: `${id}/`, matchedBy: `org:${id}` })), + ...correlation.boxIds.map((id) => ({ prefix: `${id}/`, matchedBy: `box:${id}` })), + ...correlation.executionIds.map((id) => ({ prefix: `${id}/`, matchedBy: `execution:${id}` })), + ...correlation.jobIds.map((id) => ({ prefix: `${id}/`, matchedBy: `job:${id}` })), + ...correlation.traceIds.map((id) => ({ prefix: `${id}/`, matchedBy: `trace:${id}` })), + ] + const seen = new Set() + return [...correlated, ...configured].filter((candidate) => { + if (!candidate.prefix || seen.has(candidate.prefix)) { + return false + } + seen.add(candidate.prefix) + return true + }) + } + + private normalizePrefix(prefix: string): string { + return prefix.startsWith('/') ? prefix.slice(1) : prefix + } +} diff --git a/apps/api/src/admin/services/observability.service.spec.ts b/apps/api/src/admin/services/observability.service.spec.ts new file mode 100644 index 000000000..f1f9e7f8e --- /dev/null +++ b/apps/api/src/admin/services/observability.service.spec.ts @@ -0,0 +1,1067 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ServiceUnavailableException } from '@nestjs/common' +import { AdminObservabilityService } from './observability.service' + +describe('AdminObservabilityService', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + + function buildService( + options: { + configured: boolean + queryRows?: any[] + clickstackBaseUrl?: string + clickstackDashboardUrl?: string + clickstackLogSourceId?: string + clickstackTraceSourceId?: string + clickstackMetricSourceId?: string + } = { configured: true }, + ) { + const clickhouseService = { + isConfigured: jest.fn().mockReturnValue(options.configured), + query: jest.fn().mockResolvedValue(options.queryRows ?? []), + } + const configService = { + get: jest.fn((key: string) => { + if (key === 'observability.clickstackBaseUrl') { + return options.clickstackBaseUrl + } + if (key === 'observability.clickstackDashboardUrl') { + return options.clickstackDashboardUrl + } + if (key === 'observability.clickstackLogSourceId') { + return options.clickstackLogSourceId + } + if (key === 'observability.clickstackTraceSourceId') { + return options.clickstackTraceSourceId + } + if (key === 'observability.clickstackMetricSourceId') { + return options.clickstackMetricSourceId + } + return undefined + }), + } + const overviewService = { + listBoxes: jest.fn().mockResolvedValue([]), + listRunners: jest.fn().mockResolvedValue([]), + listMachines: jest.fn().mockResolvedValue([]), + } + const auditService = { + getAllLogs: jest.fn().mockResolvedValue({ items: [], total: 0, page: 1, totalPages: 0 }), + } + const cloudWatchLogReader = { + getRelatedLogs: jest.fn().mockResolvedValue({ + logs: [], + status: { source: 'cloudwatch', state: 'available', count: 0 }, + }), + } + const s3ObjectReader = { + listRelatedObjects: jest.fn().mockResolvedValue({ + objects: [], + status: { source: 's3', state: 'available', count: 0 }, + }), + } + + return { + clickhouseService, + overviewService, + auditService, + cloudWatchLogReader, + s3ObjectReader, + service: new AdminObservabilityService( + clickhouseService as any, + configService as any, + overviewService as any, + auditService as any, + cloudWatchLogReader as any, + s3ObjectReader as any, + ), + } + } + + it('reports missing backend without querying ClickHouse when it is not configured', async () => { + const { service, clickhouseService } = buildService({ configured: false }) + + await expect(service.getStatus()).resolves.toEqual({ + backend: { + configured: false, + state: 'missing', + message: 'ClickHouse/ClickStack is not configured', + }, + layers: [ + { layer: 'api', state: 'missing', signals: { logs: 'missing', traces: 'missing', metrics: 'missing' } }, + { layer: 'runner', state: 'missing', signals: { logs: 'missing', traces: 'missing', metrics: 'missing' } }, + { layer: 'ec2_host', state: 'missing', signals: { logs: 'missing', traces: 'missing', metrics: 'missing' } }, + { layer: 'box', state: 'missing', signals: { logs: 'missing', traces: 'missing', metrics: 'missing' } }, + ], + }) + expect(clickhouseService.query).not.toHaveBeenCalled() + }) + + it('does not return fake empty logs when ClickHouse is not configured', async () => { + const { service } = buildService({ configured: false }) + + await expect( + service.getLogs({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + page: 1, + limit: 100, + }), + ).rejects.toBeInstanceOf(ServiceUnavailableException) + }) + + it('builds admin log queries with layer and resource filters', async () => { + const { service, clickhouseService } = buildService({ configured: true }) + + await service.getLogs({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + page: 2, + limit: 25, + layer: 'box', + serviceName: 'boxlite-box', + orgId: 'org-1', + userId: 'user-1', + boxId: 'box-1', + runnerId: 'runner-1', + machineId: 'machine-1', + severities: ['ERROR'], + search: 'entrypoint', + }) + + const [countQuery, countParams] = clickhouseService.query.mock.calls[0] + const [logsQuery, logsParams] = clickhouseService.query.mock.calls[1] + + expect(countQuery).toContain('multiIf(') + expect(countQuery).toContain("ServiceName = 'boxlite-runner', 'runner'") + expect(countQuery).toContain('= {layer:String}') + expect(countQuery).toContain('ServiceName = {serviceName:String}') + expect(countQuery).toContain("ResourceAttributes['boxlite.org_id'] = {orgId:String}") + expect(countQuery).toContain("LogAttributes['boxlite.org_id'] = {orgId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.user_id'] = {userId:String}") + expect(countQuery).toContain("LogAttributes['boxlite.user_id'] = {userId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.box_id'] = {boxId:String}") + expect(countQuery).toContain("LogAttributes['boxlite.box_id'] = {boxId:String}") + expect(countQuery).toContain('ServiceName = {boxServiceName:String}') + expect(countQuery).toContain("ResourceAttributes['boxlite.runner_id'] = {runnerId:String}") + expect(countQuery).toContain("LogAttributes['boxlite.runner_id'] = {runnerId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.machine_id'] = {machineId:String}") + expect(countQuery).toContain("LogAttributes['boxlite.machine_id'] = {machineId:String}") + expect(countQuery).toContain('lower(SeverityText) IN ({severities:Array(String)})') + expect(logsQuery).toContain('ORDER BY Timestamp DESC') + expect(countParams).toMatchObject({ + layer: 'box', + serviceName: 'boxlite-box', + orgId: 'org-1', + userId: 'user-1', + boxId: 'box-1', + runnerId: 'runner-1', + machineId: 'machine-1', + boxServiceName: 'box-box-1', + severities: ['error'], + search: '%entrypoint%', + offset: 25, + limit: 25, + }) + expect(logsParams).toEqual(countParams) + }) + + it('builds admin trace queries with span attribute resource filters', async () => { + const { service, clickhouseService } = buildService({ configured: true }) + + await service.getTraces({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + page: 1, + limit: 25, + orgId: 'org-1', + userId: 'user-1', + boxId: 'box-1', + runnerId: 'runner-1', + machineId: 'machine-1', + }) + + const [countQuery, countParams] = clickhouseService.query.mock.calls[0] + const [tracesQuery, tracesParams] = clickhouseService.query.mock.calls[1] + + expect(countQuery).toContain("ResourceAttributes['boxlite.org_id'] = {orgId:String}") + expect(countQuery).toContain("SpanAttributes['boxlite.org_id'] = {orgId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.user_id'] = {userId:String}") + expect(countQuery).toContain("SpanAttributes['boxlite.user_id'] = {userId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.box_id'] = {boxId:String}") + expect(countQuery).toContain("SpanAttributes['boxlite.box_id'] = {boxId:String}") + expect(countQuery).toContain('ServiceName = {boxServiceName:String}') + expect(countQuery).toContain("ResourceAttributes['boxlite.runner_id'] = {runnerId:String}") + expect(countQuery).toContain("SpanAttributes['boxlite.runner_id'] = {runnerId:String}") + expect(countQuery).toContain("ResourceAttributes['boxlite.machine_id'] = {machineId:String}") + expect(countQuery).toContain("SpanAttributes['boxlite.machine_id'] = {machineId:String}") + expect(tracesQuery).toContain('ORDER BY startTime DESC') + expect(countParams).toMatchObject({ + orgId: 'org-1', + userId: 'user-1', + boxId: 'box-1', + boxServiceName: 'box-box-1', + runnerId: 'runner-1', + machineId: 'machine-1', + }) + expect(tracesParams).toEqual(countParams) + }) + + it('returns serviceName and resolved layer per span for trace drill-down', async () => { + const { service, clickhouseService } = buildService({ + configured: true, + queryRows: [ + { + TraceId: 'trace-1', + SpanId: 'span-1', + ParentSpanId: '', + SpanName: 'GET /x', + Timestamp: '2026-06-08 07:00:00', + Duration: 1000, + ServiceName: 'box-abc', + ResourceAttributes: {}, + SpanAttributes: {}, + StatusCode: 'Error', + StatusMessage: 'boom', + }, + ], + }) + + const spans = await service.getTraceSpans('trace-1', { + from: '2026-06-08T00:00:00.000Z', + to: '2026-06-08T01:00:00.000Z', + page: 1, + limit: 50, + }) + + const [spansQuery] = clickhouseService.query.mock.calls[0] + expect(spansQuery).toContain('ServiceName') + expect(spans[0]).toMatchObject({ + spanId: 'span-1', + serviceName: 'box-abc', + layer: 'box', + }) + }) + + it('uses a default time range when logs are queried without from/to', async () => { + const { service, clickhouseService } = buildService({ configured: true }) + + await service.getLogs({ limit: 5 }) + + const [, params] = clickhouseService.query.mock.calls[0] + expect(params.from).toBeInstanceOf(Date) + expect(params.to).toBeInstanceOf(Date) + expect(Number.isNaN(params.from.getTime())).toBe(false) + expect(Number.isNaN(params.to.getTime())).toBe(false) + expect(params.to.getTime() - params.from.getTime()).toBe(60 * 60 * 1000) + }) + + it('checks all ClickHouse metric tables when building layer status', async () => { + const { service, clickhouseService } = buildService({ configured: true }) + + await service.getStatus() + + const [statusQuery] = clickhouseService.query.mock.calls[0] + expect(statusQuery).toContain('otel_metrics_gauge') + expect(statusQuery).toContain('otel_metrics_sum') + expect(statusQuery).toContain('otel_metrics_summary') + expect(statusQuery).toContain('otel_metrics_histogram') + expect(statusQuery).toContain('otel_metrics_exponential_histogram') + expect(statusQuery).not.toContain('otel_metrics_exp_histogram') + expect(statusQuery).toContain("ServiceName = 'boxlite-runner', 'runner'") + }) + + it('reports configured backend clearly when no OTel rows have been observed', async () => { + const { service } = buildService({ configured: true, queryRows: [] }) + + await expect(service.getStatus()).resolves.toMatchObject({ + backend: { + configured: true, + state: 'configured', + message: 'ClickHouse is configured, but no OTel logs, traces, or metrics have been observed yet', + }, + layers: [ + { layer: 'api', state: 'configured' }, + { layer: 'runner', state: 'configured' }, + { layer: 'ec2_host', state: 'configured' }, + { layer: 'box', state: 'configured' }, + ], + }) + }) + + it('uses epoch milliseconds for layer status freshness to avoid local timezone parsing', async () => { + jest.spyOn(Date, 'now').mockReturnValue(Date.UTC(2026, 5, 5, 15, 40, 0, 0)) + const { service, clickhouseService } = buildService({ + configured: true, + queryRows: [ + { + layer: 'api', + signal: 'logs', + lastSeenMs: String(Date.UTC(2026, 5, 5, 15, 39, 0, 0)), + }, + ], + }) + + const status = await service.getStatus() + expect(status.backend).toMatchObject({ configured: true, state: 'receiving' }) + expect(status.layers.find((layer) => layer.layer === 'api')).toMatchObject({ + layer: 'api', + state: 'receiving', + signals: { logs: 'receiving', traces: 'configured', metrics: 'configured' }, + lastSeen: '2026-06-05T15:39:00.000Z', + }) + + const [statusQuery] = clickhouseService.query.mock.calls[0] + expect(statusQuery).toContain('toUnixTimestamp64Milli') + expect(statusQuery).toContain('lastSeenMs') + }) + + it('queries scalar metrics from gauge and sum tables for charts', async () => { + const { service, clickhouseService } = buildService({ + configured: true, + queryRows: [ + { + timestamp: '2026-06-05T00:00:00.000Z', + MetricName: 'boxlite.runner.started_boxes', + layer: 'runner', + value: 3, + }, + ], + }) + + await expect( + service.getMetrics({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + page: 1, + limit: 100, + layer: 'runner', + metricNames: ['boxlite.runner.started_boxes'], + }), + ).resolves.toEqual({ + series: [ + { + metricName: 'boxlite.runner.started_boxes', + layer: 'runner', + dataPoints: [{ timestamp: '2026-06-05T00:00:00.000Z', value: 3 }], + }, + ], + }) + + const [metricsQuery, metricsParams] = clickhouseService.query.mock.calls[0] + expect(metricsQuery).toContain('FROM otel_metrics_gauge') + expect(metricsQuery).toContain('FROM otel_metrics_sum') + expect(metricsQuery).toContain('multiIf(') + expect(metricsQuery).toContain('as layer') + expect(metricsQuery).toContain('GROUP BY timestamp, MetricName, layer') + expect(metricsQuery).toContain('MetricName IN ({metricNames:Array(String)})') + expect(metricsParams).toMatchObject({ + layer: 'runner', + metricNames: ['boxlite.runner.started_boxes'], + }) + }) + + it('derives box correlation from box service names when resource attributes are not present', async () => { + const { service, clickhouseService, overviewService, cloudWatchLogReader, s3ObjectReader } = buildService({ + configured: true, + }) + clickhouseService.query.mockImplementation(async (query: string) => { + if (query.includes('FROM otel_traces') && query.includes('ResourceAttributes')) { + return [ + { + TraceId: 'trace-box-1', + SpanId: 'span-1', + ParentSpanId: '', + SpanName: 'GET /version', + Timestamp: '2026-06-05T00:00:00.000Z', + Duration: 1_000_000, + ServiceName: 'box-box-1', + ResourceAttributes: {}, + SpanAttributes: {}, + StatusCode: 'STATUS_CODE_OK', + StatusMessage: '', + }, + ] + } + if (query.includes('SELECT count() as count') && query.includes('FROM otel_logs')) { + return [{ count: 0 }] + } + return [] + }) + overviewService.listBoxes.mockResolvedValue([ + { + id: 'box-1', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-1', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + }, + { + id: 'box-2', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-2', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + }, + ]) + overviewService.listRunners.mockResolvedValue([{ id: 'runner-1', state: 'ready', draining: false }]) + overviewService.listMachines.mockResolvedValue([{ host: 'runner-1', region: 'us-east-1', boxes: 1 }]) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + traceId: 'trace-box-1', + }) + + expect(result.correlation).toMatchObject({ + traceIds: ['trace-box-1'], + orgIds: ['org-1'], + boxIds: ['box-1'], + runnerIds: ['runner-1'], + machineIds: ['runner-1'], + serviceNames: ['box-box-1'], + }) + expect(result.boxes.map((box) => box.id)).toEqual(['box-1']) + expect(result.runners.map((runner) => runner.id)).toEqual(['runner-1']) + expect(result.machines.map((machine) => machine.host)).toEqual(['runner-1']) + expect(cloudWatchLogReader.getRelatedLogs).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + boxIds: ['box-1'], + }), + ) + expect(s3ObjectReader.listRelatedObjects).toHaveBeenCalledWith( + expect.objectContaining({ + boxIds: ['box-1'], + }), + ) + }) + + it('does not narrow related logs by a correlated orgId when a specific box is targeted', async () => { + const { service, clickhouseService, overviewService } = buildService({ configured: true }) + clickhouseService.query.mockImplementation(async (query: string) => { + if (query.includes('FROM otel_traces') && query.includes('ResourceAttributes')) { + return [ + { + TraceId: 'trace-box-1', + SpanId: 'span-1', + ParentSpanId: '', + SpanName: 'GET /version', + Timestamp: '2026-06-05T00:00:00.000Z', + Duration: 1_000_000, + ServiceName: 'boxlite-api', + ResourceAttributes: { 'boxlite.org_id': 'org-1' }, + SpanAttributes: { 'boxlite.org_id': 'org-1' }, + StatusCode: 'STATUS_CODE_OK', + StatusMessage: '', + }, + ] + } + if (query.includes('SELECT count() as count') && query.includes('FROM otel_logs')) { + return [{ count: 1 }] + } + return [] + }) + overviewService.listBoxes.mockResolvedValue([ + { + id: 'box-1', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-1', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + }, + ]) + + await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + traceId: 'trace-box-1', + boxId: 'box-1', + }) + + const relatedLogQueries = clickhouseService.query.mock.calls + .map((call) => call[0] as string) + .filter((sql) => sql.includes('FROM otel_logs') && !sql.includes('count() as count')) + + // org filter must NOT be AND-ed in: box self-logs carry ServiceName=box- but + // no dot-namespaced boxlite.org_id, so an org clause would silently drop them. + expect(relatedLogQueries.length).toBeGreaterThan(0) + for (const sql of relatedLogQueries) { + expect(sql).not.toContain('boxlite.org_id') + } + // the box is still scoped via its service name match + expect(relatedLogQueries.some((sql) => sql.includes('ServiceName = {boxServiceName:String}'))).toBe(true) + }) + + it('investigates one trace across telemetry, platform state, CloudWatch, S3, audit, and xLog', async () => { + const { service, clickhouseService, overviewService, auditService, cloudWatchLogReader, s3ObjectReader } = + buildService({ + configured: true, + clickstackBaseUrl: 'https://clickstack.boxlite.dev/getting-started?chcServiceId=svc-1', + clickstackDashboardUrl: 'https://clickstack.boxlite.dev/dashboards/dashboard-1?chcServiceId=svc-1', + clickstackLogSourceId: 'logs-source-1', + clickstackTraceSourceId: 'traces-source-1', + clickstackMetricSourceId: 'metrics-source-1', + }) + clickhouseService.query.mockImplementation(async (query: string) => { + if (query.includes('FROM otel_traces') && query.includes('ResourceAttributes')) { + return [ + { + TraceId: 'trace-1', + SpanId: 'span-1', + ParentSpanId: '', + SpanName: 'POST /api/boxes', + Timestamp: '2026-06-05T00:00:00.000Z', + Duration: 4_000_000, + ServiceName: 'boxlite-api', + ResourceAttributes: { + 'boxlite.layer': 'api', + 'boxlite.org_id': 'org-1', + 'boxlite.user_id': 'user-1', + 'boxlite.box_id': 'box-1', + 'boxlite.runner_id': 'runner-1', + 'boxlite.machine_id': 'machine-1', + }, + SpanAttributes: { + 'boxlite.request_id': 'req-1', + 'boxlite.operation_id': 'op-1', + 'boxlite.execution_id': 'exec-1', + 'boxlite.job_id': 'job-1', + }, + StatusCode: 'STATUS_CODE_OK', + StatusMessage: '', + }, + ] + } + if (query.includes('SELECT count() as count') && query.includes('FROM otel_logs')) { + return [{ count: 1 }] + } + if (query.includes('FROM otel_logs')) { + return [ + { + Timestamp: '2026-06-05T00:00:01.000Z', + Body: 'boxlite exec output', + SeverityText: 'INFO', + SeverityNumber: 9, + ServiceName: 'boxlite-api', + ResourceAttributes: { + 'boxlite.user_id': 'user-1', + 'boxlite.box_id': 'box-1', + 'boxlite.runner_id': 'runner-1', + }, + LogAttributes: { + 'boxlite.request_id': 'req-1', + 'boxlite.execution_id': 'exec-1', + 'boxlite.job_id': 'job-1', + 'boxlite.stream': 'stdout', + 'boxlite.output': 'hello from exec\n', + }, + TraceId: 'trace-1', + SpanId: 'span-1', + }, + ] + } + if (query.includes('FROM (') && query.includes('otel_metrics_gauge')) { + return [ + { + timestamp: '2026-06-05T00:01:00.000Z', + MetricName: 'boxlite.runner.cpu.usage', + layer: 'runner', + value: 0.42, + }, + ] + } + return [] + }) + overviewService.listBoxes.mockResolvedValue([ + { + id: 'box-1', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-1', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + }, + { + id: 'box-internal-2', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-2', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + }, + ]) + overviewService.listRunners.mockResolvedValue([{ id: 'runner-1', state: 'ready', draining: false }]) + overviewService.listMachines.mockResolvedValue([{ host: 'machine-1', region: 'us-east-1', boxes: 1 }]) + auditService.getAllLogs.mockResolvedValue({ + items: [ + { + id: 'audit-1', + actorId: 'user-1', + actorEmail: 'admin@example.com', + organizationId: 'org-1', + action: 'create', + targetType: 'box', + targetId: 'box-1', + createdAt: new Date('2026-06-05T00:00:02.000Z'), + }, + ], + total: 1, + page: 1, + totalPages: 1, + }) + cloudWatchLogReader.getRelatedLogs.mockResolvedValue({ + logs: [ + { + timestamp: '2026-06-05T00:00:03.000Z', + body: 'runner attach stderr', + severityText: 'ERROR', + serviceName: 'cloudwatch:Api', + resourceAttributes: { + 'boxlite.source': 'cloudwatch', + 'boxlite.layer': 'api', + 'boxlite.box_id': 'box-1', + }, + logAttributes: { + 'boxlite.execution_id': 'exec-1', + 'boxlite.job_id': 'job-1', + 'boxlite.stream': 'stderr', + }, + traceId: 'trace-1', + spanId: 'span-1', + }, + ], + status: { source: 'cloudwatch', state: 'available', count: 1 }, + }) + s3ObjectReader.listRelatedObjects.mockResolvedValue({ + objects: [ + { + bucket: 'bucket-1', + key: 'box-1/xlog.txt', + size: 12, + matchedBy: 'box:box-1', + }, + ], + status: { source: 's3', state: 'available', count: 1 }, + }) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + traceId: 'trace-1', + }) + + expect(result.correlation).toMatchObject({ + traceIds: expect.arrayContaining(['trace-1']), + orgIds: expect.arrayContaining(['org-1']), + userIds: expect.arrayContaining(['user-1']), + boxIds: expect.arrayContaining(['box-1']), + runnerIds: expect.arrayContaining(['runner-1']), + machineIds: expect.arrayContaining(['machine-1']), + requestIds: expect.arrayContaining(['req-1']), + operationIds: expect.arrayContaining(['op-1']), + executionIds: expect.arrayContaining(['exec-1']), + jobIds: expect.arrayContaining(['job-1']), + serviceNames: expect.arrayContaining(['boxlite-api', 'cloudwatch:Api']), + }) + expect(result.traceSpans).toHaveLength(1) + expect(result.logs).toHaveLength(2) + expect(result.metrics.series).toHaveLength(1) + expect(result.boxes.map((box) => box.id)).toEqual(['box-1']) + expect(result.runners.map((runner) => runner.id)).toEqual(['runner-1']) + expect(result.machines.map((machine) => machine.host)).toEqual(['machine-1']) + expect(result.auditLogs.map((log) => log.id)).toEqual(['audit-1']) + expect(result.xlogs).toEqual([ + expect.objectContaining({ + source: 'clickhouse_logs', + executionId: 'exec-1', + jobId: 'job-1', + stream: 'stdout', + body: 'hello from exec\n', + }), + expect.objectContaining({ + source: 'cloudwatch_logs', + executionId: 'exec-1', + jobId: 'job-1', + stream: 'stderr', + body: 'runner attach stderr', + }), + ]) + expect(result.s3Objects).toEqual([ + expect.objectContaining({ + bucket: 'bucket-1', + key: 'box-1/xlog.txt', + matchedBy: 'box:box-1', + }), + ]) + expect(result.resource).toMatchObject({ + type: 'box', + title: 'Box box-1', + state: 'started', + }) + expect(result.timeline.map((event) => event.source)).toEqual( + expect.arrayContaining(['trace', 'log', 'audit', 'xlog']), + ) + expect(result.commands.api).toContain('/admin/observability/investigate') + expect(result.commands.aiAgentPrompt).toContain('X-BoxLite-Source=agent') + expect(result.externalLinks.clickstack).toMatchObject({ + configured: true, + missingSources: [], + dashboardUrl: 'https://clickstack.boxlite.dev/dashboards/dashboard-1?chcServiceId=svc-1', + query: expect.stringContaining('TraceId'), + }) + expect(result.externalLinks.clickstack.logsUrl).toContain('https://clickstack.boxlite.dev/search') + expect(result.externalLinks.clickstack.logsUrl).toContain('source=logs-source-1') + expect(result.externalLinks.clickstack.logsUrl).toContain('whereLanguage=sql') + expect(result.externalLinks.clickstack.logsUrl).toContain('chcServiceId=svc-1') + expect(result.externalLinks.clickstack.logsUrl).not.toContain('view=logs') + expect(result.externalLinks.clickstack.tracesUrl).toContain('source=traces-source-1') + expect(result.externalLinks.clickstack.tracesUrl).toContain('traceId=trace-1') + expect(result.externalLinks.clickstack.metricsUrl).toContain('https://clickstack.boxlite.dev/chart') + if (!result.externalLinks.clickstack.logsUrl || !result.externalLinks.clickstack.tracesUrl) { + throw new Error('Expected ClickStack logs and traces links') + } + const logsUrl = new URL(result.externalLinks.clickstack.logsUrl) + expect(logsUrl.searchParams.get('from')).toBe(String(new Date('2026-06-05T00:00:00.000Z').getTime())) + expect(logsUrl.searchParams.get('to')).toBe(String(new Date('2026-06-05T01:00:00.000Z').getTime())) + expect(logsUrl.searchParams.get('isLive')).toBe('false') + expect(logsUrl.searchParams.get('whereLanguage')).toBe('sql') + expect(logsUrl.searchParams.get('where')).toContain("TraceId = 'trace-1'") + expect(logsUrl.searchParams.get('where')).toContain("LogAttributes['boxlite.user_id'] = 'user-1'") + expect(logsUrl.searchParams.get('where')).toContain("ResourceAttributes['boxlite.box_id'] = 'box-1'") + expect(logsUrl.searchParams.get('where')).toContain("LogAttributes['boxlite.box_id'] = 'box-1'") + const tracesUrl = new URL(result.externalLinks.clickstack.tracesUrl) + expect(tracesUrl.searchParams.get('traceId')).toBe('trace-1') + expect(tracesUrl.searchParams.get('whereLanguage')).toBe('sql') + expect(tracesUrl.searchParams.get('where')).toContain("SpanAttributes['boxlite.user_id'] = 'user-1'") + expect(tracesUrl.searchParams.get('where')).toContain("SpanAttributes['boxlite.box_id'] = 'box-1'") + if (!result.externalLinks.clickstack.metricsUrl) { + throw new Error('Expected ClickStack metrics link') + } + const metricsUrl = new URL(result.externalLinks.clickstack.metricsUrl) + expect(metricsUrl.searchParams.get('from')).toBe(String(new Date('2026-06-05T00:00:00.000Z').getTime())) + expect(metricsUrl.searchParams.get('to')).toBe(String(new Date('2026-06-05T01:00:00.000Z').getTime())) + expect(metricsUrl.searchParams.get('isLive')).toBe('false') + const metricsConfig = JSON.parse(metricsUrl.searchParams.get('config') ?? '{}') + expect(metricsConfig).toMatchObject({ + source: 'metrics-source-1', + displayType: 'line', + granularity: 'auto', + select: [expect.objectContaining({ aggFn: 'count' })], + }) + expect(result.operations).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: 'recover:box-1', state: 'disabled' }), + expect.objectContaining({ id: 'cordon:runner-1', state: 'enabled' }), + expect.objectContaining({ id: 'drain:runner-1', state: 'enabled' }), + expect.objectContaining({ id: 'resize:box-1', state: 'request_only' }), + ]), + ) + expect(cloudWatchLogReader.getRelatedLogs).toHaveBeenCalledWith( + expect.any(Object), + expect.objectContaining({ + traceIds: ['trace-1'], + boxIds: ['box-1'], + }), + ) + expect(s3ObjectReader.listRelatedObjects).toHaveBeenCalledWith( + expect.objectContaining({ + boxIds: ['box-1'], + executionIds: ['exec-1'], + }), + ) + expect(result.sources).toEqual( + expect.arrayContaining([ + expect.objectContaining({ source: 'clickhouse', state: 'available', count: 3 }), + expect.objectContaining({ source: 'cloudwatch', state: 'available', count: 1 }), + expect.objectContaining({ source: 'postgres', state: 'available', count: 3 }), + expect.objectContaining({ source: 'audit', state: 'available', count: 1 }), + expect.objectContaining({ source: 's3', state: 'available', count: 1 }), + expect.objectContaining({ source: 'xlog', state: 'available', count: 2 }), + expect.objectContaining({ source: 'clickstack', state: 'available', count: 3 }), + ]), + ) + }) + + it('matches prefixed admin observability audit target ids to related resources', async () => { + const { service, overviewService, auditService } = buildService({ configured: true }) + + overviewService.listBoxes.mockResolvedValue([ + { + id: 'box-public-1', + organizationId: 'org-1', + state: 'stopped', + runnerId: 'runner-1', + cpu: 1, + memoryGiB: 1, + createdAt: '2026-06-05T00:00:00.000Z', + owner: { name: 'Brian Luo', email: 'brian.luo@polygala.ai', orgName: 'Personal', personal: true }, + }, + ]) + overviewService.listRunners.mockResolvedValue([{ id: 'runner-1', state: 'ready', draining: false }]) + overviewService.listMachines.mockResolvedValue([{ host: 'runner-1', region: 'us', boxes: 1 }]) + auditService.getAllLogs.mockResolvedValue({ + items: [ + { + id: 'audit-prefixed-box', + actorId: 'agent-1', + actorEmail: 'agent@example.com', + organizationId: 'admin-org', + action: 'read', + targetType: 'observability', + targetId: 'boxId:box-public-1', + source: 'agent', + createdAt: new Date('2026-06-05T00:00:02.000Z'), + }, + { + id: 'audit-prefixed-box-internal', + actorId: 'agent-1', + actorEmail: 'agent@example.com', + organizationId: 'admin-org', + action: 'read', + targetType: 'observability', + targetId: 'boxId:box-internal-1', + source: 'agent', + createdAt: new Date('2026-06-05T00:00:03.000Z'), + }, + { + id: 'audit-other', + actorId: 'agent-1', + actorEmail: 'agent@example.com', + organizationId: 'admin-org', + action: 'read', + targetType: 'observability', + targetId: 'traceId:trace-other', + source: 'agent', + createdAt: new Date('2026-06-05T00:00:04.000Z'), + }, + ], + total: 3, + page: 1, + totalPages: 1, + }) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + boxId: 'box-public-1', + runnerId: 'runner-1', + machineId: 'runner-1', + }) + + expect(result.auditLogs.map((log) => log.id)).toEqual(['audit-prefixed-box']) + expect(result.sources).toEqual( + expect.arrayContaining([expect.objectContaining({ source: 'audit', state: 'available', count: 1 })]), + ) + }) + + it('summarizes explicit user and organization investigations without collapsing to the first box', async () => { + const { service, overviewService, auditService } = buildService({ + configured: true, + clickstackBaseUrl: 'https://clickstack.boxlite.dev/search?chcServiceId=svc-1', + clickstackLogSourceId: 'logs-source-1', + clickstackTraceSourceId: 'traces-source-1', + }) + + overviewService.listBoxes.mockResolvedValue([ + { + id: 'box-1', + organizationId: 'org-1', + state: 'started', + runnerId: 'runner-1', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-06-05T00:00:00.000Z', + owner: { userId: 'user-1', name: 'Brian Luo', email: 'brian@example.com', orgName: 'Personal', personal: true }, + }, + ]) + auditService.getAllLogs.mockResolvedValue({ + items: [ + { + id: 'audit-user-actor', + actorId: 'user-1', + actorEmail: 'brian@example.com', + organizationId: 'org-other', + action: 'read', + targetType: 'user', + targetId: 'userId:user-1', + createdAt: new Date('2026-06-05T00:00:02.000Z'), + }, + ], + total: 1, + page: 1, + totalPages: 1, + }) + + const userResult = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + orgId: 'org-1', + userId: 'user-1', + }) + const orgResult = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + orgId: 'org-1', + }) + + expect(userResult.resource).toMatchObject({ + type: 'user', + title: 'User user-1', + identifiers: expect.objectContaining({ userId: 'user-1', orgId: 'org-1' }), + }) + expect(userResult.boxes.map((box) => box.id)).toEqual(['box-1']) + expect(userResult.auditLogs.map((log) => log.id)).toEqual(['audit-user-actor']) + expect(userResult.externalLinks.clickstack.query).toContain('boxlite.user_id') + expect(userResult.commands.api).toContain('userId=user-1') + expect(orgResult.resource).toMatchObject({ + type: 'org', + title: 'Organization org-1', + identifiers: expect.objectContaining({ orgId: 'org-1' }), + }) + }) + + it('summarizes request and operation investigations as first-class resources', async () => { + const { service } = buildService({ configured: true }) + + const requestResult = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + requestId: 'req-1', + }) + const operationResult = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + operationId: 'op-1', + }) + + expect(requestResult.resource).toMatchObject({ type: 'request', title: 'Request req-1' }) + expect(requestResult.externalLinks.clickstack.query).toContain('boxlite.request_id') + expect(operationResult.resource).toMatchObject({ type: 'operation', title: 'Operation op-1' }) + expect(operationResult.externalLinks.clickstack.query).toContain('boxlite.operation_id') + }) + + it('does not report ClickStack as useful when ClickHouse has no matching OTel rows', async () => { + const { service } = buildService({ + configured: true, + clickstackBaseUrl: 'https://hyperdx.clickhouse.cloud/search?chcServiceId=svc-1', + clickstackLogSourceId: 'logs-source-1', + clickstackTraceSourceId: 'traces-source-1', + clickstackMetricSourceId: 'metrics-source-1', + }) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + traceId: 'trace-empty', + }) + + expect(result.sources).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + source: 'clickhouse', + state: 'missing', + count: 0, + message: + 'ClickHouse is configured, but no matching OTel logs, traces, or metrics were found for this context', + }), + expect.objectContaining({ + source: 'clickstack', + state: 'missing', + count: 0, + message: + 'ClickStack source ids are configured, but ClickHouse has no matching OTel rows for this investigation; the third-party page will be empty until collector ingestion writes data', + }), + ]), + ) + }) + + it('reports ClickStack as missing when source ids are not configured', async () => { + const { service } = buildService({ + configured: false, + clickstackBaseUrl: 'https://hyperdx.clickhouse.cloud/search?chcServiceId=svc-1', + }) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + traceId: 'trace-1', + }) + + expect(result.externalLinks.clickstack).toMatchObject({ + configured: true, + missingSources: ['logs', 'traces', 'metrics'], + message: + 'ClickStack is reachable, but logs, traces, metrics source ids need to be configured for one-click queries', + sourceSetup: [ + expect.objectContaining({ + kind: 'logs', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID', + database: 'otel', + table: 'otel_logs', + timestampColumn: 'Timestamp', + }), + expect.objectContaining({ + kind: 'traces', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_TRACE_SOURCE_ID', + database: 'otel', + table: 'otel_traces', + timestampColumn: 'Timestamp', + }), + expect.objectContaining({ + kind: 'metrics', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_METRIC_SOURCE_ID', + database: 'otel', + timestampColumn: 'TimeUnix', + metricTables: expect.objectContaining({ + gauge: 'otel_metrics_gauge', + sum: 'otel_metrics_sum', + }), + }), + ], + }) + expect(result.externalLinks.clickstack.logsUrl).not.toContain('source=') + expect(result.sources).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + source: 'clickstack', + state: 'missing', + count: 0, + message: result.externalLinks.clickstack.message, + }), + ]), + ) + }) + + it('uses valid ClickHouse SQL for ClickStack fallback queries without identifiers', async () => { + const { service } = buildService({ + configured: false, + clickstackBaseUrl: 'https://hyperdx.clickhouse.cloud/search?chcServiceId=svc-1', + clickstackLogSourceId: 'logs-source-1', + }) + + const result = await service.investigate({ + from: '2026-06-05T00:00:00.000Z', + to: '2026-06-05T01:00:00.000Z', + }) + + expect(result.externalLinks.clickstack.query).toBe("ServiceName != ''") + expect(result.externalLinks.clickstack.logsUrl).toContain('ServiceName+%21%3D+%27%27') + expect(result.externalLinks.clickstack.logsUrl).not.toContain('empty') + }) +}) diff --git a/apps/api/src/admin/services/observability.service.ts b/apps/api/src/admin/services/observability.service.ts new file mode 100644 index 000000000..4eb068c69 --- /dev/null +++ b/apps/api/src/admin/services/observability.service.ts @@ -0,0 +1,1838 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Inject, Injectable, ServiceUnavailableException } from '@nestjs/common' +import { ClickHouseService } from '../../clickhouse/clickhouse.service' +import { TypedConfigService } from '../../config/typed-config.service' +import { LogEntryDto } from '../../box-telemetry/dto/log-entry.dto' +import { MetricsResponseDto, MetricDataPointDto, MetricSeriesDto } from '../../box-telemetry/dto/metrics-response.dto' +import { PaginatedLogsDto } from '../../box-telemetry/dto/paginated-logs.dto' +import { PaginatedTracesDto } from '../../box-telemetry/dto/paginated-traces.dto' +import { TraceSpanDto } from '../../box-telemetry/dto/trace-span.dto' +import { TraceSummaryDto } from '../../box-telemetry/dto/trace-summary.dto' +import { AdminBoxItemDto, AdminMachineItemDto, AdminRunnerItemDto } from '../dto/admin-overview.dto' +import { + AdminObservabilityAuditLogDto, + AdminObservabilityClickStackSourceSetupDto, + AdminObservabilityCommandsDto, + AdminObservabilityCorrelationDto, + AdminObservabilityExternalLinksDto, + AdminObservabilityInvestigateQueryParamsDto, + AdminObservabilityInvestigateResponseDto, + AdminObservabilityOperationDto, + AdminObservabilityResourceSummaryDto, + AdminObservabilityS3ObjectDto, + AdminObservabilitySourceStatusDto, + AdminObservabilityTimelineEventDto, + AdminObservabilityXLogDto, +} from '../dto/observability-investigate.dto' +import { + AdminObservabilityLogsQueryParamsDto, + AdminObservabilityMetricsQueryParamsDto, + AdminObservabilityQueryParamsDto, + OBSERVABILITY_LAYERS, + ObservabilityLayer, +} from '../dto/observability-query.dto' +import { + AdminObservabilityLayerSignalsDto, + AdminObservabilityLayerStatusDto, + AdminObservabilityStatusDto, + ObservabilityState, +} from '../dto/observability-status.dto' + +export const ADMIN_AUDIT_LOG_READER = 'ADMIN_AUDIT_LOG_READER' +export const ADMIN_PLATFORM_STATE_READER = 'ADMIN_PLATFORM_STATE_READER' +export const ADMIN_CLOUDWATCH_LOG_READER = 'ADMIN_CLOUDWATCH_LOG_READER' +export const ADMIN_S3_OBJECT_READER = 'ADMIN_S3_OBJECT_READER' + +interface AdminAuditLogLike { + id: string + actorId: string + actorEmail: string + organizationId?: string + action: string + targetType?: string + targetId?: string + statusCode?: number + errorMessage?: string + source?: string + metadata?: Record + createdAt: Date +} + +interface AdminAuditLogReader { + getAllLogs( + page?: number, + limit?: number, + filters?: { from?: Date; to?: Date }, + nextToken?: string, + ): Promise<{ items: AdminAuditLogLike[]; total: number; page: number; totalPages: number; nextToken?: string }> +} + +interface AdminPlatformStateReader { + listBoxes(): Promise + listRunners(): Promise + listMachines(): Promise +} + +interface AdminCloudWatchLogReader { + getRelatedLogs( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ logs: LogEntryDto[]; status: AdminObservabilitySourceStatusDto }> +} + +interface AdminS3ObjectReader { + listRelatedObjects( + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ objects: AdminObservabilityS3ObjectDto[]; status: AdminObservabilitySourceStatusDto }> +} + +interface ClickHouseCountRow { + count: number +} + +interface ClickHouseLogRow { + Timestamp: string + Body: string + SeverityText: string + SeverityNumber: number + ServiceName: string + ResourceAttributes: Record + LogAttributes: Record + TraceId: string + SpanId: string +} + +interface ClickHouseTraceAggregateRow { + TraceId: string + startTime: string + endTime: string + spanCount: number + rootSpanName: string + totalDuration: number + statusCode: string +} + +interface ClickHouseSpanRow { + TraceId: string + SpanId: string + ParentSpanId: string + SpanName: string + Timestamp: string + Duration: number + ServiceName?: string + ResourceAttributes?: Record + SpanAttributes: Record + StatusCode: string + StatusMessage: string +} + +interface ClickHouseMetricRow { + timestamp: string + MetricName: string + layer: ObservabilityLayer | '' + value: number +} + +interface StatusRow { + layer: ObservabilityLayer + signal: keyof AdminObservabilityLayerSignalsDto + lastSeenMs: number | string +} + +const SIGNALS: Array = ['logs', 'traces', 'metrics'] +const STALE_AFTER_MS = 15 * 60 * 1000 +const DEFAULT_OBSERVABILITY_LOOKBACK_MS = 60 * 60 * 1000 +const LAYER_EXPRESSION_SQL = ` + multiIf( + ResourceAttributes['boxlite.layer'] != '', ResourceAttributes['boxlite.layer'], + ServiceName = 'boxlite-api', 'api', + ServiceName = 'boxlite-runner', 'runner', + ServiceName = 'boxlite-runner-host', 'ec2_host', + startsWith(ServiceName, 'box-'), 'box', + '' + ) +` + +// TS mirror of LAYER_EXPRESSION_SQL — keep both in sync. Lets consumers (esp. AI +// agents) attribute a single span to its emitting layer without re-querying. +function resolveSpanLayer(serviceName?: string, resourceAttributes?: Record): string { + const explicit = resourceAttributes?.['boxlite.layer'] + if (explicit) return explicit + if (!serviceName) return '' + if (serviceName === 'boxlite-api') return 'api' + if (serviceName === 'boxlite-runner') return 'runner' + if (serviceName === 'boxlite-runner-host') return 'ec2_host' + if (serviceName.startsWith('box-')) return 'box' + return '' +} +const SCALAR_METRICS_SOURCE_SQL = ` + SELECT TimeUnix, MetricName, Value, ServiceName, ResourceAttributes FROM otel_metrics_gauge + UNION ALL + SELECT TimeUnix, MetricName, Value, ServiceName, ResourceAttributes FROM otel_metrics_sum +` + +@Injectable() +export class AdminObservabilityService { + constructor( + private readonly clickhouseService: ClickHouseService, + private readonly configService: TypedConfigService, + @Inject(ADMIN_PLATFORM_STATE_READER) private readonly overviewService: AdminPlatformStateReader, + @Inject(ADMIN_AUDIT_LOG_READER) private readonly auditService: AdminAuditLogReader, + @Inject(ADMIN_CLOUDWATCH_LOG_READER) private readonly cloudWatchLogReader: AdminCloudWatchLogReader, + @Inject(ADMIN_S3_OBJECT_READER) private readonly s3ObjectReader: AdminS3ObjectReader, + ) {} + + async getStatus(): Promise { + if (!this.clickhouseService.isConfigured()) { + return { + backend: { + configured: false, + state: 'missing', + message: 'ClickHouse/ClickStack is not configured', + }, + layers: this.buildMissingLayers(), + } + } + + try { + const rows = await this.clickhouseService.query(` + SELECT layer, signal, max(lastSeenMs) AS lastSeenMs + FROM ( + SELECT ${LAYER_EXPRESSION_SQL} AS layer, 'logs' AS signal, toUnixTimestamp64Milli(max(Timestamp)) AS lastSeenMs + FROM otel_logs + WHERE layer != '' + GROUP BY layer + UNION ALL + SELECT ${LAYER_EXPRESSION_SQL} AS layer, 'traces' AS signal, toUnixTimestamp64Milli(max(Timestamp)) AS lastSeenMs + FROM otel_traces + WHERE layer != '' + GROUP BY layer + UNION ALL + SELECT ${LAYER_EXPRESSION_SQL} AS layer, 'metrics' AS signal, toUnixTimestamp64Milli(max(TimeUnix)) AS lastSeenMs + FROM ( + SELECT TimeUnix, ServiceName, ResourceAttributes FROM otel_metrics_gauge + UNION ALL + SELECT TimeUnix, ServiceName, ResourceAttributes FROM otel_metrics_sum + UNION ALL + SELECT TimeUnix, ServiceName, ResourceAttributes FROM otel_metrics_summary + UNION ALL + SELECT TimeUnix, ServiceName, ResourceAttributes FROM otel_metrics_histogram + UNION ALL + SELECT TimeUnix, ServiceName, ResourceAttributes FROM otel_metrics_exponential_histogram + ) + WHERE layer != '' + GROUP BY layer + ) + GROUP BY layer, signal + `) + + const layers = this.buildConfiguredLayers(rows) + const hasObservedTelemetry = layers.some((layer) => Boolean(layer.lastSeen)) + return { + backend: { + configured: true, + state: layers.some((layer) => layer.state === 'receiving') ? 'receiving' : 'configured', + message: hasObservedTelemetry + ? undefined + : 'ClickHouse is configured, but no OTel logs, traces, or metrics have been observed yet', + }, + layers, + } + } catch (error) { + return { + backend: { + configured: true, + state: 'error', + message: error instanceof Error ? error.message : 'ClickHouse status query failed', + }, + layers: OBSERVABILITY_LAYERS.map((layer) => ({ + layer, + state: 'error', + signals: { logs: 'error', traces: 'error', metrics: 'error' }, + })), + } + } + } + + async getLogs(query: AdminObservabilityLogsQueryParamsDto): Promise { + this.assertConfigured() + const params = this.buildBaseParams(query) + const whereClause = this.buildWhereClause('Timestamp', query, params, { eventAttributesColumn: 'LogAttributes' }) + + if (query.severities && query.severities.length > 0) { + whereClause.push('lower(SeverityText) IN ({severities:Array(String)})') + params.severities = query.severities.map((severity) => severity.toLowerCase()) + } + if (query.search) { + whereClause.push('Body ILIKE {search:String}') + params.search = `%${query.search}%` + } + this.pushTraceIdFilter(whereClause, params, query.traceId) + this.pushAttributeFilter(whereClause, params, 'LogAttributes', 'boxlite.user_id', 'userId', query.userId) + this.pushAttributeFilter(whereClause, params, 'LogAttributes', 'boxlite.request_id', 'requestId', query.requestId) + this.pushAttributeFilter( + whereClause, + params, + 'LogAttributes', + 'boxlite.operation_id', + 'operationId', + query.operationId, + ) + this.pushAttributeFilter( + whereClause, + params, + 'LogAttributes', + 'boxlite.execution_id', + 'executionId', + query.executionId, + ) + this.pushAttributeFilter(whereClause, params, 'LogAttributes', 'boxlite.job_id', 'jobId', query.jobId) + + const whereSql = whereClause.join('\n AND ') + const countResult = await this.clickhouseService.query( + ` + SELECT count() as count + FROM otel_logs + WHERE ${whereSql} + `, + params, + ) + const total = countResult[0]?.count || 0 + + const rows = await this.clickhouseService.query( + ` + SELECT Timestamp, Body, SeverityText, SeverityNumber, ServiceName, + ResourceAttributes, LogAttributes, TraceId, SpanId + FROM otel_logs + WHERE ${whereSql} + ORDER BY Timestamp DESC + LIMIT {limit:UInt32} OFFSET {offset:UInt32} + `, + params, + ) + + return { + items: rows.map((row) => ({ + timestamp: row.Timestamp, + body: row.Body, + severityText: row.SeverityText, + severityNumber: row.SeverityNumber, + serviceName: row.ServiceName, + resourceAttributes: row.ResourceAttributes || {}, + logAttributes: row.LogAttributes || {}, + traceId: row.TraceId || undefined, + spanId: row.SpanId || undefined, + })) as LogEntryDto[], + total, + page: query.page ?? 1, + totalPages: Math.ceil(total / (query.limit ?? 100)), + } + } + + async getTraces(query: AdminObservabilityQueryParamsDto): Promise { + this.assertConfigured() + const params = this.buildBaseParams(query) + const whereClause = this.buildWhereClause('Timestamp', query, params, { eventAttributesColumn: 'SpanAttributes' }) + this.pushTraceIdFilter(whereClause, params, query.traceId) + this.pushAttributeFilter(whereClause, params, 'SpanAttributes', 'boxlite.user_id', 'userId', query.userId) + this.pushAttributeFilter(whereClause, params, 'SpanAttributes', 'boxlite.request_id', 'requestId', query.requestId) + this.pushAttributeFilter( + whereClause, + params, + 'SpanAttributes', + 'boxlite.operation_id', + 'operationId', + query.operationId, + ) + this.pushAttributeFilter( + whereClause, + params, + 'SpanAttributes', + 'boxlite.execution_id', + 'executionId', + query.executionId, + ) + this.pushAttributeFilter(whereClause, params, 'SpanAttributes', 'boxlite.job_id', 'jobId', query.jobId) + const whereSql = whereClause.join('\n AND ') + + const countResult = await this.clickhouseService.query( + ` + SELECT count(DISTINCT TraceId) as count + FROM otel_traces + WHERE ${whereSql} + `, + params, + ) + const total = countResult[0]?.count || 0 + + const rows = await this.clickhouseService.query( + ` + SELECT + TraceId, + min(Timestamp) as startTime, + max(Timestamp) as endTime, + count() as spanCount, + argMinIf(SpanName, Timestamp, ParentSpanId = '') as rootSpanName, + max(Duration) as totalDuration, + any(StatusCode) as statusCode + FROM otel_traces + WHERE ${whereSql} + GROUP BY TraceId + ORDER BY startTime DESC + LIMIT {limit:UInt32} OFFSET {offset:UInt32} + `, + params, + ) + + return { + items: rows.map((row) => ({ + traceId: row.TraceId, + rootSpanName: row.rootSpanName, + startTime: row.startTime, + endTime: row.endTime, + durationMs: row.totalDuration / 1_000_000, + spanCount: row.spanCount, + statusCode: row.statusCode || undefined, + })) as TraceSummaryDto[], + total, + page: query.page ?? 1, + totalPages: Math.ceil(total / (query.limit ?? 100)), + } + } + + async getTraceSpans(traceId: string, query: AdminObservabilityQueryParamsDto): Promise { + this.assertConfigured() + const params = this.buildBaseParams(query) + params.traceId = traceId + const whereClause = this.buildWhereClause('Timestamp', query, params, { eventAttributesColumn: 'SpanAttributes' }) + whereClause.push('TraceId = {traceId:String}') + + const rows = await this.clickhouseService.query( + ` + SELECT TraceId, SpanId, ParentSpanId, SpanName, Timestamp, Duration, ServiceName, + ResourceAttributes, SpanAttributes, StatusCode, StatusMessage + FROM otel_traces + WHERE ${whereClause.join('\n AND ')} + ORDER BY Timestamp ASC + `, + params, + ) + + return rows.map((row) => this.toTraceSpan(row)) + } + + async getMetrics(query: AdminObservabilityMetricsQueryParamsDto): Promise { + this.assertConfigured() + const params = this.buildBaseParams(query) + const whereClause = this.buildWhereClause('TimeUnix', query, params) + if (query.metricNames && query.metricNames.length > 0) { + whereClause.push('MetricName IN ({metricNames:Array(String)})') + params.metricNames = query.metricNames + } + + const rows = await this.clickhouseService.query( + ` + SELECT + toStartOfInterval(TimeUnix, INTERVAL 1 MINUTE) as timestamp, + MetricName, + ${LAYER_EXPRESSION_SQL} as layer, + avg(Value) as value + FROM (${SCALAR_METRICS_SOURCE_SQL}) + WHERE ${whereClause.join('\n AND ')} + GROUP BY timestamp, MetricName, layer + ORDER BY timestamp ASC + `, + params, + ) + + const seriesMap = new Map< + string, + { metricName: string; layer?: ObservabilityLayer; dataPoints: MetricDataPointDto[] } + >() + for (const row of rows) { + const layer = OBSERVABILITY_LAYERS.includes(row.layer as ObservabilityLayer) + ? (row.layer as ObservabilityLayer) + : undefined + const seriesKey = `${row.MetricName}:${layer ?? 'unknown'}` + let series = seriesMap.get(seriesKey) + if (!series) { + series = { metricName: row.MetricName, layer, dataPoints: [] } + seriesMap.set(seriesKey, series) + } + series.dataPoints.push({ timestamp: row.timestamp, value: row.value }) + } + + const series: MetricSeriesDto[] = Array.from(seriesMap.values()) + + return { series } + } + + async investigate( + query: AdminObservabilityInvestigateQueryParamsDto, + ): Promise { + const correlation = this.createEmptyCorrelation() + this.collectQueryCorrelation(correlation, query) + + const sources: AdminObservabilitySourceStatusDto[] = [] + let traceSpans: TraceSpanDto[] = [] + let logs: LogEntryDto[] = [] + let metrics: MetricsResponseDto = { series: [] } + let xlogs: AdminObservabilityXLogDto[] = [] + let s3Objects: AdminObservabilityS3ObjectDto[] = [] + let clickhouseTelemetryCount: number | undefined + + if (!this.clickhouseService.isConfigured()) { + sources.push({ + source: 'clickhouse', + state: 'not_configured', + message: 'ClickHouse/ClickStack is not configured', + count: 0, + }) + } else { + try { + const traceRows = await this.getInvestigationTraceRows(query) + traceSpans = traceRows.map((row) => this.toTraceSpan(row)) + for (const row of traceRows) { + this.collectTraceRowCorrelation(correlation, row) + } + + const relatedQuery = this.buildRelatedTelemetryQuery(query, correlation) + const [logPage, metricResponse] = await Promise.all([this.getLogs(relatedQuery), this.getMetrics(relatedQuery)]) + logs = logPage.items + metrics = metricResponse + for (const log of logs) { + this.collectLogCorrelation(correlation, log) + } + + const clickhouseCount = traceSpans.length + logs.length + metrics.series.length + clickhouseTelemetryCount = clickhouseCount + sources.push({ + source: 'clickhouse', + state: clickhouseCount > 0 ? 'available' : 'missing', + message: + clickhouseCount > 0 + ? undefined + : 'ClickHouse is configured, but no matching OTel logs, traces, or metrics were found for this context', + count: clickhouseCount, + }) + } catch (error) { + sources.push({ + source: 'clickhouse', + state: 'error', + message: error instanceof Error ? error.message : 'ClickHouse investigation query failed', + count: 0, + }) + } + } + + const { logs: cloudWatchLogs, cloudWatchStatus } = await this.getRelatedCloudWatchLogs(query, correlation) + logs = [...logs, ...cloudWatchLogs] + for (const log of cloudWatchLogs) { + this.collectLogCorrelation(correlation, log) + } + sources.push(cloudWatchStatus) + xlogs = this.buildXLogs(logs) + + const { boxes, runners, machines, postgresStatus } = await this.getRelatedPlatformState(correlation) + sources.push(postgresStatus) + + const { auditLogs, auditStatus } = await this.getRelatedAuditLogs(query, correlation) + sources.push(auditStatus) + + const s3Result = await this.getRelatedS3Objects(correlation) + s3Objects = s3Result.objects + sources.push(s3Result.s3Status) + + sources.push(this.buildXLogStatus(query, correlation, xlogs)) + const resource = this.buildResourceSummary(query, correlation, boxes, runners, machines) + const timeline = this.buildTimeline(traceSpans, logs, auditLogs, xlogs) + const operations = this.buildOperations(boxes, runners) + const commands = this.buildCommands(query, correlation) + const externalLinks = this.buildExternalLinks(query, correlation) + sources.push(this.buildClickStackStatus(externalLinks, clickhouseTelemetryCount)) + + return { + resource, + correlation, + sources, + traceSpans, + logs, + metrics, + boxes, + runners, + machines, + auditLogs, + xlogs, + s3Objects, + timeline, + operations, + commands, + externalLinks, + } + } + + private assertConfigured() { + if (!this.clickhouseService.isConfigured()) { + throw new ServiceUnavailableException('ClickHouse/ClickStack is not configured') + } + } + + private buildResourceSummary( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + boxes: AdminBoxItemDto[], + runners: AdminRunnerItemDto[], + machines: AdminMachineItemDto[], + ): AdminObservabilityResourceSummaryDto { + const timeRange = this.buildTimeRange(query) + const base = { + identifiers: this.buildIdentifierMap(correlation), + timeRange: { + from: timeRange.from.toISOString(), + to: timeRange.to.toISOString(), + }, + } + + const hasObjectTarget = Boolean(query.boxId || query.runnerId || query.machineId) + const hasEventTarget = Boolean( + query.traceId || query.requestId || query.operationId || query.executionId || query.jobId, + ) + if (!hasObjectTarget && !hasEventTarget && (query.userId || correlation.userIds[0])) { + const userId = query.userId ?? correlation.userIds[0] + return { + ...base, + type: 'user', + title: `User ${userId}`, + subtitle: correlation.orgIds[0] ? `Organization ${correlation.orgIds[0]}` : undefined, + } + } + + if (!hasObjectTarget && !hasEventTarget && (query.orgId || correlation.orgIds[0])) { + const orgId = query.orgId ?? correlation.orgIds[0] + return { + ...base, + type: 'org', + title: `Organization ${orgId}`, + } + } + + const box = boxes[0] + if (box) { + return { + ...base, + type: 'box', + title: `Box ${box.id}`, + subtitle: box.id, + state: box.state, + owner: box.owner?.email || box.owner?.name, + } + } + + const runner = runners[0] + if (runner) { + return { + ...base, + type: 'runner', + title: `Runner ${runner.id}`, + subtitle: runner.region, + state: runner.draining ? 'draining' : runner.state, + } + } + + const machine = machines[0] + if (machine) { + return { + ...base, + type: 'machine', + title: `Machine ${machine.host}`, + subtitle: machine.region, + state: machine.cpuWaterline >= 90 || machine.memWaterline >= 90 ? 'pressure' : 'online', + } + } + + if (correlation.traceIds[0]) { + return { + ...base, + type: 'trace', + title: `Trace ${correlation.traceIds[0]}`, + } + } + + if (correlation.requestIds[0]) { + return { + ...base, + type: 'request', + title: `Request ${correlation.requestIds[0]}`, + } + } + + if (correlation.operationIds[0]) { + return { + ...base, + type: 'operation', + title: `Operation ${correlation.operationIds[0]}`, + } + } + + if (correlation.executionIds[0]) { + return { + ...base, + type: 'execution', + title: `Execution ${correlation.executionIds[0]}`, + } + } + + if (correlation.jobIds[0]) { + return { + ...base, + type: 'job', + title: `Job ${correlation.jobIds[0]}`, + } + } + + return { + ...base, + type: 'unknown', + title: 'Platform investigation', + subtitle: 'No specific resource has been resolved yet', + } + } + + private buildIdentifierMap(correlation: AdminObservabilityCorrelationDto): Record { + const identifiers: Record = {} + const entries: Array<[string, string | undefined]> = [ + ['traceId', correlation.traceIds[0]], + ['orgId', correlation.orgIds[0]], + ['userId', correlation.userIds[0]], + ['boxId', correlation.boxIds[0]], + ['runnerId', correlation.runnerIds[0]], + ['machineId', correlation.machineIds[0]], + ['requestId', correlation.requestIds[0]], + ['operationId', correlation.operationIds[0]], + ['executionId', correlation.executionIds[0]], + ['jobId', correlation.jobIds[0]], + ] + for (const [key, value] of entries) { + if (value) identifiers[key] = value + } + return identifiers + } + + private buildTimeline( + traceSpans: TraceSpanDto[], + logs: LogEntryDto[], + auditLogs: AdminObservabilityAuditLogDto[], + xlogs: AdminObservabilityXLogDto[], + ): AdminObservabilityTimelineEventDto[] { + const events: AdminObservabilityTimelineEventDto[] = [ + ...traceSpans.map((span) => ({ + timestamp: span.timestamp, + source: 'trace', + title: span.spanName, + detail: span.statusMessage, + severity: span.statusCode, + identifiers: { + traceId: span.traceId, + spanId: span.spanId, + }, + })), + ...logs.map((log) => ({ + timestamp: log.timestamp, + source: 'log', + title: log.serviceName || 'log', + detail: log.body, + severity: log.severityText, + identifiers: { + ...(log.traceId ? { traceId: log.traceId } : {}), + ...(log.spanId ? { spanId: log.spanId } : {}), + }, + })), + ...auditLogs.map((log) => ({ + timestamp: log.createdAt instanceof Date ? log.createdAt.toISOString() : String(log.createdAt), + source: 'audit', + title: log.action, + detail: log.errorMessage || log.actorEmail, + severity: log.statusCode && log.statusCode >= 400 ? 'error' : 'info', + identifiers: { + ...(log.organizationId ? { orgId: log.organizationId } : {}), + ...(log.targetId ? { targetId: log.targetId } : {}), + ...(log.source ? { requestSource: log.source } : {}), + }, + })), + ...xlogs.map((log) => ({ + timestamp: log.timestamp, + source: 'xlog', + title: log.stream || log.serviceName, + detail: log.body, + severity: log.severityText, + identifiers: { + ...(log.executionId ? { executionId: log.executionId } : {}), + ...(log.jobId ? { jobId: log.jobId } : {}), + ...(log.traceId ? { traceId: log.traceId } : {}), + }, + })), + ] + + return events + .filter((event) => Number.isFinite(new Date(event.timestamp).getTime())) + .sort((left, right) => new Date(left.timestamp).getTime() - new Date(right.timestamp).getTime()) + .slice(0, 100) + } + + private buildOperations(boxes: AdminBoxItemDto[], runners: AdminRunnerItemDto[]): AdminObservabilityOperationDto[] { + const operations: AdminObservabilityOperationDto[] = [] + for (const box of boxes.slice(0, 5)) { + const state = String(box.state).toLowerCase() + const canRecover = state.includes('error') || state.includes('failed') + operations.push({ + id: `recover:${box.id}`, + label: 'Recover box', + state: canRecover ? 'enabled' : 'disabled', + method: 'POST', + path: `/admin/box/${box.id}/recover`, + targetId: box.id, + reason: canRecover ? 'Box is in a recoverable failure state' : 'Recover is only enabled for failed boxes', + }) + operations.push({ + id: `resize:${box.id}`, + label: 'Resize box', + state: 'request_only', + method: 'POST', + path: `/admin/box/${box.id}/resize-request`, + targetId: box.id, + reason: 'Resize requires an explicit request flow; direct resize is disabled in first phase', + }) + } + + for (const runner of runners.slice(0, 5)) { + operations.push({ + id: `cordon:${runner.id}`, + label: runner.unschedulable ? 'Uncordon runner' : 'Cordon runner', + state: 'enabled', + method: 'PATCH', + path: `/admin/runners/${runner.id}/scheduling`, + targetId: runner.id, + reason: runner.unschedulable + ? 'Runner is already cordoned and can be returned to scheduling' + : 'Prevent new boxes from scheduling on this runner', + }) + operations.push({ + id: `drain:${runner.id}`, + label: 'Drain runner', + state: runner.draining ? 'disabled' : 'enabled', + method: 'PATCH', + path: `/admin/runners/${runner.id}/draining`, + targetId: runner.id, + reason: runner.draining ? 'Runner is already draining' : 'Move runner into draining mode', + }) + operations.push({ + id: `scale:${runner.id}`, + label: 'Scale fleet', + state: 'request_only', + method: 'POST', + path: `/admin/runners/${runner.id}/scale-request`, + targetId: runner.id, + reason: 'Fleet scaling is intentionally request-only in the first Admin Diagnose phase', + }) + } + + if (operations.length === 0) { + operations.push({ + id: 'quota:request', + label: 'Request quota change', + state: 'request_only', + method: 'POST', + path: '/admin/quota/request', + reason: 'No concrete box or runner was resolved; quota actions need a scoped resource', + }) + } + + return operations + } + + private buildCommands( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): AdminObservabilityCommandsDto { + const queryString = this.buildInvestigationQueryString(query, correlation) + return { + api: `GET /admin/observability/investigate${queryString ? `?${queryString}` : ''}`, + aiAgentPrompt: + `Use BoxLite Admin API only. Query GET /admin/observability/investigate${queryString ? `?${queryString}` : ''} ` + + 'with header X-BoxLite-Source=agent, then summarize resource, sources, missing reasons, timeline, xLog, audit, and next operations.', + } + } + + private buildExternalLinks( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): AdminObservabilityExternalLinksDto { + const baseUrl = this.configService.get('observability.clickstackBaseUrl') + const dashboardUrl = this.configService.get('observability.clickstackDashboardUrl') + const logSourceId = this.configService.get('observability.clickstackLogSourceId') + const traceSourceId = this.configService.get('observability.clickstackTraceSourceId') + const metricSourceId = this.configService.get('observability.clickstackMetricSourceId') + const timeRange = this.buildTimeRange(query) + const queryContext = this.buildClickStackQueryContext(query, correlation) + const clickstackLogQuery = this.buildClickStackQuery(correlation, 'LogAttributes') + const clickstackTraceQuery = this.buildClickStackQuery(correlation, 'SpanAttributes') + const missingSources = [ + ...(!logSourceId ? ['logs'] : []), + ...(!traceSourceId ? ['traces'] : []), + ...(!metricSourceId ? ['metrics'] : []), + ] + const sourceSetup = this.buildClickStackSourceSetup(missingSources) + if (!baseUrl) { + return { + clickstack: { + configured: false, + message: 'ADMIN_OBSERVABILITY_CLICKSTACK_URL is not configured', + sourceSetup, + query: clickstackTraceQuery, + queryContext, + }, + } + } + + return { + clickstack: { + configured: true, + missingSources, + sourceSetup, + message: + missingSources.length > 0 + ? `ClickStack is reachable, but ${missingSources.join(', ')} source id${ + missingSources.length === 1 ? '' : 's' + } need to be configured for one-click queries` + : undefined, + dashboardUrl, + logsUrl: this.buildClickStackSearchUrl(baseUrl, { + sourceId: logSourceId, + timeRange, + where: clickstackLogQuery, + }), + tracesUrl: this.buildClickStackSearchUrl(baseUrl, { + sourceId: traceSourceId, + timeRange, + where: clickstackTraceQuery, + traceId: correlation.traceIds[0], + }), + metricsUrl: this.buildClickStackChartUrl(baseUrl, { + sourceId: metricSourceId, + timeRange, + }), + query: clickstackTraceQuery, + queryContext, + }, + } + } + + private buildClickStackSourceSetup( + missingSources: string[], + ): AdminObservabilityClickStackSourceSetupDto[] | undefined { + if (missingSources.length === 0) { + return undefined + } + + const allSources: Record = { + logs: { + kind: 'logs', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID', + name: 'BoxLite Logs', + dataType: 'Log', + database: 'otel', + table: 'otel_logs', + timestampColumn: 'Timestamp', + defaultSelect: 'Timestamp, ServiceName, SeverityText, Body', + fields: { + serviceName: 'ServiceName', + severityText: 'SeverityText', + body: 'Body', + eventAttributes: 'LogAttributes', + resourceAttributes: 'ResourceAttributes', + traceId: 'TraceId', + spanId: 'SpanId', + implicitColumn: 'Body', + displayedTimestamp: 'Timestamp', + }, + }, + traces: { + kind: 'traces', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_TRACE_SOURCE_ID', + name: 'BoxLite Traces', + dataType: 'Trace', + database: 'otel', + table: 'otel_traces', + timestampColumn: 'Timestamp', + defaultSelect: 'Timestamp, ServiceName, StatusCode, round(Duration / 1e6), SpanName', + fields: { + serviceName: 'ServiceName', + eventAttributes: 'SpanAttributes', + resourceAttributes: 'ResourceAttributes', + traceId: 'TraceId', + spanId: 'SpanId', + duration: 'Duration', + durationPrecision: '9', + parentSpanId: 'ParentSpanId', + spanName: 'SpanName', + spanKind: 'SpanKind', + statusCode: 'StatusCode', + statusMessage: 'StatusMessage', + spanEvents: 'Events', + implicitColumn: 'SpanName', + displayedTimestamp: 'Timestamp', + }, + }, + metrics: { + kind: 'metrics', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_METRIC_SOURCE_ID', + name: 'BoxLite Metrics', + dataType: 'OTEL Metrics', + database: 'otel', + timestampColumn: 'TimeUnix', + fields: { + serviceName: 'ServiceName', + resourceAttributes: 'ResourceAttributes', + }, + metricTables: { + gauge: 'otel_metrics_gauge', + histogram: 'otel_metrics_histogram', + sum: 'otel_metrics_sum', + summary: 'otel_metrics_summary', + 'exponential histogram': 'otel_metrics_exponential_histogram', + }, + }, + } + + return missingSources.map((source) => allSources[source]).filter(Boolean) + } + + private buildClickStackStatus( + externalLinks: AdminObservabilityExternalLinksDto, + clickhouseTelemetryCount?: number, + ): AdminObservabilitySourceStatusDto { + if (externalLinks.clickstack.configured) { + const missingSources = externalLinks.clickstack.missingSources ?? [] + if (missingSources.length > 0) { + return { + source: 'clickstack', + state: 'missing', + message: externalLinks.clickstack.message, + count: 3 - missingSources.length, + } + } + if (clickhouseTelemetryCount === 0) { + return { + source: 'clickstack', + state: 'missing', + message: + 'ClickStack source ids are configured, but ClickHouse has no matching OTel rows for this investigation; the third-party page will be empty until collector ingestion writes data', + count: 0, + } + } + + return { + source: 'clickstack', + state: 'available', + message: 'ClickStack deep links are configured for human exploration', + count: 3, + } + } + + return { + source: 'clickstack', + state: 'not_configured', + message: externalLinks.clickstack.message, + count: 0, + } + } + + private buildClickStackSearchUrl( + baseUrl: string, + options: { + sourceId?: string + timeRange: { from: Date; to: Date } + where: string + traceId?: string + }, + ): string { + try { + const url = new URL(baseUrl) + url.pathname = '/search' + if (options.sourceId) { + url.searchParams.set('source', options.sourceId) + } + url.searchParams.set('from', options.timeRange.from.getTime().toString()) + url.searchParams.set('to', options.timeRange.to.getTime().toString()) + url.searchParams.set('isLive', 'false') + url.searchParams.set('where', options.where) + url.searchParams.set('whereLanguage', 'sql') + if (options.traceId) { + url.searchParams.set('traceId', options.traceId) + } + return url.toString() + } catch { + const separator = baseUrl.includes('?') ? '&' : '?' + const params = new URLSearchParams({ + from: options.timeRange.from.getTime().toString(), + to: options.timeRange.to.getTime().toString(), + isLive: 'false', + where: options.where, + whereLanguage: 'sql', + }) + if (options.sourceId) { + params.set('source', options.sourceId) + } + if (options.traceId) { + params.set('traceId', options.traceId) + } + return `${baseUrl}${separator}${params.toString()}` + } + } + + private buildClickStackChartUrl( + baseUrl: string, + options: { + sourceId?: string + timeRange: { from: Date; to: Date } + }, + ): string { + const config = options.sourceId + ? JSON.stringify({ + source: options.sourceId, + select: [ + { + aggFn: 'count', + aggCondition: '', + aggConditionLanguage: 'lucene', + valueExpression: '', + }, + ], + where: '', + whereLanguage: 'lucene', + displayType: 'line', + granularity: 'auto', + alignDateRangeToGranularity: true, + }) + : undefined + try { + const url = new URL(baseUrl) + url.pathname = '/chart' + url.searchParams.set('from', options.timeRange.from.getTime().toString()) + url.searchParams.set('to', options.timeRange.to.getTime().toString()) + url.searchParams.set('isLive', 'false') + if (config) { + url.searchParams.set('config', config) + } + return url.toString() + } catch { + const separator = baseUrl.includes('?') ? '&' : '?' + const params = new URLSearchParams({ + from: options.timeRange.from.getTime().toString(), + to: options.timeRange.to.getTime().toString(), + isLive: 'false', + }) + if (config) { + params.set('config', config) + } + return `${baseUrl}${separator}${params.toString()}` + } + } + + private buildClickStackQueryContext( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): Record { + const timeRange = this.buildTimeRange(query) + return { + from: timeRange.from.toISOString(), + to: timeRange.to.toISOString(), + ...this.buildIdentifierMap(correlation), + } + } + + private buildClickStackQuery( + correlation: AdminObservabilityCorrelationDto, + eventAttributesColumn?: 'LogAttributes' | 'SpanAttributes', + ): string { + const clauses = [ + this.queryClause('TraceId', correlation.traceIds[0]), + ...this.attributeQueryClauses('boxlite.org_id', correlation.orgIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.user_id', correlation.userIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.box_id', correlation.boxIds[0], eventAttributesColumn), + this.queryClause('ServiceName', this.boxServiceName(correlation.boxIds[0])), + ...this.attributeQueryClauses('boxlite.runner_id', correlation.runnerIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.machine_id', correlation.machineIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.execution_id', correlation.executionIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.job_id', correlation.jobIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.request_id', correlation.requestIds[0], eventAttributesColumn), + ...this.attributeQueryClauses('boxlite.operation_id', correlation.operationIds[0], eventAttributesColumn), + ].filter((clause): clause is string => Boolean(clause)) + return clauses.length > 0 ? clauses.join(' OR ') : "ServiceName != ''" + } + + private attributeQueryClauses( + attributeName: string, + value?: string, + eventAttributesColumn?: 'LogAttributes' | 'SpanAttributes', + ): string[] { + if (!value) { + return [] + } + return [ + this.queryClause(`ResourceAttributes['${attributeName}']`, value), + eventAttributesColumn ? this.queryClause(`${eventAttributesColumn}['${attributeName}']`, value) : undefined, + ].filter((clause): clause is string => Boolean(clause)) + } + + private queryClause(field: string, value?: string): string | undefined { + if (!value) { + return undefined + } + return `${field} = '${value.replace(/'/g, "\\'")}'` + } + + private buildInvestigationQueryString( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): string { + const params = new URLSearchParams() + const timeRange = this.buildTimeRange(query) + params.set('from', timeRange.from.toISOString()) + params.set('to', timeRange.to.toISOString()) + params.set('limit', String(query.limit ?? 100)) + const identifiers = this.buildIdentifierMap(correlation) + for (const [key, value] of Object.entries(identifiers)) { + params.set(key, value) + } + return params.toString() + } + + private buildMissingLayers(): AdminObservabilityLayerStatusDto[] { + return OBSERVABILITY_LAYERS.map((layer) => ({ + layer, + state: 'missing', + signals: { logs: 'missing', traces: 'missing', metrics: 'missing' }, + })) + } + + private buildConfiguredLayers(rows: StatusRow[]): AdminObservabilityLayerStatusDto[] { + const now = Date.now() + return OBSERVABILITY_LAYERS.map((layer) => { + const signals: AdminObservabilityLayerSignalsDto = { + logs: 'configured', + traces: 'configured', + metrics: 'configured', + } + let lastSeenMs = 0 + + for (const signal of SIGNALS) { + const row = rows.find((candidate) => candidate.layer === layer && candidate.signal === signal) + if (row?.lastSeenMs === undefined || row.lastSeenMs === null) { + continue + } + const seenAt = Number(row.lastSeenMs) + if (!Number.isFinite(seenAt)) { + continue + } + lastSeenMs = Math.max(lastSeenMs, seenAt) + signals[signal] = now - seenAt <= STALE_AFTER_MS ? 'receiving' : 'stale' + } + + const state = this.mergeSignalState(signals) + return { + layer, + state, + signals, + ...(lastSeenMs > 0 ? { lastSeen: new Date(lastSeenMs).toISOString() } : {}), + } + }) + } + + private mergeSignalState(signals: AdminObservabilityLayerSignalsDto): ObservabilityState { + if (SIGNALS.some((signal) => signals[signal] === 'receiving')) { + return 'receiving' + } + if (SIGNALS.some((signal) => signals[signal] === 'stale')) { + return 'stale' + } + return 'configured' + } + + private async getInvestigationTraceRows( + query: AdminObservabilityInvestigateQueryParamsDto, + ): Promise { + const traceIds = query.traceId + ? [query.traceId] + : (await this.getTraces({ ...query, page: 1, limit: 5 })).items.map((trace) => trace.traceId) + if (traceIds.length === 0) { + return [] + } + + const rows: ClickHouseSpanRow[] = [] + for (const traceId of traceIds.slice(0, 5)) { + const params = this.buildBaseParams(query) + params.traceId = traceId + const whereClause = this.buildWhereClause('Timestamp', query, params, { eventAttributesColumn: 'SpanAttributes' }) + whereClause.push('TraceId = {traceId:String}') + + rows.push( + ...(await this.clickhouseService.query( + ` + SELECT TraceId, SpanId, ParentSpanId, SpanName, Timestamp, Duration, ServiceName, + ResourceAttributes, SpanAttributes, StatusCode, StatusMessage + FROM otel_traces + WHERE ${whereClause.join('\n AND ')} + ORDER BY Timestamp ASC + `, + params, + )), + ) + } + + return rows + } + + private toTraceSpan(row: ClickHouseSpanRow): TraceSpanDto { + return { + traceId: row.TraceId, + spanId: row.SpanId, + parentSpanId: row.ParentSpanId || undefined, + spanName: row.SpanName, + serviceName: row.ServiceName || undefined, + layer: resolveSpanLayer(row.ServiceName, row.ResourceAttributes) || undefined, + timestamp: row.Timestamp, + durationNs: row.Duration, + spanAttributes: row.SpanAttributes || {}, + statusCode: row.StatusCode || undefined, + statusMessage: row.StatusMessage || undefined, + } + } + + private buildRelatedTelemetryQuery( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): AdminObservabilityLogsQueryParamsDto & AdminObservabilityMetricsQueryParamsDto { + // When the caller targets a specific resource (box/runner/machine), do NOT + // narrow related logs/metrics by an org/user id harvested from correlated API spans. + // Self-emitted box/runner telemetry carries ServiceName (box-) + underscore + // attrs, not the dot-namespaced boxlite.org_id the org filter matches, so an AND-ed + // org clause silently drops exactly those rows. The resource id is already org-scoped, + // so omitting the harvested org filter does not broaden results across orgs. + const resourceTargeted = Boolean(query.boxId || query.runnerId || query.machineId) + return { + ...query, + page: 1, + limit: Math.min(query.limit ?? 100, 100), + traceId: query.traceId ?? correlation.traceIds[0], + orgId: resourceTargeted ? query.orgId : (query.orgId ?? correlation.orgIds[0]), + userId: resourceTargeted ? query.userId : (query.userId ?? correlation.userIds[0]), + boxId: query.boxId ?? correlation.boxIds[0], + runnerId: query.runnerId ?? correlation.runnerIds[0], + machineId: query.machineId ?? correlation.machineIds[0], + requestId: query.requestId ?? correlation.requestIds[0], + operationId: query.operationId ?? correlation.operationIds[0], + executionId: query.executionId ?? correlation.executionIds[0], + jobId: query.jobId ?? correlation.jobIds[0], + } + } + + private createEmptyCorrelation(): AdminObservabilityCorrelationDto { + return { + traceIds: [], + orgIds: [], + userIds: [], + boxIds: [], + runnerIds: [], + machineIds: [], + requestIds: [], + operationIds: [], + executionIds: [], + jobIds: [], + serviceNames: [], + } + } + + private collectQueryCorrelation( + correlation: AdminObservabilityCorrelationDto, + query: AdminObservabilityInvestigateQueryParamsDto, + ) { + this.addUnique(correlation.traceIds, query.traceId) + this.addUnique(correlation.orgIds, query.orgId) + this.addUnique(correlation.userIds, query.userId) + this.addUnique(correlation.boxIds, query.boxId) + this.addUnique(correlation.runnerIds, query.runnerId) + this.addUnique(correlation.machineIds, query.machineId) + this.addUnique(correlation.requestIds, query.requestId) + this.addUnique(correlation.operationIds, query.operationId) + this.addUnique(correlation.executionIds, query.executionId) + this.addUnique(correlation.jobIds, query.jobId) + this.addUnique(correlation.serviceNames, query.serviceName) + } + + private collectTraceRowCorrelation(correlation: AdminObservabilityCorrelationDto, row: ClickHouseSpanRow) { + this.addUnique(correlation.traceIds, row.TraceId) + this.collectServiceNameCorrelation(correlation, row.ServiceName) + this.collectAttributeCorrelation(correlation, row.ResourceAttributes) + this.collectAttributeCorrelation(correlation, row.SpanAttributes) + } + + private collectLogCorrelation(correlation: AdminObservabilityCorrelationDto, log: LogEntryDto) { + this.addUnique(correlation.traceIds, log.traceId) + this.collectServiceNameCorrelation(correlation, log.serviceName) + this.collectAttributeCorrelation(correlation, log.resourceAttributes) + this.collectAttributeCorrelation(correlation, log.logAttributes) + } + + private buildXLogs(logs: LogEntryDto[]): AdminObservabilityXLogDto[] { + return logs + .map((log): AdminObservabilityXLogDto | null => { + const attributes = { + ...(log.resourceAttributes ?? {}), + ...(log.logAttributes ?? {}), + } + const executionId = this.readAttribute(attributes, [ + 'boxlite.execution_id', + 'execution_id', + 'execution.id', + 'exec_id', + ]) + const jobId = this.readAttribute(attributes, ['boxlite.job_id', 'job_id', 'job.id']) + + if (!executionId && !jobId) { + return null + } + + return { + source: attributes['boxlite.source'] === 'cloudwatch' ? 'cloudwatch_logs' : 'clickhouse_logs', + timestamp: log.timestamp, + serviceName: log.serviceName, + body: this.resolveXLogBody(log.body, attributes), + severityText: log.severityText, + traceId: log.traceId, + spanId: log.spanId, + executionId, + jobId, + stream: this.readAttribute(attributes, ['boxlite.stream', 'stream', 'log.stream']), + attributes, + } + }) + .filter((entry): entry is AdminObservabilityXLogDto => entry !== null) + } + + private resolveXLogBody(body: string, attributes: Record): string { + return this.readOutputAttribute(attributes, ['boxlite.output', 'xlog.output', 'exec.output']) ?? body + } + + private readOutputAttribute(attributes: Record, names: string[]): string | undefined { + for (const name of names) { + const value = attributes[name] + if (typeof value === 'string' && value.length > 0) { + return value + } + } + return undefined + } + + private buildXLogStatus( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + xlogs: AdminObservabilityXLogDto[], + ): AdminObservabilitySourceStatusDto { + if (xlogs.length > 0) { + return { source: 'xlog', state: 'available', count: xlogs.length } + } + + const hasExecutionContext = + Boolean(query.executionId || query.jobId) || correlation.executionIds.length > 0 || correlation.jobIds.length > 0 + + return { + source: 'xlog', + state: 'missing', + message: hasExecutionContext + ? 'No ClickHouse logs with execution/job attributes were found for this investigation' + : 'No execution_id/job_id correlation was discovered; runner attach output is not persisted as historical xLog yet', + count: 0, + } + } + + private collectAttributeCorrelation( + correlation: AdminObservabilityCorrelationDto, + attributes?: Record, + ) { + if (!attributes) { + return + } + this.addUnique(correlation.orgIds, attributes['boxlite.org_id']) + this.addUnique(correlation.userIds, this.readAttribute(attributes, ['boxlite.user_id', 'user_id', 'user.id'])) + this.addUnique(correlation.boxIds, attributes['boxlite.box_id']) + this.addUnique(correlation.runnerIds, attributes['boxlite.runner_id']) + this.addUnique(correlation.machineIds, attributes['boxlite.machine_id']) + this.addUnique(correlation.requestIds, attributes['boxlite.request_id']) + this.addUnique(correlation.operationIds, attributes['boxlite.operation_id']) + this.addUnique(correlation.executionIds, attributes['boxlite.execution_id']) + this.addUnique(correlation.jobIds, attributes['boxlite.job_id']) + } + + private collectServiceNameCorrelation(correlation: AdminObservabilityCorrelationDto, serviceName?: string) { + this.addUnique(correlation.serviceNames, serviceName) + this.addUnique(correlation.boxIds, this.boxIdFromServiceName(serviceName)) + } + + private boxIdFromServiceName(serviceName?: string): string | undefined { + if (!serviceName?.startsWith('box-')) { + return undefined + } + const boxId = serviceName.slice('box-'.length).trim() + return boxId || undefined + } + + private readAttribute(attributes: Record, names: string[]): string | undefined { + for (const name of names) { + const value = attributes[name] + if (typeof value === 'string' && value.trim()) { + return value.trim() + } + } + return undefined + } + + private async getRelatedPlatformState(correlation: AdminObservabilityCorrelationDto): Promise<{ + boxes: AdminBoxItemDto[] + runners: AdminRunnerItemDto[] + machines: AdminMachineItemDto[] + postgresStatus: AdminObservabilitySourceStatusDto + }> { + try { + const [allBoxes, allRunners, allMachines] = await Promise.all([ + this.overviewService.listBoxes(), + this.overviewService.listRunners(), + this.overviewService.listMachines(), + ]) + + const boxes = allBoxes.filter((box) => this.matchesBox(box, correlation)) + for (const box of boxes) { + this.addUnique(correlation.orgIds, box.organizationId) + this.addUnique(correlation.boxIds, box.id) + this.addUnique(correlation.runnerIds, box.runnerId) + } + + const runners = allRunners.filter((runner) => correlation.runnerIds.includes(runner.id)) + for (const runner of runners) { + this.addUnique(correlation.machineIds, runner.id) + } + + const machines = allMachines.filter( + (machine) => correlation.machineIds.includes(machine.host) || correlation.runnerIds.includes(machine.host), + ) + for (const machine of machines) { + this.addUnique(correlation.machineIds, machine.host) + } + + const count = boxes.length + runners.length + machines.length + return { + boxes, + runners, + machines, + postgresStatus: { + source: 'postgres', + state: count > 0 ? 'available' : 'missing', + message: + count > 0 + ? undefined + : 'Postgres is reachable, but no box, runner, or machine matched the current correlation identifiers', + count, + }, + } + } catch (error) { + return { + boxes: [], + runners: [], + machines: [], + postgresStatus: { + source: 'postgres', + state: 'error', + message: error instanceof Error ? error.message : 'Postgres platform state query failed', + count: 0, + }, + } + } + } + + private async getRelatedAuditLogs( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ auditLogs: AdminObservabilityAuditLogDto[]; auditStatus: AdminObservabilitySourceStatusDto }> { + try { + const result = await this.auditService.getAllLogs(1, 50, { + ...this.buildTimeRange(query), + }) + const targetIds = this.buildAuditTargetIds(correlation) + const auditLogs = result.items + .filter((log) => { + return ( + (log.organizationId && correlation.orgIds.includes(log.organizationId)) || + (log.actorId && correlation.userIds.includes(log.actorId)) || + (log.targetId && targetIds.has(log.targetId)) + ) + }) + .map((log) => ({ + id: log.id, + actorId: log.actorId, + actorEmail: log.actorEmail, + organizationId: log.organizationId, + action: log.action, + targetType: log.targetType, + targetId: log.targetId, + statusCode: log.statusCode, + errorMessage: log.errorMessage, + source: log.source, + metadata: log.metadata, + createdAt: log.createdAt, + })) + + return { + auditLogs, + auditStatus: { + source: 'audit', + state: auditLogs.length > 0 ? 'available' : 'missing', + message: auditLogs.length > 0 ? undefined : 'No audit logs matched the resolved organization or target IDs', + count: auditLogs.length, + }, + } + } catch (error) { + return { + auditLogs: [], + auditStatus: { + source: 'audit', + state: 'error', + message: error instanceof Error ? error.message : 'AuditLog query failed', + count: 0, + }, + } + } + } + + private buildAuditTargetIds(correlation: AdminObservabilityCorrelationDto): Set { + const targetIds = new Set() + this.addAuditTargetIds(targetIds, correlation.orgIds, 'orgId') + this.addAuditTargetIds(targetIds, correlation.userIds, 'userId') + this.addAuditTargetIds(targetIds, correlation.boxIds, 'boxId') + this.addAuditTargetIds(targetIds, correlation.runnerIds, 'runnerId') + this.addAuditTargetIds(targetIds, correlation.machineIds, 'machineId') + return targetIds + } + + private addAuditTargetIds(targetIds: Set, values: string[], scopedKey: string) { + for (const value of values) { + targetIds.add(value) + targetIds.add(`${scopedKey}:${value}`) + } + } + + private async getRelatedCloudWatchLogs( + query: AdminObservabilityInvestigateQueryParamsDto, + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ logs: LogEntryDto[]; cloudWatchStatus: AdminObservabilitySourceStatusDto }> { + try { + const result = await this.cloudWatchLogReader.getRelatedLogs(query, correlation) + return { logs: result.logs, cloudWatchStatus: result.status } + } catch (error) { + return { + logs: [], + cloudWatchStatus: { + source: 'cloudwatch', + state: 'error', + message: error instanceof Error ? error.message : 'CloudWatch log lookup failed', + count: 0, + }, + } + } + } + + private async getRelatedS3Objects( + correlation: AdminObservabilityCorrelationDto, + ): Promise<{ objects: AdminObservabilityS3ObjectDto[]; s3Status: AdminObservabilitySourceStatusDto }> { + try { + const result = await this.s3ObjectReader.listRelatedObjects(correlation) + return { objects: result.objects, s3Status: result.status } + } catch (error) { + return { + objects: [], + s3Status: { + source: 's3', + state: 'error', + message: error instanceof Error ? error.message : 'S3 object lookup failed', + count: 0, + }, + } + } + } + + private matchesBox(box: AdminBoxItemDto, correlation: AdminObservabilityCorrelationDto): boolean { + if (correlation.boxIds.includes(box.id)) { + return true + } + + if (correlation.boxIds.length > 0) { + return false + } + + if (box.runnerId && correlation.runnerIds.includes(box.runnerId)) { + return true + } + + return correlation.orgIds.includes(box.organizationId) + } + + private addUnique(target: string[], value: unknown) { + if (typeof value !== 'string') { + return + } + const trimmed = value.trim() + if (trimmed && !target.includes(trimmed)) { + target.push(trimmed) + } + } + + private buildBaseParams(query: AdminObservabilityQueryParamsDto): Record { + const page = query.page ?? 1 + const limit = query.limit ?? 100 + return { + ...this.buildTimeRange(query), + limit, + offset: (page - 1) * limit, + } + } + + private buildTimeRange(query: AdminObservabilityQueryParamsDto): { from: Date; to: Date } { + const to = query.to ? new Date(query.to) : new Date() + const from = query.from ? new Date(query.from) : new Date(to.getTime() - DEFAULT_OBSERVABILITY_LOOKBACK_MS) + return { from, to } + } + + private buildWhereClause( + timestampColumn: 'Timestamp' | 'TimeUnix', + query: AdminObservabilityQueryParamsDto, + params: Record, + options: { eventAttributesColumn?: 'LogAttributes' | 'SpanAttributes' } = {}, + ): string[] { + const whereClause = [`${timestampColumn} >= {from:DateTime64}`, `${timestampColumn} <= {to:DateTime64}`] + + if (query.layer) { + whereClause.push(`${LAYER_EXPRESSION_SQL} = {layer:String}`) + params.layer = query.layer + } + if (query.serviceName) { + whereClause.push('ServiceName = {serviceName:String}') + params.serviceName = query.serviceName + } + this.pushResourceAttributeFilter( + whereClause, + params, + 'boxlite.org_id', + 'orgId', + query.orgId, + options.eventAttributesColumn, + ) + this.pushResourceAttributeFilter( + whereClause, + params, + 'boxlite.user_id', + 'userId', + query.userId, + options.eventAttributesColumn, + ) + this.pushBoxIdFilter(whereClause, params, query.boxId, options.eventAttributesColumn) + this.pushResourceAttributeFilter( + whereClause, + params, + 'boxlite.runner_id', + 'runnerId', + query.runnerId, + options.eventAttributesColumn, + ) + this.pushResourceAttributeFilter( + whereClause, + params, + 'boxlite.machine_id', + 'machineId', + query.machineId, + options.eventAttributesColumn, + ) + + return whereClause + } + + private pushResourceAttributeFilter( + whereClause: string[], + params: Record, + attributeName: string, + paramName: string, + value?: string, + eventAttributesColumn?: 'LogAttributes' | 'SpanAttributes', + ) { + if (!value) { + return + } + const clauses = [`ResourceAttributes['${attributeName}'] = {${paramName}:String}`] + if (eventAttributesColumn) { + clauses.push(`${eventAttributesColumn}['${attributeName}'] = {${paramName}:String}`) + } + whereClause.push(clauses.length === 1 ? clauses[0] : `(${clauses.join(' OR ')})`) + params[paramName] = value + } + + private pushBoxIdFilter( + whereClause: string[], + params: Record, + boxId?: string, + eventAttributesColumn?: 'LogAttributes' | 'SpanAttributes', + ) { + if (!boxId) { + return + } + const clauses = [`ResourceAttributes['boxlite.box_id'] = {boxId:String}`] + if (eventAttributesColumn) { + clauses.push(`${eventAttributesColumn}['boxlite.box_id'] = {boxId:String}`) + } + clauses.push('ServiceName = {boxServiceName:String}') + whereClause.push(`(${clauses.join(' OR ')})`) + params.boxId = boxId + params.boxServiceName = this.boxServiceName(boxId) + } + + private boxServiceName(boxId?: string): string | undefined { + return boxId ? `box-${boxId}` : undefined + } + + private pushTraceIdFilter(whereClause: string[], params: Record, traceId?: string) { + if (!traceId) { + return + } + whereClause.push('TraceId = {traceId:String}') + params.traceId = traceId + } + + private pushAttributeFilter( + whereClause: string[], + params: Record, + columnName: 'LogAttributes' | 'SpanAttributes', + attributeName: string, + paramName: string, + value?: string, + ) { + if (!value) { + return + } + whereClause.push( + `(${columnName}['${attributeName}'] = {${paramName}:String} OR ResourceAttributes['${attributeName}'] = {${paramName}:String})`, + ) + params[paramName] = value + } +} diff --git a/apps/api/src/admin/services/overview.service.spec.ts b/apps/api/src/admin/services/overview.service.spec.ts new file mode 100644 index 000000000..3202d4c67 --- /dev/null +++ b/apps/api/src/admin/services/overview.service.spec.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxClass } from '../../box/enums/box-class.enum' +import { RunnerState } from '../../box/enums/runner-state.enum' +import { AdminOverviewService } from './overview.service' + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'uuid-test'), + validate: jest.fn(() => true), +})) + +describe('AdminOverviewService', () => { + function buildService(runners: any[] = [], drainingRunners: any[] = []) { + const runnerService = { + findAllFull: jest.fn().mockResolvedValue(runners), + findDrainingPaginated: jest.fn().mockResolvedValue(drainingRunners), + } + + return { + runnerService, + service: new AdminOverviewService({} as any, runnerService as any, {} as any, {} as any), + } + } + + it('does not expose runner api keys in Admin runner items', async () => { + const now = new Date('2026-06-08T00:00:00.000Z') + const { service } = buildService([ + { + id: 'runner-1', + domain: 'runner.example.com', + apiUrl: 'https://runner.example.com', + proxyUrl: 'https://proxy.runner.example.com', + apiKey: 'runner-secret-key', + cpu: 4, + memoryGiB: 8, + diskGiB: 100, + gpu: 0, + gpuType: '', + class: BoxClass.SMALL, + currentCpuUsagePercentage: 1, + currentMemoryUsagePercentage: 2, + currentDiskUsagePercentage: 3, + currentAllocatedCpu: 1, + currentAllocatedMemoryGiB: 1, + currentAllocatedDiskGiB: 1, + currentStartedBoxes: 0, + availabilityScore: 100, + region: 'us', + name: 'runner-1', + state: RunnerState.READY, + lastChecked: now, + unschedulable: false, + createdAt: now, + updatedAt: now, + apiVersion: '0', + appVersion: 'v0.0.0-dev', + }, + ]) + + const runners = await service.listRunners() + + expect(runners).toHaveLength(1) + expect(runners[0]).toMatchObject({ + id: 'runner-1', + state: RunnerState.READY, + draining: false, + }) + expect(runners[0]).not.toHaveProperty('apiKey') + expect(JSON.stringify(runners[0])).not.toContain('runner-secret-key') + }) +}) diff --git a/apps/api/src/admin/services/overview.service.ts b/apps/api/src/admin/services/overview.service.ts new file mode 100644 index 000000000..b30f6962b --- /dev/null +++ b/apps/api/src/admin/services/overview.service.ts @@ -0,0 +1,215 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable } from '@nestjs/common' +import { UserService } from '../../user/user.service' +import { RunnerService } from '../../box/services/runner.service' +import { BoxRepository } from '../../box/repositories/box.repository' +import { RunnerState } from '../../box/enums/runner-state.enum' +import { BoxState } from '../../box/enums/box-state.enum' +import { OrganizationService } from '../../organization/services/organization.service' +import { + AdminBoxOwnerDto, + AdminBoxItemDto, + AdminMachineItemDto, + AdminOverviewDto, + AdminRunnerDto, + AdminRunnerItemDto, + AdminUserItemDto, +} from '../dto/admin-overview.dto' + +// Large enough to fetch all draining runners in one call without adding a new +// service method — the draining set is always a small subset of runners. +const ALL_DRAINING_TAKE = 10_000 + +type BoxStateCountRow = { + state: BoxState + count: string | number +} + +type BoxWithOwnerInput = { + organizationId: string +} + +@Injectable() +export class AdminOverviewService { + constructor( + private readonly userService: UserService, + private readonly runnerService: RunnerService, + private readonly boxRepository: BoxRepository, + private readonly organizationService: OrganizationService, + ) {} + + async getOverview(): Promise { + const [users, runners, boxes, drainingRunners] = await Promise.all([ + this.userService.findAll(), + this.runnerService.findAllFull(), + this.getBoxStateBreakdown(), + this.runnerService.findDrainingPaginated(0, ALL_DRAINING_TAKE), + ]) + + const onlineRunners = runners.filter((r) => r.state === RunnerState.READY) + const onlineCount = onlineRunners.length + const drainingCount = drainingRunners.length + + const totalCpu = runners.reduce((sum, r) => sum + r.cpu, 0) + const totalAllocated = runners.reduce((sum, r) => sum + r.currentAllocatedCpu, 0) + // Utilisation reflects live load → average over online (READY) runners only; + // unresponsive runners report 0% and would otherwise dilute the figure to ~0. + const avgCpuUtil = + onlineRunners.length > 0 + ? onlineRunners.reduce((sum, r) => sum + r.currentCpuUsagePercentage, 0) / onlineRunners.length / 100 + : 0 + const oversell = totalCpu > 0 ? totalAllocated / totalCpu : 0 + + return { + users: users.length, + activeBoxes: boxes.byState[BoxState.STARTED] ?? 0, + boxes, + runners: { + online: onlineCount, + total: runners.length, + draining: drainingCount, + }, + cluster: { + cpuUtil: avgCpuUtil, + oversell, + }, + } + } + + private async getBoxStateBreakdown() { + const rows = await this.boxRepository + .createQueryBuilder('box') + .select('box.state', 'state') + .addSelect('COUNT(*)', 'count') + .groupBy('box.state') + .getRawMany() + + const byState = rows.reduce>((acc, row) => { + acc[row.state] = Number(row.count) + return acc + }, {}) + + return { + total: Object.values(byState).reduce((sum, count) => sum + count, 0), + byState, + } + } + + async listUsers(): Promise { + const users = await this.userService.findAll() + return users.map((u) => ({ + id: u.id, + email: u.email, + name: u.name, + role: u.role, + })) + } + + async listBoxes(): Promise { + const boxes = await this.boxRepository.find() + const ownersByOrganizationId = await this.resolveBoxOwners(boxes) + + return boxes.map((s) => ({ + id: s.id, + organizationId: s.organizationId, + state: s.state, + runnerId: s.runnerId, + cpu: s.cpu, + memoryGiB: s.mem, + createdAt: s.createdAt.toISOString(), + owner: ownersByOrganizationId.get(s.organizationId) ?? this.fallbackBoxOwner(s.organizationId), + })) + } + + private async resolveBoxOwners(boxes: BoxWithOwnerInput[]): Promise> { + const organizationIds = Array.from(new Set(boxes.map((box) => box.organizationId).filter(Boolean))) + const organizations = await this.organizationService.findByIds(organizationIds) + const creatorIds = Array.from(new Set(organizations.map((organization) => organization.createdBy).filter(Boolean))) + const users = await this.userService.findByIds(creatorIds) + const usersById = new Map(users.map((user) => [user.id, user])) + + return new Map( + organizations.map((organization) => [ + organization.id, + this.toBoxOwner(organization, usersById.get(organization.createdBy)), + ]), + ) + } + + private toBoxOwner( + // NOTE(integration 2026-06-08): default-organization-membership removed the + // Organization.personal column — "personal" is now a per-authenticated-user alias, + // not an org attribute. Admin overview has no authenticated-user context, so it cannot + // resolve personal-ness per org; present every org org-style. Revisit if admin needs to + // flag default/personal orgs (would require a membership lookup). + organization: { createdBy?: string; name: string }, + creator?: { name: string; email: string }, + ): AdminBoxOwnerDto { + return { + userId: organization.createdBy, + name: organization.name, + email: creator?.email ?? '', + orgName: organization.name, + personal: false, + } + } + + private fallbackBoxOwner(organizationId: string): AdminBoxOwnerDto { + return { + name: organizationId, + email: '', + orgName: organizationId, + personal: false, + } + } + + async listRunners(): Promise { + const [runners, drainingRunners] = await Promise.all([ + this.runnerService.findAllFull(), + this.runnerService.findDrainingPaginated(0, ALL_DRAINING_TAKE), + ]) + + const drainingIds = new Set(drainingRunners.map((r) => r.id)) + + return runners.map((runner) => ({ + ...this.toAdminRunnerItem(runner), + draining: drainingIds.has(runner.id), + })) + } + + async listMachines(): Promise { + const runners = await this.runnerService.findAllFull() + return runners.map((r) => this.toMachineDto(r)) + } + + private toMachineDto(r: { + id: string + region: string + cpu: number + currentAllocatedCpu: number + currentCpuUsagePercentage: number + currentMemoryUsagePercentage: number + currentStartedBoxes: number + }): AdminMachineItemDto { + // Guard divide-by-zero: if runner has no cpu capacity, oversell = 0 + const oversellCpu = r.cpu > 0 ? r.currentAllocatedCpu / r.cpu : 0 + return { + host: r.id, + region: r.region, + oversellCpu, + cpuWaterline: r.currentCpuUsagePercentage, + memWaterline: r.currentMemoryUsagePercentage, + boxes: r.currentStartedBoxes, + } + } + + private toAdminRunnerItem(runner: AdminRunnerDto & { apiKey?: string }): AdminRunnerDto { + const { apiKey: _apiKey, ...safeRunner } = runner + return safeRunner + } +} diff --git a/apps/api/src/analytics/services/analytics.service.ts b/apps/api/src/analytics/services/analytics.service.ts index e8ea2765b..3e1e2038e 100644 --- a/apps/api/src/analytics/services/analytics.service.ts +++ b/apps/api/src/analytics/services/analytics.service.ts @@ -6,14 +6,14 @@ import { Injectable, Logger } from '@nestjs/common' import { OnEvent } from '@nestjs/event-emitter' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SandboxCreatedEvent } from '../../sandbox/events/sandbox-create.event' -import { SandboxDesiredStateUpdatedEvent } from '../../sandbox/events/sandbox-desired-state-updated.event' -import { SandboxDestroyedEvent } from '../../sandbox/events/sandbox-destroyed.event' -import { SandboxPublicStatusUpdatedEvent } from '../../sandbox/events/sandbox-public-status-updated.event' -import { SandboxStartedEvent } from '../../sandbox/events/sandbox-started.event' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SandboxStoppedEvent } from '../../sandbox/events/sandbox-stopped.event' +import { BoxEvents } from '../../box/constants/box-events.constants' +import { BoxCreatedEvent } from '../../box/events/box-create.event' +import { BoxDesiredStateUpdatedEvent } from '../../box/events/box-desired-state-updated.event' +import { BoxDestroyedEvent } from '../../box/events/box-destroyed.event' +import { BoxPublicStatusUpdatedEvent } from '../../box/events/box-public-status-updated.event' +import { BoxStartedEvent } from '../../box/events/box-started.event' +import { BoxStateUpdatedEvent } from '../../box/events/box-state-updated.event' +import { BoxStoppedEvent } from '../../box/events/box-stopped.event' import { PostHog } from 'posthog-node' import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' import { Organization } from '../../organization/entities/organization.entity' @@ -40,49 +40,45 @@ export class AnalyticsService { }) } - @OnEvent(SandboxEvents.CREATED) - async handleSandboxCreatedEvent(event: SandboxCreatedEvent) { - this.logger.debug(`Sandbox created: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.CREATED) + async handleBoxCreatedEvent(event: BoxCreatedEvent) { + this.logger.debug(`Box created: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.STARTED) - async handleSandboxStartedEvent(event: SandboxStartedEvent) { - this.logger.debug(`Sandbox started: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.STARTED) + async handleBoxStartedEvent(event: BoxStartedEvent) { + this.logger.debug(`Box started: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.STOPPED) - async handleSandboxStoppedEvent(event: SandboxStoppedEvent) { - this.logger.debug(`Sandbox stopped: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.STOPPED) + async handleBoxStoppedEvent(event: BoxStoppedEvent) { + this.logger.debug(`Box stopped: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.DESTROYED) - async handleSandboxDestroyedEvent(event: SandboxDestroyedEvent) { - this.logger.debug(`Sandbox destroyed: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.DESTROYED) + async handleBoxDestroyedEvent(event: BoxDestroyedEvent) { + this.logger.debug(`Box destroyed: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.PUBLIC_STATUS_UPDATED) - async handleSandboxPublicStatusUpdatedEvent(event: SandboxPublicStatusUpdatedEvent) { - this.logger.debug(`Sandbox public status updated: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.PUBLIC_STATUS_UPDATED) + async handleBoxPublicStatusUpdatedEvent(event: BoxPublicStatusUpdatedEvent) { + this.logger.debug(`Box public status updated: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.DESIRED_STATE_UPDATED) - async handleSandboxDesiredStateUpdatedEvent(event: SandboxDesiredStateUpdatedEvent) { - this.logger.debug(`Sandbox desired state updated: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.DESIRED_STATE_UPDATED) + async handleBoxDesiredStateUpdatedEvent(event: BoxDesiredStateUpdatedEvent) { + this.logger.debug(`Box desired state updated: ${JSON.stringify(event)}`) } - @OnEvent(SandboxEvents.STATE_UPDATED) - async handleSandboxStateUpdatedEvent(event: SandboxStateUpdatedEvent) { - this.logger.debug(`Sandbox state updated: ${JSON.stringify(event)}`) + @OnEvent(BoxEvents.STATE_UPDATED) + async handleBoxStateUpdatedEvent(event: BoxStateUpdatedEvent) { + this.logger.debug(`Box state updated: ${JSON.stringify(event)}`) } @OnAsyncEvent({ event: OrganizationEvents.CREATED, }) - async handlePersonalOrganizationCreatedEvent(payload: Organization) { - if (!payload.personal) { - return - } - + async handleOrganizationCreatedEvent(payload: Organization) { if (!this.posthog) { return } @@ -91,10 +87,12 @@ export class AnalyticsService { groupType: 'organization', groupKey: payload.id, properties: { - name: `Personal - ${payload.createdBy}`, + name: payload.name, created_at: payload.createdAt, created_by: payload.createdBy, - personal: payload.personal, + is_default_for_creator: payload.users?.some( + (user) => user.userId === payload.createdBy && user.isDefaultForUser, + ), environment: this.configService.get('posthog.environment'), }, }) diff --git a/apps/api/src/api-key/api-key.module.ts b/apps/api/src/api-key/api-key.module.ts index 672ff8e9a..324f59c8e 100644 --- a/apps/api/src/api-key/api-key.module.ts +++ b/apps/api/src/api-key/api-key.module.ts @@ -10,7 +10,7 @@ import { ApiKeyService } from './api-key.service' import { ApiKey } from './api-key.entity' import { TypeOrmModule } from '@nestjs/typeorm' import { OrganizationModule } from '../organization/organization.module' -import { RedisLockProvider } from '../sandbox/common/redis-lock.provider' +import { RedisLockProvider } from '../box/common/redis-lock.provider' @Module({ imports: [OrganizationModule, TypeOrmModule.forFeature([ApiKey])], diff --git a/apps/api/src/api-key/api-key.service.ts b/apps/api/src/api-key/api-key.service.ts index 07eba3238..b0823c3e0 100644 --- a/apps/api/src/api-key/api-key.service.ts +++ b/apps/api/src/api-key/api-key.service.ts @@ -9,13 +9,14 @@ import { InjectRepository } from '@nestjs/typeorm' import { EntityManager, Repository, ArrayOverlap } from 'typeorm' import { ApiKey } from './api-key.entity' import { OrganizationResourcePermission } from '../organization/enums/organization-resource-permission.enum' -import { RedisLockProvider } from '../sandbox/common/redis-lock.provider' +import { RedisLockProvider } from '../box/common/redis-lock.provider' import { OnAsyncEvent } from '../common/decorators/on-async-event.decorator' import { OrganizationEvents } from '../organization/constants/organization-events.constant' import { OrganizationResourcePermissionsUnassignedEvent } from '../organization/events/organization-resource-permissions-unassigned.event' import { InjectRedis } from '@nestjs-modules/ioredis' import Redis from 'ioredis' -import { generateApiKeyHash, generateApiKeyValue } from '../common/utils/api-key' +import { extractKeyDisplayPrefix, generateApiKeyHash, generateApiKeyValue } from '../common/utils/api-key' +import { TypedConfigService } from '../config/typed-config.service' @Injectable() export class ApiKeyService { @@ -26,10 +27,11 @@ export class ApiKeyService { private apiKeyRepository: Repository, private readonly redisLockProvider: RedisLockProvider, @InjectRedis() private readonly redis: Redis, + private readonly configService: TypedConfigService, ) {} private getApiKeyPrefix(value: string): string { - return value.substring(0, 3) + return extractKeyDisplayPrefix(value) } private getApiKeySuffix(value: string): string { @@ -49,7 +51,14 @@ export class ApiKeyService { throw new ConflictException('API key with this name already exists') } - const value = apiKeyValue || generateApiKeyValue() + // User dashboard keys: env-split class from the deployment mode; the + // brand prefix is operator-overridable via API_KEY_PREFIX (default `blk`). + const value = + apiKeyValue || + generateApiKeyValue( + this.configService.getOrThrow('apiKey.prefix'), + this.configService.get('production') ? 'live' : 'test', + ) const apiKey = await this.apiKeyRepository.save({ organizationId, @@ -66,6 +75,28 @@ export class ApiKeyService { return { apiKey, value } } + async ensureApiKeyValue( + organizationId: string, + userId: string, + name: string, + permissions: OrganizationResourcePermission[], + value: string, + expiresAt?: Date, + ): Promise<{ apiKey: ApiKey; value: string }> { + const keyHash = generateApiKeyHash(value) + const existingKey = await this.apiKeyRepository.findOne({ where: { organizationId, userId, name } }) + + if (existingKey) { + if (existingKey.keyHash === keyHash) { + return { apiKey: existingKey, value } + } + + await this.deleteWithEntityManager(this.apiKeyRepository.manager, existingKey) + } + + return this.createApiKey(organizationId, userId, name, permissions, expiresAt, value) + } + async getApiKeys(organizationId: string, userId?: string): Promise { const apiKeys = await this.apiKeyRepository.find({ where: { organizationId, userId }, diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts index 49e81c1e4..592aaa2f7 100644 --- a/apps/api/src/app.module.ts +++ b/apps/api/src/app.module.ts @@ -10,17 +10,16 @@ import { FailedAuthRateLimitMiddleware } from './common/middleware/failed-auth-r import { AppService } from './app.service' import { UserModule } from './user/user.module' import { TypeOrmModule } from '@nestjs/typeorm' -import { SandboxModule } from './sandbox/sandbox.module' +import { BoxModule } from './box/box.module' import { AuthModule } from './auth/auth.module' import { ServeStaticModule } from '@nestjs/serve-static' import { join } from 'path' +import { setDashboardStaticHeaders } from './serve-static-cache' import { ApiKeyModule } from './api-key/api-key.module' import { seconds, ThrottlerModule } from '@nestjs/throttler' -import { DockerRegistryModule } from './docker-registry/docker-registry.module' import { RedisModule, getRedisConnectionToken } from '@nestjs-modules/ioredis' import { ScheduleModule } from '@nestjs/schedule' import { EventEmitterModule } from '@nestjs/event-emitter' -import { UsageModule } from './usage/usage.module' import { AnalyticsModule } from './analytics/analytics.module' import { OrganizationModule } from './organization/organization.module' import { EmailModule } from './email/email.module' @@ -43,7 +42,7 @@ import { RegionModule } from './region/region.module' import { BodyParserErrorModule } from './common/modules/body-parser-error.module' import { AdminModule } from './admin/admin.module' import { ClickHouseModule } from './clickhouse/clickhouse.module' -import { SandboxTelemetryModule } from './sandbox-telemetry/sandbox-telemetry.module' +import { BoxTelemetryModule } from './box-telemetry/box-telemetry.module' import { BoxliteRestModule } from './boxlite-rest/boxlite-rest.module' @Module({ @@ -118,7 +117,11 @@ import { BoxliteRestModule } from './boxlite-rest/boxlite-rest.module' exclude: ['/api/{*path}'], renderPath: '/', serveStaticOptions: { + // Disable serve-static's own default Cache-Control; setHeaders applies a + // content-addressed policy: hashed /assets/* immutable-forever, HTML + // no-cache. Stops the ~600KB bundle re-downloading on every visit. cacheControl: false, + setHeaders: setDashboardStaticHeaders, }, }), RedisModule.forRootAsync({ @@ -145,8 +148,8 @@ import { BoxliteRestModule } from './boxlite-rest/boxlite-rest.module' { name: 'anonymous', config: rateLimit.anonymous }, { name: 'failed-auth', config: rateLimit.failedAuth }, { name: 'authenticated', config: rateLimit.authenticated }, - { name: 'sandbox-create', config: rateLimit.sandboxCreate }, - { name: 'sandbox-lifecycle', config: rateLimit.sandboxLifecycle }, + { name: 'box-create', config: rateLimit.boxCreate }, + { name: 'box-lifecycle', config: rateLimit.boxLifecycle }, ] .filter(({ config }) => config.ttl !== undefined && config.limit !== undefined) .map(({ name, config }) => ({ @@ -168,10 +171,8 @@ import { BoxliteRestModule } from './boxlite-rest/boxlite-rest.module' ApiKeyModule, AuthModule, UserModule, - SandboxModule, - DockerRegistryModule, + BoxModule, ScheduleModule.forRoot(), - UsageModule, AnalyticsModule, OrganizationModule, RegionModule, @@ -196,7 +197,7 @@ import { BoxliteRestModule } from './boxlite-rest/boxlite-rest.module' AuditModule, HealthModule, ClickHouseModule, - SandboxTelemetryModule, + BoxTelemetryModule, BoxliteRestModule, OpenFeatureModule.forRoot({ contextFactory: (request: ExecutionContext) => { diff --git a/apps/api/src/app.service.spec.ts b/apps/api/src/app.service.spec.ts new file mode 100644 index 000000000..dc4e995d1 --- /dev/null +++ b/apps/api/src/app.service.spec.ts @@ -0,0 +1,62 @@ +jest.mock('./box/services/runner.service', () => ({ + RunnerService: class RunnerService {}, +})) +jest.mock('./box/runner-adapter/runnerAdapter', () => ({ + RunnerAdapterFactory: class RunnerAdapterFactory {}, +})) + +const { AppService } = require('./app.service') as typeof import('./app.service') + +describe('AppService admin bootstrap', () => { + it('syncs existing admin organization quota from config', async () => { + const configValues: Record = { + 'admin.apiKey': 'boxlite-local-admin-key', + 'defaultRegion.id': 'us', + } + const configService = { + get: jest.fn((key: string) => configValues[key]), + getOrThrow: jest.fn((key: string) => { + const value = configValues[key] + if (value === undefined) { + throw new Error(`Configuration key "${key}" is undefined`) + } + return value + }), + } + const userService = { + findOne: jest.fn().mockResolvedValue({ id: 'boxlite-admin' }), + create: jest.fn(), + } + const organizationService = { + findDefaultForUser: jest.fn().mockResolvedValue({ id: 'org-1' }), + } + const apiKeyService = { + ensureApiKeyValue: jest.fn().mockResolvedValue({ value: 'boxlite-local-admin-key' }), + } + + const service = new AppService( + configService as never, + userService as never, + organizationService as never, + apiKeyService as never, + {} as never, + {} as never, + {} as never, + {} as never, + {} as never, + ) as unknown as { + initializeAdminUser: () => Promise + } + + await service.initializeAdminUser() + + expect(userService.create).not.toHaveBeenCalled() + expect(apiKeyService.ensureApiKeyValue).toHaveBeenCalledWith( + 'org-1', + 'boxlite-admin', + 'boxlite-admin', + [], + 'boxlite-local-admin-key', + ) + }) +}) diff --git a/apps/api/src/app.service.ts b/apps/api/src/app.service.ts index 8d76a48a7..b847929e2 100644 --- a/apps/api/src/app.service.ts +++ b/apps/api/src/app.service.ts @@ -5,21 +5,18 @@ */ import { Injectable, Logger, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common' -import { DockerRegistryService } from './docker-registry/services/docker-registry.service' -import { RegistryType } from './docker-registry/enums/registry-type.enum' import { OrganizationService } from './organization/services/organization.service' import { UserService } from './user/user.service' import { ApiKeyService } from './api-key/api-key.service' import { EventEmitterReadinessWatcher } from '@nestjs/event-emitter' -import { SnapshotService } from './sandbox/services/snapshot.service' import { SystemRole } from './user/enums/system-role.enum' import { TypedConfigService } from './config/typed-config.service' import { SchedulerRegistry } from '@nestjs/schedule' import { RegionService } from './region/services/region.service' -import { RunnerService } from './sandbox/services/runner.service' -import { RunnerAdapterFactory } from './sandbox/runner-adapter/runnerAdapter' +import { RunnerService } from './box/services/runner.service' +import { RunnerAdapterFactory } from './box/runner-adapter/runnerAdapter' import { RegionType } from './region/enums/region-type.enum' -import { RunnerState } from './sandbox/enums/runner-state.enum' +import { RunnerState } from './box/enums/runner-state.enum' export const BOXLITE_ADMIN_USER_ID = 'boxlite-admin' @@ -28,13 +25,11 @@ export class AppService implements OnApplicationBootstrap, OnApplicationShutdown private readonly logger = new Logger(AppService.name) constructor( - private readonly dockerRegistryService: DockerRegistryService, private readonly configService: TypedConfigService, private readonly userService: UserService, private readonly organizationService: OrganizationService, private readonly apiKeyService: ApiKeyService, private readonly eventEmitterReadinessWatcher: EventEmitterReadinessWatcher, - private readonly snapshotService: SnapshotService, private readonly schedulerRegistry: SchedulerRegistry, private readonly regionService: RegionService, private readonly runnerService: RunnerService, @@ -55,17 +50,12 @@ export class AppService implements OnApplicationBootstrap, OnApplicationShutdown await this.initializeDefaultRegion() await this.initializeAdminUser() - await this.initializeTransientRegistry() - await this.initializeBackupRegistry() - await this.initializeInternalRegistry() - await this.initializeBackupRegistry() // Default runner init is not awaited because v2 runners depend on the API to be ready - this.initializeDefaultRunner() - .then(() => this.initializeDefaultSnapshot()) - .catch((error) => { - this.logger.error('Error initializing default runner', error) - }) + // TODO(image-rewrite): system template seeding removed with box_template; rebuild here. + this.initializeDefaultRunner().catch((error) => { + this.logger.error('Error initializing default runner', error) + }) } private async stopAllCronJobs(): Promise { @@ -167,175 +157,38 @@ export class AppService implements OnApplicationBootstrap, OnApplicationShutdown } private async initializeAdminUser(): Promise { - if (await this.userService.findOne(BOXLITE_ADMIN_USER_ID)) { - return + let user = await this.userService.findOne(BOXLITE_ADMIN_USER_ID) + if (!user) { + user = await this.userService.create({ + id: BOXLITE_ADMIN_USER_ID, + name: 'BoxLite Admin', + defaultOrganizationDefaultRegionId: this.configService.getOrThrow('defaultRegion.id'), + role: SystemRole.ADMIN, + }) } - const user = await this.userService.create({ - id: BOXLITE_ADMIN_USER_ID, - name: 'BoxLite Admin', - personalOrganizationQuota: { - totalCpuQuota: this.configService.getOrThrow('admin.totalCpuQuota'), - totalMemoryQuota: this.configService.getOrThrow('admin.totalMemoryQuota'), - totalDiskQuota: this.configService.getOrThrow('admin.totalDiskQuota'), - maxCpuPerSandbox: this.configService.getOrThrow('admin.maxCpuPerSandbox'), - maxMemoryPerSandbox: this.configService.getOrThrow('admin.maxMemoryPerSandbox'), - maxDiskPerSandbox: this.configService.getOrThrow('admin.maxDiskPerSandbox'), - snapshotQuota: this.configService.getOrThrow('admin.snapshotQuota'), - maxSnapshotSize: this.configService.getOrThrow('admin.maxSnapshotSize'), - volumeQuota: this.configService.getOrThrow('admin.volumeQuota'), - }, - personalOrganizationDefaultRegionId: this.configService.getOrThrow('defaultRegion.id'), - role: SystemRole.ADMIN, - }) - const personalOrg = await this.organizationService.findPersonal(user.id) - const { value } = await this.apiKeyService.createApiKey( - personalOrg.id, + const defaultOrg = await this.organizationService.findDefaultForUser(user.id) + const { value } = await this.apiKeyService.ensureApiKeyValue( + defaultOrg.id, user.id, BOXLITE_ADMIN_USER_ID, [], - undefined, this.configService.getOrThrow('admin.apiKey'), ) this.logger.log( ` ========================================= ========================================= -Admin user created with API key: ${value} +Admin API key ensured: ${this.maskApiKeyForLog(value)} ========================================= =========================================`, ) } - private async initializeTransientRegistry(): Promise { - const existingRegistry = await this.dockerRegistryService.getAvailableTransientRegistry( - this.configService.getOrThrow('defaultRegion.id'), - ) - if (existingRegistry) { - return - } - - const registryUrl = this.configService.getOrThrow('transientRegistry.url') - const registryAdmin = this.configService.getOrThrow('transientRegistry.admin') - const registryPassword = this.configService.getOrThrow('transientRegistry.password') - const registryProjectId = this.configService.getOrThrow('transientRegistry.projectId') - - if (!registryUrl || !registryAdmin || !registryPassword || !registryProjectId) { - this.logger.warn('Registry configuration not found, skipping transient registry setup') - return - } - - this.logger.log('Initializing default transient registry...') - - await this.dockerRegistryService.create({ - name: 'Transient Registry', - url: registryUrl, - username: registryAdmin, - password: registryPassword, - project: registryProjectId, - registryType: RegistryType.TRANSIENT, - isDefault: true, - }) - - this.logger.log('Default transient registry initialized successfully') - } - - private async initializeInternalRegistry(): Promise { - const existingRegistry = await this.dockerRegistryService.getAvailableInternalRegistry( - this.configService.getOrThrow('defaultRegion.id'), - ) - if (existingRegistry) { - return - } - - const registryUrl = this.configService.getOrThrow('internalRegistry.url') - const registryAdmin = this.configService.getOrThrow('internalRegistry.admin') - const registryPassword = this.configService.getOrThrow('internalRegistry.password') - const registryProjectId = this.configService.getOrThrow('internalRegistry.projectId') - - if (!registryUrl || !registryAdmin || !registryPassword || !registryProjectId) { - this.logger.warn('Registry configuration not found, skipping internal registry setup') - return + private maskApiKeyForLog(value: string): string { + if (value.length <= 8) { + return '********' } - - this.logger.log('Initializing default internal registry...') - - await this.dockerRegistryService.create({ - name: 'Internal Registry', - url: registryUrl, - username: registryAdmin, - password: registryPassword, - project: registryProjectId, - registryType: RegistryType.INTERNAL, - isDefault: true, - }) - - this.logger.log('Default internal registry initialized successfully') - } - - private async initializeBackupRegistry(): Promise { - const existingRegistry = await this.dockerRegistryService.getAvailableBackupRegistry( - this.configService.getOrThrow('defaultRegion.id'), - ) - if (existingRegistry) { - return - } - - const registryUrl = this.configService.getOrThrow('internalRegistry.url') - const registryAdmin = this.configService.getOrThrow('internalRegistry.admin') - const registryPassword = this.configService.getOrThrow('internalRegistry.password') - const registryProjectId = this.configService.getOrThrow('internalRegistry.projectId') - - if (!registryUrl || !registryAdmin || !registryPassword || !registryProjectId) { - this.logger.warn('Registry configuration not found, skipping backup registry setup') - return - } - - this.logger.log('Initializing default backup registry...') - - await this.dockerRegistryService.create( - { - name: 'Backup Registry', - url: registryUrl, - username: registryAdmin, - password: registryPassword, - project: registryProjectId, - registryType: RegistryType.BACKUP, - isDefault: true, - }, - undefined, - true, - ) - - this.logger.log('Default backup registry initialized successfully') - } - - private async initializeDefaultSnapshot(): Promise { - const adminPersonalOrg = await this.organizationService.findPersonal(BOXLITE_ADMIN_USER_ID) - - try { - const existingSnapshot = await this.snapshotService.getSnapshotByName( - this.configService.getOrThrow('defaultSnapshot'), - adminPersonalOrg.id, - ) - if (existingSnapshot) { - return - } - } catch { - this.logger.log('Default snapshot not found, creating...') - } - - const defaultSnapshot = this.configService.getOrThrow('defaultSnapshot') - - await this.snapshotService.createFromPull( - adminPersonalOrg, - { - name: defaultSnapshot, - imageName: defaultSnapshot, - }, - true, - ) - - this.logger.log('Default snapshot created successfully') + return `${value.slice(0, 4)}...${value.slice(-4)}` } } diff --git a/apps/api/src/audit/audit.module.ts b/apps/api/src/audit/audit.module.ts index 086ff2f70..b6d84ab8f 100644 --- a/apps/api/src/audit/audit.module.ts +++ b/apps/api/src/audit/audit.module.ts @@ -11,7 +11,7 @@ import { AuditLog } from './entities/audit-log.entity' import { AuditService } from './services/audit.service' import { AuditInterceptor } from './interceptors/audit.interceptor' import { AuditLogSubscriber } from './subscribers/audit-log.subscriber' -import { RedisLockProvider } from '../sandbox/common/redis-lock.provider' +import { RedisLockProvider } from '../box/common/redis-lock.provider' import { AuditController } from './controllers/audit.controller' import { AuditKafkaConsumerController } from './publishers/kafka/audit-kafka-consumer.controller' import { ClientsModule, Transport } from '@nestjs/microservices' diff --git a/apps/api/src/audit/decorators/audit.decorator.ts b/apps/api/src/audit/decorators/audit.decorator.ts index c1bd92266..3575d5737 100644 --- a/apps/api/src/audit/decorators/audit.decorator.ts +++ b/apps/api/src/audit/decorators/audit.decorator.ts @@ -13,11 +13,13 @@ export type TypedRequest = Omit & { body: T } export const MASKED_AUDIT_VALUE = '********' +export type AuditTargetId = string | string[] | null | undefined + export interface AuditContext { action: AuditAction targetType?: AuditTarget - targetIdFromRequest?: (req: Request) => string | null | undefined - targetIdFromResult?: (result: any) => string | null | undefined + targetIdFromRequest?: (req: Request) => AuditTargetId + targetIdFromResult?: (result: any) => AuditTargetId requestMetadata?: Record any> } diff --git a/apps/api/src/audit/dto/create-audit-log.dto.ts b/apps/api/src/audit/dto/create-audit-log.dto.ts deleted file mode 100644 index 78cb2f379..000000000 --- a/apps/api/src/audit/dto/create-audit-log.dto.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { IsEnum, IsOptional } from 'class-validator' -import { AuditAction } from '../enums/audit-action.enum' -import { AuditTarget } from '../enums/audit-target.enum' - -@ApiSchema({ name: 'CreateAuditLog' }) -export class CreateAuditLogDto { - @ApiProperty() - actorId: string - - @ApiProperty() - actorEmail: string - - @ApiPropertyOptional() - @IsOptional() - organizationId?: string - - @ApiProperty({ - enum: AuditAction, - }) - @IsEnum(AuditAction) - action: AuditAction - - @ApiPropertyOptional({ - enum: AuditTarget, - }) - @IsOptional() - @IsEnum(AuditTarget) - targetType?: AuditTarget - - @ApiPropertyOptional() - @IsOptional() - targetId?: string -} diff --git a/apps/api/src/audit/enums/audit-action.enum.ts b/apps/api/src/audit/enums/audit-action.enum.ts index bb2e01913..b624f844d 100644 --- a/apps/api/src/audit/enums/audit-action.enum.ts +++ b/apps/api/src/audit/enums/audit-action.enum.ts @@ -12,8 +12,6 @@ export enum AuditAction { LOGIN = 'login', SET_DEFAULT = 'set_default', UPDATE_ACCESS = 'update_access', - UPDATE_QUOTA = 'update_quota', - UPDATE_REGION_QUOTA = 'update_region_quota', SUSPEND = 'suspend', UNSUSPEND = 'unsuspend', ACCEPT = 'accept', @@ -31,9 +29,7 @@ export enum AuditAction { CREATE_BACKUP = 'create_backup', UPDATE_PUBLIC_STATUS = 'update_public_status', SET_AUTO_STOP_INTERVAL = 'set_auto_stop_interval', - SET_AUTO_ARCHIVE_INTERVAL = 'set_auto_archive_interval', SET_AUTO_DELETE_INTERVAL = 'set_auto_delete_interval', - ARCHIVE = 'archive', GET_PORT_PREVIEW_URL = 'get_port_preview_url', SET_GENERAL_STATUS = 'set_general_status', ACTIVATE = 'activate', @@ -41,13 +37,12 @@ export enum AuditAction { UPDATE_NETWORK_SETTINGS = 'update_network_settings', SEND_WEBHOOK_MESSAGE = 'send_webhook_message', INITIALIZE_WEBHOOKS = 'initialize_webhooks', - UPDATE_SANDBOX_DEFAULT_LIMITED_NETWORK_EGRESS = 'update_sandbox_default_limited_network_egress', + UPDATE_BOX_DEFAULT_LIMITED_NETWORK_EGRESS = 'update_box_default_limited_network_egress', CREATE_SSH_ACCESS = 'create_ssh_access', REVOKE_SSH_ACCESS = 'revoke_ssh_access', RECOVER = 'recover', REGENERATE_PROXY_API_KEY = 'regenerate_proxy_api_key', REGENERATE_SSH_GATEWAY_API_KEY = 'regenerate_ssh_gateway_api_key', - REGENERATE_SNAPSHOT_MANAGER_CREDENTIALS = 'regenerate_snapshot_manager_credentials', // toolbox actions (must be prefixed with 'toolbox_') TOOLBOX_DELETE_FILE = 'toolbox_delete_file', TOOLBOX_DOWNLOAD_FILE = 'toolbox_download_file', diff --git a/apps/api/src/audit/enums/audit-target.enum.ts b/apps/api/src/audit/enums/audit-target.enum.ts index 6424d3a82..16e758218 100644 --- a/apps/api/src/audit/enums/audit-target.enum.ts +++ b/apps/api/src/audit/enums/audit-target.enum.ts @@ -10,11 +10,10 @@ export enum AuditTarget { ORGANIZATION_INVITATION = 'organization_invitation', ORGANIZATION_ROLE = 'organization_role', ORGANIZATION_USER = 'organization_user', - DOCKER_REGISTRY = 'docker_registry', RUNNER = 'runner', - SANDBOX = 'sandbox', - SNAPSHOT = 'snapshot', + BOX = 'box', USER = 'user', VOLUME = 'volume', REGION = 'region', + OBSERVABILITY = 'observability', } diff --git a/apps/api/src/audit/interceptors/audit.interceptor.spec.ts b/apps/api/src/audit/interceptors/audit.interceptor.spec.ts new file mode 100644 index 000000000..1e99df794 --- /dev/null +++ b/apps/api/src/audit/interceptors/audit.interceptor.spec.ts @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CallHandler, ExecutionContext } from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { firstValueFrom, of } from 'rxjs' +import { AuditContext } from '../decorators/audit.decorator' +import { AuditAction } from '../enums/audit-action.enum' +import { AuditTarget } from '../enums/audit-target.enum' +import { AuditInterceptor } from './audit.interceptor' +import { CustomHeaders } from '../../common/constants/header.constants' + +jest.mock('uuid', () => ({ v4: () => 'uuid-test' })) + +describe('AuditInterceptor', () => { + it('records the BoxLite source header and structured metadata for audited admin reads', async () => { + const auditContext: AuditContext = { + action: AuditAction.READ, + targetType: AuditTarget.OBSERVABILITY, + targetIdFromRequest: (req) => `traceId:${req.query.traceId}`, + requestMetadata: { + surface: () => 'admin_observability', + query: (req) => ({ traceId: req.query.traceId }), + }, + } + const reflector = { + get: jest.fn().mockReturnValue(auditContext), + } as unknown as Reflector + const auditService = { + createLog: jest.fn().mockResolvedValue({ id: 'audit-1' }), + updateLog: jest.fn().mockResolvedValue({ id: 'audit-1' }), + } + const configService = { get: jest.fn() } + const interceptor = new AuditInterceptor(reflector, auditService as any, configService as any) + const request = { + url: '/admin/observability/investigate?traceId=trace-1', + ip: '127.0.0.1', + query: { traceId: 'trace-1' }, + user: { userId: 'user-1', email: 'admin@example.com', organizationId: 'org-1' }, + get: jest.fn((name: string) => { + if (name === CustomHeaders.SOURCE.name) return 'agent' + if (name === 'user-agent') return 'boxlite-agent-test' + return undefined + }), + } + const response = { statusCode: 200 } + const executionContext = { + getHandler: jest.fn(), + switchToHttp: () => ({ + getRequest: () => request, + getResponse: () => response, + }), + } as unknown as ExecutionContext + const next = { + handle: jest.fn().mockReturnValue(of({ organizationId: 'org-1' })), + } as unknown as CallHandler + + await expect(firstValueFrom(interceptor.intercept(executionContext, next))).resolves.toEqual({ + organizationId: 'org-1', + }) + + expect(auditService.createLog).toHaveBeenCalledWith( + expect.objectContaining({ + actorId: 'user-1', + actorEmail: 'admin@example.com', + organizationId: 'org-1', + action: AuditAction.READ, + targetType: AuditTarget.OBSERVABILITY, + targetId: 'traceId:trace-1', + source: 'agent', + userAgent: 'boxlite-agent-test', + metadata: { + surface: 'admin_observability', + query: { traceId: 'trace-1' }, + }, + }), + ) + expect(auditService.updateLog).toHaveBeenCalledWith('audit-1', { + organizationId: 'org-1', + targetId: 'traceId:trace-1', + statusCode: 200, + }) + }) +}) diff --git a/apps/api/src/audit/interceptors/audit.interceptor.ts b/apps/api/src/audit/interceptors/audit.interceptor.ts index e20960eb8..1e8e85c93 100644 --- a/apps/api/src/audit/interceptors/audit.interceptor.ts +++ b/apps/api/src/audit/interceptors/audit.interceptor.ts @@ -18,7 +18,7 @@ import { import { Reflector } from '@nestjs/core' import { Request, Response } from 'express' import { Observable, Subscriber, firstValueFrom } from 'rxjs' -import { AUDIT_CONTEXT_KEY, AuditContext } from '../decorators/audit.decorator' +import { AUDIT_CONTEXT_KEY, AuditContext, AuditTargetId } from '../decorators/audit.decorator' import { AuditLog, AuditLogMetadata } from '../entities/audit-log.entity' import { AuditAction } from '../enums/audit-action.enum' import { AuditService } from '../services/audit.service' @@ -120,18 +120,18 @@ export class AuditInterceptor implements NestInterceptor { /** * Resolves the identifier of the target resource from the initial request or the response object. * - * Prioritizes resolving the ID from the response object as the request may not include a unique resource identifier (e.g. delete sandbox by name). + * Prioritizes resolving the ID from the response object as the request may not include a unique resource identifier (e.g. delete box by name). */ private resolveTargetId(auditContext: AuditContext, request: RequestWithUser, result?: any): string | null { if (auditContext.targetIdFromResult && result) { - const targetId = auditContext.targetIdFromResult(result) + const targetId = this.normalizeTargetId(auditContext.targetIdFromResult(result)) if (targetId) { return targetId } } if (auditContext.targetIdFromRequest) { - const targetId = auditContext.targetIdFromRequest(request) + const targetId = this.normalizeTargetId(auditContext.targetIdFromRequest(request)) if (targetId) { return targetId } @@ -140,6 +140,14 @@ export class AuditInterceptor implements NestInterceptor { return null } + private normalizeTargetId(targetId: AuditTargetId): string | null { + if (Array.isArray(targetId)) { + return targetId[0] ?? null + } + + return targetId ?? null + } + private resolveRequestMetadata(auditContext: AuditContext, request: RequestWithUser): AuditLogMetadata | null { if (!auditContext.requestMetadata) { return null diff --git a/apps/api/src/audit/services/audit.service.ts b/apps/api/src/audit/services/audit.service.ts index 41a47b4e8..6c52d8714 100644 --- a/apps/api/src/audit/services/audit.service.ts +++ b/apps/api/src/audit/services/audit.service.ts @@ -13,7 +13,7 @@ import { UpdateAuditLogInternalDto } from '../dto/update-audit-log-internal.dto' import { AuditLog } from '../entities/audit-log.entity' import { PaginatedList } from '../../common/interfaces/paginated-list.interface' import { TypedConfigService } from '../../config/typed-config.service' -import { RedisLockProvider } from '../../sandbox/common/redis-lock.provider' +import { RedisLockProvider } from '../../box/common/redis-lock.provider' import { AUDIT_LOG_PUBLISHER, AUDIT_STORAGE_ADAPTER } from '../constants/audit-tokens' import { AuditLogStorageAdapter } from '../interfaces/audit-storage.interface' import { AuditLogPublisher } from '../interfaces/audit-publisher.interface' diff --git a/apps/api/src/auth/api-key.strategy.spec.ts b/apps/api/src/auth/api-key.strategy.spec.ts new file mode 100644 index 000000000..b94426fda --- /dev/null +++ b/apps/api/src/auth/api-key.strategy.spec.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +jest.mock('../box/services/runner.service', () => ({ + RunnerService: class RunnerService {}, +})) +jest.mock('../region/services/region.service', () => ({ + RegionService: class RegionService {}, +})) + +import { ApiKeyStrategy } from './api-key.strategy' + +const jwtToken = 'eyJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJib3hsaXRlIn0.signature' + +function createStrategy() { + const redis = { + get: jest.fn().mockResolvedValue(null), + setex: jest.fn(), + } + const apiKeyService = { + getApiKeyByValue: jest.fn().mockRejectedValue(new Error('not found')), + updateLastUsedAt: jest.fn(), + } + const userService = { + findOne: jest.fn(), + } + const configService = { + get: jest.fn().mockReturnValue(undefined), + getOrThrow: jest.fn(() => { + throw new Error('missing config') + }), + } + const runnerService = { + findByApiKey: jest.fn().mockResolvedValue(null), + } + const regionService = { + findOneByProxyApiKey: jest.fn().mockResolvedValue(null), + findOneBySshGatewayApiKey: jest.fn().mockResolvedValue(null), + } + + return { + strategy: new ApiKeyStrategy( + redis as any, + apiKeyService as any, + userService as any, + configService as any, + runnerService as any, + regionService as any, + ), + mocks: { redis, apiKeyService, userService, configService, runnerService, regionService }, + } +} + +describe('ApiKeyStrategy', () => { + it('delegates JWT-shaped bearer tokens to the JWT strategy before reading API-key config', async () => { + const { strategy, mocks } = createStrategy() + + await expect(strategy.validate(jwtToken)).resolves.toBeNull() + + expect(mocks.configService.getOrThrow).not.toHaveBeenCalled() + expect(mocks.apiKeyService.getApiKeyByValue).not.toHaveBeenCalled() + }) + + it('treats missing internal API-key config as optional for ordinary API-key lookup', async () => { + const { strategy, mocks } = createStrategy() + + await expect(strategy.validate('unknown-api-key')).resolves.toBeNull() + + expect(mocks.configService.get).toHaveBeenCalledWith('sshGateway.apiKey') + expect(mocks.configService.get).toHaveBeenCalledWith('proxy.apiKey') + expect(mocks.configService.getOrThrow).not.toHaveBeenCalled() + expect(mocks.apiKeyService.getApiKeyByValue).toHaveBeenCalledWith('unknown-api-key') + }) +}) diff --git a/apps/api/src/auth/api-key.strategy.ts b/apps/api/src/auth/api-key.strategy.ts index 12637355b..778493470 100644 --- a/apps/api/src/auth/api-key.strategy.ts +++ b/apps/api/src/auth/api-key.strategy.ts @@ -15,7 +15,7 @@ import { TypedConfigService } from '../config/typed-config.service' import { InjectRedis } from '@nestjs-modules/ioredis' import Redis from 'ioredis' import { SystemRole } from '../user/enums/system-role.enum' -import { RunnerService } from '../sandbox/services/runner.service' +import { RunnerService } from '../box/services/runner.service' import { generateApiKeyHash } from '../common/utils/api-key' import { RegionService } from '../region/services/region.service' import { JWT_REGEX } from './constants/jwt-regex.constant' @@ -50,15 +50,20 @@ export class ApiKeyStrategy extends PassportStrategy(Strategy, 'api-key') implem this.logger.debug('Validate method called') this.logger.debug(`Validating API key: ${token.substring(0, 8)}...`) - const sshGatewayApiKey = this.configService.getOrThrow('sshGateway.apiKey') - if (sshGatewayApiKey === token) { + // Tokens matching JWT structure are not API keys. Return null so Passport can continue with the JWT strategy. + if (JWT_REGEX.test(token)) { + return null + } + + const sshGatewayApiKey = this.configService.get('sshGateway.apiKey') + if (sshGatewayApiKey && sshGatewayApiKey === token) { return { role: 'ssh-gateway', } } - const proxyApiKey = this.configService.getOrThrow('proxy.apiKey') - if (proxyApiKey === token) { + const proxyApiKey = this.configService.get('proxy.apiKey') + if (proxyApiKey && proxyApiKey === token) { return { role: 'proxy', } @@ -78,11 +83,6 @@ export class ApiKeyStrategy extends PassportStrategy(Strategy, 'api-key') implem } } - // Tokens matching JWT structure are not API keys — skip DB lookups and delegate to the JWT strategy. - if (JWT_REGEX.test(token)) { - return null - } - try { let apiKey = await this.getApiKeyCache(token) if (!apiKey) { diff --git a/apps/api/src/auth/auth.module.spec.ts b/apps/api/src/auth/auth.module.spec.ts new file mode 100644 index 000000000..a02301abe --- /dev/null +++ b/apps/api/src/auth/auth.module.spec.ts @@ -0,0 +1,68 @@ +jest.mock('../box/services/runner.service', () => ({ + RunnerService: class RunnerService {}, +})) +jest.mock('../region/services/region.service', () => ({ + RegionService: class RegionService {}, +})) +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), +})) + +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { MODULE_METADATA } from '@nestjs/common/constants' +import { AuthModule } from './auth.module' +import { JwtStrategy } from './jwt.strategy' + +function getJwtStrategyProviderFactory() { + const providers = Reflect.getMetadata(MODULE_METADATA.PROVIDERS, AuthModule) as Array + const provider = providers.find((candidate) => candidate?.provide === JwtStrategy) + if (!provider?.useFactory) { + throw new Error('JwtStrategy provider factory not found') + } + return provider.useFactory as (...args: any[]) => Promise +} + +describe('AuthModule JwtStrategy provider', () => { + it('validates JWT issuer against the public issuer while keeping JWKS on the internal issuer', async () => { + const createJwtStrategy = getJwtStrategyProviderFactory() + const oidcMetadataService = { + getMetadata: jest.fn().mockResolvedValue({ + issuer: 'https://dev-j60pjpmu6neaeaga.us.auth0.com/', + jwks_uri: 'https://dev-j60pjpmu6neaeaga.us.auth0.com/.well-known/jwks.json', + }), + } + const configService = { + get: jest.fn((key: string) => { + switch (key) { + case 'skipConnections': + return false + case 'oidc.audience': + return 'https://dev.boxlite.ai/api' + case 'oidc.publicIssuer': + return 'https://auth.dev.boxlite.ai' + default: + return undefined + } + }), + getOrThrow: jest.fn((key: string) => { + if (key === 'oidc.issuer') { + return 'https://dev-j60pjpmu6neaeaga.us.auth0.com' + } + throw new Error(`missing config ${key}`) + }), + } + + const strategy = await createJwtStrategy({} as any, oidcMetadataService as any, configService as any) + + expect((strategy as any).options).toMatchObject({ + audience: 'https://dev.boxlite.ai/api', + issuer: 'https://auth.dev.boxlite.ai/', + jwksUri: 'https://dev-j60pjpmu6neaeaga.us.auth0.com/.well-known/jwks.json', + }) + }) +}) diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts index 6272903dc..d34e7f2d3 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -10,16 +10,14 @@ import { JwtStrategy } from './jwt.strategy' import { ApiKeyStrategy } from './api-key.strategy' import { UserModule } from '../user/user.module' import { ApiKeyModule } from '../api-key/api-key.module' -import { SandboxModule } from '../sandbox/sandbox.module' +import { BoxModule } from '../box/box.module' import { TypedConfigService } from '../config/typed-config.service' -import { HttpModule, HttpService } from '@nestjs/axios' -import { OidcMetadata } from 'oidc-client-ts' -import { firstValueFrom } from 'rxjs' import { UserService } from '../user/user.service' import { TypedConfigModule } from '../config/typed-config.module' -import { catchError, map } from 'rxjs/operators' +import { OidcMetadataService } from '../config/oidc-metadata.service' import { FailedAuthTrackerService } from './failed-auth-tracker.service' import { RegionModule } from '../region/region.module' +import { LogoutController } from './logout.controller' @Module({ imports: [ PassportModule.register({ @@ -30,52 +28,73 @@ import { RegionModule } from '../region/region.module' TypedConfigModule, UserModule, ApiKeyModule, - SandboxModule, + BoxModule, RegionModule, - HttpModule, ], + controllers: [LogoutController], providers: [ ApiKeyStrategy, { provide: JwtStrategy, - useFactory: async (userService: UserService, httpService: HttpService, configService: TypedConfigService) => { + useFactory: async ( + userService: UserService, + oidcMetadataService: OidcMetadataService, + configService: TypedConfigService, + ) => { if (configService.get('skipConnections')) { return } - // Get the OpenID configuration from the issuer - const discoveryUrl = `${configService.get('oidc.issuer')}/.well-known/openid-configuration` - const metadata = await firstValueFrom( - httpService.get(discoveryUrl).pipe( - map((response) => response.data as OidcMetadata), - catchError((error) => { - throw new Error(`Failed to fetch OpenID configuration: ${error.message}`) - }), - ), - ) + const metadata = await oidcMetadataService.getMetadata() let jwksUri = metadata.jwks_uri const internalIssuer = configService.getOrThrow('oidc.issuer') const publicIssuer = configService.get('oidc.publicIssuer') if (publicIssuer) { - // Replace localhost URLs with Docker network URLs for internal API use + // Keep JWKS reachable from the API container, while validating the + // token issuer that browser clients actually receive. jwksUri = metadata.jwks_uri.replace(publicIssuer, internalIssuer) } return new JwtStrategy( { audience: configService.get('oidc.audience'), - issuer: metadata.issuer, + issuer: toPublicJwtIssuer(metadata.issuer, internalIssuer, publicIssuer), jwksUri: jwksUri, }, userService, configService, ) }, - inject: [UserService, HttpService, TypedConfigService], + inject: [UserService, OidcMetadataService, TypedConfigService], }, FailedAuthTrackerService, ], exports: [PassportModule, JwtStrategy, ApiKeyStrategy, FailedAuthTrackerService], }) export class AuthModule {} + +function toPublicJwtIssuer(metadataIssuer: string, internalIssuer: string, publicIssuer?: string): string { + if (!publicIssuer) { + return metadataIssuer + } + + const internalBase = stripTrailingSlashes(internalIssuer) + const publicBase = stripTrailingSlashes(publicIssuer) + + if (metadataIssuer === internalBase) { + return publicBase + } + if (metadataIssuer.startsWith(`${internalBase}/`)) { + return `${publicBase}${metadataIssuer.substring(internalBase.length)}` + } + if (metadataIssuer === publicBase || metadataIssuer.startsWith(`${publicBase}/`)) { + return metadataIssuer + } + + return publicIssuer +} + +function stripTrailingSlashes(value: string): string { + return value.replace(/\/+$/, '') +} diff --git a/apps/api/src/auth/jwt.strategy.spec.ts b/apps/api/src/auth/jwt.strategy.spec.ts new file mode 100644 index 000000000..a9e5ff6b0 --- /dev/null +++ b/apps/api/src/auth/jwt.strategy.spec.ts @@ -0,0 +1,57 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Request } from 'express' +import { JwtStrategy } from './jwt.strategy' +import { UserService } from '../user/user.service' +import { TypedConfigService } from '../config/typed-config.service' + +const DEFAULT_REGION_ID = 'region-default-id' +const DEFAULT_ORG_QUOTA = { totalCpuQuota: 8 } + +function buildStrategy() { + const createdUser = { id: 'user-1', role: 'user', email: 'new@boxlite.dev' } + + const userService = { + findOne: jest.fn().mockResolvedValue(null), // new user → triggers create() + create: jest.fn().mockResolvedValue(createdUser), + update: jest.fn(), + } as unknown as UserService + + const configService = { + getOrThrow: jest.fn((key: string) => { + if (key === 'defaultRegion.id') return DEFAULT_REGION_ID + if (key === 'defaultOrganizationQuota') return DEFAULT_ORG_QUOTA + throw new Error(`unexpected config key: ${key}`) + }), + } as unknown as TypedConfigService + + const strategy = new JwtStrategy( + { jwksUri: 'https://example.com/.well-known/jwks.json', audience: 'aud', issuer: 'iss' }, + userService, + configService, + ) + + return { strategy, userService } +} + +describe('JwtStrategy.validate — auto-created user', () => { + it('anchors the Personal org to the default region for a new OIDC user', async () => { + const { strategy, userService } = buildStrategy() + const request = { get: jest.fn().mockReturnValue(undefined) } as unknown as Request + + await strategy.validate(request, { sub: 'user-1', email: 'new@boxlite.dev', email_verified: true }) + + // The bug: without defaultOrganizationDefaultRegionId, the downstream + // UserCreatedEvent → handleUserCreatedEvent creates the default org with + // defaultRegionId=undefined. Assert the strategy forwards the configured + // region id into the create DTO. + expect(userService.create).toHaveBeenCalledTimes(1) + expect(userService.create).toHaveBeenCalledWith( + expect.objectContaining({ defaultOrganizationDefaultRegionId: DEFAULT_REGION_ID }), + ) + }) +}) diff --git a/apps/api/src/auth/jwt.strategy.ts b/apps/api/src/auth/jwt.strategy.ts index 579fc8849..ce4a8c35e 100644 --- a/apps/api/src/auth/jwt.strategy.ts +++ b/apps/api/src/auth/jwt.strategy.ts @@ -71,7 +71,12 @@ export class JwtStrategy extends PassportStrategy(Strategy) { name: payload.name || payload.username || 'Unknown', email: email || '', emailVerified: payload.email_verified || false, - personalOrganizationQuota: this.configService.getOrThrow('defaultOrganizationQuota'), + // Anchor the auto-created default organization to the platform's + // default region, matching the admin-seed path in AppService. Without + // this, OrganizationService.handleUserCreatedEvent creates the org + // with defaultRegionId=undefined and downstream callers that read + // organization.defaultRegionId fail for every OIDC-created user. + defaultOrganizationDefaultRegionId: this.configService.getOrThrow('defaultRegion.id'), }) this.logger.debug(`Created new user with ID: ${userId}`) } else if (user.name === 'Unknown' || !user.email) { diff --git a/apps/api/src/auth/logout.controller.ts b/apps/api/src/auth/logout.controller.ts new file mode 100644 index 000000000..284e910f8 --- /dev/null +++ b/apps/api/src/auth/logout.controller.ts @@ -0,0 +1,85 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, Get, Logger, Query, Res, UseGuards } from '@nestjs/common' +import { ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger' +import { Response } from 'express' +import { AnonymousRateLimitGuard } from '../common/guards/anonymous-rate-limit.guard' +import { parseHttpUrl } from '../config/oidc-metadata.service' +import { TypedConfigService } from '../config/typed-config.service' + +@ApiTags('auth') +@Controller('auth') +export class LogoutController { + private readonly logger = new Logger(LogoutController.name) + + constructor(private readonly configService: TypedConfigService) {} + + @Get('end-session') + @UseGuards(AnonymousRateLimitGuard) + @ApiOperation({ + summary: 'OIDC RP-initiated logout endpoint', + description: + 'Implements OpenID Connect RP-Initiated Logout 1.0 for IdPs (e.g. Dex) that do not natively advertise end_session_endpoint. Validates the post-logout redirect target, then 302-redirects the browser back to the SPA.', + }) + @ApiQuery({ name: 'post_logout_redirect_uri', required: false }) + @ApiQuery({ name: 'id_token_hint', required: false }) + @ApiQuery({ name: 'state', required: false }) + @ApiResponse({ status: 302, description: 'Redirect to post_logout_redirect_uri' }) + @ApiResponse({ status: 400, description: 'post_logout_redirect_uri not allowed' }) + endSession( + @Query('post_logout_redirect_uri') postLogoutRedirectUri: string | undefined, + @Query('id_token_hint') _idTokenHint: string | undefined, + @Query('state') state: string | undefined, + @Res() res: Response, + ) { + const dashboardUrl = this.configService.getOrThrow('dashboardUrl') + const allowlist = this.getAllowlist(dashboardUrl) + + const target = postLogoutRedirectUri || dashboardUrl + if (!validatePostLogoutRedirect(target, allowlist)) { + this.logger.warn(`Rejected post_logout_redirect_uri: ${target}`) + res.status(400).send('post_logout_redirect_uri not in allowlist') + return + } + + const finalUrl = state ? appendStateParam(target, state) : target + res.redirect(302, finalUrl) + } + + private getAllowlist(dashboardUrl: string): string[] { + const extra = this.configService.get('oidc.postLogoutRedirectAllowlist') + const extras = extra + ? extra + .split(',') + .map((s) => s.trim()) + .filter(Boolean) + : [] + return [dashboardUrl, ...extras].filter(Boolean) + } +} + +/** + * Open-redirect prevention for RP-initiated logout. Returns true iff `uri` + * passes the shared `parseHttpUrl` policy AND its origin matches the origin + * of some allowlist entry. Origin-match (not exact-equality) lets the SPA + * redirect to deep paths under the same origin without registering each one. + */ +function validatePostLogoutRedirect(uri: string, allowlist: string[]): boolean { + const target = parseHttpUrl(uri) + if (!target) return false + for (const allowed of allowlist) { + const parsed = parseHttpUrl(allowed) + if (parsed && parsed.origin === target.origin) return true + } + return false +} + +function appendStateParam(url: string, state: string): string { + const u = new URL(url) + u.searchParams.set('state', state) + return u.toString() +} diff --git a/apps/api/src/auth/or.guard.ts b/apps/api/src/auth/or.guard.ts index ca360e3ba..bf6a33899 100644 --- a/apps/api/src/auth/or.guard.ts +++ b/apps/api/src/auth/or.guard.ts @@ -7,6 +7,8 @@ import { Injectable, ExecutionContext, Logger, CanActivate, Type, mixin } from '@nestjs/common' import { ModuleRef } from '@nestjs/core' +export const OR_GUARD_INNER_GUARDS = Symbol('OR_GUARD_INNER_GUARDS') + /** * Creates an OrGuard that allows access if at least one of the provided guards allows access. * It tries each guard in sequence and returns true on the first successful guard. @@ -44,5 +46,10 @@ export function OrGuard(guards: Type[]): Type { } } - return mixin(OrGuardMixin) + const guardClass = OrGuardMixin as Type & { + [OR_GUARD_INNER_GUARDS]?: Type[] + } + guardClass[OR_GUARD_INNER_GUARDS] = guards + + return mixin(guardClass) } diff --git a/apps/api/src/auth/system-action.guard.spec.ts b/apps/api/src/auth/system-action.guard.spec.ts new file mode 100644 index 000000000..4546430ec --- /dev/null +++ b/apps/api/src/auth/system-action.guard.spec.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ExecutionContext, Type } from '@nestjs/common' +import { Reflector } from '@nestjs/core' +import { RequiredApiRole } from '../common/decorators/required-role.decorator' +import { SystemRole } from '../user/enums/system-role.enum' +import { SystemActionGuard } from './system-action.guard' + +function httpContext(request: Record, controllerClass: Type): ExecutionContext { + return { + getClass: () => controllerClass, + getHandler: () => function handler() {}, + switchToHttp: () => ({ + getRequest: () => request, + }), + } as unknown as ExecutionContext +} + +describe('SystemActionGuard', () => { + function createGuard() { + return new SystemActionGuard(new Reflector()) + } + + it('allows Admin API callers through Admin-only routes', async () => { + class AdminOnlyController {} + RequiredApiRole([SystemRole.ADMIN])(AdminOnlyController) + + const request = { + user: { + role: SystemRole.ADMIN, + }, + } + + await expect(createGuard().canActivate(httpContext(request, AdminOnlyController))).resolves.toBe(true) + }) + + it('rejects ordinary users from Admin-only routes', async () => { + class AdminOnlyController {} + RequiredApiRole([SystemRole.ADMIN])(AdminOnlyController) + + const request = { + user: { + role: SystemRole.USER, + }, + } + + await expect(createGuard().canActivate(httpContext(request, AdminOnlyController))).resolves.toBe(false) + }) + + it('allows routes without a required role to continue to downstream guards or handlers', async () => { + class UnscopedController {} + + const request = { + user: { + role: SystemRole.USER, + }, + } + + await expect(createGuard().canActivate(httpContext(request, UnscopedController))).resolves.toBe(true) + }) +}) diff --git a/apps/api/src/box-telemetry/box-telemetry.module.ts b/apps/api/src/box-telemetry/box-telemetry.module.ts new file mode 100644 index 000000000..557be4cb3 --- /dev/null +++ b/apps/api/src/box-telemetry/box-telemetry.module.ts @@ -0,0 +1,18 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Module } from '@nestjs/common' +import { BoxTelemetryController } from './controllers/box-telemetry.controller' +import { BoxTelemetryService } from './services/box-telemetry.service' +import { BoxModule } from '../box/box.module' +import { OrganizationModule } from '../organization/organization.module' + +@Module({ + imports: [BoxModule, OrganizationModule], + controllers: [BoxTelemetryController], + providers: [BoxTelemetryService], + exports: [BoxTelemetryService], +}) +export class BoxTelemetryModule {} diff --git a/apps/api/src/box-telemetry/controllers/box-telemetry.controller.ts b/apps/api/src/box-telemetry/controllers/box-telemetry.controller.ts new file mode 100644 index 000000000..03479ce08 --- /dev/null +++ b/apps/api/src/box-telemetry/controllers/box-telemetry.controller.ts @@ -0,0 +1,143 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common' +import { ApiOAuth2, ApiResponse, ApiOperation, ApiParam, ApiTags, ApiHeader, ApiBearerAuth } from '@nestjs/swagger' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' +import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' +import { BoxAccessGuard } from '../../box/guards/box-access.guard' +import { CustomHeaders } from '../../common/constants/header.constants' +import { BoxTelemetryService } from '../services/box-telemetry.service' +import { LogsQueryParamsDto, TelemetryQueryParamsDto, MetricsQueryParamsDto } from '../dto/telemetry-query-params.dto' +import { PaginatedLogsDto } from '../dto/paginated-logs.dto' +import { PaginatedTracesDto } from '../dto/paginated-traces.dto' +import { TraceSpanDto } from '../dto/trace-span.dto' +import { MetricsResponseDto } from '../dto/metrics-response.dto' +import { RequireFlagsEnabled } from '@openfeature/nestjs-sdk' +import { AnalyticsApiDisabledGuard } from '../guards/analytics-api-disabled.guard' + +@ApiTags('box') +@Controller('box') +@ApiHeader(CustomHeaders.ORGANIZATION_ID) +@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard, AnalyticsApiDisabledGuard) +@ApiOAuth2(['openid', 'profile', 'email']) +@ApiBearerAuth() +export class BoxTelemetryController { + constructor(private readonly boxTelemetryService: BoxTelemetryService) {} + + @Get(':boxId/telemetry/logs') + @ApiOperation({ + summary: 'Get box logs', + operationId: 'getBoxLogs', + description: 'Retrieve OTEL logs for a box within a time range', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Paginated list of log entries', + type: PaginatedLogsDto, + }) + @UseGuards(BoxAccessGuard) + @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) + async getBoxLogs(@Param('boxId') boxId: string, @Query() queryParams: LogsQueryParamsDto): Promise { + return this.boxTelemetryService.getLogs( + boxId, + queryParams.from, + queryParams.to, + queryParams.page ?? 1, + queryParams.limit ?? 100, + queryParams.severities, + queryParams.search, + ) + } + + @Get(':boxId/telemetry/traces') + @ApiOperation({ + summary: 'Get box traces', + operationId: 'getBoxTraces', + description: 'Retrieve OTEL traces for a box within a time range', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Paginated list of trace summaries', + type: PaginatedTracesDto, + }) + @UseGuards(BoxAccessGuard) + @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) + async getBoxTraces( + @Param('boxId') boxId: string, + @Query() queryParams: TelemetryQueryParamsDto, + ): Promise { + return this.boxTelemetryService.getTraces( + boxId, + queryParams.from, + queryParams.to, + queryParams.page ?? 1, + queryParams.limit ?? 100, + ) + } + + @Get(':boxId/telemetry/traces/:traceId') + @ApiOperation({ + summary: 'Get trace spans', + operationId: 'getBoxTraceSpans', + description: 'Retrieve all spans for a specific trace', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiParam({ + name: 'traceId', + description: 'ID of the trace', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'List of spans in the trace', + type: [TraceSpanDto], + }) + @UseGuards(BoxAccessGuard) + @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) + async getBoxTraceSpans(@Param('boxId') boxId: string, @Param('traceId') traceId: string): Promise { + return this.boxTelemetryService.getTraceSpans(boxId, traceId) + } + + @Get(':boxId/telemetry/metrics') + @ApiOperation({ + summary: 'Get box metrics', + operationId: 'getBoxMetrics', + description: 'Retrieve OTEL metrics for a box within a time range', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Metrics time series data', + type: MetricsResponseDto, + }) + @UseGuards(BoxAccessGuard) + @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) + async getBoxMetrics( + @Param('boxId') boxId: string, + @Query() queryParams: MetricsQueryParamsDto, + ): Promise { + return this.boxTelemetryService.getMetrics(boxId, queryParams.from, queryParams.to, queryParams.metricNames) + } +} diff --git a/apps/api/src/sandbox-telemetry/dto/index.ts b/apps/api/src/box-telemetry/dto/index.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/index.ts rename to apps/api/src/box-telemetry/dto/index.ts diff --git a/apps/api/src/sandbox-telemetry/dto/log-entry.dto.ts b/apps/api/src/box-telemetry/dto/log-entry.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/log-entry.dto.ts rename to apps/api/src/box-telemetry/dto/log-entry.dto.ts diff --git a/apps/api/src/sandbox-telemetry/dto/metrics-response.dto.ts b/apps/api/src/box-telemetry/dto/metrics-response.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/metrics-response.dto.ts rename to apps/api/src/box-telemetry/dto/metrics-response.dto.ts diff --git a/apps/api/src/sandbox-telemetry/dto/paginated-logs.dto.ts b/apps/api/src/box-telemetry/dto/paginated-logs.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/paginated-logs.dto.ts rename to apps/api/src/box-telemetry/dto/paginated-logs.dto.ts diff --git a/apps/api/src/sandbox-telemetry/dto/paginated-traces.dto.ts b/apps/api/src/box-telemetry/dto/paginated-traces.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/paginated-traces.dto.ts rename to apps/api/src/box-telemetry/dto/paginated-traces.dto.ts diff --git a/apps/api/src/sandbox-telemetry/dto/telemetry-query-params.dto.ts b/apps/api/src/box-telemetry/dto/telemetry-query-params.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/telemetry-query-params.dto.ts rename to apps/api/src/box-telemetry/dto/telemetry-query-params.dto.ts diff --git a/apps/api/src/sandbox-telemetry/dto/trace-span.dto.ts b/apps/api/src/box-telemetry/dto/trace-span.dto.ts similarity index 80% rename from apps/api/src/sandbox-telemetry/dto/trace-span.dto.ts rename to apps/api/src/box-telemetry/dto/trace-span.dto.ts index efc52bbf6..dd154dc5c 100644 --- a/apps/api/src/sandbox-telemetry/dto/trace-span.dto.ts +++ b/apps/api/src/box-telemetry/dto/trace-span.dto.ts @@ -19,6 +19,12 @@ export class TraceSpanDto { @ApiProperty({ description: 'Span name' }) spanName: string + @ApiPropertyOptional({ description: 'Emitting service name (e.g. boxlite-api, boxlite-runner, box-)' }) + serviceName?: string + + @ApiPropertyOptional({ description: 'Resolved emitting layer: api | runner | ec2_host | box' }) + layer?: string + @ApiProperty({ description: 'Span start timestamp' }) timestamp: string diff --git a/apps/api/src/sandbox-telemetry/dto/trace-summary.dto.ts b/apps/api/src/box-telemetry/dto/trace-summary.dto.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/dto/trace-summary.dto.ts rename to apps/api/src/box-telemetry/dto/trace-summary.dto.ts diff --git a/apps/api/src/sandbox-telemetry/guards/analytics-api-disabled.guard.ts b/apps/api/src/box-telemetry/guards/analytics-api-disabled.guard.ts similarity index 100% rename from apps/api/src/sandbox-telemetry/guards/analytics-api-disabled.guard.ts rename to apps/api/src/box-telemetry/guards/analytics-api-disabled.guard.ts diff --git a/apps/api/src/box-telemetry/index.ts b/apps/api/src/box-telemetry/index.ts new file mode 100644 index 000000000..66555bb38 --- /dev/null +++ b/apps/api/src/box-telemetry/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +export * from './box-telemetry.module' +export * from './services/box-telemetry.service' +export * from './dto' diff --git a/apps/api/src/box-telemetry/services/box-telemetry.service.ts b/apps/api/src/box-telemetry/services/box-telemetry.service.ts new file mode 100644 index 000000000..33af62afa --- /dev/null +++ b/apps/api/src/box-telemetry/services/box-telemetry.service.ts @@ -0,0 +1,293 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { ClickHouseService } from '../../clickhouse/clickhouse.service' +import { LogEntryDto } from '../dto/log-entry.dto' +import { PaginatedLogsDto } from '../dto/paginated-logs.dto' +import { TraceSummaryDto } from '../dto/trace-summary.dto' +import { TraceSpanDto } from '../dto/trace-span.dto' +import { PaginatedTracesDto } from '../dto/paginated-traces.dto' +import { MetricsResponseDto, MetricSeriesDto, MetricDataPointDto } from '../dto/metrics-response.dto' + +interface ClickHouseLogRow { + Timestamp: string + Body: string + SeverityText: string + SeverityNumber: number + ServiceName: string + ResourceAttributes: Record + LogAttributes: Record + TraceId: string + SpanId: string +} + +interface ClickHouseTraceAggregateRow { + TraceId: string + startTime: string + endTime: string + spanCount: number + rootSpanName: string + totalDuration: number + statusCode: string +} + +interface ClickHouseSpanRow { + TraceId: string + SpanId: string + ParentSpanId: string + SpanName: string + Timestamp: string + Duration: number + SpanAttributes: Record + StatusCode: string + StatusMessage: string +} + +interface ClickHouseMetricRow { + timestamp: string + MetricName: string + value: number +} + +interface ClickHouseCountRow { + count: number +} + +@Injectable() +export class BoxTelemetryService { + private readonly logger = new Logger(BoxTelemetryService.name) + + constructor(private readonly clickhouseService: ClickHouseService) {} + + private getServiceName(boxId: string): string { + return `box-${boxId}` + } + + isConfigured(): boolean { + return this.clickhouseService.isConfigured() + } + + async getLogs( + boxId: string, + from: string, + to: string, + page: number, + limit: number, + severities?: string[], + search?: string, + ): Promise { + const serviceName = this.getServiceName(boxId) + const offset = (page - 1) * limit + + // Build WHERE clause for optional filters + let whereClause = `ServiceName = {serviceName:String} + AND Timestamp >= {from:DateTime64} + AND Timestamp <= {to:DateTime64}` + + if (severities && severities.length > 0) { + whereClause += ` AND SeverityText IN ({severities:Array(String)})` + } + + if (search) { + whereClause += ` AND Body ILIKE {search:String}` + } + + const params: Record = { + serviceName, + from: new Date(from), + to: new Date(to), + limit, + offset, + } + + if (severities && severities.length > 0) { + params.severities = severities + } + + if (search) { + params.search = `%${search}%` + } + + // Get total count + const countQuery = ` + SELECT count() as count + FROM otel_logs + WHERE ${whereClause} + ` + const countResult = await this.clickhouseService.query(countQuery, params) + const total = countResult[0]?.count || 0 + + // Get paginated logs + const logsQuery = ` + SELECT Timestamp, Body, SeverityText, SeverityNumber, ServiceName, + ResourceAttributes, LogAttributes, TraceId, SpanId + FROM otel_logs + WHERE ${whereClause} + ORDER BY Timestamp DESC + LIMIT {limit:UInt32} OFFSET {offset:UInt32} + ` + const rows = await this.clickhouseService.query(logsQuery, params) + + const items: LogEntryDto[] = rows.map((row) => ({ + timestamp: row.Timestamp, + body: row.Body, + severityText: row.SeverityText, + severityNumber: row.SeverityNumber, + serviceName: row.ServiceName, + resourceAttributes: row.ResourceAttributes || {}, + logAttributes: row.LogAttributes || {}, + traceId: row.TraceId || undefined, + spanId: row.SpanId || undefined, + })) + + return { + items, + total, + page, + totalPages: Math.ceil(total / limit), + } + } + + async getTraces(boxId: string, from: string, to: string, page: number, limit: number): Promise { + const serviceName = this.getServiceName(boxId) + const offset = (page - 1) * limit + + const params = { + serviceName, + from: new Date(from), + to: new Date(to), + limit, + offset, + } + + // Get total count of unique traces + const countQuery = ` + SELECT count(DISTINCT TraceId) as count + FROM otel_traces + WHERE ServiceName = {serviceName:String} + AND Timestamp >= {from:DateTime64} + AND Timestamp <= {to:DateTime64} + ` + const countResult = await this.clickhouseService.query(countQuery, params) + const total = countResult[0]?.count || 0 + + // Get aggregated trace data + const tracesQuery = ` + SELECT + TraceId, + min(Timestamp) as startTime, + max(Timestamp) as endTime, + count() as spanCount, + argMinIf(SpanName, Timestamp, ParentSpanId = '') as rootSpanName, + max(Duration) as totalDuration, + any(StatusCode) as statusCode + FROM otel_traces + WHERE ServiceName = {serviceName:String} + AND Timestamp >= {from:DateTime64} + AND Timestamp <= {to:DateTime64} + GROUP BY TraceId + ORDER BY startTime DESC + LIMIT {limit:UInt32} OFFSET {offset:UInt32} + ` + const rows = await this.clickhouseService.query(tracesQuery, params) + + const items: TraceSummaryDto[] = rows.map((row) => ({ + traceId: row.TraceId, + rootSpanName: row.rootSpanName, + startTime: row.startTime, + endTime: row.endTime, + durationMs: row.totalDuration / 1_000_000, // Convert nanoseconds to milliseconds + spanCount: row.spanCount, + statusCode: row.statusCode || undefined, + })) + + return { + items, + total, + page, + totalPages: Math.ceil(total / limit), + } + } + + async getTraceSpans(boxId: string, traceId: string): Promise { + const serviceName = this.getServiceName(boxId) + + const query = ` + SELECT TraceId, SpanId, ParentSpanId, SpanName, Timestamp, Duration, + SpanAttributes, StatusCode, StatusMessage + FROM otel_traces + WHERE TraceId = {traceId:String} + AND ServiceName = {serviceName:String} + ORDER BY Timestamp ASC + ` + + const rows = await this.clickhouseService.query(query, { traceId, serviceName }) + + return rows.map((row) => ({ + traceId: row.TraceId, + spanId: row.SpanId, + parentSpanId: row.ParentSpanId || undefined, + spanName: row.SpanName, + timestamp: row.Timestamp, + durationNs: row.Duration, + spanAttributes: row.SpanAttributes || {}, + statusCode: row.StatusCode || undefined, + statusMessage: row.StatusMessage || undefined, + })) + } + + async getMetrics(boxId: string, from: string, to: string, metricNames?: string[]): Promise { + const serviceName = this.getServiceName(boxId) + + let whereClause = `ServiceName = {serviceName:String} + AND TimeUnix >= {from:DateTime64} + AND TimeUnix <= {to:DateTime64}` + + const params: Record = { + serviceName, + from: new Date(from), + to: new Date(to), + } + + if (metricNames && metricNames.length > 0) { + whereClause += ` AND MetricName IN ({metricNames:Array(String)})` + params.metricNames = metricNames + } + + // Query gauge metrics with 1-minute intervals + const gaugeQuery = ` + SELECT + toStartOfInterval(TimeUnix, INTERVAL 1 MINUTE) as timestamp, + MetricName, + avg(Value) as value + FROM otel_metrics_gauge + WHERE ${whereClause} + GROUP BY timestamp, MetricName + ORDER BY timestamp ASC + ` + + const rows = await this.clickhouseService.query(gaugeQuery, params) + + // Group by metric name + const seriesMap = new Map() + for (const row of rows) { + if (!seriesMap.has(row.MetricName)) { + seriesMap.set(row.MetricName, []) + } + seriesMap.get(row.MetricName)!.push({ + timestamp: row.timestamp, + value: row.value, + }) + } + + const series: MetricSeriesDto[] = Array.from(seriesMap.entries()).map(([metricName, dataPoints]) => ({ + metricName, + dataPoints, + })) + + return { series } + } +} diff --git a/apps/api/src/box/box.module.ts b/apps/api/src/box/box.module.ts new file mode 100644 index 000000000..d5234b785 --- /dev/null +++ b/apps/api/src/box/box.module.ts @@ -0,0 +1,108 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Module } from '@nestjs/common' +import { DataSource } from 'typeorm' +import { BoxController } from './controllers/box.controller' +import { BoxService } from './services/box.service' +import { TypeOrmModule } from '@nestjs/typeorm' +import { Box } from './entities/box.entity' +import { UserModule } from '../user/user.module' +import { RunnerService } from './services/runner.service' +import { Runner } from './entities/runner.entity' +import { RunnerController } from './controllers/runner.controller' +import { BoxManager } from './managers/box.manager' +import { RedisLockProvider } from './common/redis-lock.provider' +import { OrganizationModule } from '../organization/organization.module' +import { BoxWarmPoolService } from './services/box-warm-pool.service' +import { WarmPool } from './entities/warm-pool.entity' +import { PreviewController } from './controllers/preview.controller' +import { VolumeController } from './controllers/volume.controller' +import { VolumeService } from './services/volume.service' +import { VolumeManager } from './managers/volume.manager' +import { Volume } from './entities/volume.entity' +import { VolumeSubscriber } from './subscribers/volume.subscriber' +import { RunnerSubscriber } from './subscribers/runner.subscriber' +import { RunnerAdapterFactory } from './runner-adapter/runnerAdapter' +import { BoxStartAction } from './managers/box-actions/box-start.action' +import { BoxStopAction } from './managers/box-actions/box-stop.action' +import { BoxDestroyAction } from './managers/box-actions/box-destroy.action' +import { SshAccess } from './entities/ssh-access.entity' +import { BoxRepository } from './repositories/box.repository' +import { RegionModule } from '../region/region.module' +import { Region } from '../region/entities/region.entity' +import { JobController } from './controllers/job.controller' +import { JobService } from './services/job.service' +import { JobStateHandlerService } from './services/job-state-handler.service' +import { Job } from './entities/job.entity' +import { BoxLookupCacheInvalidationService } from './services/box-lookup-cache-invalidation.service' +import { BoxAccessGuard } from './guards/box-access.guard' +import { RunnerAccessGuard } from './guards/runner-access.guard' +import { RegionRunnerAccessGuard } from './guards/region-runner-access.guard' +import { RegionBoxAccessGuard } from './guards/region-box-access.guard' +import { ProxyGuard } from './guards/proxy.guard' +import { SshGatewayGuard } from './guards/ssh-gateway.guard' +import { EventEmitter2 } from '@nestjs/event-emitter' +import { BoxLastActivity } from './entities/box-last-activity.entity' +import { BoxActivityService } from './services/box-activity.service' +import { BoxStateWaiterService } from './services/box-state-waiter.service' + +@Module({ + imports: [ + UserModule, + OrganizationModule, + RegionModule, + TypeOrmModule.forFeature([Box, Runner, WarmPool, Volume, SshAccess, Region, Job, BoxLastActivity]), + ], + controllers: [BoxController, RunnerController, PreviewController, VolumeController, JobController], + providers: [ + BoxService, + BoxManager, + BoxWarmPoolService, + RunnerService, + BoxLookupCacheInvalidationService, + RedisLockProvider, + VolumeService, + VolumeManager, + VolumeSubscriber, + RunnerSubscriber, + RunnerAdapterFactory, + BoxStartAction, + BoxStopAction, + BoxDestroyAction, + JobService, + JobStateHandlerService, + BoxActivityService, + BoxStateWaiterService, + BoxAccessGuard, + RunnerAccessGuard, + RegionRunnerAccessGuard, + RegionBoxAccessGuard, + ProxyGuard, + SshGatewayGuard, + { + provide: BoxRepository, + inject: [DataSource, EventEmitter2, BoxLookupCacheInvalidationService], + useFactory: ( + dataSource: DataSource, + eventEmitter: EventEmitter2, + boxLookupCacheInvalidationService: BoxLookupCacheInvalidationService, + ) => new BoxRepository(dataSource, eventEmitter, boxLookupCacheInvalidationService), + }, + ], + exports: [ + BoxService, + RunnerService, + RedisLockProvider, + VolumeService, + VolumeManager, + BoxRepository, + RunnerAdapterFactory, + BoxActivityService, + BoxStateWaiterService, + ], +}) +export class BoxModule {} diff --git a/apps/api/src/sandbox/common/redis-lock.provider.ts b/apps/api/src/box/common/redis-lock.provider.ts similarity index 100% rename from apps/api/src/sandbox/common/redis-lock.provider.ts rename to apps/api/src/box/common/redis-lock.provider.ts diff --git a/apps/api/src/sandbox/common/runner-service-info.ts b/apps/api/src/box/common/runner-service-info.ts similarity index 100% rename from apps/api/src/sandbox/common/runner-service-info.ts rename to apps/api/src/box/common/runner-service-info.ts diff --git a/apps/api/src/box/constants/box-events.constants.ts b/apps/api/src/box/constants/box-events.constants.ts new file mode 100644 index 000000000..574825952 --- /dev/null +++ b/apps/api/src/box/constants/box-events.constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export const BoxEvents = { + ARCHIVED: 'box.archived', + STATE_UPDATED: 'box.state.updated', + DESIRED_STATE_UPDATED: 'box.desired-state.updated', + CREATED: 'box.created', + STARTED: 'box.started', + STOPPED: 'box.stopped', + DESTROYED: 'box.destroyed', + PUBLIC_STATUS_UPDATED: 'box.public-status.updated', + ORGANIZATION_UPDATED: 'box.organization.updated', +} as const diff --git a/apps/api/src/box/constants/box.constants.ts b/apps/api/src/box/constants/box.constants.ts new file mode 100644 index 000000000..8b406c314 --- /dev/null +++ b/apps/api/src/box/constants/box.constants.ts @@ -0,0 +1,7 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export const BOX_WARM_POOL_UNASSIGNED_ORGANIZATION = '00000000-0000-0000-0000-000000000000' diff --git a/apps/api/src/box/constants/curated-images.constant.spec.ts b/apps/api/src/box/constants/curated-images.constant.spec.ts new file mode 100644 index 000000000..2fda47f59 --- /dev/null +++ b/apps/api/src/box/constants/curated-images.constant.spec.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BadRequestError } from '../../exceptions/bad-request.exception' +import { assertSupportedImage, supportedImages } from './curated-images.constant' + +describe('supported image allowlist', () => { + const ENV_KEYS = ['BOXLITE_SYSTEM_BASE_IMAGE', 'BOXLITE_SYSTEM_PYTHON_IMAGE', 'BOXLITE_SYSTEM_NODE_IMAGE'] + const saved: Record = {} + + beforeEach(() => { + // Isolate from the host env so the pinned fallback refs are deterministic. + for (const k of ENV_KEYS) { + saved[k] = process.env[k] + delete process.env[k] + } + }) + + afterEach(() => { + for (const k of ENV_KEYS) { + if (saved[k] === undefined) delete process.env[k] + else process.env[k] = saved[k] + } + }) + + it('exposes the three curated ghcr refs, base first (the default)', () => { + const supported = supportedImages() + expect(supported).toEqual([ + 'ghcr.io/boxlite-ai/boxlite-agent-base:20260605-p0-r3', + 'ghcr.io/boxlite-ai/boxlite-agent-python:20260605-p0-r3', + 'ghcr.io/boxlite-ai/boxlite-agent-node:20260605-p0-r3', + ]) + }) + + it('accepts each supported ref verbatim', () => { + for (const ref of supportedImages()) { + expect(assertSupportedImage(ref)).toBe(ref) + } + }) + + it('defaults to the base ref when no image is supplied', () => { + expect(assertSupportedImage(undefined)).toBe(supportedImages()[0]) + }) + + it('prefers the env-configured ref over the curated fallback', () => { + process.env.BOXLITE_SYSTEM_PYTHON_IMAGE = 'ghcr.io/boxlite-ai/override@sha256:deadbeef' + expect(assertSupportedImage('ghcr.io/boxlite-ai/override@sha256:deadbeef')).toBe( + 'ghcr.io/boxlite-ai/override@sha256:deadbeef', + ) + }) + + it('rejects anything outside the allowlist, naming the supported refs', () => { + expect(() => assertSupportedImage('alpine:3.23')).toThrow(BadRequestError) + expect(() => assertSupportedImage('ghcr.io/evil/image:latest')).toThrow(BadRequestError) + // legacy curated keys are no longer accepted -- only full refs are + expect(() => assertSupportedImage('python')).toThrow(BadRequestError) + expect(() => assertSupportedImage('nope')).toThrow(/Supported images: .*boxlite-agent-base/) + }) +}) diff --git a/apps/api/src/box/constants/curated-images.constant.ts b/apps/api/src/box/constants/curated-images.constant.ts new file mode 100644 index 000000000..f70a15208 --- /dev/null +++ b/apps/api/src/box/constants/curated-images.constant.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BadRequestError } from '../../exceptions/bad-request.exception' + +/** + * Temporary curated-image gate: boxes may only boot from this fixed set of curated OCI + * refs, because the runner pulls with its own private-registry token and must never be + * handed an arbitrary user-supplied image. The gate is deliberately thin and sits only + * at the request boundary (BoxService create / warm-pool); everything downstream treats + * `image` as an opaque OCI ref. When per-org custom images land, delete this file and + * its call sites — no other layer knows the curated set exists. + * + * Env overrides (set on the Api service in apps/infra/sst.config.ts) allow ref + * rotation without a code deploy; the fallbacks cover local/dev runs. + */ +type SupportedImageSource = { + envVar: string + fallbackRef: string +} + +const SUPPORTED_IMAGE_SOURCES: SupportedImageSource[] = [ + { + envVar: 'BOXLITE_SYSTEM_BASE_IMAGE', + fallbackRef: 'ghcr.io/boxlite-ai/boxlite-agent-base:20260605-p0-r3', + }, + { + envVar: 'BOXLITE_SYSTEM_PYTHON_IMAGE', + fallbackRef: 'ghcr.io/boxlite-ai/boxlite-agent-python:20260605-p0-r3', + }, + { + envVar: 'BOXLITE_SYSTEM_NODE_IMAGE', + fallbackRef: 'ghcr.io/boxlite-ai/boxlite-agent-node:20260605-p0-r3', + }, +] + +/** Curated OCI refs a box may boot from. The first entry is the default image. */ +export function supportedImages(): string[] { + return SUPPORTED_IMAGE_SOURCES.map(({ envVar, fallbackRef }) => process.env[envVar] || fallbackRef) +} + +/** + * Validate a user-supplied OCI ref at the request boundary. Undefined selects the + * default image; anything outside the supported set is rejected with the full list so + * callers can self-correct. + */ +export function assertSupportedImage(image: string | undefined): string { + const supported = supportedImages() + + if (image === undefined) { + return supported[0] + } + if (!supported.includes(image)) { + throw new BadRequestError(`Unsupported image '${image}'. Supported images: ${supported.join(', ')}`) + } + return image +} diff --git a/apps/api/src/sandbox/constants/errors-for-recovery.ts b/apps/api/src/box/constants/errors-for-recovery.ts similarity index 100% rename from apps/api/src/sandbox/constants/errors-for-recovery.ts rename to apps/api/src/box/constants/errors-for-recovery.ts diff --git a/apps/api/src/sandbox/constants/runner-events.ts b/apps/api/src/box/constants/runner-events.ts similarity index 100% rename from apps/api/src/sandbox/constants/runner-events.ts rename to apps/api/src/box/constants/runner-events.ts diff --git a/apps/api/src/sandbox/constants/runner-name-regex.constant.ts b/apps/api/src/box/constants/runner-name-regex.constant.ts similarity index 100% rename from apps/api/src/sandbox/constants/runner-name-regex.constant.ts rename to apps/api/src/box/constants/runner-name-regex.constant.ts diff --git a/apps/api/src/sandbox/constants/volume-events.ts b/apps/api/src/box/constants/volume-events.ts similarity index 100% rename from apps/api/src/sandbox/constants/volume-events.ts rename to apps/api/src/box/constants/volume-events.ts diff --git a/apps/api/src/sandbox/constants/warmpool-events.constants.ts b/apps/api/src/box/constants/warmpool-events.constants.ts similarity index 100% rename from apps/api/src/sandbox/constants/warmpool-events.constants.ts rename to apps/api/src/box/constants/warmpool-events.constants.ts diff --git a/apps/api/src/box/controllers/box.controller.ts b/apps/api/src/box/controllers/box.controller.ts new file mode 100644 index 000000000..c70d76627 --- /dev/null +++ b/apps/api/src/box/controllers/box.controller.ts @@ -0,0 +1,901 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { + Controller, + Get, + Post, + Delete, + Body, + Param, + Query, + Logger, + UseGuards, + HttpCode, + UseInterceptors, + Put, +} from '@nestjs/common' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { BoxService } from '../services/box.service' +import { + ApiOAuth2, + ApiResponse, + ApiQuery, + ApiOperation, + ApiParam, + ApiTags, + ApiHeader, + ApiBearerAuth, +} from '@nestjs/swagger' +import { BoxDto, BoxLabelsDto } from '../dto/box.dto' +import { ResizeBoxDto } from '../dto/resize-box.dto' +import { UpdateBoxStateDto } from '../dto/update-box-state.dto' +import { PaginatedBoxesDto } from '../dto/paginated-boxes.dto' +import { RunnerService } from '../services/runner.service' +import { RunnerAuthGuard } from '../../auth/runner-auth.guard' +import { RunnerContextDecorator } from '../../common/decorators/runner-context.decorator' +import { RunnerContext } from '../../common/interfaces/runner-context.interface' +import { BoxState } from '../enums/box-state.enum' +import { Box } from '../entities/box.entity' +import { ContentTypeInterceptor } from '../../common/interceptors/content-type.interceptors' +import { BoxAccessGuard } from '../guards/box-access.guard' +import { CustomHeaders } from '../../common/constants/header.constants' +import { AuthContext } from '../../common/decorators/auth-context.decorator' +import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' +import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' +import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' +import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' +import { PortPreviewUrlDto, SignedPortPreviewUrlDto } from '../dto/port-preview-url.dto' +import { BadRequestError } from '../../exceptions/bad-request.exception' +import { BoxStateUpdatedEvent } from '../events/box-state-updated.event' +import { Audit, TypedRequest } from '../../audit/decorators/audit.decorator' +import { AuditAction } from '../../audit/enums/audit-action.enum' +import { AuditTarget } from '../../audit/enums/audit-target.enum' +// import { UpdateBoxNetworkSettingsDto } from '../dto/update-box-network-settings.dto' +import { SshAccessDto, SshAccessValidationDto } from '../dto/ssh-access.dto' +import { ListBoxesQueryDto } from '../dto/list-boxes-query.dto' +import { ProxyGuard } from '../guards/proxy.guard' +import { OrGuard } from '../../auth/or.guard' +import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' +import { SkipThrottle } from '@nestjs/throttler' +import { ThrottlerScope } from '../../common/decorators/throttler-scope.decorator' +import { SshGatewayGuard } from '../guards/ssh-gateway.guard' +import { ToolboxProxyUrlDto } from '../dto/toolbox-proxy-url.dto' +import { InjectRedis } from '@nestjs-modules/ioredis' +import { Redis } from 'ioredis' +import { BOX_EVENT_CHANNEL } from '../../common/constants/constants' +import { RequireFlagsEnabled } from '@openfeature/nestjs-sdk' +import { FeatureFlags } from '../../common/constants/feature-flags' +import { RegionBoxAccessGuard } from '../guards/region-box-access.guard' + +@ApiTags('box') +@Controller('box') +@ApiHeader(CustomHeaders.ORGANIZATION_ID) +@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard) +@ApiOAuth2(['openid', 'profile', 'email']) +@ApiBearerAuth() +export class BoxController { + private readonly logger = new Logger(BoxController.name) + private readonly boxCallbacks: Map void> = new Map() + private readonly redisSubscriber: Redis + constructor( + private readonly runnerService: RunnerService, + private readonly boxService: BoxService, + @InjectRedis() private readonly redis: Redis, + ) { + this.redisSubscriber = this.redis.duplicate() + this.redisSubscriber.subscribe(BOX_EVENT_CHANNEL) + this.redisSubscriber.on('message', (channel, message) => { + if (channel !== BOX_EVENT_CHANNEL) { + return + } + + try { + const event = JSON.parse(message) as BoxStateUpdatedEvent + this.handleBoxStateUpdated(event) + } catch (error) { + this.logger.error('Failed to parse box state updated event:', error) + return + } + }) + } + + @Get() + @ApiOperation({ + summary: 'List all boxes', + operationId: 'listBoxes', + }) + @ApiResponse({ + status: 200, + description: 'List of all boxes', + type: [BoxDto], + }) + @ApiQuery({ + name: 'verbose', + required: false, + type: Boolean, + description: 'Include verbose output', + }) + @ApiQuery({ + name: 'labels', + type: String, + required: false, + example: '{"label1": "value1", "label2": "value2"}', + description: 'JSON encoded labels to filter by', + }) + @ApiQuery({ + name: 'includeErroredDeleted', + required: false, + type: Boolean, + description: 'Include errored and deleted boxes', + }) + async listBoxes( + @AuthContext() authContext: OrganizationAuthContext, + @Query('verbose') verbose?: boolean, + @Query('labels') labelsQuery?: string, + @Query('includeErroredDeleted') includeErroredDeleted?: boolean, + ): Promise { + const labels = labelsQuery ? JSON.parse(labelsQuery) : undefined + const boxes = await this.boxService.findAllDeprecated(authContext.organizationId, labels, includeErroredDeleted) + + return this.boxService.toBoxDtos(boxes) + } + + @Get('paginated') + @ApiOperation({ + summary: 'List all boxes paginated', + operationId: 'listBoxesPaginated', + }) + @ApiResponse({ + status: 200, + description: 'Paginated list of all boxes', + type: PaginatedBoxesDto, + }) + async listBoxesPaginated( + @AuthContext() authContext: OrganizationAuthContext, + @Query() queryParams: ListBoxesQueryDto, + ): Promise { + const { + page, + limit, + id, + name, + labels, + includeErroredDeleted: includeErroredDestroyed, + states, + regions, + minCpu, + maxCpu, + minMemoryGiB, + maxMemoryGiB, + minDiskGiB, + maxDiskGiB, + lastEventAfter, + lastEventBefore, + sort: sortField, + order: sortDirection, + } = queryParams + + const result = await this.boxService.findAll( + authContext.organizationId, + page, + limit, + { + id, + name, + labels: labels ? JSON.parse(labels) : undefined, + includeErroredDestroyed, + states, + regionIds: regions, + minCpu, + maxCpu, + minMemoryGiB, + maxMemoryGiB, + minDiskGiB, + maxDiskGiB, + lastEventAfter, + lastEventBefore, + }, + { + field: sortField, + direction: sortDirection, + }, + ) + + return { + items: await this.boxService.toBoxDtos(result.items), + total: result.total, + page: result.page, + totalPages: result.totalPages, + } + } + + @Get('for-runner') + @UseGuards(RunnerAuthGuard) + @ApiOperation({ + summary: 'Get boxes for the authenticated runner', + operationId: 'getBoxesForRunner', + }) + @ApiQuery({ + name: 'states', + required: false, + type: String, + description: 'Comma-separated list of box states to filter by', + }) + @ApiQuery({ + name: 'skipReconcilingBoxes', + required: false, + type: Boolean, + description: 'Skip boxes where state differs from desired state', + }) + @ApiResponse({ + status: 200, + description: 'List of boxes for the authenticated runner', + type: [BoxDto], + }) + async getBoxesForRunner( + @RunnerContextDecorator() runnerContext: RunnerContext, + @Query('states') states?: string, + @Query('skipReconcilingBoxes') skipReconcilingBoxes?: string, + ): Promise { + const stateArray = states + ? states.split(',').map((s) => { + if (!Object.values(BoxState).includes(s as BoxState)) { + throw new BadRequestError(`Invalid box state: ${s}`) + } + return s as BoxState + }) + : undefined + + const skip = skipReconcilingBoxes === 'true' + const boxes = await this.boxService.findByRunnerId(runnerContext.runnerId, stateArray, skip) + + return this.boxService.toBoxDtos(boxes) + } + + @Get(':boxIdOrName') + @ApiOperation({ + summary: 'Get box details', + operationId: 'getBox', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiQuery({ + name: 'verbose', + required: false, + type: Boolean, + description: 'Include verbose output', + }) + @ApiResponse({ + status: 200, + description: 'Box details', + type: BoxDto, + }) + @UseGuards(BoxAccessGuard) + async getBox( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + @Query('verbose') verbose?: boolean, + ): Promise { + const box = await this.boxService.findOneByIdOrName(boxIdOrName, authContext.organizationId) + + return this.boxService.toBoxDto(box) + } + + @Post(':boxIdOrName/recover') + @HttpCode(200) + @SkipThrottle({ authenticated: true }) + @ThrottlerScope('box-lifecycle') + @ApiOperation({ + summary: 'Recover box from error state', + operationId: 'recoverBox', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Recovery initiated', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.RECOVER, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + }) + async recoverBox( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + ): Promise { + const recoveredBox = await this.boxService.recover(boxIdOrName, authContext.organization) + let boxDto = await this.boxService.toBoxDto(recoveredBox) + + if (boxDto.state !== BoxState.STARTED) { + boxDto = await this.waitForBoxStarted(boxDto, 30) + } + + return boxDto + } + + @Post(':boxIdOrName/resize') + @HttpCode(200) + @UseInterceptors(ContentTypeInterceptor) + @SkipThrottle({ authenticated: true }) + @ThrottlerScope('box-lifecycle') + @ApiOperation({ + summary: 'Resize box resources', + operationId: 'resizeBox', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Box has been resized', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @RequireFlagsEnabled({ flags: [{ flagKey: FeatureFlags.BOX_RESIZE, defaultValue: false }] }) + @Audit({ + action: AuditAction.RESIZE, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + body: (req: TypedRequest) => ({ + cpu: req.body?.cpu, + memory: req.body?.memory, + disk: req.body?.disk, + }), + }, + }) + async resizeBox( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Body() resizeBoxDto: ResizeBoxDto, + ): Promise { + const box = await this.boxService.resize(boxIdOrName, resizeBoxDto, authContext.organization) + return this.boxService.toBoxDto(box) + } + + @Put(':boxIdOrName/labels') + @UseInterceptors(ContentTypeInterceptor) + @ApiOperation({ + summary: 'Replace box labels', + operationId: 'replaceLabels', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Labels have been successfully replaced', + type: BoxLabelsDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.REPLACE_LABELS, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + body: (req: TypedRequest) => ({ + labels: req.body?.labels, + }), + }, + }) + async replaceLabels( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Body() labelsDto: BoxLabelsDto, + ): Promise { + const box = await this.boxService.replaceLabels(boxIdOrName, labelsDto.labels, authContext.organizationId) + return this.boxService.toBoxDto(box) + } + + @Put(':boxId/state') + @UseInterceptors(ContentTypeInterceptor) + @ApiOperation({ + summary: 'Update box state', + operationId: 'updateBoxState', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Box state has been successfully updated', + }) + @UseGuards(RunnerAuthGuard) + @UseGuards(BoxAccessGuard) + async updateBoxState(@Param('boxId') boxId: string, @Body() updateStateDto: UpdateBoxStateDto): Promise { + await this.boxService.updateState( + boxId, + updateStateDto.state, + updateStateDto.recoverable, + updateStateDto.errorReason, + ) + } + + @Post(':boxIdOrName/public/:isPublic') + @ApiOperation({ + summary: 'Update public status', + operationId: 'updatePublicStatus', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'isPublic', + description: 'Public status to set', + type: 'boolean', + }) + @ApiResponse({ + status: 200, + description: 'Public status has been successfully updated', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.UPDATE_PUBLIC_STATUS, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + params: (req) => ({ + isPublic: req.params.isPublic, + }), + }, + }) + async updatePublicStatus( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('isPublic') isPublic: boolean, + ): Promise { + const box = await this.boxService.updatePublicStatus(boxIdOrName, isPublic, authContext.organizationId) + return this.boxService.toBoxDto(box) + } + + @Post(':boxId/last-activity') + @ApiOperation({ + summary: 'Update box last activity', + operationId: 'updateLastActivity', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 201, + description: 'Last activity has been updated', + }) + @UseGuards(OrGuard([BoxAccessGuard, ProxyGuard, SshGatewayGuard, RegionBoxAccessGuard])) + async updateLastActivity(@Param('boxId') boxId: string): Promise { + await this.boxService.updateLastActivityAt(boxId, new Date()) + } + + @Post(':boxIdOrName/autostop/:interval') + @ApiOperation({ + summary: 'Set box auto-stop interval', + operationId: 'setAutostopInterval', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'interval', + description: 'Auto-stop interval in minutes (0 to disable)', + type: 'number', + }) + @ApiResponse({ + status: 200, + description: 'Auto-stop interval has been set', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.SET_AUTO_STOP_INTERVAL, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + params: (req) => ({ + interval: req.params.interval, + }), + }, + }) + async setAutostopInterval( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('interval') interval: number, + ): Promise { + const box = await this.boxService.setAutostopInterval(boxIdOrName, interval, authContext.organizationId) + return this.boxService.toBoxDto(box) + } + + @Post(':boxIdOrName/autodelete/:interval') + @ApiOperation({ + summary: 'Set box auto-delete interval', + operationId: 'setAutoDeleteInterval', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'interval', + description: + 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', + type: 'number', + }) + @ApiResponse({ + status: 200, + description: 'Auto-delete interval has been set', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.SET_AUTO_DELETE_INTERVAL, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + params: (req) => ({ + interval: req.params.interval, + }), + }, + }) + async setAutoDeleteInterval( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('interval') interval: number, + ): Promise { + const box = await this.boxService.setAutoDeleteInterval(boxIdOrName, interval, authContext.organizationId) + return this.boxService.toBoxDto(box) + } + + // TODO: Network settings endpoint will not be enabled for now + // @Post(':boxIdOrName/network-settings') + // @ApiOperation({ + // summary: 'Update box network settings', + // operationId: 'updateNetworkSettings', + // }) + // @ApiParam({ + // name: 'boxIdOrName', + // description: 'ID or name of the box', + // type: 'string', + // }) + // @ApiResponse({ + // status: 200, + // description: 'Network settings have been updated', + // type: BoxDto, + // }) + // @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + // @UseGuards(BoxAccessGuard) + // @Audit({ + // action: AuditAction.UPDATE_NETWORK_SETTINGS, + // targetType: AuditTarget.BOX, + // targetIdFromRequest: (req) => req.params.boxIdOrName, + // targetIdFromResult: (result: BoxDto) => result?.id, + // requestMetadata: { + // body: (req: TypedRequest) => ({ + // networkBlockAll: req.body?.networkBlockAll, + // networkAllowList: req.body?.networkAllowList, + // }), + // }, + // }) + // async updateNetworkSettings( + // @AuthContext() authContext: OrganizationAuthContext, + // @Param('boxIdOrName') boxIdOrName: string, + // @Body() networkSettings: UpdateBoxNetworkSettingsDto, + // ): Promise { + // const box = await this.boxService.updateNetworkSettings( + // boxIdOrName, + // networkSettings.networkBlockAll, + // networkSettings.networkAllowList, + // authContext.organizationId, + // ) + // return BoxDto.fromBox(box, '') + // } + + @Get(':boxIdOrName/ports/:port/preview-url') + @ApiOperation({ + summary: 'Get preview URL for a box port', + operationId: 'getPortPreviewUrl', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'port', + description: 'Port number to get preview URL for', + type: 'number', + }) + @ApiResponse({ + status: 200, + description: 'Preview URL for the specified port', + type: PortPreviewUrlDto, + }) + @UseGuards(BoxAccessGuard) + async getPortPreviewUrl( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('port') port: number, + ): Promise { + return this.boxService.getPortPreviewUrl(boxIdOrName, authContext.organizationId, port) + } + + @Get(':boxIdOrName/ports/:port/signed-preview-url') + @ApiOperation({ + summary: 'Get signed preview URL for a box port', + operationId: 'getSignedPortPreviewUrl', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'port', + description: 'Port number to get signed preview URL for', + type: 'integer', + }) + @ApiQuery({ + name: 'expiresInSeconds', + required: false, + type: 'integer', + description: 'Expiration time in seconds (default: 60 seconds)', + }) + @ApiResponse({ + status: 200, + description: 'Signed preview URL for the specified port', + type: SignedPortPreviewUrlDto, + }) + @UseGuards(BoxAccessGuard) + async getSignedPortPreviewUrl( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('port') port: number, + @Query('expiresInSeconds') expiresInSeconds?: number, + ): Promise { + return this.boxService.getSignedPortPreviewUrl(boxIdOrName, authContext.organizationId, port, expiresInSeconds) + } + + @Post(':boxIdOrName/ports/:port/signed-preview-url/:token/expire') + @ApiOperation({ + summary: 'Expire signed preview URL for a box port', + operationId: 'expireSignedPortPreviewUrl', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiParam({ + name: 'port', + description: 'Port number to expire signed preview URL for', + type: 'integer', + }) + @ApiParam({ + name: 'token', + description: 'Token to expire signed preview URL for', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Signed preview URL has been expired', + }) + @UseGuards(BoxAccessGuard) + async expireSignedPortPreviewUrl( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Param('port') port: number, + @Param('token') token: string, + ): Promise { + await this.boxService.expireSignedPreviewUrlToken(boxIdOrName, authContext.organizationId, token, port) + } + + @Post(':boxIdOrName/ssh-access') + @HttpCode(200) + @ApiOperation({ + summary: 'Create SSH access for box', + operationId: 'createSshAccess', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiQuery({ + name: 'expiresInMinutes', + required: false, + type: Number, + description: 'Expiration time in minutes (default: 60)', + }) + @ApiResponse({ + status: 200, + description: 'SSH access has been created', + type: SshAccessDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.CREATE_SSH_ACCESS, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: SshAccessDto) => result?.boxId, + requestMetadata: { + query: (req) => ({ + expiresInMinutes: req.query.expiresInMinutes, + }), + }, + }) + async createSshAccess( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Query('expiresInMinutes') expiresInMinutes?: number, + ): Promise { + return await this.boxService.createSshAccess(boxIdOrName, expiresInMinutes, authContext.organizationId) + } + + @Delete(':boxIdOrName/ssh-access') + @HttpCode(200) + @ApiOperation({ + summary: 'Revoke SSH access for box', + operationId: 'revokeSshAccess', + }) + @ApiParam({ + name: 'boxIdOrName', + description: 'ID or name of the box', + type: 'string', + }) + @ApiQuery({ + name: 'token', + required: false, + type: String, + description: 'SSH access token to revoke. If not provided, all SSH access for the box will be revoked.', + }) + @ApiResponse({ + status: 200, + description: 'SSH access has been revoked', + type: BoxDto, + }) + @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_BOXES]) + @UseGuards(BoxAccessGuard) + @Audit({ + action: AuditAction.REVOKE_SSH_ACCESS, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxIdOrName, + targetIdFromResult: (result: BoxDto) => result?.id, + requestMetadata: { + query: (req) => ({ + token: req.query.token, + }), + }, + }) + async revokeSshAccess( + @AuthContext() authContext: OrganizationAuthContext, + @Param('boxIdOrName') boxIdOrName: string, + @Query('token') token?: string, + ): Promise { + const box = await this.boxService.revokeSshAccess(boxIdOrName, token, authContext.organizationId) + return this.boxService.toBoxDto(box) + } + + @Get('ssh-access/validate') + @ApiOperation({ + summary: 'Validate SSH access for box', + operationId: 'validateSshAccess', + }) + @ApiQuery({ + name: 'token', + required: true, + type: String, + description: 'SSH access token to validate', + }) + @ApiResponse({ + status: 200, + description: 'SSH access validation result', + type: SshAccessValidationDto, + }) + async validateSshAccess(@Query('token') token: string): Promise { + const result = await this.boxService.validateSshAccess(token) + return SshAccessValidationDto.fromValidationResult(result.valid, result.boxId) + } + + @Get(':boxId/toolbox-proxy-url') + @ApiOperation({ + summary: 'Get toolbox proxy URL for a box', + operationId: 'getToolboxProxyUrl', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Toolbox proxy URL for the specified box', + type: ToolboxProxyUrlDto, + }) + @UseGuards(BoxAccessGuard) + async getToolboxProxyUrl(@Param('boxId') boxId: string): Promise { + const url = await this.boxService.getToolboxProxyUrl(boxId) + return new ToolboxProxyUrlDto(url) + } + + // wait up to `timeoutSeconds` for the box to start; if it doesn’t, return current box + private async waitForBoxStarted(box: BoxDto, timeoutSeconds: number): Promise { + let latestBox: Box + const waitForStarted = new Promise((resolve, reject) => { + let timeout: NodeJS.Timeout + const handleStateUpdated = (event: BoxStateUpdatedEvent) => { + if (event.box.id !== box.id) { + return + } + latestBox = event.box + if (event.box.state === BoxState.STARTED) { + this.boxCallbacks.delete(box.id) + clearTimeout(timeout) + resolve(this.boxService.toBoxDto(event.box)) + } + if (event.box.state === BoxState.ERROR) { + this.boxCallbacks.delete(box.id) + clearTimeout(timeout) + reject(new BadRequestError(`Box failed to start: ${event.box.errorReason}`)) + } + } + + this.boxCallbacks.set(box.id, handleStateUpdated) + + timeout = setTimeout(() => { + this.boxCallbacks.delete(box.id) + if (latestBox) { + resolve(this.boxService.toBoxDto(latestBox)) + } else { + resolve(box) + } + }, timeoutSeconds * 1000) + }) + + return waitForStarted + } + + private handleBoxStateUpdated(event: BoxStateUpdatedEvent) { + const callback = this.boxCallbacks.get(event.box.id) + if (callback) { + callback(event) + } + } +} diff --git a/apps/api/src/sandbox/controllers/job.controller.ts b/apps/api/src/box/controllers/job.controller.ts similarity index 100% rename from apps/api/src/sandbox/controllers/job.controller.ts rename to apps/api/src/box/controllers/job.controller.ts diff --git a/apps/api/src/box/controllers/preview.controller.ts b/apps/api/src/box/controllers/preview.controller.ts new file mode 100644 index 000000000..0c2828c7e --- /dev/null +++ b/apps/api/src/box/controllers/preview.controller.ts @@ -0,0 +1,178 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import Redis from 'ioredis' +import { Controller, Get, Param, Logger, NotFoundException, UseGuards, Req } from '@nestjs/common' +import { BoxService } from '../services/box.service' +import { ApiResponse, ApiOperation, ApiParam, ApiTags, ApiOAuth2, ApiBearerAuth } from '@nestjs/swagger' +import { InjectRedis } from '@nestjs-modules/ioredis' +import { CombinedAuthGuard } from '../../auth/combined-auth.guard' +import { OrganizationUserService } from '../../organization/services/organization-user.service' + +@ApiTags('preview') +@Controller('preview') +export class PreviewController { + private readonly logger = new Logger(PreviewController.name) + + constructor( + @InjectRedis() private readonly redis: Redis, + private readonly boxService: BoxService, + private readonly organizationUserService: OrganizationUserService, + ) {} + + @Get(':boxId/public') + @ApiOperation({ + summary: 'Check if box is public', + operationId: 'isBoxPublic', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Public status of the box', + type: Boolean, + }) + async isBoxPublic(@Param('boxId') boxId: string): Promise { + const cached = await this.redis.get(`preview:public:${boxId}`) + if (cached) { + if (cached === '1') { + return true + } + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + try { + const isPublic = await this.boxService.isBoxPublic(boxId) + // for private boxes, throw 404 as well + // to prevent using the method to check if a box exists + if (!isPublic) { + // cache the result for 3 seconds to avoid unnecessary requests to the database + await this.redis.setex(`preview:public:${boxId}`, 3, '0') + + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + // cache the result for 3 seconds to avoid unnecessary requests to the database + await this.redis.setex(`preview:public:${boxId}`, 3, '1') + return true + } catch (ex) { + if (ex instanceof NotFoundException) { + // cache the not found box as well + // as it is the same case as for the private boxes + await this.redis.setex(`preview:public:${boxId}`, 3, '0') + throw ex + } + throw ex + } + } + + @Get(':boxId/validate/:authToken') + @ApiOperation({ + summary: 'Check if box auth token is valid', + operationId: 'isValidAuthToken', + }) + @ApiParam({ + name: 'boxId', + description: 'ID of the box', + type: 'string', + }) + @ApiParam({ + name: 'authToken', + description: 'Auth token of the box', + type: 'string', + }) + @ApiResponse({ + status: 200, + description: 'Box auth token validation status', + type: Boolean, + }) + async isValidAuthToken(@Param('boxId') boxId: string, @Param('authToken') authToken: string): Promise { + const cached = await this.redis.get(`preview:token:${boxId}:${authToken}`) + if (cached) { + if (cached === '1') { + return true + } + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + const box = await this.boxService.findOne(boxId) + if (!box) { + await this.redis.setex(`preview:token:${boxId}:${authToken}`, 3, '0') + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + if (box.authToken === authToken) { + await this.redis.setex(`preview:token:${boxId}:${authToken}`, 3, '1') + return true + } + await this.redis.setex(`preview:token:${boxId}:${authToken}`, 3, '0') + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + @Get(':boxId/access') + @ApiOperation({ + summary: 'Check if user has access to the box', + operationId: 'hasBoxAccess', + }) + @ApiResponse({ + status: 200, + description: 'User access status to the box', + type: Boolean, + }) + @UseGuards(CombinedAuthGuard) + @ApiOAuth2(['openid', 'profile', 'email']) + @ApiBearerAuth() + async hasBoxAccess(@Req() req: Request, @Param('boxId') boxId: string): Promise { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const userId = req.user?.userId + + const cached = await this.redis.get(`preview:access:${boxId}:${userId}`) + if (cached) { + if (cached === '1') { + return true + } + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + const box = await this.boxService.findOne(boxId) + const hasAccess = await this.organizationUserService.exists(box.organizationId, userId) + if (!hasAccess) { + await this.redis.setex(`preview:access:${boxId}:${userId}`, 3, '0') + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + // if user has access, keep it in cache longer + await this.redis.setex(`preview:access:${boxId}:${userId}`, 30, '1') + return true + } + + @Get(':signedPreviewToken/:port/box-id') + @ApiOperation({ + summary: 'Get box ID from signed preview URL token', + operationId: 'getBoxIdFromSignedPreviewUrlToken', + }) + @ApiParam({ + name: 'signedPreviewToken', + description: 'Signed preview URL token', + type: 'string', + }) + @ApiParam({ + name: 'port', + description: 'Port number to get box ID from signed preview URL token', + type: 'number', + }) + @ApiResponse({ + status: 200, + description: 'Box ID from signed preview URL token', + type: String, + }) + async getBoxIdFromSignedPreviewUrlToken( + @Param('signedPreviewToken') signedPreviewToken: string, + @Param('port') port: number, + ): Promise { + return this.boxService.getBoxIdFromSignedPreviewUrlToken(signedPreviewToken, port) + } +} diff --git a/apps/api/src/sandbox/controllers/runner.controller.ts b/apps/api/src/box/controllers/runner.controller.ts similarity index 90% rename from apps/api/src/sandbox/controllers/runner.controller.ts rename to apps/api/src/box/controllers/runner.controller.ts index 24cb5112b..95854fdc3 100644 --- a/apps/api/src/sandbox/controllers/runner.controller.ts +++ b/apps/api/src/box/controllers/runner.controller.ts @@ -12,7 +12,6 @@ import { Param, Patch, UseGuards, - Query, Delete, HttpCode, NotFoundException, @@ -21,22 +20,12 @@ import { } from '@nestjs/common' import { CreateRunnerDto } from '../dto/create-runner.dto' import { RunnerService } from '../services/runner.service' -import { - ApiOAuth2, - ApiTags, - ApiOperation, - ApiBearerAuth, - ApiResponse, - ApiQuery, - ApiParam, - ApiHeader, -} from '@nestjs/swagger' +import { ApiOAuth2, ApiTags, ApiOperation, ApiBearerAuth, ApiResponse, ApiParam, ApiHeader } from '@nestjs/swagger' import { SystemActionGuard } from '../../auth/system-action.guard' import { RequiredApiRole } from '../../common/decorators/required-role.decorator' import { SystemRole } from '../../user/enums/system-role.enum' import { ProxyGuard } from '../guards/proxy.guard' import { RunnerDto } from '../dto/runner.dto' -import { RunnerSnapshotDto } from '../dto/runner-snapshot.dto' import { Audit, TypedRequest } from '../../audit/decorators/audit.decorator' import { AuditAction } from '../../audit/enums/audit-action.enum' import { AuditTarget } from '../../audit/enums/audit-target.enum' @@ -56,7 +45,7 @@ import { RequiredOrganizationResourcePermissions } from '../../organization/deco import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' import { CreateRunnerResponseDto } from '../dto/create-runner-response.dto' -import { RegionSandboxAccessGuard } from '../guards/region-sandbox-access.guard' +import { RegionBoxAccessGuard } from '../guards/region-box-access.guard' import { RunnerFullDto } from '../dto/runner-full.dto' import { RegionType } from '../../region/enums/region-type.enum' import { RegionService } from '../../region/services/region.service' @@ -140,20 +129,20 @@ export class RunnerController { return this.runnerService.findOneFullOrFail(runnerContext.runnerId) } - @Get('/by-sandbox/:sandboxId') + @Get('/by-box/:boxId') @HttpCode(200) @ApiOperation({ - summary: 'Get runner by sandbox ID', - operationId: 'getRunnerBySandboxId', + summary: 'Get runner by box ID', + operationId: 'getRunnerByBoxId', }) @ApiResponse({ status: 200, type: RunnerFullDto, }) - @UseGuards(OrGuard([SystemActionGuard, ProxyGuard, SshGatewayGuard, RegionSandboxAccessGuard])) + @UseGuards(OrGuard([SystemActionGuard, ProxyGuard, SshGatewayGuard, RegionBoxAccessGuard])) @RequiredApiRole([SystemRole.ADMIN]) - async getRunnerBySandboxId(@Param('sandboxId') sandboxId: string): Promise { - const runner = await this.runnerService.findBySandboxId(sandboxId) + async getRunnerByBoxId(@Param('boxId') boxId: string): Promise { + const runner = await this.runnerService.findByBoxId(boxId) if (!runner) { throw new NotFoundException('Runner not found') @@ -162,28 +151,6 @@ export class RunnerController { return RunnerFullDto.fromRunner(runner) } - @Get('/by-snapshot-ref') - @HttpCode(200) - @ApiOperation({ - summary: 'Get runners by snapshot ref', - operationId: 'getRunnersBySnapshotRef', - }) - @ApiResponse({ - status: 200, - type: [RunnerSnapshotDto], - }) - @ApiQuery({ - name: 'ref', - description: 'Snapshot ref', - type: String, - required: true, - }) - @UseGuards(OrGuard([SystemActionGuard, ProxyGuard, SshGatewayGuard])) - @RequiredApiRole([SystemRole.ADMIN, 'proxy', 'ssh-gateway']) - async getRunnersBySnapshotRef(@Query('ref') ref: string): Promise { - return this.runnerService.getRunnersBySnapshotRef(ref) - } - @Get(':id') @HttpCode(200) @ApiOperation({ diff --git a/apps/api/src/sandbox/controllers/volume.controller.ts b/apps/api/src/box/controllers/volume.controller.ts similarity index 98% rename from apps/api/src/sandbox/controllers/volume.controller.ts rename to apps/api/src/box/controllers/volume.controller.ts index efd67368c..67ef259d6 100644 --- a/apps/api/src/sandbox/controllers/volume.controller.ts +++ b/apps/api/src/box/controllers/volume.controller.ts @@ -149,7 +149,7 @@ export class VolumeController { }) @ApiResponse({ status: 409, - description: 'Volume is in use by one or more sandboxes', + description: 'Volume is in use by one or more boxes', }) @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.DELETE_VOLUMES]) @Audit({ diff --git a/apps/api/src/box/dto/box-lifecycle.dto.spec.ts b/apps/api/src/box/dto/box-lifecycle.dto.spec.ts new file mode 100644 index 000000000..73cb060c0 --- /dev/null +++ b/apps/api/src/box/dto/box-lifecycle.dto.spec.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' +import { BoxDto } from './box.dto' + +jest.mock('uuid', () => ({ + v4: () => '057963b2-60ca-4356-81fc-11503e15f249', +})) + +describe('BoxDto lifecycle policy exposure', () => { + it('does not expose the unsupported auto-archive lifecycle policy', () => { + const box = new Box('us', 'data-loader') + box.organizationId = '057963b2-60ca-4356-81fc-11503e15f249' + box.osUser = 'boxlite' + + const dto = BoxDto.fromBox(box, 'https://proxy.boxlite.dev/toolbox') + + expect(dto).not.toHaveProperty('autoArchiveInterval') + }) +}) diff --git a/apps/api/src/box/dto/box.dto.spec.ts b/apps/api/src/box/dto/box.dto.spec.ts new file mode 100644 index 000000000..e6c1f2916 --- /dev/null +++ b/apps/api/src/box/dto/box.dto.spec.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' +import { BoxDto } from './box.dto' + +describe('BoxDto public identity', () => { + it('exposes a single public id without a legacy boxId alias', () => { + const box = new Box('us', 'data-loader') + box.organizationId = '057963b2-60ca-4356-81fc-11503e15f249' + box.osUser = 'boxlite' + + const dto = BoxDto.fromBox(box, 'https://proxy.boxlite.dev/toolbox') + + expect(dto.id).toBe(box.id) + expect((dto as any).boxId).toBeUndefined() + }) +}) diff --git a/apps/api/src/box/dto/box.dto.ts b/apps/api/src/box/dto/box.dto.ts new file mode 100644 index 000000000..802f22566 --- /dev/null +++ b/apps/api/src/box/dto/box.dto.ts @@ -0,0 +1,316 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' +import { BoxState } from '../enums/box-state.enum' +import { IsEnum, IsOptional } from 'class-validator' +import { Box } from '../entities/box.entity' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { BoxClass } from '../enums/box-class.enum' + +@ApiSchema({ name: 'BoxVolume' }) +export class BoxVolume { + @ApiProperty({ + description: 'The ID of the volume', + example: 'volume123', + }) + volumeId: string + + @ApiProperty({ + description: 'The mount path for the volume', + example: '/data', + }) + mountPath: string + + @ApiPropertyOptional({ + description: + 'Optional subpath within the volume to mount. When specified, only this S3 prefix will be accessible. When omitted, the entire volume is mounted.', + example: 'users/alice', + }) + subpath?: string +} + +@ApiSchema({ name: 'Box' }) +export class BoxDto { + @ApiProperty({ + description: 'The public 12-character Box ID', + example: 'aB3cD4eF5gH6', + }) + id: string + + @ApiProperty({ + description: 'The organization ID of the box', + example: 'organization123', + }) + organizationId: string + + @ApiProperty({ + description: 'The name of the box', + example: 'MyBox', + }) + name: string + + @ApiProperty({ + description: 'The user associated with the project', + example: 'boxlite', + }) + user: string + + @ApiProperty({ + description: 'Environment variables for the box', + type: 'object', + additionalProperties: { type: 'string' }, + example: { NODE_ENV: 'production' }, + }) + env: Record + + @ApiProperty({ + description: 'Labels for the box', + type: 'object', + additionalProperties: { type: 'string' }, + example: { 'boxlite.io/public': 'true' }, + }) + labels: { [key: string]: string } + + @ApiProperty({ + description: 'Whether the box http preview is public', + example: false, + }) + public: boolean + + @ApiProperty({ + description: 'Whether to block all network access for the box', + example: false, + }) + networkBlockAll: boolean + + @ApiPropertyOptional({ + description: 'Comma-separated list of allowed CIDR network addresses for the box', + example: '192.168.1.0/16,10.0.0.0/24', + }) + networkAllowList?: string + + @ApiProperty({ + description: 'The target environment for the box', + example: 'local', + }) + target: string + + @ApiPropertyOptional({ + description: 'The image used for the box', + example: 'boxlite/base', + required: false, + }) + @IsOptional() + image?: string + + @ApiProperty({ + description: 'The CPU quota for the box', + example: 2, + }) + cpu: number + + @ApiProperty({ + description: 'The GPU quota for the box', + example: 0, + }) + gpu: number + + @ApiProperty({ + description: 'The memory quota for the box', + example: 4, + }) + memory: number + + @ApiProperty({ + description: 'The disk quota for the box', + example: 10, + }) + disk: number + + @ApiPropertyOptional({ + description: 'The state of the box', + enum: BoxState, + enumName: 'BoxState', + example: Object.values(BoxState)[0], + required: false, + }) + @IsEnum(BoxState) + @IsOptional() + state?: BoxState + + @ApiPropertyOptional({ + description: 'The desired state of the box', + enum: BoxDesiredState, + enumName: 'BoxDesiredState', + example: Object.values(BoxDesiredState)[0], + required: false, + }) + @IsEnum(BoxDesiredState) + @IsOptional() + desiredState?: BoxDesiredState + + @ApiPropertyOptional({ + description: 'The error reason of the box', + example: 'The box is not running', + required: false, + }) + @IsOptional() + errorReason?: string + + @ApiPropertyOptional({ + description: 'Whether the box error is recoverable.', + example: true, + required: false, + }) + @IsOptional() + recoverable?: boolean + + @ApiPropertyOptional({ + description: 'Auto-stop interval in minutes (0 means disabled)', + example: 30, + required: false, + }) + @IsOptional() + autoStopInterval?: number + + @ApiPropertyOptional({ + description: + 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', + example: 30, + required: false, + }) + @IsOptional() + autoDeleteInterval?: number + + @ApiPropertyOptional({ + description: 'Array of volumes attached to the box', + type: [BoxVolume], + required: false, + }) + @IsOptional() + volumes?: BoxVolume[] + + @ApiPropertyOptional({ + description: 'The creation timestamp of the box', + example: '2024-10-01T12:00:00Z', + required: false, + }) + @IsOptional() + createdAt?: string + + @ApiPropertyOptional({ + description: 'The last update timestamp of the box', + example: '2024-10-01T12:00:00Z', + required: false, + }) + @IsOptional() + updatedAt?: string + + @ApiPropertyOptional({ + description: 'The class of the box', + enum: BoxClass, + example: Object.values(BoxClass)[0], + required: false, + deprecated: true, + }) + @IsEnum(BoxClass) + @IsOptional() + class?: BoxClass + + @ApiPropertyOptional({ + description: 'The version of the daemon running in the box', + example: '1.0.0', + required: false, + }) + @IsOptional() + daemonVersion?: string + + @ApiPropertyOptional({ + description: 'The runner ID of the box', + example: 'runner123', + required: false, + }) + @IsOptional() + runnerId?: string + + @ApiProperty({ + description: 'The toolbox proxy URL for the box', + example: 'https://proxy.app.boxlite.io/toolbox', + }) + toolboxProxyUrl: string + + static fromBox(box: Box, toolboxProxyUrl: string): BoxDto { + return { + id: box.id, + organizationId: box.organizationId, + name: box.name, + target: box.region, + image: box.image, + user: box.osUser, + env: box.env, + cpu: box.cpu, + gpu: box.gpu, + memory: box.mem, + disk: box.disk, + public: box.public, + networkBlockAll: box.networkBlockAll, + networkAllowList: box.networkAllowList, + labels: box.labels, + volumes: box.volumes, + state: this.getBoxState(box), + desiredState: box.desiredState, + errorReason: box.errorReason, + recoverable: box.recoverable, + autoStopInterval: box.autoStopInterval, + autoDeleteInterval: box.autoDeleteInterval, + class: box.class, + createdAt: box.createdAt ? new Date(box.createdAt).toISOString() : undefined, + updatedAt: box.updatedAt ? new Date(box.updatedAt).toISOString() : undefined, + daemonVersion: box.daemonVersion, + runnerId: box.runnerId, + toolboxProxyUrl, + } + } + + private static getBoxState(box: Box): BoxState { + switch (box.state) { + case BoxState.STARTED: + if (box.desiredState === BoxDesiredState.STOPPED) { + return BoxState.STOPPING + } + if (box.desiredState === BoxDesiredState.DESTROYED) { + return BoxState.DESTROYING + } + break + case BoxState.STOPPED: + if (box.desiredState === BoxDesiredState.STARTED) { + return BoxState.STARTING + } + if (box.desiredState === BoxDesiredState.DESTROYED) { + return BoxState.DESTROYING + } + break + case BoxState.UNKNOWN: + if (box.desiredState === BoxDesiredState.STARTED) { + return BoxState.CREATING + } + break + } + return box.state + } +} + +@ApiSchema({ name: 'BoxLabels' }) +export class BoxLabelsDto { + @ApiProperty({ + description: 'Key-value pairs of labels', + example: { environment: 'dev', team: 'backend' }, + type: 'object', + additionalProperties: { type: 'string' }, + }) + labels: { [key: string]: string } +} diff --git a/apps/api/src/box/dto/create-box.dto.ts b/apps/api/src/box/dto/create-box.dto.ts new file mode 100644 index 000000000..b1265fe8f --- /dev/null +++ b/apps/api/src/box/dto/create-box.dto.ts @@ -0,0 +1,162 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { IsEnum, IsObject, IsOptional, IsString, IsNumber, IsBoolean, IsArray } from 'class-validator' +import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' +import { BoxClass } from '../enums/box-class.enum' +import { BoxVolume } from './box.dto' + +@ApiSchema({ name: 'CreateBox' }) +export class CreateBoxDto { + @ApiPropertyOptional({ + description: 'The name of the box. If not provided, the box ID will be used as the name', + example: 'MyBox', + }) + @IsOptional() + @IsString() + name?: string + + @ApiPropertyOptional({ + description: 'The image to use for the box', + example: 'boxlite/base', + }) + @IsOptional() + @IsString() + image?: string + + @ApiPropertyOptional({ + description: 'The user associated with the project', + example: 'boxlite', + }) + @IsOptional() + @IsString() + user?: string + + @ApiPropertyOptional({ + description: 'Environment variables for the box', + type: 'object', + additionalProperties: { type: 'string' }, + example: { NODE_ENV: 'production' }, + }) + @IsOptional() + @IsObject() + env?: { [key: string]: string } + + @ApiPropertyOptional({ + description: 'Labels for the box', + type: 'object', + additionalProperties: { type: 'string' }, + example: { 'boxlite.io/public': 'true' }, + }) + @IsOptional() + @IsObject() + labels?: { [key: string]: string } + + @ApiPropertyOptional({ + description: 'Whether the box http preview is publicly accessible', + example: false, + }) + @IsOptional() + @IsBoolean() + public?: boolean + + @ApiPropertyOptional({ + description: 'Whether to block all network access for the box', + example: false, + }) + @IsOptional() + @IsBoolean() + networkBlockAll?: boolean + + @ApiPropertyOptional({ + description: 'Comma-separated list of allowed CIDR network addresses for the box', + example: '192.168.1.0/16,10.0.0.0/24', + }) + @IsOptional() + @IsString() + networkAllowList?: string + + @ApiPropertyOptional({ + description: 'The box class type', + enum: BoxClass, + example: Object.values(BoxClass)[0], + }) + @IsOptional() + @IsEnum(BoxClass) + class?: BoxClass + + @ApiPropertyOptional({ + description: 'The target (region) where the box will be created', + example: 'us', + }) + @IsOptional() + @IsString() + target?: string + + @ApiPropertyOptional({ + description: 'CPU cores allocated to the box', + example: 2, + type: 'integer', + }) + @IsOptional() + @IsNumber() + cpu?: number + + @ApiPropertyOptional({ + description: 'GPU units allocated to the box', + example: 1, + type: 'integer', + }) + @IsOptional() + @IsNumber() + gpu?: number + + @ApiPropertyOptional({ + description: 'Memory allocated to the box in GB', + example: 1, + type: 'integer', + }) + @IsOptional() + @IsNumber() + memory?: number + + @ApiPropertyOptional({ + description: 'Disk space allocated to the box in GB', + example: 3, + type: 'integer', + }) + @IsOptional() + @IsNumber() + disk?: number + + @ApiPropertyOptional({ + description: 'Auto-stop interval in minutes (0 means disabled)', + example: 30, + type: 'integer', + }) + @IsOptional() + @IsNumber() + autoStopInterval?: number + + @ApiPropertyOptional({ + description: + 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', + example: 30, + type: 'integer', + }) + @IsOptional() + @IsNumber() + autoDeleteInterval?: number + + @ApiPropertyOptional({ + description: 'Array of volumes to attach to the box', + type: [BoxVolume], + required: false, + }) + @IsOptional() + @IsArray() + volumes?: BoxVolume[] +} diff --git a/apps/api/src/sandbox/dto/create-runner-internal.dto.ts b/apps/api/src/box/dto/create-runner-internal.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/create-runner-internal.dto.ts rename to apps/api/src/box/dto/create-runner-internal.dto.ts diff --git a/apps/api/src/sandbox/dto/create-runner-response.dto.ts b/apps/api/src/box/dto/create-runner-response.dto.ts similarity index 94% rename from apps/api/src/sandbox/dto/create-runner-response.dto.ts rename to apps/api/src/box/dto/create-runner-response.dto.ts index eb5a19f6b..09eaaa2b6 100644 --- a/apps/api/src/sandbox/dto/create-runner-response.dto.ts +++ b/apps/api/src/box/dto/create-runner-response.dto.ts @@ -17,7 +17,7 @@ export class CreateRunnerResponseDto { @ApiProperty({ description: 'The API key for the runner', - example: 'dtn_1234567890', + example: 'blk_svc_1234567890', }) apiKey: string diff --git a/apps/api/src/sandbox/dto/create-runner.dto.ts b/apps/api/src/box/dto/create-runner.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/create-runner.dto.ts rename to apps/api/src/box/dto/create-runner.dto.ts diff --git a/apps/api/src/sandbox/dto/create-volume.dto.ts b/apps/api/src/box/dto/create-volume.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/create-volume.dto.ts rename to apps/api/src/box/dto/create-volume.dto.ts diff --git a/apps/api/src/sandbox/dto/download-files.dto.ts b/apps/api/src/box/dto/download-files.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/download-files.dto.ts rename to apps/api/src/box/dto/download-files.dto.ts diff --git a/apps/api/src/box/dto/job-type-map.dto.ts b/apps/api/src/box/dto/job-type-map.dto.ts new file mode 100644 index 000000000..df29f2a59 --- /dev/null +++ b/apps/api/src/box/dto/job-type-map.dto.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { JobType } from '../enums/job-type.enum' +import { ResourceType } from '../enums/resource-type.enum' + +/** + * Type-safe mapping between JobType and its corresponding ResourceType(s) + Payload + * This ensures compile-time safety when creating jobs + * resourceType is an array of allowed ResourceTypes - the user can supply any of them + */ +export interface JobTypeMap { + [JobType.CREATE_BOX]: { + resourceType: [ResourceType.BOX] + } + [JobType.START_BOX]: { + resourceType: [ResourceType.BOX] + } + [JobType.STOP_BOX]: { + resourceType: [ResourceType.BOX] + } + [JobType.DESTROY_BOX]: { + resourceType: [ResourceType.BOX] + } + [JobType.RESIZE_BOX]: { + resourceType: [ResourceType.BOX] + } + [JobType.CREATE_BACKUP]: { + resourceType: [ResourceType.BOX] + } + [JobType.PULL_ARTIFACT]: { + resourceType: [ResourceType.ARTIFACT] + } + [JobType.REMOVE_ARTIFACT]: { + resourceType: [ResourceType.ARTIFACT] + } + [JobType.UPDATE_BOX_NETWORK_SETTINGS]: { + resourceType: [ResourceType.BOX] + } + [JobType.INSPECT_ARTIFACT_IN_REGISTRY]: { + resourceType: [ResourceType.ARTIFACT] + } + [JobType.RECOVER_BOX]: { + resourceType: [ResourceType.BOX] + } +} + +/** + * Helper type to extract the allowed resource types for a given JobType as a union + */ +export type ResourceTypeForJobType = JobTypeMap[T]['resourceType'][number] diff --git a/apps/api/src/sandbox/dto/job.dto.ts b/apps/api/src/box/dto/job.dto.ts similarity index 94% rename from apps/api/src/sandbox/dto/job.dto.ts rename to apps/api/src/box/dto/job.dto.ts index 614b6bc9d..372e0f892 100644 --- a/apps/api/src/sandbox/dto/job.dto.ts +++ b/apps/api/src/box/dto/job.dto.ts @@ -28,7 +28,7 @@ export class JobDto { description: 'The type of the job', enum: JobType, enumName: 'JobType', - example: JobType.CREATE_SANDBOX, + example: JobType.CREATE_BOX, }) @IsEnum(JobType) type: JobType @@ -45,14 +45,14 @@ export class JobDto { @ApiProperty({ description: 'The type of resource this job operates on', enum: ResourceType, - example: ResourceType.SANDBOX, + example: ResourceType.BOX, }) @IsEnum(ResourceType) resourceType: ResourceType @ApiProperty({ - description: 'The ID of the resource this job operates on (sandboxId, snapshotRef, etc.)', - example: 'sandbox123', + description: 'The ID of the resource this job operates on (boxId, etc.)', + example: 'box123', }) @IsString() resourceId: string @@ -75,7 +75,7 @@ export class JobDto { @ApiPropertyOptional({ description: 'Error message if the job failed', - example: 'Failed to create sandbox', + example: 'Failed to create box', }) @IsOptional() @IsString() @@ -181,7 +181,7 @@ export class UpdateJobStatusDto { @ApiPropertyOptional({ description: 'Error message if the job failed', - example: 'Failed to create sandbox', + example: 'Failed to create box', }) @IsOptional() @IsString() diff --git a/apps/api/src/box/dto/list-boxes-query.dto.ts b/apps/api/src/box/dto/list-boxes-query.dto.ts new file mode 100644 index 000000000..14b1c98b7 --- /dev/null +++ b/apps/api/src/box/dto/list-boxes-query.dto.ts @@ -0,0 +1,240 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiSchema } from '@nestjs/swagger' +import { IsBoolean, IsInt, IsOptional, IsString, IsArray, IsEnum, IsDate, Min } from 'class-validator' +import { Type } from 'class-transformer' +import { BoxState } from '../enums/box-state.enum' +import { ToArray } from '../../common/decorators/to-array.decorator' +import { PageNumber } from '../../common/decorators/page-number.decorator' +import { PageLimit } from '../../common/decorators/page-limit.decorator' + +export enum BoxSortField { + ID = 'id', + NAME = 'name', + STATE = 'state', + REGION = 'region', + UPDATED_AT = 'updatedAt', + CREATED_AT = 'createdAt', +} + +export enum BoxSortDirection { + ASC = 'asc', + DESC = 'desc', +} + +export const DEFAULT_BOX_SORT_FIELD = BoxSortField.CREATED_AT +export const DEFAULT_BOX_SORT_DIRECTION = BoxSortDirection.DESC + +const VALID_QUERY_STATES = Object.values(BoxState).filter((state) => state !== BoxState.DESTROYED) + +@ApiSchema({ name: 'ListBoxesQuery' }) +export class ListBoxesQueryDto { + @PageNumber(1) + page = 1 + + @PageLimit(100) + limit = 100 + + @ApiProperty({ + name: 'id', + description: 'Filter by partial Box ID or name match', + required: false, + type: String, + example: 'abc123', + }) + @IsOptional() + @IsString() + id?: string + + @ApiProperty({ + name: 'name', + description: 'Filter by partial name match', + required: false, + type: String, + example: 'abc123', + }) + @IsOptional() + @IsString() + name?: string + + @ApiProperty({ + name: 'labels', + description: 'JSON encoded labels to filter by', + required: false, + type: String, + example: '{"label1": "value1", "label2": "value2"}', + }) + @IsOptional() + @IsString() + labels?: string + + @ApiProperty({ + name: 'includeErroredDeleted', + description: 'Include results with errored state and deleted desired state', + required: false, + type: Boolean, + default: false, + }) + @IsOptional() + @Type(() => Boolean) + @IsBoolean() + includeErroredDeleted?: boolean + + @ApiProperty({ + name: 'states', + description: 'List of states to filter by', + required: false, + enum: VALID_QUERY_STATES, + isArray: true, + }) + @IsOptional() + @ToArray() + @IsArray() + @IsEnum(VALID_QUERY_STATES, { + each: true, + message: `each value must be one of the following values: ${VALID_QUERY_STATES.join(', ')}`, + }) + states?: BoxState[] + + @ApiProperty({ + name: 'regions', + description: 'List of regions to filter by', + required: false, + type: [String], + }) + @IsOptional() + @ToArray() + @IsArray() + @IsString({ each: true }) + regions?: string[] + + @ApiProperty({ + name: 'minCpu', + description: 'Minimum CPU', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + minCpu?: number + + @ApiProperty({ + name: 'maxCpu', + description: 'Maximum CPU', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + maxCpu?: number + + @ApiProperty({ + name: 'minMemoryGiB', + description: 'Minimum memory in GiB', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + minMemoryGiB?: number + + @ApiProperty({ + name: 'maxMemoryGiB', + description: 'Maximum memory in GiB', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + maxMemoryGiB?: number + + @ApiProperty({ + name: 'minDiskGiB', + description: 'Minimum disk space in GiB', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + minDiskGiB?: number + + @ApiProperty({ + name: 'maxDiskGiB', + description: 'Maximum disk space in GiB', + required: false, + type: Number, + minimum: 1, + }) + @IsOptional() + @Type(() => Number) + @IsInt() + @Min(1) + maxDiskGiB?: number + + @ApiProperty({ + name: 'lastEventAfter', + description: 'Include items with last event after this timestamp', + required: false, + type: String, + format: 'date-time', + example: '2024-01-01T00:00:00Z', + }) + @IsOptional() + @Type(() => Date) + @IsDate() + lastEventAfter?: Date + + @ApiProperty({ + name: 'lastEventBefore', + description: 'Include items with last event before this timestamp', + required: false, + type: String, + format: 'date-time', + example: '2024-12-31T23:59:59Z', + }) + @IsOptional() + @Type(() => Date) + @IsDate() + lastEventBefore?: Date + + @ApiProperty({ + name: 'sort', + description: 'Field to sort by', + required: false, + enum: BoxSortField, + default: DEFAULT_BOX_SORT_FIELD, + }) + @IsOptional() + @IsEnum(BoxSortField) + sort = DEFAULT_BOX_SORT_FIELD + + @ApiProperty({ + name: 'order', + description: 'Direction to sort by', + required: false, + enum: BoxSortDirection, + default: DEFAULT_BOX_SORT_DIRECTION, + }) + @IsOptional() + @IsEnum(BoxSortDirection) + order = DEFAULT_BOX_SORT_DIRECTION +} diff --git a/apps/api/src/sandbox/dto/lsp.dto.ts b/apps/api/src/box/dto/lsp.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/lsp.dto.ts rename to apps/api/src/box/dto/lsp.dto.ts diff --git a/apps/api/src/box/dto/paginated-boxes.dto.ts b/apps/api/src/box/dto/paginated-boxes.dto.ts new file mode 100644 index 000000000..58314b521 --- /dev/null +++ b/apps/api/src/box/dto/paginated-boxes.dto.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiSchema } from '@nestjs/swagger' +import { BoxDto } from './box.dto' + +@ApiSchema({ name: 'PaginatedBoxes' }) +export class PaginatedBoxesDto { + @ApiProperty({ type: [BoxDto] }) + items: BoxDto[] + + @ApiProperty() + total: number + + @ApiProperty() + page: number + + @ApiProperty() + totalPages: number +} diff --git a/apps/api/src/sandbox/dto/port-preview-url.dto.ts b/apps/api/src/box/dto/port-preview-url.dto.ts similarity index 87% rename from apps/api/src/sandbox/dto/port-preview-url.dto.ts rename to apps/api/src/box/dto/port-preview-url.dto.ts index 758be7b58..727c373c5 100644 --- a/apps/api/src/sandbox/dto/port-preview-url.dto.ts +++ b/apps/api/src/box/dto/port-preview-url.dto.ts @@ -10,15 +10,15 @@ import { IsNumber, IsString } from 'class-validator' @ApiSchema({ name: 'PortPreviewUrl' }) export class PortPreviewUrlDto { @ApiProperty({ - description: 'ID of the sandbox', + description: 'ID of the box', example: '123456', }) @IsString() - sandboxId: string + boxId: string @ApiProperty({ description: 'Preview url', - example: 'https://{port}-{sandboxId}.{proxyDomain}', + example: 'https://{port}-{boxId}.{proxyDomain}', }) @IsString() url: string @@ -34,11 +34,11 @@ export class PortPreviewUrlDto { @ApiSchema({ name: 'SignedPortPreviewUrl' }) export class SignedPortPreviewUrlDto { @ApiProperty({ - description: 'ID of the sandbox', + description: 'ID of the box', example: '123456', }) @IsString() - sandboxId: string + boxId: string @ApiProperty({ description: 'Port number of the signed preview URL', diff --git a/apps/api/src/box/dto/resize-box.dto.ts b/apps/api/src/box/dto/resize-box.dto.ts new file mode 100644 index 000000000..aee23c886 --- /dev/null +++ b/apps/api/src/box/dto/resize-box.dto.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { IsOptional, IsNumber, Min } from 'class-validator' +import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' + +@ApiSchema({ name: 'ResizeBox' }) +export class ResizeBoxDto { + @ApiPropertyOptional({ + description: 'CPU cores to allocate to the box (minimum: 1)', + example: 2, + type: 'integer', + minimum: 1, + }) + @IsOptional() + @IsNumber() + @Min(1) + cpu?: number + + @ApiPropertyOptional({ + description: 'Memory in GB to allocate to the box (minimum: 1)', + example: 4, + type: 'integer', + minimum: 1, + }) + @IsOptional() + @IsNumber() + @Min(1) + memory?: number + + @ApiPropertyOptional({ + description: 'Disk space in GB to allocate to the box (can only be increased)', + example: 20, + type: 'integer', + minimum: 1, + }) + @IsOptional() + @IsNumber() + @Min(1) + disk?: number +} diff --git a/apps/api/src/sandbox/dto/runner-full.dto.ts b/apps/api/src/box/dto/runner-full.dto.ts similarity index 96% rename from apps/api/src/sandbox/dto/runner-full.dto.ts rename to apps/api/src/box/dto/runner-full.dto.ts index c2d4ba8b3..f6446c53e 100644 --- a/apps/api/src/sandbox/dto/runner-full.dto.ts +++ b/apps/api/src/box/dto/runner-full.dto.ts @@ -14,7 +14,7 @@ import { RegionType } from '../../region/enums/region-type.enum' export class RunnerFullDto extends RunnerDto { @ApiProperty({ description: 'The API key for the runner', - example: 'dtn_1234567890', + example: 'blk_svc_1234567890', }) apiKey: string diff --git a/apps/api/src/sandbox/dto/runner-health.dto.ts b/apps/api/src/box/dto/runner-health.dto.ts similarity index 93% rename from apps/api/src/sandbox/dto/runner-health.dto.ts rename to apps/api/src/box/dto/runner-health.dto.ts index 273def7b4..e3347241a 100644 --- a/apps/api/src/sandbox/dto/runner-health.dto.ts +++ b/apps/api/src/box/dto/runner-health.dto.ts @@ -60,18 +60,11 @@ export class RunnerHealthMetricsDto { currentAllocatedDiskGiB: number @ApiProperty({ - description: 'Number of snapshots currently stored', - example: 5, - }) - @IsNumber() - currentSnapshotCount: number - - @ApiProperty({ - description: 'Number of started sandboxes', + description: 'Number of started boxes', example: 10, }) @IsNumber() - currentStartedSandboxes: number + currentStartedBoxes: number @ApiProperty({ description: 'Total CPU cores on the runner', diff --git a/apps/api/src/sandbox/dto/runner.dto.ts b/apps/api/src/box/dto/runner.dto.ts similarity index 91% rename from apps/api/src/sandbox/dto/runner.dto.ts rename to apps/api/src/box/dto/runner.dto.ts index 8718b5b7a..8bfe2ddb9 100644 --- a/apps/api/src/sandbox/dto/runner.dto.ts +++ b/apps/api/src/box/dto/runner.dto.ts @@ -7,7 +7,7 @@ import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' import { IsEnum, IsOptional } from 'class-validator' import { Runner } from '../entities/runner.entity' -import { SandboxClass } from '../enums/sandbox-class.enum' +import { BoxClass } from '../enums/box-class.enum' import { RunnerState } from '../enums/runner-state.enum' @ApiSchema({ name: 'Runner' }) @@ -77,12 +77,12 @@ export class RunnerDto { @ApiProperty({ description: 'The class of the runner', - enum: SandboxClass, - enumName: 'SandboxClass', - example: SandboxClass.SMALL, + enum: BoxClass, + enumName: 'BoxClass', + example: BoxClass.SMALL, }) - @IsEnum(SandboxClass) - class: SandboxClass + @IsEnum(BoxClass) + class: BoxClass @ApiPropertyOptional({ description: 'Current CPU usage percentage', @@ -121,16 +121,10 @@ export class RunnerDto { currentAllocatedDiskGiB: number @ApiPropertyOptional({ - description: 'Current snapshot count', - example: 12, - }) - currentSnapshotCount: number - - @ApiPropertyOptional({ - description: 'Current number of started sandboxes', + description: 'Current number of started boxes', example: 5, }) - currentStartedSandboxes: number + currentStartedBoxes: number @ApiPropertyOptional({ description: 'Runner availability score', @@ -225,8 +219,7 @@ export class RunnerDto { currentAllocatedCpu: runner.currentAllocatedCpu, currentAllocatedMemoryGiB: runner.currentAllocatedMemoryGiB, currentAllocatedDiskGiB: runner.currentAllocatedDiskGiB, - currentSnapshotCount: runner.currentSnapshotCount, - currentStartedSandboxes: runner.currentStartedSandboxes, + currentStartedBoxes: runner.currentStartedBoxes, availabilityScore: runner.availabilityScore, region: runner.region, name: runner.name, diff --git a/apps/api/src/sandbox/dto/ssh-access.dto.ts b/apps/api/src/box/dto/ssh-access.dto.ts similarity index 86% rename from apps/api/src/sandbox/dto/ssh-access.dto.ts rename to apps/api/src/box/dto/ssh-access.dto.ts index 109ed1580..faa7553ed 100644 --- a/apps/api/src/sandbox/dto/ssh-access.dto.ts +++ b/apps/api/src/box/dto/ssh-access.dto.ts @@ -15,10 +15,10 @@ export class SshAccessDto { id: string @ApiProperty({ - description: 'ID of the sandbox this SSH access is for', + description: 'ID of the box this SSH access is for', example: '123e4567-e89b-12d3-a456-426614174000', }) - sandboxId: string + boxId: string @ApiProperty({ description: 'SSH access token', @@ -45,7 +45,7 @@ export class SshAccessDto { updatedAt: Date @ApiProperty({ - description: 'SSH command to connect to the sandbox', + description: 'SSH command to connect to the box', example: 'ssh -p 2222 token@localhost', }) sshCommand: string @@ -53,7 +53,7 @@ export class SshAccessDto { static fromSshAccess(sshAccess: SshAccess, sshGatewayUrl: string): SshAccessDto { const dto = new SshAccessDto() dto.id = sshAccess.id - dto.sandboxId = sshAccess.sandboxId + dto.boxId = sshAccess.boxId dto.token = sshAccess.token dto.expiresAt = sshAccess.expiresAt dto.createdAt = sshAccess.createdAt @@ -97,25 +97,25 @@ export class SshAccessValidationDto { valid: boolean @ApiProperty({ - description: 'ID of the sandbox this SSH access is for', + description: 'ID of the box this SSH access is for', example: '123e4567-e89b-12d3-a456-426614174000', }) - sandboxId: string + boxId: string - static fromValidationResult(valid: boolean, sandboxId: string): SshAccessValidationDto { + static fromValidationResult(valid: boolean, boxId: string): SshAccessValidationDto { const dto = new SshAccessValidationDto() dto.valid = valid - dto.sandboxId = sandboxId + dto.boxId = boxId return dto } } export class RevokeSshAccessDto { @ApiProperty({ - description: 'ID of the sandbox', + description: 'ID of the box', example: '123e4567-e89b-12d3-a456-426614174000', }) - sandboxId: string + boxId: string @ApiProperty({ description: 'SSH access token to revoke', diff --git a/apps/api/src/sandbox/dto/storage-access-dto.ts b/apps/api/src/box/dto/storage-access-dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/storage-access-dto.ts rename to apps/api/src/box/dto/storage-access-dto.ts diff --git a/apps/api/src/sandbox/dto/toolbox-proxy-url.dto.ts b/apps/api/src/box/dto/toolbox-proxy-url.dto.ts similarity index 87% rename from apps/api/src/sandbox/dto/toolbox-proxy-url.dto.ts rename to apps/api/src/box/dto/toolbox-proxy-url.dto.ts index 5dfd3ad28..3cc632ae7 100644 --- a/apps/api/src/sandbox/dto/toolbox-proxy-url.dto.ts +++ b/apps/api/src/box/dto/toolbox-proxy-url.dto.ts @@ -9,7 +9,7 @@ import { ApiProperty, ApiSchema } from '@nestjs/swagger' @ApiSchema({ name: 'ToolboxProxyUrl' }) export class ToolboxProxyUrlDto { @ApiProperty({ - description: 'The toolbox proxy URL for the sandbox', + description: 'The toolbox proxy URL for the box', example: 'https://proxy.app.boxlite.io/toolbox', }) url: string diff --git a/apps/api/src/box/dto/update-box-network-settings.dto.ts b/apps/api/src/box/dto/update-box-network-settings.dto.ts new file mode 100644 index 000000000..b04604d79 --- /dev/null +++ b/apps/api/src/box/dto/update-box-network-settings.dto.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { IsOptional, IsString, IsBoolean } from 'class-validator' +import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' + +@ApiSchema({ name: 'UpdateBoxNetworkSettings' }) +export class UpdateBoxNetworkSettingsDto { + @ApiPropertyOptional({ + description: 'Whether to block all network access for the box', + example: false, + }) + @IsOptional() + @IsBoolean() + networkBlockAll?: boolean + + @ApiPropertyOptional({ + description: 'Comma-separated list of allowed CIDR network addresses for the box', + example: '192.168.1.0/16,10.0.0.0/24', + }) + @IsOptional() + @IsString() + networkAllowList?: string +} diff --git a/apps/api/src/box/dto/update-box-state.dto.ts b/apps/api/src/box/dto/update-box-state.dto.ts new file mode 100644 index 000000000..a2ee0438b --- /dev/null +++ b/apps/api/src/box/dto/update-box-state.dto.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' +import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' +import { BoxState } from '../enums/box-state.enum' + +export class UpdateBoxStateDto { + @IsEnum(BoxState) + @ApiProperty({ + description: 'The new state for the box', + enum: BoxState, + example: BoxState.STARTED, + }) + state: BoxState + + @IsOptional() + @IsString() + @ApiPropertyOptional({ + description: 'Optional error message when reporting an error state', + example: 'Failed to pull artifact image', + }) + errorReason?: string + + @IsOptional() + @IsBoolean() + @ApiPropertyOptional({ + description: 'Whether the box is recoverable', + example: true, + }) + recoverable?: boolean +} diff --git a/apps/api/src/sandbox/dto/upload-file.dto.ts b/apps/api/src/box/dto/upload-file.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/upload-file.dto.ts rename to apps/api/src/box/dto/upload-file.dto.ts diff --git a/apps/api/src/sandbox/dto/volume.dto.ts b/apps/api/src/box/dto/volume.dto.ts similarity index 100% rename from apps/api/src/sandbox/dto/volume.dto.ts rename to apps/api/src/box/dto/volume.dto.ts diff --git a/apps/api/src/box/entities/box-last-activity.entity.ts b/apps/api/src/box/entities/box-last-activity.entity.ts new file mode 100644 index 000000000..905f79c30 --- /dev/null +++ b/apps/api/src/box/entities/box-last-activity.entity.ts @@ -0,0 +1,20 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm' +import { Box } from './box.entity' + +@Entity('box_last_activity') +export class BoxLastActivity { + @PrimaryColumn({ name: 'boxId' }) + boxId: string + + @Column({ nullable: true, type: 'timestamp with time zone' }) + lastActivityAt?: Date + + @OneToOne(() => Box, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'boxId' }) + box?: Box +} diff --git a/apps/api/src/box/entities/box.entity.spec.ts b/apps/api/src/box/entities/box.entity.spec.ts new file mode 100644 index 000000000..5f87b4b88 --- /dev/null +++ b/apps/api/src/box/entities/box.entity.spec.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BOX_ID_LENGTH, BOX_ID_REGEX } from '../utils/box-id.util' +import { Box } from './box.entity' + +describe('Box entity public identity', () => { + it('mints a single 12-character public id as the primary identity', () => { + const box = new Box('us', 'data-loader') + + expect(box.id).toHaveLength(BOX_ID_LENGTH) + expect(box.id).toMatch(BOX_ID_REGEX) + expect((box as any).boxId).toBeUndefined() + expect(box.name).toBe('data-loader') + }) +}) diff --git a/apps/api/src/box/entities/box.entity.ts b/apps/api/src/box/entities/box.entity.ts new file mode 100644 index 000000000..460a37085 --- /dev/null +++ b/apps/api/src/box/entities/box.entity.ts @@ -0,0 +1,276 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Column, CreateDateColumn, Entity, Index, PrimaryColumn, OneToOne, Unique, UpdateDateColumn } from 'typeorm' +import { BoxState } from '../enums/box-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { BoxClass } from '../enums/box-class.enum' +import { BoxVolume } from '../dto/box.dto' +import { nanoid } from 'nanoid' +import { BoxLastActivity } from './box-last-activity.entity' +import { BOX_ID_LENGTH, BOX_ID_REGEX, generateBoxId } from '../utils/box-id.util' + +@Entity('box') +@Unique(['organizationId', 'name']) +@Index('box_state_idx', ['state']) +@Index('box_desiredstate_idx', ['desiredState']) +@Index('box_runnerid_idx', ['runnerId']) +@Index('box_runner_state_idx', ['runnerId', 'state']) +@Index('box_organizationid_idx', ['organizationId']) +@Index('box_region_idx', ['region']) +@Index('box_resources_idx', ['cpu', 'mem', 'disk', 'gpu']) +@Index('box_runner_state_desired_idx', ['runnerId', 'state', 'desiredState'], { + where: '"pending" = false', +}) +@Index('box_active_only_idx', ['id'], { + where: `"state" <> ALL (ARRAY['destroyed'::box_state_enum, 'archived'::box_state_enum])`, +}) +@Index('box_pending_idx', ['id'], { + where: `"pending" = true`, +}) +@Index('idx_box_authtoken', ['authToken']) +@Index('box_image_idx', ['image']) +@Index('box_labels_gin_full_idx', { synchronize: false }) +@Index('idx_box_volumes_gin', { synchronize: false }) +export class Box { + @PrimaryColumn({ type: 'character varying', length: BOX_ID_LENGTH }) + id: string + + @Column({ + type: 'uuid', + }) + organizationId: string + + @Column() + name: string + + @Column() + region: string + + @Column({ nullable: true }) + image?: string + + @Column({ + type: 'uuid', + nullable: true, + }) + runnerId?: string + + // this is the runnerId of the runner that was previously assigned to the box + // if something goes wrong with new runner assignment, we can revert to the previous runner + @Column({ + type: 'uuid', + nullable: true, + }) + prevRunnerId?: string + + @Column({ + type: 'enum', + enum: BoxClass, + default: BoxClass.SMALL, + }) + class = BoxClass.SMALL + + @Column({ + type: 'enum', + enum: BoxState, + default: BoxState.UNKNOWN, + }) + state = BoxState.UNKNOWN + + @Column({ + type: 'enum', + enum: BoxDesiredState, + default: BoxDesiredState.STARTED, + }) + desiredState = BoxDesiredState.STARTED + + @Column() + osUser: string + + @Column({ nullable: true }) + errorReason?: string + + @Column({ default: false, type: 'boolean' }) + recoverable = false + + @Column({ + type: 'jsonb', + default: {}, + }) + env: { [key: string]: string } = {} + + @Column({ default: false, type: 'boolean' }) + public = false + + @Column({ default: false, type: 'boolean' }) + networkBlockAll = false + + @Column({ nullable: true }) + networkAllowList?: string + + @Column('jsonb', { nullable: true }) + labels: { [key: string]: string } + + @Column({ type: 'int', default: 2 }) + cpu = 2 + + @Column({ type: 'int', default: 0 }) + gpu = 0 + + @Column({ type: 'int', default: 4 }) + mem = 4 + + @Column({ type: 'int', default: 10 }) + disk = 10 + + @Column({ + type: 'jsonb', + default: [], + }) + volumes: BoxVolume[] = [] + + @CreateDateColumn({ + type: 'timestamp with time zone', + }) + createdAt: Date + + @UpdateDateColumn({ + type: 'timestamp with time zone', + }) + updatedAt: Date + + @OneToOne(() => BoxLastActivity, (lastActivity) => lastActivity.box) + lastActivityAt?: BoxLastActivity + + // this is the interval in minutes after which the box will be stopped if lastActivityAt is not updated + // if set to 0, auto stop will be disabled + @Column({ default: 15, type: 'int' }) + autoStopInterval: number | undefined = 15 + + // this is the interval in minutes after which a continuously stopped workspace will be automatically deleted + // if set to negative value, auto delete will be disabled + // if set to 0, box will be immediately deleted upon stopping + @Column({ default: -1, type: 'int' }) + autoDeleteInterval: number | undefined = -1 + + @Column({ default: false, type: 'boolean' }) + pending: boolean | undefined = false + + @Column({ type: 'character varying' }) + authToken = nanoid(32).toLowerCase() + + @Column({ nullable: true }) + daemonVersion?: string + + constructor(region: string, name?: string) { + this.id = generateBoxId() + // Set name - use provided name or fallback to ID + this.name = name || this.id + this.region = region + } + + /** + * Helper method that returns the update data needed for a soft delete operation. + */ + static getSoftDeleteUpdate(box: Box): Partial { + return { + pending: true, + desiredState: BoxDesiredState.DESTROYED, + name: 'DESTROYED_' + box.name + '_' + Date.now(), + } + } + + /** + * Asserts that the current entity state is valid. + */ + assertValid(): void { + this.validateBoxId() + this.validateDesiredStateTransition() + } + + private validateBoxId(): void { + if (!BOX_ID_REGEX.test(this.id)) { + throw new Error(`Box has invalid id ${this.id}`) + } + } + + private validateDesiredStateTransition(): void { + switch (this.desiredState) { + case BoxDesiredState.STARTED: + if ( + [ + BoxState.STARTED, + BoxState.STOPPED, + BoxState.STARTING, + BoxState.CREATING, + BoxState.UNKNOWN, + BoxState.RESTORING, + BoxState.ERROR, + BoxState.RESIZING, + ].includes(this.state) + ) { + break + } + throw new Error(`Box ${this.id} is not in a valid state to be started. State: ${this.state}`) + case BoxDesiredState.STOPPED: + if ( + [BoxState.STARTED, BoxState.STOPPING, BoxState.STOPPED, BoxState.ERROR, BoxState.RESIZING].includes( + this.state, + ) + ) { + break + } + throw new Error(`Box ${this.id} is not in a valid state to be stopped. State: ${this.state}`) + case BoxDesiredState.DESTROYED: + if ( + [ + BoxState.DESTROYED, + BoxState.DESTROYING, + BoxState.STOPPED, + BoxState.STARTED, + BoxState.ARCHIVED, + BoxState.ERROR, + BoxState.ARCHIVING, + ].includes(this.state) + ) { + break + } + throw new Error(`Box ${this.id} is not in a valid state to be destroyed. State: ${this.state}`) + } + } + + /** + * Enforces domain invariants on the current entity state. + * + * @returns Additional field changes that invariant enforcement produced. + */ + enforceInvariants(): Partial { + const changes = this.getInvariantChanges() + Object.assign(this, changes) + return changes + } + + private getInvariantChanges(): Partial { + const changes: Partial = {} + + if (!this.pending && String(this.state) !== String(this.desiredState)) { + changes.pending = true + } + if (this.pending && String(this.state) === String(this.desiredState)) { + changes.pending = false + } + if (this.state === BoxState.ERROR) { + changes.pending = false + } + + if (this.state === BoxState.DESTROYED || this.state === BoxState.ARCHIVED) { + changes.runnerId = null + } + + return changes + } +} diff --git a/apps/api/src/sandbox/entities/job.entity.ts b/apps/api/src/box/entities/job.entity.ts similarity index 100% rename from apps/api/src/sandbox/entities/job.entity.ts rename to apps/api/src/box/entities/job.entity.ts diff --git a/apps/api/src/sandbox/entities/runner.entity.ts b/apps/api/src/box/entities/runner.entity.ts similarity index 92% rename from apps/api/src/sandbox/entities/runner.entity.ts rename to apps/api/src/box/entities/runner.entity.ts index d9f5954b5..e1ba1253e 100644 --- a/apps/api/src/sandbox/entities/runner.entity.ts +++ b/apps/api/src/box/entities/runner.entity.ts @@ -5,7 +5,7 @@ */ import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, Unique, UpdateDateColumn } from 'typeorm' -import { SandboxClass } from '../enums/sandbox-class.enum' +import { BoxClass } from '../enums/box-class.enum' import { RunnerState } from '../enums/runner-state.enum' import { RunnerServiceInfo } from '../common/runner-service-info' @@ -64,10 +64,10 @@ export class Runner { @Column({ type: 'enum', - enum: SandboxClass, - default: SandboxClass.SMALL, + enum: BoxClass, + default: BoxClass.SMALL, }) - class: SandboxClass + class: BoxClass @Column({ type: 'float', @@ -114,12 +114,7 @@ export class Runner { @Column({ default: 0, }) - currentSnapshotCount: number - - @Column({ - default: 0, - }) - currentStartedSandboxes: number + currentStartedBoxes: number @Column({ default: 0, @@ -205,7 +200,7 @@ export class Runner { this.domain = params.domain ?? null this.apiUrl = params.apiUrl this.proxyUrl = params.proxyUrl - this.class = SandboxClass.SMALL + this.class = BoxClass.SMALL this.apiVersion = params.apiVersion this.appVersion = params.appVersion ?? null this.gpu = null diff --git a/apps/api/src/sandbox/entities/ssh-access.entity.ts b/apps/api/src/box/entities/ssh-access.entity.ts similarity index 75% rename from apps/api/src/sandbox/entities/ssh-access.entity.ts rename to apps/api/src/box/entities/ssh-access.entity.ts index af2157500..f4a4f511b 100644 --- a/apps/api/src/sandbox/entities/ssh-access.entity.ts +++ b/apps/api/src/box/entities/ssh-access.entity.ts @@ -14,7 +14,7 @@ import { PrimaryColumn, UpdateDateColumn, } from 'typeorm' -import { Sandbox } from './sandbox.entity' +import { Box } from './box.entity' @Entity() export class SshAccess { @@ -22,8 +22,8 @@ export class SshAccess { @Generated('uuid') id: string - @Column() - sandboxId: string + @Column({ name: 'boxId' }) + boxId: string @Column({ type: 'text', @@ -41,7 +41,7 @@ export class SshAccess { @UpdateDateColumn() updatedAt: Date - @ManyToOne(() => Sandbox, { onDelete: 'CASCADE' }) - @JoinColumn({ name: 'sandboxId' }) - sandbox: Sandbox + @ManyToOne(() => Box, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'boxId' }) + box: Box } diff --git a/apps/api/src/sandbox/entities/volume.entity.ts b/apps/api/src/box/entities/volume.entity.ts similarity index 100% rename from apps/api/src/sandbox/entities/volume.entity.ts rename to apps/api/src/box/entities/volume.entity.ts diff --git a/apps/api/src/sandbox/entities/warm-pool.entity.ts b/apps/api/src/box/entities/warm-pool.entity.ts similarity index 77% rename from apps/api/src/sandbox/entities/warm-pool.entity.ts rename to apps/api/src/box/entities/warm-pool.entity.ts index 71bb73d2a..1004515ed 100644 --- a/apps/api/src/sandbox/entities/warm-pool.entity.ts +++ b/apps/api/src/box/entities/warm-pool.entity.ts @@ -5,10 +5,10 @@ */ import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' -import { SandboxClass } from '../enums/sandbox-class.enum' +import { BoxClass } from '../enums/box-class.enum' @Entity() -@Index('warm_pool_find_idx', ['snapshot', 'target', 'class', 'cpu', 'mem', 'disk', 'gpu', 'osUser', 'env']) +@Index('warm_pool_find_idx', ['image', 'target', 'class', 'cpu', 'mem', 'disk', 'gpu', 'osUser', 'env']) export class WarmPool { @PrimaryGeneratedColumn('uuid') id: string @@ -17,7 +17,7 @@ export class WarmPool { pool: number @Column() - snapshot: string + image: string @Column() target: string @@ -39,10 +39,10 @@ export class WarmPool { @Column({ type: 'enum', - enum: SandboxClass, - default: SandboxClass.SMALL, + enum: BoxClass, + default: BoxClass.SMALL, }) - class: SandboxClass + class: BoxClass @Column() osUser: string diff --git a/apps/api/src/box/enums/box-class.enum.ts b/apps/api/src/box/enums/box-class.enum.ts new file mode 100644 index 000000000..a76325276 --- /dev/null +++ b/apps/api/src/box/enums/box-class.enum.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export enum BoxClass { + SMALL = 'small', + MEDIUM = 'medium', + LARGE = 'large', +} + +export const BoxClassData = { + [BoxClass.SMALL]: { + cpu: 4, + memory: 8, + disk: 30, + }, + [BoxClass.MEDIUM]: { + cpu: 8, + memory: 16, + disk: 60, + }, + [BoxClass.LARGE]: { + cpu: 12, + memory: 24, + disk: 90, + }, +} diff --git a/apps/api/src/box/enums/box-desired-state.enum.ts b/apps/api/src/box/enums/box-desired-state.enum.ts new file mode 100644 index 000000000..2655d62b9 --- /dev/null +++ b/apps/api/src/box/enums/box-desired-state.enum.ts @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export enum BoxDesiredState { + DESTROYED = 'destroyed', + STARTED = 'started', + STOPPED = 'stopped', + RESIZED = 'resized', +} diff --git a/apps/api/src/box/enums/box-state.enum.ts b/apps/api/src/box/enums/box-state.enum.ts new file mode 100644 index 000000000..c71a6e376 --- /dev/null +++ b/apps/api/src/box/enums/box-state.enum.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export enum BoxState { + CREATING = 'creating', + RESTORING = 'restoring', + DESTROYED = 'destroyed', + DESTROYING = 'destroying', + STARTED = 'started', + STOPPED = 'stopped', + STARTING = 'starting', + STOPPING = 'stopping', + ERROR = 'error', + UNKNOWN = 'unknown', + ARCHIVED = 'archived', + ARCHIVING = 'archiving', + RESIZING = 'resizing', +} diff --git a/apps/api/src/sandbox/enums/job-status.enum.ts b/apps/api/src/box/enums/job-status.enum.ts similarity index 100% rename from apps/api/src/sandbox/enums/job-status.enum.ts rename to apps/api/src/box/enums/job-status.enum.ts diff --git a/apps/api/src/box/enums/job-type.enum.ts b/apps/api/src/box/enums/job-type.enum.ts new file mode 100644 index 000000000..a4a4e2767 --- /dev/null +++ b/apps/api/src/box/enums/job-type.enum.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export enum JobType { + CREATE_BOX = 'CREATE_BOX', + START_BOX = 'START_BOX', + STOP_BOX = 'STOP_BOX', + DESTROY_BOX = 'DESTROY_BOX', + RESIZE_BOX = 'RESIZE_BOX', + CREATE_BACKUP = 'CREATE_BACKUP', + PULL_ARTIFACT = 'PULL_ARTIFACT', + RECOVER_BOX = 'RECOVER_BOX', + INSPECT_ARTIFACT_IN_REGISTRY = 'INSPECT_ARTIFACT_IN_REGISTRY', + REMOVE_ARTIFACT = 'REMOVE_ARTIFACT', + UPDATE_BOX_NETWORK_SETTINGS = 'UPDATE_BOX_NETWORK_SETTINGS', +} diff --git a/apps/api/src/sandbox/enums/resource-type.enum.ts b/apps/api/src/box/enums/resource-type.enum.ts similarity index 78% rename from apps/api/src/sandbox/enums/resource-type.enum.ts rename to apps/api/src/box/enums/resource-type.enum.ts index 4cf815155..71e0bbddd 100644 --- a/apps/api/src/sandbox/enums/resource-type.enum.ts +++ b/apps/api/src/box/enums/resource-type.enum.ts @@ -5,7 +5,7 @@ */ export enum ResourceType { - SANDBOX = 'SANDBOX', - SNAPSHOT = 'SNAPSHOT', + BOX = 'BOX', + ARTIFACT = 'ARTIFACT', BACKUP = 'BACKUP', } diff --git a/apps/api/src/sandbox/enums/runner-state.enum.ts b/apps/api/src/box/enums/runner-state.enum.ts similarity index 100% rename from apps/api/src/sandbox/enums/runner-state.enum.ts rename to apps/api/src/box/enums/runner-state.enum.ts diff --git a/apps/api/src/sandbox/enums/volume-state.enum.ts b/apps/api/src/box/enums/volume-state.enum.ts similarity index 100% rename from apps/api/src/sandbox/enums/volume-state.enum.ts rename to apps/api/src/box/enums/volume-state.enum.ts diff --git a/apps/api/src/box/errors/box-conflict.error.ts b/apps/api/src/box/errors/box-conflict.error.ts new file mode 100644 index 000000000..938896f1e --- /dev/null +++ b/apps/api/src/box/errors/box-conflict.error.ts @@ -0,0 +1,12 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ConflictException } from '@nestjs/common' + +export class BoxConflictError extends ConflictException { + constructor() { + super('Box was modified by another operation') + } +} diff --git a/apps/api/src/sandbox/errors/job-conflict.error.ts b/apps/api/src/box/errors/job-conflict.error.ts similarity index 100% rename from apps/api/src/sandbox/errors/job-conflict.error.ts rename to apps/api/src/box/errors/job-conflict.error.ts diff --git a/apps/api/src/box/errors/runner-api-error.spec.ts b/apps/api/src/box/errors/runner-api-error.spec.ts new file mode 100644 index 000000000..c4164403a --- /dev/null +++ b/apps/api/src/box/errors/runner-api-error.spec.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { HttpStatus } from '@nestjs/common' +import { RunnerApiError } from './runner-api-error' +import { sanitizedNonJsonRunnerMessage } from '../runner-adapter/runnerAdapter.v0' + +describe('RunnerApiError', () => { + it('maps runner 5xx errors to API 502 JSON errors', () => { + const error = new RunnerApiError('Runner API returned a non-JSON error response', 503, 'runner_non_json_error') + + expect(error.getStatus()).toBe(HttpStatus.BAD_GATEWAY) + expect(error.getResponse()).toEqual({ + statusCode: HttpStatus.BAD_GATEWAY, + message: 'Runner API returned a non-JSON error response', + code: 'runner_non_json_error', + }) + expect(error.runnerStatusCode).toBe(503) + expect(error.statusCode).toBe(503) + }) + + it('preserves runner 4xx status codes for caller-correctable failures', () => { + const error = new RunnerApiError('Unsupported image', 422, 'unsupported_image') + + expect(error.getStatus()).toBe(HttpStatus.UNPROCESSABLE_ENTITY) + expect(error.getResponse()).toEqual({ + statusCode: HttpStatus.UNPROCESSABLE_ENTITY, + message: 'Unsupported image', + code: 'unsupported_image', + }) + expect(error.runnerStatusCode).toBe(422) + expect(error.statusCode).toBe(422) + }) + + it('keeps the legacy statusCode field for manager cleanup paths', () => { + const error = new RunnerApiError('not found', 404, 'not_found') + + expect(error.getStatus()).toBe(HttpStatus.NOT_FOUND) + expect(error.statusCode).toBe(404) + }) + + it('embeds a sanitized non-JSON runner response excerpt', () => { + const message = sanitizedNonJsonRunnerMessage(` + + + +

502 Bad Gateway

+ + upstream connect error or disconnect/reset before headers. token=secret-value + + + `) + + expect(message).toContain('502 Bad Gateway') + expect(message).toContain('upstream connect error') + expect(message).toContain('token=[redacted]') + expect(message).not.toContain('') + expect(message).not.toContain('do-not-include') + }) +}) diff --git a/apps/api/src/box/errors/runner-api-error.ts b/apps/api/src/box/errors/runner-api-error.ts new file mode 100644 index 000000000..0cb417f9b --- /dev/null +++ b/apps/api/src/box/errors/runner-api-error.ts @@ -0,0 +1,40 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { HttpException, HttpStatus } from '@nestjs/common' + +function normalizeStatusCode(statusCode?: number): number { + if (!statusCode) { + return HttpStatus.BAD_GATEWAY + } + + if (statusCode >= 400 && statusCode < 500) { + return statusCode + } + + return HttpStatus.BAD_GATEWAY +} + +export class RunnerApiError extends HttpException { + public readonly runnerStatusCode?: number + public readonly statusCode?: number + public readonly code: string + + constructor(message: string, statusCode?: number, code = 'RUNNER_API_ERROR') { + const apiStatusCode = normalizeStatusCode(statusCode) + super( + { + statusCode: apiStatusCode, + message, + code, + }, + apiStatusCode, + ) + this.name = 'RunnerApiError' + this.runnerStatusCode = statusCode + this.statusCode = statusCode + this.code = code + } +} diff --git a/apps/api/src/sandbox/errors/runner-not-ready.error.ts b/apps/api/src/box/errors/runner-not-ready.error.ts similarity index 100% rename from apps/api/src/sandbox/errors/runner-not-ready.error.ts rename to apps/api/src/box/errors/runner-not-ready.error.ts diff --git a/apps/api/src/box/events/box-archived.event.ts b/apps/api/src/box/events/box-archived.event.ts new file mode 100644 index 000000000..f2ecc8f8f --- /dev/null +++ b/apps/api/src/box/events/box-archived.event.ts @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxArchivedEvent { + constructor(public readonly box: Box) {} +} diff --git a/apps/api/src/box/events/box-create.event.ts b/apps/api/src/box/events/box-create.event.ts new file mode 100644 index 000000000..4b9893536 --- /dev/null +++ b/apps/api/src/box/events/box-create.event.ts @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxCreatedEvent { + constructor(public readonly box: Box) {} +} diff --git a/apps/api/src/box/events/box-desired-state-updated.event.ts b/apps/api/src/box/events/box-desired-state-updated.event.ts new file mode 100644 index 000000000..f2a985a63 --- /dev/null +++ b/apps/api/src/box/events/box-desired-state-updated.event.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' +import { BoxDesiredState } from '../enums/box-desired-state.enum' + +export class BoxDesiredStateUpdatedEvent { + constructor( + public readonly box: Box, + public readonly oldDesiredState: BoxDesiredState, + public readonly newDesiredState: BoxDesiredState, + ) {} +} diff --git a/apps/api/src/box/events/box-destroyed.event.ts b/apps/api/src/box/events/box-destroyed.event.ts new file mode 100644 index 000000000..9e88a0709 --- /dev/null +++ b/apps/api/src/box/events/box-destroyed.event.ts @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxDestroyedEvent { + constructor(public readonly box: Box) {} +} diff --git a/apps/api/src/box/events/box-organization-updated.event.ts b/apps/api/src/box/events/box-organization-updated.event.ts new file mode 100644 index 000000000..e53228cfb --- /dev/null +++ b/apps/api/src/box/events/box-organization-updated.event.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxOrganizationUpdatedEvent { + constructor( + public readonly box: Box, + public readonly oldOrganizationId: string, + public readonly newOrganizationId: string, + ) {} +} diff --git a/apps/api/src/box/events/box-public-status-updated.event.ts b/apps/api/src/box/events/box-public-status-updated.event.ts new file mode 100644 index 000000000..02ab4c75f --- /dev/null +++ b/apps/api/src/box/events/box-public-status-updated.event.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxPublicStatusUpdatedEvent { + constructor( + public readonly box: Box, + public readonly oldStatus: boolean, + public readonly newStatus: boolean, + ) {} +} diff --git a/apps/api/src/box/events/box-started.event.ts b/apps/api/src/box/events/box-started.event.ts new file mode 100644 index 000000000..1fdbb1871 --- /dev/null +++ b/apps/api/src/box/events/box-started.event.ts @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxStartedEvent { + constructor(public readonly box: Box) {} +} diff --git a/apps/api/src/box/events/box-state-updated.event.ts b/apps/api/src/box/events/box-state-updated.event.ts new file mode 100644 index 000000000..996ef98b9 --- /dev/null +++ b/apps/api/src/box/events/box-state-updated.event.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' +import { BoxState } from '../enums/box-state.enum' + +export class BoxStateUpdatedEvent { + constructor( + public readonly box: Box, + public readonly oldState: BoxState, + public readonly newState: BoxState, + ) {} +} diff --git a/apps/api/src/box/events/box-stopped.event.ts b/apps/api/src/box/events/box-stopped.event.ts new file mode 100644 index 000000000..c7f00ab4e --- /dev/null +++ b/apps/api/src/box/events/box-stopped.event.ts @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '../entities/box.entity' + +export class BoxStoppedEvent { + constructor( + public readonly box: Box, + public readonly force?: boolean, + ) {} +} diff --git a/apps/api/src/sandbox/events/runner-created.event.ts b/apps/api/src/box/events/runner-created.event.ts similarity index 100% rename from apps/api/src/sandbox/events/runner-created.event.ts rename to apps/api/src/box/events/runner-created.event.ts diff --git a/apps/api/src/sandbox/events/runner-deleted.event.ts b/apps/api/src/box/events/runner-deleted.event.ts similarity index 100% rename from apps/api/src/sandbox/events/runner-deleted.event.ts rename to apps/api/src/box/events/runner-deleted.event.ts diff --git a/apps/api/src/sandbox/events/runner-state-updated.event.ts b/apps/api/src/box/events/runner-state-updated.event.ts similarity index 100% rename from apps/api/src/sandbox/events/runner-state-updated.event.ts rename to apps/api/src/box/events/runner-state-updated.event.ts diff --git a/apps/api/src/sandbox/events/runner-unschedulable-updated.event.ts b/apps/api/src/box/events/runner-unschedulable-updated.event.ts similarity index 100% rename from apps/api/src/sandbox/events/runner-unschedulable-updated.event.ts rename to apps/api/src/box/events/runner-unschedulable-updated.event.ts diff --git a/apps/api/src/sandbox/events/volume-created.event.ts b/apps/api/src/box/events/volume-created.event.ts similarity index 100% rename from apps/api/src/sandbox/events/volume-created.event.ts rename to apps/api/src/box/events/volume-created.event.ts diff --git a/apps/api/src/sandbox/events/volume-last-used-at-updated.event.ts b/apps/api/src/box/events/volume-last-used-at-updated.event.ts similarity index 100% rename from apps/api/src/sandbox/events/volume-last-used-at-updated.event.ts rename to apps/api/src/box/events/volume-last-used-at-updated.event.ts diff --git a/apps/api/src/sandbox/events/volume-state-updated.event.ts b/apps/api/src/box/events/volume-state-updated.event.ts similarity index 100% rename from apps/api/src/sandbox/events/volume-state-updated.event.ts rename to apps/api/src/box/events/volume-state-updated.event.ts diff --git a/apps/api/src/sandbox/events/warmpool-topup-requested.event.ts b/apps/api/src/box/events/warmpool-topup-requested.event.ts similarity index 100% rename from apps/api/src/sandbox/events/warmpool-topup-requested.event.ts rename to apps/api/src/box/events/warmpool-topup-requested.event.ts diff --git a/apps/api/src/box/guards/box-access.guard.ts b/apps/api/src/box/guards/box-access.guard.ts new file mode 100644 index 000000000..691b2d1b9 --- /dev/null +++ b/apps/api/src/box/guards/box-access.guard.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, CanActivate, ExecutionContext, NotFoundException, ForbiddenException } from '@nestjs/common' +import { BoxService } from '../services/box.service' +import { OrganizationAuthContext, BaseAuthContext } from '../../common/interfaces/auth-context.interface' +import { isRunnerContext, RunnerContext } from '../../common/interfaces/runner-context.interface' +import { SystemRole } from '../../user/enums/system-role.enum' +import { isProxyContext } from '../../common/interfaces/proxy-context.interface' +import { isSshGatewayContext } from '../../common/interfaces/ssh-gateway-context.interface' +import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' +import { + isRegionSSHGatewayContext, + RegionSSHGatewayContext, +} from '../../common/interfaces/region-ssh-gateway.interface' + +@Injectable() +export class BoxAccessGuard implements CanActivate { + constructor(private readonly boxService: BoxService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + // TODO: remove deprecated request.params.workspaceId param once we remove the deprecated workspace controller + const boxIdOrName: string = + request.params.boxIdOrName || request.params.boxId || request.params.id || request.params.workspaceId + + // TODO: initialize authContext safely + const authContext: BaseAuthContext = request.user + + try { + switch (true) { + case isRunnerContext(authContext): { + // For runner authentication, verify that the runner ID matches the box's runner ID + const runnerContext = authContext as RunnerContext + const boxRunnerId = await this.boxService.getRunnerId(boxIdOrName) + if (boxRunnerId !== runnerContext.runnerId) { + throw new ForbiddenException('Runner ID does not match box runner ID') + } + break + } + case isRegionProxyContext(authContext): + case isRegionSSHGatewayContext(authContext): { + // For region proxy/ssh gateway authentication, verify that the runner's region ID matches the region ID + const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext + const boxRegionId = await this.boxService.getRegionId(boxIdOrName) + if (boxRegionId !== regionContext.regionId) { + throw new ForbiddenException(`Box region ID does not match region ${regionContext.role} region ID`) + } + break + } + case isProxyContext(authContext): + case isSshGatewayContext(authContext): + return true + default: { + // For user/organization authentication, check organization access + const orgAuthContext = authContext as OrganizationAuthContext + const boxOrganizationId = await this.boxService.getOrganizationId(boxIdOrName, orgAuthContext.organizationId) + if (orgAuthContext.role !== SystemRole.ADMIN && boxOrganizationId !== orgAuthContext.organizationId) { + throw new ForbiddenException('Request organization ID does not match resource organization ID') + } + } + } + return true + } catch (error) { + if (!(error instanceof NotFoundException)) { + console.error(error) + } + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + } +} diff --git a/apps/api/src/sandbox/guards/job-access.guard.ts b/apps/api/src/box/guards/job-access.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/job-access.guard.ts rename to apps/api/src/box/guards/job-access.guard.ts diff --git a/apps/api/src/sandbox/guards/proxy.guard.ts b/apps/api/src/box/guards/proxy.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/proxy.guard.ts rename to apps/api/src/box/guards/proxy.guard.ts diff --git a/apps/api/src/box/guards/region-box-access.guard.ts b/apps/api/src/box/guards/region-box-access.guard.ts new file mode 100644 index 000000000..20da06eca --- /dev/null +++ b/apps/api/src/box/guards/region-box-access.guard.ts @@ -0,0 +1,43 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, CanActivate, ExecutionContext, NotFoundException, ForbiddenException } from '@nestjs/common' +import { BoxService } from '../services/box.service' +import { BaseAuthContext } from '../../common/interfaces/auth-context.interface' +import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' +import { + isRegionSSHGatewayContext, + RegionSSHGatewayContext, +} from '../../common/interfaces/region-ssh-gateway.interface' + +@Injectable() +export class RegionBoxAccessGuard implements CanActivate { + constructor(private readonly boxService: BoxService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + const boxId: string = request.params.boxId || request.params.id + + const authContext: BaseAuthContext = request.user + + if (!isRegionProxyContext(authContext) && !isRegionSSHGatewayContext(authContext)) { + return false + } + + try { + const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext + const boxRegionId = await this.boxService.getRegionId(boxId) + if (boxRegionId !== regionContext.regionId) { + throw new ForbiddenException(`Box region ID does not match region ${regionContext.role} region ID`) + } + return true + } catch (error) { + if (!(error instanceof NotFoundException)) { + console.error(error) + } + throw new NotFoundException(`Box with ID or name ${boxId} not found`) + } + } +} diff --git a/apps/api/src/sandbox/guards/region-runner-access.guard.ts b/apps/api/src/box/guards/region-runner-access.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/region-runner-access.guard.ts rename to apps/api/src/box/guards/region-runner-access.guard.ts diff --git a/apps/api/src/sandbox/guards/runner-access.guard.ts b/apps/api/src/box/guards/runner-access.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/runner-access.guard.ts rename to apps/api/src/box/guards/runner-access.guard.ts diff --git a/apps/api/src/sandbox/guards/ssh-gateway.guard.ts b/apps/api/src/box/guards/ssh-gateway.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/ssh-gateway.guard.ts rename to apps/api/src/box/guards/ssh-gateway.guard.ts diff --git a/apps/api/src/sandbox/guards/volume-access.guard.ts b/apps/api/src/box/guards/volume-access.guard.ts similarity index 100% rename from apps/api/src/sandbox/guards/volume-access.guard.ts rename to apps/api/src/box/guards/volume-access.guard.ts diff --git a/apps/api/src/box/managers/box-actions/box-destroy.action.ts b/apps/api/src/box/managers/box-actions/box-destroy.action.ts new file mode 100644 index 000000000..fb804d2a6 --- /dev/null +++ b/apps/api/src/box/managers/box-actions/box-destroy.action.ts @@ -0,0 +1,71 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable } from '@nestjs/common' +import { Box } from '../../entities/box.entity' +import { BoxState } from '../../enums/box-state.enum' +import { DONT_SYNC_AGAIN, BoxAction, SyncState, SYNC_AGAIN } from './box.action' +import { RunnerState } from '../../enums/runner-state.enum' +import { RunnerService } from '../../services/runner.service' +import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' +import { BoxRepository } from '../../repositories/box.repository' +import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' +import { WithSpan } from '../../../common/decorators/otel.decorator' + +@Injectable() +export class BoxDestroyAction extends BoxAction { + constructor( + protected runnerService: RunnerService, + protected runnerAdapterFactory: RunnerAdapterFactory, + protected boxRepository: BoxRepository, + protected redisLockProvider: RedisLockProvider, + ) { + super(runnerService, runnerAdapterFactory, boxRepository, redisLockProvider) + } + + @WithSpan() + async run(box: Box, lockCode: LockCode): Promise { + if (box.state === BoxState.DESTROYED) { + return DONT_SYNC_AGAIN + } + + if (box.state === BoxState.ARCHIVED) { + await this.updateBoxState(box, BoxState.DESTROYED, lockCode) + return DONT_SYNC_AGAIN + } + + const runner = await this.runnerService.findOneOrFail(box.runnerId) + if (runner.state !== RunnerState.READY) { + return DONT_SYNC_AGAIN + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + try { + const boxInfo = await runnerAdapter.boxInfo(box.id) + + if (boxInfo.state === BoxState.DESTROYED) { + await this.updateBoxState(box, BoxState.DESTROYED, lockCode) + return DONT_SYNC_AGAIN + } + + if (box.state !== BoxState.DESTROYING) { + await runnerAdapter.destroyBox(box.id) + await this.updateBoxState(box, BoxState.DESTROYING, lockCode) + } + + return SYNC_AGAIN + } catch (error) { + // if the box is not found on runner, it is already destroyed + if (error.response?.status === 404 || error.statusCode === 404) { + await this.updateBoxState(box, BoxState.DESTROYED, lockCode) + return DONT_SYNC_AGAIN + } + + throw error + } + } +} diff --git a/apps/api/src/box/managers/box-actions/box-start.action.spec.ts b/apps/api/src/box/managers/box-actions/box-start.action.spec.ts new file mode 100644 index 000000000..e0b112820 --- /dev/null +++ b/apps/api/src/box/managers/box-actions/box-start.action.spec.ts @@ -0,0 +1,217 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +import { BoxStartAction } from './box-start.action' +import { BoxAction, SYNC_AGAIN } from './box.action' +import { Box } from '../../entities/box.entity' +import { Runner } from '../../entities/runner.entity' +import { BoxState } from '../../enums/box-state.enum' +import { BoxDesiredState } from '../../enums/box-desired-state.enum' +import { RunnerState } from '../../enums/runner-state.enum' +import { LockCode } from '../../common/redis-lock.provider' + +describe('BoxStartAction.handleRunnerBoxStoppedStateOnDesiredStateStart', () => { + it('restarts a stopped box on its own runner (no cross-runner reassignment)', async () => { + const ownRunnerId = 'runner-own-1' + + const box = new Box('region-1', 'my-box') + box.runnerId = ownRunnerId + box.state = BoxState.STOPPED + box.desiredState = BoxDesiredState.STARTED + box.pending = true + + const ownRunner = { id: ownRunnerId, state: RunnerState.READY } as Runner + + // findOneOrFail must return the runner that matches the requested id so we can + // prove the action selected box.runnerId and nothing else. + const runnerService = { + findOneOrFail: jest.fn(async (id: string) => { + if (id !== ownRunnerId) { + throw new Error(`unexpected runner lookup: ${id}`) + } + return ownRunner + }), + } + + // Capture the runner the action chose to start the box on. + let runnerUsedForStart: Runner | undefined + const startBox = jest.fn(async () => undefined) + const runnerAdapterFactory = { + create: jest.fn(async (runner: Runner) => { + runnerUsedForStart = runner + return { startBox } as any + }), + } + + const lockCode = new LockCode('lock-1') + const updatedFields: Partial[] = [] + const boxRepository = { + update: jest.fn(async (_id: string, opts: { updateData: Partial }) => { + updatedFields.push(opts.updateData) + return box + }), + } + const redisLockProvider = { + getCode: jest.fn(async () => lockCode), + } + const organizationService = { + findOne: jest.fn(async () => ({ boxMetadata: {} })), + } + + const action = new BoxStartAction( + runnerService as any, + runnerAdapterFactory as any, + boxRepository as any, + organizationService as any, + {} as any, // configService + redisLockProvider as any, + {} as any, // boxActivityService + ) + + const result = await (action as BoxAction).run(box, lockCode) + + // The action started the box on its OWN runner, not a different one. + expect(runnerUsedForStart?.id).toBe(ownRunnerId) + expect(startBox).toHaveBeenCalledWith(box.id, box.authToken, expect.any(Object)) + // findOneOrFail was only ever asked about the box's own runner. + for (const call of runnerService.findOneOrFail.mock.calls) { + expect(call[0]).toBe(ownRunnerId) + } + expect(result).toBe(SYNC_AGAIN) + expect(updatedFields.some((u) => u.state === BoxState.STARTING)).toBe(true) + }) + + it('moves a stopped box with no runner to ERROR (cross-runner recovery is not supported)', async () => { + const box = new Box('region-1', 'orphan-box') + box.runnerId = null + box.state = BoxState.STOPPED + box.desiredState = BoxDesiredState.STARTED + box.pending = true + + const runnerService = { findOneOrFail: jest.fn() } + const runnerAdapterFactory = { create: jest.fn() } + const lockCode = new LockCode('lock-2') + const updatedFields: Partial[] = [] + const boxRepository = { + update: jest.fn(async (_id: string, opts: { updateData: Partial }) => { + updatedFields.push(opts.updateData) + return box + }), + } + const redisLockProvider = { getCode: jest.fn(async () => lockCode) } + const organizationService = { findOne: jest.fn(async () => ({ boxMetadata: {} })) } + + const action = new BoxStartAction( + runnerService as any, + runnerAdapterFactory as any, + boxRepository as any, + organizationService as any, + {} as any, + redisLockProvider as any, + {} as any, + ) + + await (action as BoxAction).run(box, lockCode) + + // No runner lookup or adapter creation: there is no runner to recover onto. + expect(runnerService.findOneOrFail).not.toHaveBeenCalled() + expect(runnerAdapterFactory.create).not.toHaveBeenCalled() + expect(updatedFields.some((u) => u.state === BoxState.ERROR)).toBe(true) + }) +}) + +describe('BoxStartAction.handleRunnerBoxUnknownStateOnDesiredStateStart', () => { + it('boots an unknown box via runnerAdapter.createBox and moves it to CREATING', async () => { + const runnerId = 'runner-boot-1' + + const box = new Box('region-1', 'fresh-box') + box.runnerId = runnerId + box.image = 'boxlite/base' + box.state = BoxState.UNKNOWN + box.desiredState = BoxDesiredState.STARTED + box.pending = true + + const runner = { id: runnerId, state: RunnerState.READY } as Runner + const runnerService = { findOneOrFail: jest.fn(async () => runner) } + + const createBox = jest.fn(async () => undefined) + const runnerAdapterFactory = { create: jest.fn(async () => ({ createBox }) as any) } + + const lockCode = new LockCode('lock-boot-1') + const updatedFields: Partial[] = [] + const boxRepository = { + update: jest.fn(async (_id: string, opts: { updateData: Partial }) => { + updatedFields.push(opts.updateData) + return box + }), + } + const redisLockProvider = { getCode: jest.fn(async () => lockCode) } + const organizationService = { findOne: jest.fn(async () => ({ boxMetadata: {} })) } + + const action = new BoxStartAction( + runnerService as any, + runnerAdapterFactory as any, + boxRepository as any, + organizationService as any, + {} as any, + redisLockProvider as any, + {} as any, + ) + + const result = await (action as BoxAction).run(box, lockCode) + + expect(createBox).toHaveBeenCalledWith(box, expect.any(Object)) + expect(result).toBe(SYNC_AGAIN) + expect(updatedFields.some((u) => u.state === BoxState.CREATING)).toBe(true) + }) + + it('moves an unknown box with no image to ERROR without calling createBox', async () => { + const runnerId = 'runner-boot-2' + + const box = new Box('region-1', 'imageless-box') + box.runnerId = runnerId + box.state = BoxState.UNKNOWN + box.desiredState = BoxDesiredState.STARTED + box.pending = true + + const runner = { id: runnerId, state: RunnerState.READY } as Runner + const runnerService = { findOneOrFail: jest.fn(async () => runner) } + + const createBox = jest.fn(async () => undefined) + const runnerAdapterFactory = { create: jest.fn(async () => ({ createBox }) as any) } + + const lockCode = new LockCode('lock-boot-2') + const updatedFields: Partial[] = [] + const boxRepository = { + update: jest.fn(async (_id: string, opts: { updateData: Partial }) => { + updatedFields.push(opts.updateData) + return box + }), + } + const redisLockProvider = { getCode: jest.fn(async () => lockCode) } + const organizationService = { findOne: jest.fn(async () => ({ boxMetadata: {} })) } + + const action = new BoxStartAction( + runnerService as any, + runnerAdapterFactory as any, + boxRepository as any, + organizationService as any, + {} as any, + redisLockProvider as any, + {} as any, + ) + + await (action as BoxAction).run(box, lockCode) + + expect(createBox).not.toHaveBeenCalled() + expect(updatedFields.some((u) => u.state === BoxState.ERROR)).toBe(true) + }) +}) diff --git a/apps/api/src/box/managers/box-actions/box-start.action.ts b/apps/api/src/box/managers/box-actions/box-start.action.ts new file mode 100644 index 000000000..414de0c97 --- /dev/null +++ b/apps/api/src/box/managers/box-actions/box-start.action.ts @@ -0,0 +1,231 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { BoxRepository } from '../../repositories/box.repository' +import { Box } from '../../entities/box.entity' +import { BoxState } from '../../enums/box-state.enum' +import { DONT_SYNC_AGAIN, BoxAction, SYNC_AGAIN, SyncState } from './box.action' +import { RunnerState } from '../../enums/runner-state.enum' +import { RunnerService } from '../../services/runner.service' +import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' +import { OrganizationService } from '../../../organization/services/organization.service' +import { TypedConfigService } from '../../../config/typed-config.service' +import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' +import { WithSpan } from '../../../common/decorators/otel.decorator' +import { BoxActivityService } from '../../services/box-activity.service' + +@Injectable() +export class BoxStartAction extends BoxAction { + protected readonly logger = new Logger(BoxStartAction.name) + constructor( + protected runnerService: RunnerService, + protected runnerAdapterFactory: RunnerAdapterFactory, + protected boxRepository: BoxRepository, + protected readonly organizationService: OrganizationService, + protected readonly configService: TypedConfigService, + protected readonly redisLockProvider: RedisLockProvider, + private readonly boxActivityService: BoxActivityService, + ) { + super(runnerService, runnerAdapterFactory, boxRepository, redisLockProvider) + } + + @WithSpan() + async run(box: Box, lockCode: LockCode): Promise { + switch (box.state) { + case BoxState.UNKNOWN: { + return this.handleRunnerBoxUnknownStateOnDesiredStateStart(box, lockCode) + } + case BoxState.STOPPED: { + return this.handleRunnerBoxStoppedStateOnDesiredStateStart(box, lockCode) + } + case BoxState.RESTORING: + case BoxState.CREATING: + case BoxState.STARTING: { + return this.handleRunnerBoxStartedStateCheck(box, lockCode) + } + case BoxState.ERROR: { + this.logger.error(`Box ${box.id} is in error state on desired state start`) + return DONT_SYNC_AGAIN + } + } + + return DONT_SYNC_AGAIN + } + + private async handleRunnerBoxUnknownStateOnDesiredStateStart(box: Box, lockCode: LockCode): Promise { + const runner = await this.runnerService.findOneOrFail(box.runnerId) + if (runner.state !== RunnerState.READY) { + return DONT_SYNC_AGAIN + } + + if (!box.image) { + await this.updateBoxState(box, BoxState.ERROR, lockCode, undefined, 'Box has no image to create from') + return DONT_SYNC_AGAIN + } + + const organization = await this.organizationService.findOne(box.organizationId) + + const metadata: { [key: string]: string } = { ...organization?.boxMetadata } + if (box.volumes?.length) { + metadata['volumes'] = JSON.stringify( + box.volumes.map((v) => ({ volumeId: v.volumeId, mountPath: v.mountPath, subpath: v.subpath })), + ) + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + await runnerAdapter.createBox(box, metadata) + + await this.updateBoxState(box, BoxState.CREATING, lockCode) + return SYNC_AGAIN + } + + private async handleRunnerBoxStoppedStateOnDesiredStateStart(box: Box, lockCode: LockCode): Promise { + const organization = await this.organizationService.findOne(box.organizationId) + + // A stopped box restarts on its own runner. Cross-runner recovery is not supported. + if (box.runnerId === null) { + await this.updateBoxState(box, BoxState.ERROR, lockCode, undefined, 'Box has no runner') + return DONT_SYNC_AGAIN + } + + const runner = await this.runnerService.findOneOrFail(box.runnerId) + + if (runner.state !== RunnerState.READY) { + return DONT_SYNC_AGAIN + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + const metadata: { [key: string]: string } = { ...organization?.boxMetadata } + if (box.volumes?.length) { + metadata['volumes'] = JSON.stringify( + box.volumes.map((v) => ({ volumeId: v.volumeId, mountPath: v.mountPath, subpath: v.subpath })), + ) + } + + await runnerAdapter.startBox(box.id, box.authToken, metadata) + + await this.updateBoxState(box, BoxState.STARTING, lockCode) + return SYNC_AGAIN + } + + // used to check if box is started on runner and update box state accordingly + // also used to handle the case where a box is started on a runner and then transferred to a new runner + private async handleRunnerBoxStartedStateCheck(box: Box, lockCode: LockCode): Promise { + // edge case when box is being transferred to a new runner + if (!box.runnerId) { + return SYNC_AGAIN + } + + const runner = await this.runnerService.findOneOrFail(box.runnerId) + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + const boxInfo = await runnerAdapter.boxInfo(box.id) + + switch (boxInfo.state) { + case BoxState.STARTED: { + await this.updateBoxState(box, BoxState.STARTED, lockCode, undefined, undefined, boxInfo.daemonVersion) + + // if box was transferred to a new runner, remove it from the old runner + if (box.prevRunnerId) { + await this.removeBoxFromPreviousRunner(box) + } + + return DONT_SYNC_AGAIN + } + case BoxState.STARTING: + if (await this.checkTimeoutError(box, 5, 'Timeout while starting box')) { + return DONT_SYNC_AGAIN + } + break + case BoxState.RESTORING: + if (await this.checkTimeoutError(box, 30, 'Timeout while starting box')) { + return DONT_SYNC_AGAIN + } + break + case BoxState.CREATING: { + if (await this.checkTimeoutError(box, 15, 'Timeout while creating box')) { + return DONT_SYNC_AGAIN + } + break + } + case BoxState.UNKNOWN: { + await this.updateBoxState(box, BoxState.UNKNOWN, lockCode) + break + } + case BoxState.ERROR: { + await this.updateBoxState( + box, + BoxState.ERROR, + lockCode, + undefined, + 'Box entered error state on runner during startup wait loop', + ) + break + } + case BoxState.DESTROYED: { + this.logger.warn( + `Box ${box.id} is in destroyed state while starting on runner ${box.runnerId}, prev runner ${box.prevRunnerId}`, + ) + await this.checkTimeoutError(box, 15, 'Timeout while starting box: Box is in unknown state on runner') + return DONT_SYNC_AGAIN + } + // also any other state that is not STARTED + default: { + this.logger.error(`Box ${box.id} is in unexpected state ${boxInfo.state}`) + await this.updateBoxState( + box, + BoxState.ERROR, + lockCode, + undefined, + `Box is in unexpected state: ${boxInfo.state}`, + ) + break + } + } + + return SYNC_AGAIN + } + + private async checkTimeoutError(box: Box, timeoutMinutes: number, errorReason: string): Promise { + const lastActivityAt = await this.boxActivityService.getLastActivityAt(box.id) + if (lastActivityAt && lastActivityAt.getTime() < Date.now() - 1000 * 60 * timeoutMinutes) { + const updateData: Partial = { + state: BoxState.ERROR, + errorReason, + recoverable: false, + } + await this.boxRepository.update(box.id, { updateData, entity: box }) + return true + } + return false + } + + private async removeBoxFromPreviousRunner(box: Box): Promise { + const runner = await this.runnerService.findOne(box.prevRunnerId) + if (!runner) { + this.logger.warn(`Previously assigned runner ${box.prevRunnerId} for box ${box.id} not found`) + + await this.boxRepository.update(box.id, { updateData: { prevRunnerId: null } }, true) + return + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + try { + // First try to destroy the box + await runnerAdapter.destroyBox(box.id) + } catch (error) { + if (error.response?.status !== 404 && error.statusCode !== 404) { + this.logger.error(`Failed to cleanup box ${box.id} on previous runner ${runner.id}:`, error) + throw error + } + } + + await this.boxRepository.update(box.id, { updateData: { prevRunnerId: null } }, true) + } +} diff --git a/apps/api/src/box/managers/box-actions/box-stop.action.ts b/apps/api/src/box/managers/box-actions/box-stop.action.ts new file mode 100644 index 000000000..13da106ff --- /dev/null +++ b/apps/api/src/box/managers/box-actions/box-stop.action.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable } from '@nestjs/common' +import { Box } from '../../entities/box.entity' +import { BoxState } from '../../enums/box-state.enum' +import { DONT_SYNC_AGAIN, BoxAction, SyncState, SYNC_AGAIN } from './box.action' +import { RunnerState } from '../../enums/runner-state.enum' +import { RunnerService } from '../../services/runner.service' +import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' +import { BoxRepository } from '../../repositories/box.repository' +import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' +import { WithSpan } from '../../../common/decorators/otel.decorator' + +@Injectable() +export class BoxStopAction extends BoxAction { + constructor( + protected runnerService: RunnerService, + protected runnerAdapterFactory: RunnerAdapterFactory, + protected boxRepository: BoxRepository, + protected redisLockProvider: RedisLockProvider, + ) { + super(runnerService, runnerAdapterFactory, boxRepository, redisLockProvider) + } + + @WithSpan() + async run(box: Box, lockCode: LockCode, force?: boolean): Promise { + const runner = await this.runnerService.findOneOrFail(box.runnerId) + if (runner.state !== RunnerState.READY) { + return DONT_SYNC_AGAIN + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + if (box.state === BoxState.STARTED) { + // stop box + await runnerAdapter.stopBox(box.id, force) + await this.updateBoxState(box, BoxState.STOPPING, lockCode) + + // sync states again immediately for box + return SYNC_AGAIN + } + + if (box.state !== BoxState.STOPPING && box.state !== BoxState.ERROR) { + return DONT_SYNC_AGAIN + } + + const boxInfo = await runnerAdapter.boxInfo(box.id) + + if (boxInfo.state === BoxState.STOPPED) { + await this.updateBoxState(box, BoxState.STOPPED, lockCode) + return DONT_SYNC_AGAIN + } else if (boxInfo.state === BoxState.ERROR) { + await this.updateBoxState(box, BoxState.ERROR, lockCode, undefined, 'Box is in error state on runner') + return DONT_SYNC_AGAIN + } + + return SYNC_AGAIN + } +} diff --git a/apps/api/src/box/managers/box-actions/box.action.ts b/apps/api/src/box/managers/box-actions/box.action.ts new file mode 100644 index 000000000..89dcba875 --- /dev/null +++ b/apps/api/src/box/managers/box-actions/box.action.ts @@ -0,0 +1,96 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { RunnerService } from '../../services/runner.service' +import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' +import { Box } from '../../entities/box.entity' +import { BoxRepository } from '../../repositories/box.repository' +import { BoxState } from '../../enums/box-state.enum' +import { getStateChangeLockKey } from '../../utils/lock-key.util' +import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' + +export const SYNC_AGAIN = 'sync-again' +export const DONT_SYNC_AGAIN = 'dont-sync-again' +export type SyncState = typeof SYNC_AGAIN | typeof DONT_SYNC_AGAIN + +@Injectable() +export abstract class BoxAction { + protected readonly logger = new Logger(BoxAction.name) + + constructor( + protected readonly runnerService: RunnerService, + protected runnerAdapterFactory: RunnerAdapterFactory, + protected readonly boxRepository: BoxRepository, + protected readonly redisLockProvider: RedisLockProvider, + ) {} + + abstract run(box: Box, lockCode: LockCode): Promise + + protected async updateBoxState( + box: Box, + state: BoxState, + expectedLockCode: LockCode, + runnerId?: string | null | undefined, + errorReason?: string, + daemonVersion?: string, + recoverable?: boolean, + ) { + // check if the lock code is still valid + const lockKey = getStateChangeLockKey(box.id) + const currentLockCode = await this.redisLockProvider.getCode(lockKey) + + if (currentLockCode === null) { + this.logger.warn( + `no lock code found - state update action expired - skipping - boxId: ${box.id} - state: ${state}`, + ) + return + } + + if (expectedLockCode.getCode() !== currentLockCode.getCode()) { + this.logger.warn( + `lock code mismatch - state update action expired - skipping - boxId: ${box.id} - state: ${state}`, + ) + return + } + + if (!box.pending) { + const err = new Error(`box ${box.id} is not in a pending state`) + this.logger.error(err) + return + } + + const updateData: Partial = { + state, + } + + if (runnerId !== undefined) { + updateData.runnerId = runnerId + } + + if (errorReason !== undefined) { + updateData.errorReason = errorReason + if (state === BoxState.ERROR) { + updateData.recoverable = recoverable ?? false + } + } + + if (box.state === BoxState.ERROR && !box.errorReason) { + updateData.errorReason = 'Box is in error state during update' + updateData.recoverable = false + } + + if (daemonVersion !== undefined) { + updateData.daemonVersion = daemonVersion + } + + if (recoverable !== undefined) { + updateData.recoverable = recoverable + } + + await this.boxRepository.update(box.id, { updateData, entity: box }) + } +} diff --git a/apps/api/src/box/managers/box.manager.reconcile.spec.ts b/apps/api/src/box/managers/box.manager.reconcile.spec.ts new file mode 100644 index 000000000..e524744d1 --- /dev/null +++ b/apps/api/src/box/managers/box.manager.reconcile.spec.ts @@ -0,0 +1,195 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxManager } from './box.manager' +import { BoxState } from '../enums/box-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { Box } from '../entities/box.entity' + +type Candidate = { id: string; runnerId: string | null } + +function buildHarness(opts: { + candidates: Candidate[] + apiVersion?: string + failedStartJobs?: number + boxLocked?: boolean + globalLockAcquired?: boolean +}) { + const updateWhere = jest.fn().mockResolvedValue(undefined) + + const queryBuilder: any = { + select: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + orderBy: jest.fn().mockReturnThis(), + limit: jest.fn().mockReturnThis(), + getMany: jest.fn().mockResolvedValue(opts.candidates), + } + const boxRepository: any = { + createQueryBuilder: jest.fn().mockReturnValue(queryBuilder), + updateWhere, + } + + const collectQueryClauses = () => + [...queryBuilder.where.mock.calls, ...queryBuilder.andWhere.mock.calls].map((call: any[]) => String(call[0])) + + const runnerService: any = { + getRunnerApiVersion: jest.fn().mockResolvedValue(opts.apiVersion ?? '2'), + } + + const redisLockProvider: any = { + lock: jest.fn().mockResolvedValue(opts.globalLockAcquired ?? true), + unlock: jest.fn().mockResolvedValue(undefined), + isLocked: jest.fn().mockResolvedValue(opts.boxLocked ?? false), + } + + const jobRepository: any = { + count: jest.fn().mockResolvedValue(opts.failedStartJobs ?? 0), + } + + const manager = new BoxManager( + boxRepository, + runnerService, + redisLockProvider, + {} as any, + {} as any, + {} as any, + jobRepository, + ) + + return { manager, updateWhere, runnerService, redisLockProvider, jobRepository, collectQueryClauses } +} + +describe('BoxManager.reconcileErroredBoxes', () => { + afterEach(() => jest.clearAllMocks()) + + it('flips a recoverable ERROR box to STOPPED so the start flow can re-drive it', async () => { + const { manager, updateWhere } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + }) + + await manager.reconcileErroredBoxes() + + expect(updateWhere).toHaveBeenCalledTimes(1) + expect(updateWhere).toHaveBeenCalledWith('box-1', { + updateData: { state: BoxState.STOPPED, errorReason: null, recoverable: false }, + whereCondition: { + state: BoxState.ERROR, + pending: false, + desiredState: BoxDesiredState.STARTED, + runnerId: 'runner-1', + }, + }) + }) + + it('keeps reconciled boxes pending because STOPPED is only a recovery waypoint toward STARTED', () => { + const box = new Box('us-east-1', 'box-1') + box.state = BoxState.ERROR + box.desiredState = BoxDesiredState.STARTED + box.pending = false + + Object.assign(box, { state: BoxState.STOPPED, errorReason: null, recoverable: false }) + const invariantChanges = box.enforceInvariants() + + expect(invariantChanges.pending).toBe(true) + expect(box.pending).toBe(true) + expect(box.state).toBe(BoxState.STOPPED) + expect(box.desiredState).toBe(BoxDesiredState.STARTED) + }) + + it('does not gate eligibility on the storage-only recoverable flag, so split-brain ERROR boxes qualify', async () => { + const { manager, updateWhere, collectQueryClauses } = buildHarness({ + // A split-brain box (CREATE failed late while the box exists on the runner) + // is reported with recoverable=false; it must still be eligible for reconcile. + candidates: [{ id: 'box-splitbrain', runnerId: 'runner-1' }], + }) + + await manager.reconcileErroredBoxes() + + const clauses = collectQueryClauses() + // The errors #578 targets (split-brain, timeout) are recoverable=false. Pre-filtering on + // box.recoverable (set true only for storage-full) would make the loop recover nothing. + expect(clauses.some((clause) => clause.includes('recoverable'))).toBe(false) + // Eligibility is still scoped to ERROR boxes that want to be STARTED. + expect(clauses.some((clause) => clause.includes('box.state'))).toBe(true) + expect(clauses.some((clause) => clause.includes('desiredState'))).toBe(true) + // And such a (non-recoverable) box is still driven back toward STARTED. + expect(updateWhere).toHaveBeenCalledWith('box-splitbrain', { + updateData: { state: BoxState.STOPPED, errorReason: null, recoverable: false }, + whereCondition: { + state: BoxState.ERROR, + pending: false, + desiredState: BoxDesiredState.STARTED, + runnerId: 'runner-1', + }, + }) + }) + + it('guards the atomic transition with the same stable fields used for eligibility', async () => { + const { manager, updateWhere } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + }) + + await manager.reconcileErroredBoxes() + + expect(updateWhere).toHaveBeenCalledWith('box-1', { + updateData: { state: BoxState.STOPPED, errorReason: null, recoverable: false }, + whereCondition: { + state: BoxState.ERROR, + pending: false, + desiredState: BoxDesiredState.STARTED, + runnerId: 'runner-1', + }, + }) + }) + + it('does not retry a box that already hit the recovery attempt ceiling', async () => { + const { manager, updateWhere, jobRepository } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + failedStartJobs: 5, // MAX_RECOVER_ATTEMPTS + }) + + await manager.reconcileErroredBoxes() + + expect(jobRepository.count).toHaveBeenCalledTimes(1) + expect(updateWhere).not.toHaveBeenCalled() + }) + + it('skips boxes on non-v2 runners', async () => { + const { manager, updateWhere, jobRepository } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + apiVersion: '1', + }) + + await manager.reconcileErroredBoxes() + + expect(jobRepository.count).not.toHaveBeenCalled() + expect(updateWhere).not.toHaveBeenCalled() + }) + + it('skips boxes the sync loop is already holding a lock on', async () => { + const { manager, updateWhere } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + boxLocked: true, + }) + + await manager.reconcileErroredBoxes() + + expect(updateWhere).not.toHaveBeenCalled() + }) + + it('bails out without scanning when the global lock is held by another worker', async () => { + const { manager, updateWhere, redisLockProvider } = buildHarness({ + candidates: [{ id: 'box-1', runnerId: 'runner-1' }], + globalLockAcquired: false, + }) + + await manager.reconcileErroredBoxes() + + expect(updateWhere).not.toHaveBeenCalled() + expect(redisLockProvider.unlock).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/box/managers/box.manager.sync-error-job.spec.ts b/apps/api/src/box/managers/box.manager.sync-error-job.spec.ts new file mode 100644 index 000000000..da835e2a3 --- /dev/null +++ b/apps/api/src/box/managers/box.manager.sync-error-job.spec.ts @@ -0,0 +1,111 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxManager } from './box.manager' +import { BoxState } from '../enums/box-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { JobType } from '../enums/job-type.enum' +import { JobStatus } from '../enums/job-status.enum' + +// Job's constructor calls uuid's v4(), which needs a global WebCrypto the node-18 +// jest env doesn't expose. Mock it like the other box specs (e.g. +// box-start.action.spec) so constructing a Job in the code under test stays +// deterministic and crypto-free. +jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') })) + +// Guards the reconcile retry ceiling: when a START_BOX sync throws *before* a +// START_BOX job is ever persisted (synchronous runner lookup / adapter failure), +// syncInstanceState must record a terminal FAILED START_BOX job so the ceiling — +// which counts FAILED START_BOX jobs — can see the attempt. Otherwise the box is +// flipped out of ERROR forever. +function buildHarness(opts: { + box: { state: BoxState; desiredState: BoxDesiredState; runnerId: string | null } + failingAction: 'start' | 'stop' +}) { + const box = { id: 'box-1', ...opts.box } as any + + const insert = jest.fn().mockResolvedValue(undefined) + const updateWhere = jest.fn().mockResolvedValue(undefined) + + const boxRepository: any = { + findOneOrFail: jest.fn().mockResolvedValue(box), + updateWhere, + } + const runnerService: any = { + getRunnerApiVersion: jest.fn().mockResolvedValue('2'), + } + const redisLockProvider: any = { + lock: jest.fn().mockResolvedValue(true), + unlock: jest.fn().mockResolvedValue(undefined), + isLocked: jest.fn().mockResolvedValue(false), + } + const thrower = jest.fn().mockRejectedValue(new Error('runner unreachable')) + const boxStartAction: any = { run: opts.failingAction === 'start' ? thrower : jest.fn() } + const boxStopAction: any = { run: opts.failingAction === 'stop' ? thrower : jest.fn() } + const boxDestroyAction: any = { run: jest.fn() } + const jobRepository: any = { insert, count: jest.fn().mockResolvedValue(0) } + + const manager = new BoxManager( + boxRepository, + runnerService, + redisLockProvider, + boxStartAction, + boxStopAction, + boxDestroyAction, + jobRepository, + ) + return { manager, insert, updateWhere } +} + +describe('BoxManager.syncInstanceState — failed START_BOX accounting', () => { + afterEach(() => jest.clearAllMocks()) + + it('records a terminal FAILED START_BOX job when a STARTED sync throws before a job is persisted', async () => { + const { manager, insert, updateWhere } = buildHarness({ + box: { state: BoxState.STOPPED, desiredState: BoxDesiredState.STARTED, runnerId: 'runner-1' }, + failingAction: 'start', + }) + + await manager.syncInstanceState('box-1') + + // The box is still transitioned to ERROR ... + expect(updateWhere).toHaveBeenCalledTimes(1) + // ... and a FAILED START_BOX job is recorded so the reconcile ceiling counts it. + expect(insert).toHaveBeenCalledTimes(1) + const job = insert.mock.calls[0][0] + expect(job).toMatchObject({ + type: JobType.START_BOX, + status: JobStatus.FAILED, + resourceId: 'box-1', + runnerId: 'runner-1', + }) + // completedAt set => row stays outside the "one incomplete job per box" partial-unique index. + expect(job.completedAt).toBeInstanceOf(Date) + }) + + it('does not record a START_BOX job when the failing sync is not a START (e.g. STOPPED)', async () => { + const { manager, insert, updateWhere } = buildHarness({ + box: { state: BoxState.STARTED, desiredState: BoxDesiredState.STOPPED, runnerId: 'runner-1' }, + failingAction: 'stop', + }) + + await manager.syncInstanceState('box-1') + + expect(updateWhere).toHaveBeenCalledTimes(1) // still transitions to ERROR + expect(insert).not.toHaveBeenCalled() // but no synthetic START_BOX job + }) + + it('skips the synthetic job when the box has no runner', async () => { + const { manager, insert } = buildHarness({ + box: { state: BoxState.STOPPED, desiredState: BoxDesiredState.STARTED, runnerId: null }, + failingAction: 'start', + }) + + await manager.syncInstanceState('box-1') + + expect(insert).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/box/managers/box.manager.ts b/apps/api/src/box/managers/box.manager.ts new file mode 100755 index 000000000..4c66e8278 --- /dev/null +++ b/apps/api/src/box/managers/box.manager.ts @@ -0,0 +1,587 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common' +import { Cron, CronExpression } from '@nestjs/schedule' +import { InjectRepository } from '@nestjs/typeorm' +import { MoreThan, Repository } from 'typeorm' +import { randomUUID } from 'crypto' + +import { BoxConflictError } from '../errors/box-conflict.error' +import { JobConflictError } from '../errors/job-conflict.error' +import { BoxState } from '../enums/box-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { RunnerService } from '../services/runner.service' + +import { RedisLockProvider, LockCode } from '../common/redis-lock.provider' + +import { BOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/box.constants' + +import { BoxEvents } from '../constants/box-events.constants' +import { BoxStoppedEvent } from '../events/box-stopped.event' +import { BoxStartedEvent } from '../events/box-started.event' +import { BoxDestroyedEvent } from '../events/box-destroyed.event' +import { BoxCreatedEvent } from '../events/box-create.event' + +import { WithInstrumentation, WithSpan } from '../../common/decorators/otel.decorator' + +import { BoxStartAction } from './box-actions/box-start.action' +import { BoxStopAction } from './box-actions/box-stop.action' +import { BoxDestroyAction } from './box-actions/box-destroy.action' +import { SYNC_AGAIN, DONT_SYNC_AGAIN } from './box-actions/box.action' + +import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' +import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' +import { setTimeout } from 'timers/promises' +import { LogExecution } from '../../common/decorators/log-execution.decorator' +import { BoxRepository } from '../repositories/box.repository' +import { getStateChangeLockKey } from '../utils/lock-key.util' +import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' +import { sanitizeBoxError } from '../utils/sanitize-error.util' +import { Box } from '../entities/box.entity' +import { Job } from '../entities/job.entity' +import { JobType } from '../enums/job-type.enum' +import { JobStatus } from '../enums/job-status.enum' +import { ResourceType } from '../enums/resource-type.enum' + +// Auto-recovery bounds for the reconcile-errored loop. +const MAX_RECOVER_ATTEMPTS = 5 +const MAX_RECONCILE_PER_RUN = 200 +// Only reconcile boxes whose last change has settled for this long. +const RECONCILE_MIN_AGE_MS = 60_000 +// Window over which prior failed recovery attempts are counted against a box. +// Bounds auto-recovery within a burst while letting a long-quiet box be retried again later. +const RECONCILE_FAILURE_WINDOW_MS = 60 * 60 * 1000 + +@Injectable() +export class BoxManager implements TrackableJobExecutions, OnApplicationShutdown { + activeJobs = new Set() + + private readonly logger = new Logger(BoxManager.name) + + constructor( + private readonly boxRepository: BoxRepository, + private readonly runnerService: RunnerService, + private readonly redisLockProvider: RedisLockProvider, + private readonly boxStartAction: BoxStartAction, + private readonly boxStopAction: BoxStopAction, + private readonly boxDestroyAction: BoxDestroyAction, + @InjectRepository(Job) + private readonly jobRepository: Repository, + ) {} + + async onApplicationShutdown() { + // wait for all active jobs to finish + while (this.activeJobs.size > 0) { + this.logger.log(`Waiting for ${this.activeJobs.size} active jobs to finish`) + await setTimeout(1000) + } + } + + @Cron(CronExpression.EVERY_10_SECONDS, { name: 'auto-stop-check' }) + @TrackJobExecution() + @WithInstrumentation() + @LogExecution('auto-stop-check') + @WithInstrumentation() + async autostopCheck(): Promise { + const lockKey = 'auto-stop-check-worker-selected' + // lock the sync to only run one instance at a time + if (!(await this.redisLockProvider.lock(lockKey, 60))) { + return + } + + try { + const readyRunners = await this.runnerService.findAllReady() + + // Process all runners in parallel + await Promise.all( + readyRunners.map(async (runner) => { + const boxes = await this.boxRepository + .createQueryBuilder('box') + .innerJoin('box_last_activity', 'activity', 'activity."boxId" = box.id') + .where('box."runnerId" = :runnerId', { runnerId: runner.id }) + .andWhere('box."organizationId" != :warmPoolOrg', { + warmPoolOrg: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + }) + .andWhere('box.state = :state', { state: BoxState.STARTED }) + .andWhere('box."desiredState" = :desiredState', { + desiredState: BoxDesiredState.STARTED, + }) + .andWhere('box.pending != true') + .andWhere('box."autoStopInterval" != 0') + .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * box."autoStopInterval"') + .limit(100) + .getMany() + + await Promise.all( + boxes.map(async (box) => { + const lockKey = getStateChangeLockKey(box.id) + const acquired = await this.redisLockProvider.lock(lockKey, 30) + if (!acquired) { + return + } + + let updateData: Partial = {} + + // if auto-delete interval is 0, delete the box immediately + if (box.autoDeleteInterval === 0) { + updateData = Box.getSoftDeleteUpdate(box) + } else { + updateData.pending = true + updateData.desiredState = BoxDesiredState.STOPPED + } + + this.logger.log( + `Auto-stopping box ${box.id}: autoStopInterval=${box.autoStopInterval}min, autoDeleteInterval=${box.autoDeleteInterval}`, + ) + + try { + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: box.state }, + }) + + this.syncInstanceState(box.id).catch(this.logger.error) + } catch (error) { + this.logger.error(`Error processing auto-stop state for box ${box.id}:`, error) + } finally { + await this.redisLockProvider.unlock(lockKey) + } + }), + ) + }), + ) + } finally { + await this.redisLockProvider.unlock(lockKey) + } + } + + @Cron(CronExpression.EVERY_10_SECONDS, { name: 'auto-delete-check' }) + @TrackJobExecution() + @LogExecution('auto-delete-check') + @WithInstrumentation() + async autoDeleteCheck(): Promise { + const lockKey = 'auto-delete-check-worker-selected' + // lock the sync to only run one instance at a time + if (!(await this.redisLockProvider.lock(lockKey, 60))) { + return + } + + try { + const readyRunners = await this.runnerService.findAllReady() + + // Process all runners in parallel + await Promise.all( + readyRunners.map(async (runner) => { + const boxes = await this.boxRepository + .createQueryBuilder('box') + .innerJoin('box_last_activity', 'activity', 'activity."boxId" = box.id') + .where('box."runnerId" = :runnerId', { runnerId: runner.id }) + .andWhere('box."organizationId" != :warmPoolOrg', { + warmPoolOrg: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + }) + .andWhere('box.state = :state', { state: BoxState.STOPPED }) + .andWhere('box."desiredState" = :desiredState', { + desiredState: BoxDesiredState.STOPPED, + }) + .andWhere('box.pending != true') + .andWhere('box."autoDeleteInterval" >= 0') + .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * box."autoDeleteInterval"') + .orderBy('activity."lastActivityAt"', 'ASC') + .limit(100) + .getMany() + + await Promise.all( + boxes.map(async (box) => { + const lockKey = getStateChangeLockKey(box.id) + const acquired = await this.redisLockProvider.lock(lockKey, 30) + if (!acquired) { + return + } + + this.logger.log(`Auto-deleting box ${box.id}: autoDeleteInterval=${box.autoDeleteInterval}min`) + + try { + const updateData = Box.getSoftDeleteUpdate(box) + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: box.state }, + }) + + this.syncInstanceState(box.id).catch(this.logger.error) + } catch (error) { + this.logger.error(`Error processing auto-delete state for box ${box.id}:`, error) + } finally { + await this.redisLockProvider.unlock(lockKey) + } + }), + ) + }), + ) + } finally { + await this.redisLockProvider.unlock(lockKey) + } + } + + @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-states' }) + @TrackJobExecution() + @WithInstrumentation() + @LogExecution('sync-states') + async syncStates(): Promise { + const globalLockKey = 'sync-states' + const lockTtl = 10 * 60 // seconds (10 min) + if (!(await this.redisLockProvider.lock(globalLockKey, lockTtl))) { + return + } + + try { + const queryBuilder = this.boxRepository + .createQueryBuilder('box') + .select(['box.id']) + .leftJoin('box_last_activity', 'activity', 'activity."boxId" = box.id') + .where('box.state NOT IN (:...excludedStates)', { + excludedStates: [BoxState.DESTROYED, BoxState.ERROR, BoxState.RESIZING], + }) + .andWhere('box."desiredState"::text != box.state::text') + .andWhere('box."desiredState"::text IN (:...supportedDesiredStates)', { + supportedDesiredStates: [BoxDesiredState.STARTED, BoxDesiredState.STOPPED, BoxDesiredState.DESTROYED], + }) + .orderBy('activity."lastActivityAt"', 'DESC', 'NULLS LAST') + + const stream = await queryBuilder.stream() + let processedCount = 0 + const maxProcessPerRun = 1000 + const pendingProcesses: Promise[] = [] + + try { + await new Promise((resolve, reject) => { + stream.on('data', async (row: any) => { + if (processedCount >= maxProcessPerRun) { + resolve() + return + } + + const lockKey = getStateChangeLockKey(row.box_id) + if (await this.redisLockProvider.isLocked(lockKey)) { + // Box is already being processed, skip it + return + } + + // Process box asynchronously but track the promise + const processPromise = this.syncInstanceState(row.box_id).catch((err) => { + this.logger.error(`Error syncing box state for ${row.box_id}`, err) + }) + pendingProcesses.push(processPromise) + processedCount++ + + // Limit concurrent processing to avoid overwhelming the system + if (pendingProcesses.length >= 10) { + stream.pause() + Promise.allSettled(pendingProcesses.splice(0, pendingProcesses.length)) + .then(() => stream.resume()) + .catch(reject) + } + }) + + stream.on('end', () => { + Promise.allSettled(pendingProcesses) + .then(() => { + resolve() + }) + .catch(reject) + }) + + stream.on('error', reject) + }) + } finally { + if (!stream.destroyed) { + stream.destroy() + } + } + } finally { + await this.redisLockProvider.unlock(globalLockKey) + } + } + + /** + * Reconcile boxes that are stuck in ERROR but want to be STARTED. + * + * v2 runners report state only through job outcomes; a transient runner failure + * (saturation, timeout, a CREATE job that failed late while the box actually exists) + * sediments into a terminal ERROR that the regular sync loop deliberately skips, even + * though the box is healthy on the runner. This drives such boxes back toward their + * desired state by transitioning ERROR -> STOPPED, which lets the normal start flow + * issue an idempotent START_BOX (not a non-idempotent CREATE_BOX) on the existing box. + * + * Eligibility is deliberately NOT gated on the box.recoverable flag: that flag is set true + * only for storage-expansion errors (the runner's sole recoverable pattern), whereas the + * split-brain errors this loop targets — "box already exists" from a late-failing CREATE, + * job timeouts — are reported recoverable=false. The safety net is not that pre-filter but + * the idempotent START_BOX plus the bounded retry below: a box that truly cannot start fails + * START_BOX up to MAX_RECOVER_ATTEMPTS times and is then left alone, while a box that is + * actually alive on the runner is recovered on the first retry. + * + * Recovery is bounded by counting recent failed START_BOX jobs for the box so a genuinely + * dead box is not retried forever; the count is derived from the job table, not stored state. + */ + @Cron(CronExpression.EVERY_MINUTE, { name: 'reconcile-errored' }) + @TrackJobExecution() + @WithInstrumentation() + @LogExecution('reconcile-errored') + async reconcileErroredBoxes(): Promise { + const globalLockKey = 'reconcile-errored' + const lockTtl = 5 * 60 // seconds + if (!(await this.redisLockProvider.lock(globalLockKey, lockTtl))) { + return + } + + try { + // Only retry boxes whose last change has settled, to avoid racing an in-flight + // failure and to space out successive recovery attempts. + const settledBefore = new Date(Date.now() - RECONCILE_MIN_AGE_MS) + + const candidates = await this.boxRepository + .createQueryBuilder('box') + .select(['box.id', 'box.runnerId']) + .where('box.state = :state', { state: BoxState.ERROR }) + .andWhere('box.pending = false') + .andWhere('box."desiredState"::text = :desired', { desired: BoxDesiredState.STARTED }) + .andWhere('box."updatedAt" < :settledBefore', { settledBefore }) + .orderBy('box."updatedAt"', 'ASC') + .limit(MAX_RECONCILE_PER_RUN) + .getMany() + + const failureWindowStart = new Date(Date.now() - RECONCILE_FAILURE_WINDOW_MS) + + for (const candidate of candidates) { + if (!candidate.runnerId) { + continue + } + + // Scope to v2 runners: this is where the job-driven model produces the + // ERROR/runner split-brain. v1 runners are reconciled by their own paths. + if ((await this.runnerService.getRunnerApiVersion(candidate.runnerId)) !== '2') { + continue + } + + // Bound auto-recovery: each reconcile drives a START_BOX, so the number of recent + // failed START_BOX jobs for this box equals how many times we have already retried. + const recentFailures = await this.jobRepository.count({ + where: { + resourceType: ResourceType.BOX, + resourceId: candidate.id, + type: JobType.START_BOX, + status: JobStatus.FAILED, + createdAt: MoreThan(failureWindowStart), + }, + }) + if (recentFailures >= MAX_RECOVER_ATTEMPTS) { + continue + } + + // Skip boxes the sync loop is already touching. + const lockKey = getStateChangeLockKey(candidate.id) + if (await this.redisLockProvider.isLocked(lockKey)) { + continue + } + + try { + // Atomic ERROR -> STOPPED transition; the conditional whereCondition ensures + // only one transition wins and we never clobber a concurrent state change. + await this.boxRepository.updateWhere(candidate.id, { + updateData: { + state: BoxState.STOPPED, + errorReason: null, + recoverable: false, + }, + whereCondition: { + state: BoxState.ERROR, + pending: false, + desiredState: BoxDesiredState.STARTED, + runnerId: candidate.runnerId, + }, + }) + this.logger.warn( + `Reconcile: box ${candidate.id} ERROR -> STOPPED (attempt ${recentFailures + 1}/${MAX_RECOVER_ATTEMPTS}), letting start flow re-drive it`, + ) + } catch (error) { + if (error instanceof BoxConflictError) { + // Box changed under us (e.g. recovered or destroyed); nothing to do. + continue + } + this.logger.error(`Reconcile: failed to transition box ${candidate.id} out of ERROR`, error) + } + } + } finally { + await this.redisLockProvider.unlock(globalLockKey) + } + } + + /** + * Sync the state of a box. + * + * Loop to handle SYNC_AGAIN without releasing the lock or re-fetching. + * The box entity is mutated in-place by repository.update() on each iteration, + * and the lock guarantees no concurrent modification. + */ + async syncInstanceState(boxId: string, force?: boolean): Promise { + // Track the start time of the sync operation. + const startedAt = new Date() + + // Generate a random lock code to prevent race condition if box action continues after the lock expires. + const lockCode = new LockCode(randomUUID()) + + // Prevent syncState cron from running multiple instances of the same box. + const lockKey = getStateChangeLockKey(boxId) + const acquired = await this.redisLockProvider.lock(lockKey, 30, lockCode) + if (!acquired) { + return + } + + try { + const box = await this.boxRepository.findOneOrFail({ + where: { id: boxId }, + }) + + while (new Date().getTime() - startedAt.getTime() <= 10000) { + if ([BoxState.DESTROYED, BoxState.RESIZING].includes(box.state) || box.state === BoxState.ERROR) { + // Break sync loop if box reaches a terminal state. + break + } + + if (String(box.state) === String(box.desiredState)) { + this.logger.warn(`Box ${boxId} is already in the desired state ${box.desiredState}, skipping sync`) + // Break sync loop if box is already in the desired state. + break + } + + // Rely on the box action to return SYNC_AGAIN or DONT_SYNC_AGAIN to continue/break the sync loop. + let syncState = DONT_SYNC_AGAIN + + try { + switch (box.desiredState) { + case BoxDesiredState.STARTED: { + syncState = await this.boxStartAction.run(box, lockCode) + break + } + case BoxDesiredState.STOPPED: { + syncState = await this.boxStopAction.run(box, lockCode, force) + break + } + case BoxDesiredState.DESTROYED: { + syncState = await this.boxDestroyAction.run(box, lockCode) + break + } + } + } catch (error) { + if (error instanceof BoxConflictError) { + this.logger.warn(`Box ${boxId} was modified by another operation during sync, skipping error transition`) + break + } + + if (error instanceof JobConflictError) { + this.logger.debug(`Job already in progress for box ${boxId}, skipping`) + break + } + + this.logger.error(`Error processing desired state for box ${boxId}:`, error) + + const { recoverable, errorReason } = sanitizeBoxError(error) + + // A START_BOX attempt can throw before runnerAdapter.startBox() ever + // persists its START_BOX job (e.g. a synchronous runner lookup or + // adapter-creation failure). The reconcile retry ceiling counts FAILED + // START_BOX jobs, so without a record here it would keep flipping this + // box out of ERROR indefinitely. Record a terminal FAILED START_BOX job + // so the ceiling counts this attempt — best-effort, never blocking the + // ERROR transition. completedAt is set so the row stays outside the + // "one incomplete job per box" partial-unique index. + if (box.desiredState === BoxDesiredState.STARTED && box.runnerId) { + try { + await this.jobRepository.insert( + new Job({ + type: JobType.START_BOX, + runnerId: box.runnerId, + resourceType: ResourceType.BOX, + resourceId: boxId, + status: JobStatus.FAILED, + errorMessage: errorReason ?? null, + completedAt: new Date(), + }), + ) + } catch (jobError) { + this.logger.error( + `Failed to record FAILED START_BOX job for box ${boxId}; retry ceiling may undercount`, + jobError, + ) + } + } + + const updateData: Partial = { + state: BoxState.ERROR, + errorReason, + recoverable, + } + + // Update box to error state without safeguards + await this.boxRepository.updateWhere(boxId, { updateData, whereCondition: {} }) + + // Break sync loop since box is in error state. + break + } + + // Do not sync again for v2 runners + // Job completion will update the box state + if (box.runnerId && (await this.runnerService.getRunnerApiVersion(box.runnerId)) === '2') { + break + } + + // Break sync loop if box action returned DONT_SYNC_AGAIN. + if (syncState !== SYNC_AGAIN) { + break + } + } + } finally { + await this.redisLockProvider.unlock(lockKey) + } + } + + @OnAsyncEvent({ + event: BoxEvents.DESTROYED, + }) + @TrackJobExecution() + @WithSpan() + private async handleBoxDestroyedEvent(event: BoxDestroyedEvent) { + await this.syncInstanceState(event.box.id) + } + + @OnAsyncEvent({ + event: BoxEvents.STARTED, + }) + @TrackJobExecution() + @WithSpan() + private async handleBoxStartedEvent(event: BoxStartedEvent) { + await this.syncInstanceState(event.box.id) + } + + @OnAsyncEvent({ + event: BoxEvents.STOPPED, + }) + @TrackJobExecution() + @WithSpan() + private async handleBoxStoppedEvent(event: BoxStoppedEvent) { + await this.syncInstanceState(event.box.id, event.force) + } + + @OnAsyncEvent({ + event: BoxEvents.CREATED, + }) + @TrackJobExecution() + @WithSpan() + private async handleBoxCreatedEvent(event: BoxCreatedEvent) { + await this.syncInstanceState(event.box.id) + } +} diff --git a/apps/api/src/box/managers/volume.manager.spec.ts b/apps/api/src/box/managers/volume.manager.spec.ts new file mode 100644 index 000000000..ed42bfddb --- /dev/null +++ b/apps/api/src/box/managers/volume.manager.spec.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ListObjectsV2Command, S3Client } from '@aws-sdk/client-s3' +import { VolumeManager } from './volume.manager' + +const mockSend = jest.fn() + +jest.mock('@aws-sdk/client-s3', () => ({ + S3Client: jest.fn().mockImplementation(() => ({ send: mockSend })), + CreateBucketCommand: jest.fn().mockImplementation((input) => ({ input })), + ListObjectsV2Command: jest.fn().mockImplementation((input) => ({ input })), + PutBucketTaggingCommand: jest.fn().mockImplementation((input) => ({ input })), +})) + +describe('VolumeManager S3 client setup', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + function buildManager(values: Record) { + const configService = { + get: jest.fn((key: string) => values[key]), + getOrThrow: jest.fn((key: string) => { + const value = values[key] + if (value === undefined) { + throw new Error(`Missing config: ${key}`) + } + return value + }), + } + return new VolumeManager({} as any, configService as any, {} as any, {} as any, {} as any) + } + + const awsConfig = { + 's3.endpoint': 'https://s3.ap-southeast-1.amazonaws.com', + 's3.region': 'ap-southeast-1', + 's3.defaultBucket': 'boxlite-dev-storage', + } + + it('uses the SDK default chain and probes the known bucket instead of ListBuckets', async () => { + mockSend.mockResolvedValue({}) + const manager = buildManager(awsConfig) + + await manager.onModuleInit() + + // No `credentials` key at all → SDK default chain (ECS task role). + expect(S3Client).toHaveBeenCalledWith({ + endpoint: 'https://s3.ap-southeast-1.amazonaws.com', + region: 'ap-southeast-1', + forcePathStyle: true, + }) + // Scoped probe: no account-wide s3:ListAllMyBuckets needed. + expect(ListObjectsV2Command).toHaveBeenCalledWith({ Bucket: 'boxlite-dev-storage', MaxKeys: 1 }) + }) + + it('still passes static keys through when configured', () => { + buildManager({ ...awsConfig, 's3.accessKey': 'static-id', 's3.secretKey': 'static-secret' }) + + expect(S3Client).toHaveBeenCalledWith( + expect.objectContaining({ + credentials: { accessKeyId: 'static-id', secretAccessKey: 'static-secret' }, + }), + ) + }) + + it('skips the probe when no default bucket is configured', async () => { + const manager = buildManager({ 's3.endpoint': 'http://s3-compatible.local:9000', 's3.region': 'us-east-1' }) + + await manager.onModuleInit() + + expect(ListObjectsV2Command).not.toHaveBeenCalled() + expect(mockSend).not.toHaveBeenCalled() + }) + + it('rejects a lone static key instead of silently using the default chain', () => { + expect(() => buildManager({ ...awsConfig, 's3.accessKey': 'static-id' })).toThrow( + /S3_ACCESS_KEY and S3_SECRET_KEY must be set together/, + ) + }) + + it('fails fast when MinIO is configured without static keys', () => { + expect(() => buildManager({ 's3.endpoint': 'http://minio:9000', 's3.region': 'us-east-1' })).toThrow( + /MinIO requires S3_ACCESS_KEY and S3_SECRET_KEY/, + ) + }) +}) diff --git a/apps/api/src/sandbox/managers/volume.manager.ts b/apps/api/src/box/managers/volume.manager.ts similarity index 84% rename from apps/api/src/sandbox/managers/volume.manager.ts rename to apps/api/src/box/managers/volume.manager.ts index ae0b2cf6d..6dfb6e628 100644 --- a/apps/api/src/sandbox/managers/volume.manager.ts +++ b/apps/api/src/box/managers/volume.manager.ts @@ -10,7 +10,7 @@ import { Repository, In } from 'typeorm' import { Volume } from '../entities/volume.entity' import { VolumeState } from '../enums/volume-state.enum' import { Cron, CronExpression, SchedulerRegistry } from '@nestjs/schedule' -import { S3Client, CreateBucketCommand, ListBucketsCommand, PutBucketTaggingCommand } from '@aws-sdk/client-s3' +import { S3Client, CreateBucketCommand, ListObjectsV2Command, PutBucketTaggingCommand } from '@aws-sdk/client-s3' import { InjectRedis } from '@nestjs-modules/ioredis' import { Redis } from 'ioredis' import { RedisLockProvider } from '../common/redis-lock.provider' @@ -50,17 +50,28 @@ export class VolumeManager const endpoint = this.configService.getOrThrow('s3.endpoint') const region = this.configService.getOrThrow('s3.region') - const accessKeyId = this.configService.getOrThrow('s3.accessKey') - const secretAccessKey = this.configService.getOrThrow('s3.secretKey') + const accessKeyId = this.configService.get('s3.accessKey') + const secretAccessKey = this.configService.get('s3.secretKey') this.skipTestConnection = this.configService.get('skipConnections') + // Both-or-neither (mirrors observability-s3.reader.ts): a lone key is a + // typo'd pair, and silently falling back to the SDK default chain would + // mask the misconfig. + if ((accessKeyId && !secretAccessKey) || (!accessKeyId && secretAccessKey)) { + throw new Error('S3_ACCESS_KEY and S3_SECRET_KEY must be set together') + } + // MinIO cannot use the SDK default chain — fail fast at boot with a clear + // message instead of a generic auth error from the connection probe. + if (endpoint.includes('minio') && !accessKeyId) { + throw new Error('MinIO requires S3_ACCESS_KEY and S3_SECRET_KEY to be configured') + } + this.s3Client = new S3Client({ endpoint: endpoint.startsWith('http') ? endpoint : `http://${endpoint}`, region, - credentials: { - accessKeyId, - secretAccessKey, - }, + // Static keys for S3-compatible deployments (MinIO); unset on AWS, + // where the SDK default chain supplies the ECS task-role credentials. + ...(accessKeyId && secretAccessKey ? { credentials: { accessKeyId, secretAccessKey } } : {}), forcePathStyle: true, }) } @@ -95,9 +106,17 @@ export class VolumeManager } private async testConnection() { + // Probe a bucket we already know instead of ListBuckets: same + // connectivity+auth signal, but needs no account-wide + // s3:ListAllMyBuckets grant on the task role. + const bucket = this.configService.get('s3.defaultBucket') + if (!bucket) { + this.logger.debug('No default bucket configured; skipping S3 connection test') + return + } + try { - // Try a simple operation to test the connection - const command = new ListBucketsCommand({}) + const command = new ListObjectsV2Command({ Bucket: bucket, MaxKeys: 1 }) await this.s3Client.send(command) this.logger.debug('Successfully connected to S3') } catch (error) { diff --git a/apps/api/src/box/repositories/box.repository.ts b/apps/api/src/box/repositories/box.repository.ts new file mode 100644 index 000000000..2a9cb17ad --- /dev/null +++ b/apps/api/src/box/repositories/box.repository.ts @@ -0,0 +1,268 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { DataSource, EntityManager, FindOptionsWhere } from 'typeorm' +import { Box } from '../entities/box.entity' +import { BoxLastActivity } from '../entities/box-last-activity.entity' +import { Injectable, Logger, NotFoundException } from '@nestjs/common' +import { BoxConflictError } from '../errors/box-conflict.error' +import { InjectDataSource } from '@nestjs/typeorm' +import { EventEmitter2 } from '@nestjs/event-emitter' +import { BaseRepository } from '../../common/repositories/base.repository' +import { BoxEvents } from '../constants/box-events.constants' +import { BoxStateUpdatedEvent } from '../events/box-state-updated.event' +import { BoxDesiredStateUpdatedEvent } from '../events/box-desired-state-updated.event' +import { BoxPublicStatusUpdatedEvent } from '../events/box-public-status-updated.event' +import { BoxOrganizationUpdatedEvent } from '../events/box-organization-updated.event' +import { BoxLookupCacheInvalidationService } from '../services/box-lookup-cache-invalidation.service' + +@Injectable() +export class BoxRepository extends BaseRepository { + private readonly logger = new Logger(BoxRepository.name) + + constructor( + @InjectDataSource() dataSource: DataSource, + eventEmitter: EventEmitter2, + private readonly boxLookupCacheInvalidationService: BoxLookupCacheInvalidationService, + ) { + super(dataSource, eventEmitter, Box) + } + + async insert(box: Box): Promise { + const now = new Date() + if (!box.createdAt) { + box.createdAt = now + } + if (!box.updatedAt) { + box.updatedAt = now + } + + box.assertValid() + box.enforceInvariants() + + await this.dataSource.transaction(async (entityManager) => { + await entityManager.insert(Box, box) + await this.upsertLastActivity(entityManager, box.id, box.createdAt) + }) + + this.invalidateLookupCacheOnInsert(box) + + return box + } + + /** + * @param id - The ID of the box to update. + * @param params.updateData - The partial data to update. + * + * @returns `void` because a raw update is performed. + */ + async update(id: string, params: { updateData: Partial }, raw: true): Promise + /** + * @param id - The ID of the box to update. + * @param params.updateData - The partial data to update. + * @param params.entity - Optional pre-fetched box to use instead of fetching from the database. + * + * @returns The updated box. + */ + async update(id: string, params: { updateData: Partial; entity?: Box }, raw?: false): Promise + async update(id: string, params: { updateData: Partial; entity?: Box }, raw = false): Promise { + const { updateData, entity } = params + + if (raw) { + await this.repository.update(id, updateData) + return + } + + const box = entity ?? (await this.findOneBy({ id })) + if (!box) { + throw new NotFoundException('Box not found') + } + + const previousBox = { ...box } + + Object.assign(box, updateData) + box.assertValid() + const invariantChanges = box.enforceInvariants() + + await this.dataSource.transaction(async (entityManager) => { + const result = await entityManager.update( + Box, + { + id: previousBox.id, + state: previousBox.state, + desiredState: previousBox.desiredState, + pending: previousBox.pending, + organizationId: previousBox.organizationId, + }, + { ...updateData, ...invariantChanges }, + ) + if (!result.affected) { + throw new BoxConflictError() + } + box.updatedAt = new Date() + + if (previousBox.state !== box.state || previousBox.organizationId !== box.organizationId) { + await this.upsertLastActivity(entityManager, id, box.updatedAt) + } + }) + + this.emitUpdateEvents(box, previousBox) + this.invalidateLookupCacheOnUpdate(box, previousBox) + + return box + } + + /** + * Partially updates a box in the database and optionally emits a corresponding event based on the changes. + * + * Performs the update in a transaction with a pessimistic write lock to ensure consistency. + * + * @param id - The ID of the box to update. + * @param params.updateData - The partial data to update. + * @param params.whereCondition - The where condition to use for the update. + * + * @throws {BoxConflictError} if the box was modified by another operation + */ + async updateWhere( + id: string, + params: { + updateData: Partial + whereCondition: FindOptionsWhere + }, + ): Promise { + const { updateData, whereCondition } = params + + return this.manager.transaction(async (entityManager) => { + const whereClause = { + ...whereCondition, + id, + } + + const box = await entityManager.findOne(Box, { + where: whereClause, + lock: { mode: 'pessimistic_write' }, + relations: [], + loadEagerRelations: false, + }) + + if (!box) { + throw new BoxConflictError() + } + + const previousBox = { ...box } + + Object.assign(box, updateData) + box.assertValid() + const invariantChanges = box.enforceInvariants() + + await entityManager.update(Box, id, { ...updateData, ...invariantChanges }) + box.updatedAt = new Date() + + if (previousBox.state !== box.state || previousBox.organizationId !== box.organizationId) { + await this.upsertLastActivity(entityManager, id, box.updatedAt) + } + + this.emitUpdateEvents(box, previousBox) + this.invalidateLookupCacheOnUpdate(box, previousBox) + + return box + }) + } + + /** + * Upserts the last activity for a box. + */ + private async upsertLastActivity(entityManager: EntityManager, boxId: string, lastActivityAt: Date): Promise { + await entityManager.upsert(BoxLastActivity, { boxId, lastActivityAt }, ['boxId']) + } + + /** + * Invalidates the box lookup cache for the inserted box. + */ + private invalidateLookupCacheOnInsert(box: Box): void { + try { + this.boxLookupCacheInvalidationService.invalidateOrgId({ + id: box.id, + organizationId: box.organizationId, + name: box.name, + }) + } catch (error) { + this.logger.warn( + `Failed to enqueue box lookup cache invalidation on insert (id, organizationId, name) for ${box.id}: ${error instanceof Error ? error.message : String(error)}`, + ) + } + } + + /** + * Invalidates the box lookup cache for the updated box. + */ + private invalidateLookupCacheOnUpdate( + updatedBox: Box, + previousBox: Pick, + ): void { + try { + this.boxLookupCacheInvalidationService.invalidate({ + id: updatedBox.id, + organizationId: updatedBox.organizationId, + previousOrganizationId: previousBox.organizationId, + name: updatedBox.name, + previousName: previousBox.name, + }) + } catch (error) { + this.logger.warn( + `Failed to enqueue box lookup cache invalidation on update (id, organizationId, name) for ${updatedBox.id}: ${error instanceof Error ? error.message : String(error)}`, + ) + } + + try { + if (updatedBox.authToken !== previousBox.authToken) { + this.boxLookupCacheInvalidationService.invalidate({ + authToken: updatedBox.authToken, + }) + } + } catch (error) { + this.logger.warn( + `Failed to enqueue box lookup cache invalidation on update (authToken) for ${updatedBox.id}: ${error instanceof Error ? error.message : String(error)}`, + ) + } + } + + /** + * Emits events based on the changes made to a box. + */ + private emitUpdateEvents( + updatedBox: Box, + previousBox: Pick, + ): void { + if (previousBox.state !== updatedBox.state) { + this.eventEmitter.emit( + BoxEvents.STATE_UPDATED, + new BoxStateUpdatedEvent(updatedBox, previousBox.state, updatedBox.state), + ) + } + + if (previousBox.desiredState !== updatedBox.desiredState) { + this.eventEmitter.emit( + BoxEvents.DESIRED_STATE_UPDATED, + new BoxDesiredStateUpdatedEvent(updatedBox, previousBox.desiredState, updatedBox.desiredState), + ) + } + + if (previousBox.public !== updatedBox.public) { + this.eventEmitter.emit( + BoxEvents.PUBLIC_STATUS_UPDATED, + new BoxPublicStatusUpdatedEvent(updatedBox, previousBox.public, updatedBox.public), + ) + } + + if (previousBox.organizationId !== updatedBox.organizationId) { + this.eventEmitter.emit( + BoxEvents.ORGANIZATION_UPDATED, + new BoxOrganizationUpdatedEvent(updatedBox, previousBox.organizationId, updatedBox.organizationId), + ) + } + } +} diff --git a/apps/api/src/box/runner-adapter/runnerAdapter.ts b/apps/api/src/box/runner-adapter/runnerAdapter.ts new file mode 100644 index 000000000..51801b019 --- /dev/null +++ b/apps/api/src/box/runner-adapter/runnerAdapter.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { Runner } from '../entities/runner.entity' +import { ModuleRef } from '@nestjs/core' +import { RunnerAdapterV0 } from './runnerAdapter.v0' +import { RunnerAdapterV2 } from './runnerAdapter.v2' +import { Box } from '../entities/box.entity' +import { BoxState } from '../enums/box-state.enum' +import { RunnerServiceInfo } from '../common/runner-service-info' + +export interface RunnerBoxInfo { + state: BoxState + daemonVersion?: string +} + +export interface RunnerMetrics { + currentAllocatedCpu?: number + currentAllocatedDiskGiB?: number + currentAllocatedMemoryGiB?: number + currentCpuUsagePercentage?: number + currentDiskUsagePercentage?: number + currentMemoryUsagePercentage?: number + currentStartedBoxes?: number +} + +export interface RunnerInfo { + serviceHealth?: RunnerServiceInfo[] + metrics?: RunnerMetrics + appVersion?: string +} + +export interface StartBoxResponse { + daemonVersion: string +} + +export interface RunnerAdapter { + init(runner: Runner): Promise + + healthCheck(signal?: AbortSignal): Promise + + runnerInfo(signal?: AbortSignal): Promise + + boxInfo(boxId: string): Promise + createBox(box: Box, metadata?: { [key: string]: string }): Promise + startBox( + boxId: string, + authToken: string, + metadata?: { [key: string]: string }, + skipStart?: boolean, + ): Promise + stopBox(boxId: string, force?: boolean): Promise + destroyBox(boxId: string): Promise + + updateNetworkSettings( + boxId: string, + networkBlockAll?: boolean, + networkAllowList?: string, + networkLimitEgress?: boolean, + ): Promise + + recoverBox(box: Box): Promise + + resizeBox(boxId: string, cpu?: number, memory?: number, disk?: number): Promise +} + +@Injectable() +export class RunnerAdapterFactory { + private readonly logger = new Logger(RunnerAdapterFactory.name) + + constructor(private moduleRef: ModuleRef) {} + + async create(runner: Runner): Promise { + switch (runner.apiVersion) { + case '0': { + const adapter = await this.moduleRef.create(RunnerAdapterV0) + await adapter.init(runner) + return adapter + } + case '2': { + const adapter = await this.moduleRef.create(RunnerAdapterV2) + await adapter.init(runner) + return adapter + } + default: + throw new Error(`Unsupported runner version: ${runner.apiVersion}`) + } + } +} diff --git a/apps/api/src/box/runner-adapter/runnerAdapter.v0.ts b/apps/api/src/box/runner-adapter/runnerAdapter.v0.ts new file mode 100644 index 000000000..520d380e1 --- /dev/null +++ b/apps/api/src/box/runner-adapter/runnerAdapter.v0.ts @@ -0,0 +1,341 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import axios, { AxiosError } from 'axios' +import axiosDebug from 'axios-debug-log' +import axiosRetry from 'axios-retry' + +import { Injectable, Logger } from '@nestjs/common' +import { RunnerAdapter, RunnerInfo, RunnerBoxInfo, StartBoxResponse } from './runnerAdapter' +import { Runner } from '../entities/runner.entity' +import { + Configuration, + BoxApi, + EnumsBoxState, + DefaultApi, + UpdateNetworkSettingsDTO, + RecoverBoxDTO, +} from '@boxlite-ai/runner-api-client' +import { Box } from '../entities/box.entity' +import { BoxState } from '../enums/box-state.enum' +import { RunnerApiError } from '../errors/runner-api-error' + +const isDebugEnabled = process.env.DEBUG === 'true' + +// Network error codes that should trigger a retry +const RETRYABLE_NETWORK_ERROR_CODES = ['ECONNRESET', 'ETIMEDOUT'] +const RUNNER_NON_JSON_ERROR_CODE = 'runner_non_json_error' +const NON_JSON_SNIPPET_MAX_LENGTH = 180 + +function statusCodeFrom(error: AxiosError): number | undefined { + return error.response?.status || (error as any).status +} + +function codeFrom(error: AxiosError, data: unknown): string { + if (data && typeof data === 'object' && !Array.isArray(data)) { + const responseCode = (data as Record).code + if (typeof responseCode === 'string' && responseCode) { + return responseCode + } + } + + return (error as any).code || (error as any).cause?.code || 'RUNNER_API_ERROR' +} + +function messageFromJson(data: Record, fallback: string): string { + const responseMessage = data.message + if (Array.isArray(responseMessage)) { + return responseMessage.join(', ') + } + if (typeof responseMessage === 'string' && responseMessage) { + return responseMessage + } + if (typeof data.error === 'string' && data.error) { + return data.error + } + return fallback +} + +function visibleTextFromMarkup(input: string): string { + let output = '' + let index = 0 + let skipping: 'script' | 'style' | null = null + + while (index < input.length) { + const char = input[index] + if (char !== '<') { + if (!skipping) { + output += char + } + index += 1 + continue + } + + const closeIndex = input.indexOf('>', index + 1) + if (closeIndex === -1) { + if (!skipping) { + output += input.slice(index) + } + break + } + + const tagBody = input + .slice(index + 1, closeIndex) + .trim() + .toLowerCase() + const tagName = tagBody.replace(/^\//, '').split(/\s+/)[0] + if (!tagBody.startsWith('/') && (tagName === 'script' || tagName === 'style')) { + skipping = tagName + } else if (skipping && tagBody.startsWith('/') && tagName === skipping) { + skipping = null + } else if (!skipping) { + output += ' ' + } + + index = closeIndex + 1 + } + + return output +} + +export function sanitizedNonJsonRunnerMessage(data: unknown): string { + const raw = typeof data === 'string' ? data : Buffer.isBuffer(data) ? data.toString('utf8') : JSON.stringify(data) + const text = visibleTextFromMarkup(raw) + .replace(/\b(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1[redacted]') + .replace(/\b(access_token|id_token|api_key|token)=([^&\s]+)/gi, '$1=[redacted]') + .replace(/\s+/g, ' ') + .trim() + + if (!text) { + return 'Runner API returned a non-JSON error response' + } + + const suffix = text.length > NON_JSON_SNIPPET_MAX_LENGTH ? '...' : '' + return `Runner API returned a non-JSON error response: ${text.slice(0, NON_JSON_SNIPPET_MAX_LENGTH)}${suffix}` +} + +@Injectable() +export class RunnerAdapterV0 implements RunnerAdapter { + private readonly logger = new Logger(RunnerAdapterV0.name) + private boxApiClient: BoxApi + private runnerApiClient: DefaultApi + + private convertBoxState(state: EnumsBoxState): BoxState { + switch (state) { + case EnumsBoxState.BoxStateCreating: + return BoxState.CREATING + case EnumsBoxState.BoxStateRestoring: + return BoxState.RESTORING + case EnumsBoxState.BoxStateDestroyed: + return BoxState.DESTROYED + case EnumsBoxState.BoxStateDestroying: + return BoxState.DESTROYING + case EnumsBoxState.BoxStateStarted: + return BoxState.STARTED + case EnumsBoxState.BoxStateStopped: + return BoxState.STOPPED + case EnumsBoxState.BoxStateStarting: + return BoxState.STARTING + case EnumsBoxState.BoxStateStopping: + return BoxState.STOPPING + case EnumsBoxState.BoxStateError: + return BoxState.ERROR + default: + return BoxState.UNKNOWN + } + } + + public async init(runner: Runner): Promise { + if (!runner.apiUrl) { + throw new Error('Runner API URL is required') + } + + const axiosInstance = axios.create({ + baseURL: runner.apiUrl, + headers: { + Authorization: `Bearer ${runner.apiKey}`, + }, + timeout: 1 * 60 * 60 * 1000, // 1 hour + }) + + const retryErrorMap = new WeakMap() + + // Configure axios-retry to handle network errors + axiosRetry(axiosInstance, { + retries: 3, + retryDelay: axiosRetry.exponentialDelay, + retryCondition: (error) => { + // Check if error code or message matches any retryable error + const matchedErrorCode = RETRYABLE_NETWORK_ERROR_CODES.find( + (code) => + (error as any).code === code || error.message?.includes(code) || (error as any).cause?.code === code, + ) + + if (matchedErrorCode) { + retryErrorMap.set(error, matchedErrorCode) + return true + } + + return false + }, + onRetry: (retryCount, error, requestConfig) => { + this.logger.warn( + `Retrying request due to ${retryErrorMap.get(error)} (attempt ${retryCount}): ${requestConfig.method?.toUpperCase()} ${requestConfig.url}`, + ) + }, + }) + + axiosInstance.interceptors.response.use( + (response) => { + return response + }, + (error) => { + const data = error.response?.data + const statusCode = + data && typeof data === 'object' && !Array.isArray(data) && typeof data.statusCode === 'number' + ? data.statusCode + : statusCodeFrom(error) + + if (data && typeof data === 'object' && !Array.isArray(data) && !Buffer.isBuffer(data)) { + throw new RunnerApiError( + messageFromJson(data as Record, error.message), + statusCode, + codeFrom(error, data), + ) + } + + if (data !== undefined && data !== null && data !== '') { + const sanitizedMessage = sanitizedNonJsonRunnerMessage(data) + this.logger.warn(`Runner API returned non-JSON error (${statusCode || 'no status'}): ${sanitizedMessage}`) + throw new RunnerApiError(sanitizedMessage, statusCode, RUNNER_NON_JSON_ERROR_CODE) + } + + throw new RunnerApiError(error.message || 'Runner API request failed', statusCode, codeFrom(error, data)) + }, + ) + + if (isDebugEnabled) { + axiosDebug.addLogger(axiosInstance) + } + + this.boxApiClient = new BoxApi(new Configuration(), '', axiosInstance) + this.runnerApiClient = new DefaultApi(new Configuration(), '', axiosInstance) + } + + async healthCheck(signal?: AbortSignal): Promise { + const response = await this.runnerApiClient.healthCheck({ signal }) + if (response.data.status !== 'ok') { + throw new Error('Runner is not healthy') + } + } + + async runnerInfo(signal?: AbortSignal): Promise { + const response = await this.runnerApiClient.runnerInfo({ signal }) + return { + serviceHealth: response.data.serviceHealth, + metrics: response.data.metrics, + appVersion: response.data.appVersion, + } + } + + async boxInfo(boxId: string): Promise { + const boxInfo = await this.boxApiClient.info(boxId) + return { + state: this.convertBoxState(boxInfo.data.state), + daemonVersion: boxInfo.data.daemonVersion, + } + } + + async createBox(box: Box, metadata?: { [key: string]: string }): Promise { + const response = await this.boxApiClient.create({ + id: box.id, + image: box.image ?? '', + osUser: box.osUser, + cpuQuota: box.cpu, + gpuQuota: box.gpu, + memoryQuota: box.mem, + storageQuota: box.disk, + env: box.env, + networkBlockAll: box.networkBlockAll, + networkAllowList: box.networkAllowList, + metadata, + authToken: box.authToken, + organizationId: box.organizationId, + regionId: box.region, + }) + + if (!response?.data?.daemonVersion) { + return undefined + } + + return { + daemonVersion: response.data.daemonVersion, + } + } + + async startBox( + boxId: string, + authToken: string, + metadata?: { [key: string]: string }, + ): Promise { + const response = await this.boxApiClient.start(boxId, authToken, metadata) + + if (!response?.data?.daemonVersion) { + return undefined + } + + return { + daemonVersion: response.data.daemonVersion, + } + } + + async stopBox(boxId: string, force?: boolean): Promise { + await this.boxApiClient.stop(boxId, { force }) + } + + async destroyBox(boxId: string): Promise { + await this.boxApiClient.destroy(boxId) + } + + async updateNetworkSettings( + boxId: string, + networkBlockAll?: boolean, + networkAllowList?: string, + networkLimitEgress?: boolean, + ): Promise { + const updateNetworkSettingsDto: UpdateNetworkSettingsDTO = { + networkBlockAll: networkBlockAll, + networkAllowList: networkAllowList, + networkLimitEgress: networkLimitEgress, + } + + await this.boxApiClient.updateNetworkSettings(boxId, updateNetworkSettingsDto) + } + + async recoverBox(box: Box): Promise { + const recoverBoxDTO: RecoverBoxDTO = { + osUser: box.osUser, + cpuQuota: box.cpu, + gpuQuota: box.gpu, + memoryQuota: box.mem, + storageQuota: box.disk, + env: box.env, + volumes: box.volumes?.map((volume) => ({ + volumeId: volume.volumeId, + mountPath: volume.mountPath, + subpath: volume.subpath, + })), + networkBlockAll: box.networkBlockAll, + networkAllowList: box.networkAllowList, + errorReason: box.errorReason, + } + await this.boxApiClient.recover(box.id, recoverBoxDTO) + } + + async resizeBox(boxId: string, cpu?: number, memory?: number, disk?: number): Promise { + await this.boxApiClient.resize(boxId, { cpu, memory, disk }) + } +} diff --git a/apps/api/src/box/runner-adapter/runnerAdapter.v2.ts b/apps/api/src/box/runner-adapter/runnerAdapter.v2.ts new file mode 100644 index 000000000..d07c4bb16 --- /dev/null +++ b/apps/api/src/box/runner-adapter/runnerAdapter.v2.ts @@ -0,0 +1,237 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Repository, IsNull } from 'typeorm' +import { RunnerAdapter, RunnerInfo, RunnerBoxInfo, StartBoxResponse } from './runnerAdapter' +import { Runner } from '../entities/runner.entity' +import { Box } from '../entities/box.entity' +import { Job } from '../entities/job.entity' +import { BoxState } from '../enums/box-state.enum' +import { JobType } from '../enums/job-type.enum' +import { JobStatus } from '../enums/job-status.enum' +import { ResourceType } from '../enums/resource-type.enum' +import { JobService } from '../services/job.service' +import { BoxRepository } from '../repositories/box.repository' +import { UpdateNetworkSettingsDTO, RecoverBoxDTO } from '@boxlite-ai/runner-api-client' + +/** + * RunnerAdapterV2 implements RunnerAdapter for v2 runners. + * Instead of making direct API calls to the runner, it creates jobs in the database + * that the v2 runner polls and processes asynchronously. + */ +@Injectable() +export class RunnerAdapterV2 implements RunnerAdapter { + private readonly logger = new Logger(RunnerAdapterV2.name) + private runner: Runner + + constructor( + private readonly boxRepository: BoxRepository, + @InjectRepository(Job) + private readonly jobRepository: Repository, + private readonly jobService: JobService, + ) {} + + async init(runner: Runner): Promise { + this.runner = runner + } + + async healthCheck(_signal?: AbortSignal): Promise { + throw new Error('healthCheck is not supported for V2 runners') + } + + async runnerInfo(_signal?: AbortSignal): Promise { + throw new Error('runnerInfo is not supported for V2 runners') + } + + async boxInfo(boxId: string): Promise { + // Query the box entity + const box = await this.boxRepository.findOne({ + where: { id: boxId }, + }) + + if (!box) { + throw new Error(`Box ${boxId} not found`) + } + + // Query for any incomplete jobs for this box to determine transitional state + const incompleteJob = await this.jobRepository.findOne({ + where: { + resourceType: ResourceType.BOX, + resourceId: boxId, + completedAt: IsNull(), + }, + order: { createdAt: 'DESC' }, + }) + + let state = box.state + + let daemonVersion: string | undefined = undefined + + // If there's an incomplete job, infer the transitional state from job type + if (incompleteJob) { + state = this.inferStateFromJob(incompleteJob, box) + daemonVersion = incompleteJob.getResultMetadata()?.daemonVersion + } else { + // Look for latest job for this box + const latestJob = await this.jobRepository.findOne({ + where: { + resourceType: ResourceType.BOX, + resourceId: boxId, + }, + order: { createdAt: 'DESC' }, + }) + if (latestJob) { + state = this.inferStateFromJob(latestJob, box) + daemonVersion = latestJob.getResultMetadata()?.daemonVersion + } + } + + return { + state, + daemonVersion, + } + } + + private inferStateFromJob(job: Job, box: Box): BoxState { + // Map job types to transitional states + switch (job.type) { + case JobType.CREATE_BOX: + return job.status === JobStatus.COMPLETED ? BoxState.STARTED : BoxState.CREATING + case JobType.START_BOX: + return job.status === JobStatus.COMPLETED ? BoxState.STARTED : BoxState.STARTING + case JobType.STOP_BOX: + return job.status === JobStatus.COMPLETED ? BoxState.STOPPED : BoxState.STOPPING + case JobType.DESTROY_BOX: + return job.status === JobStatus.COMPLETED ? BoxState.DESTROYED : BoxState.DESTROYING + default: + // For other job types (backup, etc.), return current box state + return box.state + } + } + + async createBox(box: Box, metadata?: { [key: string]: string }): Promise { + if (!box.image) { + throw new Error(`Box ${box.id} has no image; cannot create on runner`) + } + + const payload = { + id: box.id, + image: box.image, + osUser: box.osUser, + cpuQuota: box.cpu, + gpuQuota: box.gpu, + memoryQuota: box.mem, + storageQuota: box.disk, + env: box.env, + volumes: box.volumes?.map((volume) => ({ + volumeId: volume.volumeId, + mountPath: volume.mountPath, + subpath: volume.subpath, + })), + networkBlockAll: box.networkBlockAll, + networkAllowList: box.networkAllowList, + metadata, + authToken: box.authToken, + organizationId: box.organizationId, + regionId: box.region, + } + + await this.jobService.createJob(null, JobType.CREATE_BOX, this.runner.id, ResourceType.BOX, box.id, payload) + + this.logger.debug(`Created CREATE_BOX job for box ${box.id} on runner ${this.runner.id}`) + + // Daemon version is set in the job result metadata once the runner completes the job. + return undefined + } + + async startBox( + boxId: string, + authToken: string, + metadata?: { [key: string]: string }, + ): Promise { + await this.jobService.createJob(null, JobType.START_BOX, this.runner.id, ResourceType.BOX, boxId, { + authToken, + metadata, + }) + + this.logger.debug(`Created START_BOX job for box ${boxId} on runner ${this.runner.id}`) + + // Daemon version will be set in the job result metadata + return undefined + } + + async stopBox(boxId: string, force?: boolean): Promise { + await this.jobService.createJob(null, JobType.STOP_BOX, this.runner.id, ResourceType.BOX, boxId, { + force, + }) + + this.logger.debug(`Created STOP_BOX job for box ${boxId} on runner ${this.runner.id}`) + } + + async destroyBox(boxId: string): Promise { + await this.jobService.createJob(null, JobType.DESTROY_BOX, this.runner.id, ResourceType.BOX, boxId) + + this.logger.debug(`Created DESTROY_BOX job for box ${boxId} on runner ${this.runner.id}`) + } + + async recoverBox(box: Box): Promise { + const recoverBoxDTO: RecoverBoxDTO = { + osUser: box.osUser, + cpuQuota: box.cpu, + gpuQuota: box.gpu, + memoryQuota: box.mem, + storageQuota: box.disk, + env: box.env, + volumes: box.volumes?.map((volume) => ({ + volumeId: volume.volumeId, + mountPath: volume.mountPath, + subpath: volume.subpath, + })), + networkBlockAll: box.networkBlockAll, + networkAllowList: box.networkAllowList, + errorReason: box.errorReason, + } + await this.jobService.createJob(null, JobType.RECOVER_BOX, this.runner.id, ResourceType.BOX, box.id, recoverBoxDTO) + + this.logger.debug(`Created RECOVER_BOX job for box ${box.id} on runner ${this.runner.id}`) + } + + async updateNetworkSettings( + boxId: string, + networkBlockAll?: boolean, + networkAllowList?: string, + networkLimitEgress?: boolean, + ): Promise { + const payload: UpdateNetworkSettingsDTO = { + networkBlockAll: networkBlockAll, + networkAllowList: networkAllowList, + networkLimitEgress: networkLimitEgress, + } + + await this.jobService.createJob( + null, + JobType.UPDATE_BOX_NETWORK_SETTINGS, + this.runner.id, + ResourceType.BOX, + boxId, + payload, + ) + + this.logger.debug(`Created UPDATE_BOX_NETWORK_SETTINGS job for box ${boxId} on runner ${this.runner.id}`) + } + + async resizeBox(boxId: string, cpu?: number, memory?: number, disk?: number): Promise { + await this.jobService.createJob(null, JobType.RESIZE_BOX, this.runner.id, ResourceType.BOX, boxId, { + cpu, + memory, + disk, + }) + + this.logger.debug(`Created RESIZE_BOX job for box ${boxId} on runner ${this.runner.id}`) + } +} diff --git a/apps/api/src/box/services/box-activity.service.ts b/apps/api/src/box/services/box-activity.service.ts new file mode 100644 index 000000000..e8a7781c3 --- /dev/null +++ b/apps/api/src/box/services/box-activity.service.ts @@ -0,0 +1,179 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { InjectRedis } from '@nestjs-modules/ioredis' +import Redis from 'ioredis' +import { InjectDataSource } from '@nestjs/typeorm' +import { DataSource, IsNull, Raw } from 'typeorm' +import { Cron, CronExpression } from '@nestjs/schedule' +import { RedisLockProvider } from '../common/redis-lock.provider' +import { BoxLastActivity } from '../entities/box-last-activity.entity' +import { LogExecution } from '../../common/decorators/log-execution.decorator' +import { WithInstrumentation } from '../../common/decorators/otel.decorator' +import { TypedConfigService } from '../../config/typed-config.service' + +const REDIS_ACTIVITY_KEY = 'box:activity' + +interface BoxActivityUpdate { + boxId: string + lastActivityAt: Date +} + +@Injectable() +export class BoxActivityService { + private readonly logger = new Logger(BoxActivityService.name) + + constructor( + @InjectRedis() private readonly redis: Redis, + @InjectDataSource() private readonly dataSource: DataSource, + private readonly redisLockProvider: RedisLockProvider, + private readonly configService: TypedConfigService, + ) {} + + /** + * Buffers a last activity timestamp in Redis (throttled to once per configured throttle TTL). + * + * Relies on the periodic flush to the database. + */ + async updateLastActivityAt(boxId: string, lastActivityAt: Date): Promise { + const lockKey = `box:update-last-activity:${boxId}` + const acquired = await this.redisLockProvider.lock( + lockKey, + this.configService.getOrThrow('boxActivity.throttleTtlSeconds'), + ) + if (!acquired) { + return + } + await this.redis.zadd(REDIS_ACTIVITY_KEY, lastActivityAt.getTime(), boxId) + } + + /** + * Read the last activity timestamp for a box. + * + * Checks Redis buffer first, falls back to the database. + */ + async getLastActivityAt(boxId: string): Promise { + const score = await this.redis.zscore(REDIS_ACTIVITY_KEY, boxId) + if (score !== null) { + return new Date(Number(score)) + } + + const row = await this.dataSource.getRepository(BoxLastActivity).findOne({ where: { boxId } }) + + return row?.lastActivityAt ?? null + } + + /** + * Flush buffered activity timestamps from Redis to the database in bulk. + * Processes entries in batches to avoid oversized transactions. + * + * Frequency must be < 1min to prevent unintended auto-lifecycle actions. + */ + @Cron(CronExpression.EVERY_10_SECONDS, { name: 'flush-activity-to-db' }) + @LogExecution('flush-activity-to-db') + @WithInstrumentation() + async flushActivityToDb(): Promise { + const lockKey = 'flush-activity-to-db-lock' + const lockTtl = 30 + const acquired = await this.redisLockProvider.lock(lockKey, lockTtl) + if (!acquired) { + return + } + + try { + let totalFlushed = 0 + + const batchSize = this.configService.getOrThrow('boxActivity.flushBatchSize') + const maxScore = Date.now() + + const entries = await this.redis.zrangebyscore(REDIS_ACTIVITY_KEY, '-inf', maxScore, 'WITHSCORES') + + if (entries.length === 0) { + return + } + + const updates: BoxActivityUpdate[] = [] + for (let i = 0; i < entries.length; i += 2) { + updates.push({ + boxId: entries[i], + lastActivityAt: new Date(Number(entries[i + 1])), + }) + } + + for (let offset = 0; offset < updates.length; offset += batchSize) { + const batch = updates.slice(offset, offset + batchSize) + await this.bulkUpsertActivity(batch) + totalFlushed += batch.length + } + + await this.redis.zremrangebyscore(REDIS_ACTIVITY_KEY, '-inf', maxScore) + + if (totalFlushed > 0) { + this.logger.debug(`Flushed ${totalFlushed} activity timestamps to the database`) + } + } catch (error) { + this.logger.error('Error flushing activity timestamps to the database:', error) + } finally { + await this.redisLockProvider.unlock(lockKey) + } + } + + /** + * Builds a query to upsert activity timestamps into the database. + * + * Uses a conditional upsert that only updates when the incoming timestamp is newer, preventing updates to stale buffered values. + */ + private buildUpsertQuery(values: BoxActivityUpdate | BoxActivityUpdate[]) { + return this.dataSource + .createQueryBuilder() + .insert() + .into(BoxLastActivity) + .values(values) + .orUpdate(['lastActivityAt'], ['boxId'], { + overwriteCondition: { + where: [ + { lastActivityAt: IsNull() }, + { lastActivityAt: Raw((alias) => `${alias} < EXCLUDED."lastActivityAt"`) }, + ], + }, + }) + } + + /** + * Bulk upserts activity timestamps into the database. + * + * In case of FK violations, falls back to individual upserts to skip deleted box(es). + */ + private async bulkUpsertActivity(updates: BoxActivityUpdate[]): Promise { + if (updates.length === 0) { + this.logger.debug('No activity updates to flush') + return + } + + try { + await this.buildUpsertQuery(updates).execute() + } catch (bulkUpsertError) { + if (bulkUpsertError.code === '23503') { + this.logger.warn( + 'Bulk upsert for activity timestamps failed with FK violation, falling back to individual upserts', + ) + for (const update of updates) { + try { + await this.buildUpsertQuery(update).execute() + } catch (error) { + if (error.code === '23503') { + this.logger.warn(`Skipping activity flush for box ${update.boxId} (deleted)`) + } else { + throw error + } + } + } + } else { + throw bulkUpsertError + } + } + } +} diff --git a/apps/api/src/box/services/box-lookup-cache-invalidation.service.ts b/apps/api/src/box/services/box-lookup-cache-invalidation.service.ts new file mode 100644 index 000000000..ec532cc25 --- /dev/null +++ b/apps/api/src/box/services/box-lookup-cache-invalidation.service.ts @@ -0,0 +1,156 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { DataSource } from 'typeorm' +import { + boxLookupCacheKeyByAuthToken, + boxLookupCacheKeyById, + boxLookupCacheKeyByName, + boxOrgIdCacheKeyById, + boxOrgIdCacheKeyByName, +} from '../utils/box-lookup-cache.util' + +type InvalidateBoxLookupCacheArgs = + | { + id: string + organizationId: string + name: string + previousOrganizationId?: string | null + previousName?: string | null + } + | { + authToken: string + } + +@Injectable() +export class BoxLookupCacheInvalidationService { + private readonly logger = new Logger(BoxLookupCacheInvalidationService.name) + + constructor(private readonly dataSource: DataSource) {} + + invalidate(args: InvalidateBoxLookupCacheArgs): void { + const cache = this.dataSource.queryResultCache + if (!cache) { + return + } + + if ('authToken' in args) { + cache + .remove([boxLookupCacheKeyByAuthToken({ authToken: args.authToken })]) + .then(() => this.logger.debug(`Invalidated box lookup cache for authToken ${args.authToken}`)) + .catch((error) => + this.logger.warn( + `Failed to invalidate box lookup cache for authToken ${args.authToken}: ${error instanceof Error ? error.message : String(error)}`, + ), + ) + return + } + + const organizationIds = Array.from( + new Set( + [args.organizationId, args.previousOrganizationId].filter((id): id is string => + Boolean(id && id.trim().length > 0), + ), + ), + ) + const names = Array.from( + new Set([args.name, args.previousName].filter((n): n is string => Boolean(n && n.trim().length > 0))), + ) + + const cacheIds: string[] = [] + for (const organizationId of organizationIds) { + for (const returnDestroyed of [false, true]) { + cacheIds.push( + boxLookupCacheKeyById({ + organizationId, + returnDestroyed, + id: args.id, + }), + ) + for (const boxName of names) { + cacheIds.push( + boxLookupCacheKeyByName({ + organizationId, + returnDestroyed, + boxName, + }), + ) + } + } + } + + if (cacheIds.length === 0) { + return + } + + cache + .remove(cacheIds) + .then(() => this.logger.debug(`Invalidated box lookup cache for ${args.id}`)) + .catch((error) => + this.logger.warn( + `Failed to invalidate box lookup cache for ${args.id}: ${error instanceof Error ? error.message : String(error)}`, + ), + ) + } + + invalidateOrgId(args: { + id: string + organizationId: string + name: string + previousOrganizationId?: string | null + previousName?: string | null + }): void { + const cache = this.dataSource.queryResultCache + if (!cache) { + return + } + + const organizationIds = Array.from( + new Set( + [args.organizationId, args.previousOrganizationId].filter((id): id is string => + Boolean(id && id.trim().length > 0), + ), + ), + ) + const names = Array.from( + new Set([args.name, args.previousName].filter((n): n is string => Boolean(n && n.trim().length > 0))), + ) + + const cacheIds: string[] = [] + for (const organizationId of organizationIds) { + cacheIds.push( + boxOrgIdCacheKeyById({ + organizationId, + id: args.id, + }), + ) + for (const boxName of names) { + cacheIds.push( + boxOrgIdCacheKeyByName({ + organizationId, + boxName, + }), + ) + } + } + + // Also invalidate the "no org" variants (when organizationId was not provided to getOrganizationId) + cacheIds.push(boxOrgIdCacheKeyById({ id: args.id })) + for (const boxName of names) { + cacheIds.push(boxOrgIdCacheKeyByName({ boxName })) + } + + cache + .remove(cacheIds) + .then(() => this.logger.debug(`Invalidated box orgId cache for ${args.id}`)) + .catch((error) => + this.logger.warn( + `Failed to invalidate box orgId cache for ${args.id}: ${error instanceof Error ? error.message : String(error)}`, + ), + ) + } +} diff --git a/apps/api/src/box/services/box-state-waiter.service.ts b/apps/api/src/box/services/box-state-waiter.service.ts new file mode 100644 index 000000000..023ae5a21 --- /dev/null +++ b/apps/api/src/box/services/box-state-waiter.service.ts @@ -0,0 +1,118 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { InjectRedis } from '@nestjs-modules/ioredis' +import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common' +import Redis from 'ioredis' +import { BadRequestError } from '../../exceptions/bad-request.exception' +import { BOX_EVENT_CHANNEL } from '../../common/constants/constants' +import { BoxDto } from '../dto/box.dto' +import { BoxState } from '../enums/box-state.enum' +import { BoxStateUpdatedEvent } from '../events/box-state-updated.event' +import { BoxService } from './box.service' + +@Injectable() +export class BoxStateWaiterService implements OnModuleDestroy { + private readonly logger = new Logger(BoxStateWaiterService.name) + private readonly callbacks = new Map void>() + private readonly redisSubscriber: Redis + + constructor( + private readonly boxService: BoxService, + @InjectRedis() private readonly redis: Redis, + ) { + this.redisSubscriber = this.redis.duplicate() + this.redisSubscriber.subscribe(BOX_EVENT_CHANNEL) + this.redisSubscriber.on('message', (channel, message) => { + if (channel !== BOX_EVENT_CHANNEL) { + return + } + + try { + const event = JSON.parse(message) as BoxStateUpdatedEvent + const callback = this.callbacks.get(event.box.id) + if (callback) { + callback(event) + } + } catch (error) { + this.logger.error('Failed to parse box state updated event:', error) + } + }) + } + + async onModuleDestroy() { + await this.redisSubscriber.quit() + } + + async waitForStarted(boxId: string, organizationId: string, timeoutSeconds: number): Promise { + const current = await this.boxService.findOneByIdOrName(boxId, organizationId) + + if (current.state === BoxState.STARTED) { + return this.boxService.toBoxDto(current) + } + + this.assertNotFailed(current.state, current.errorReason) + + return new Promise((resolve, reject) => { + let latestBox = current + let timeout: NodeJS.Timeout + + const finish = async (box = latestBox) => { + this.callbacks.delete(boxId) + clearTimeout(timeout) + resolve(await this.boxService.toBoxDto(box)) + } + + const fail = (error: Error) => { + this.callbacks.delete(boxId) + clearTimeout(timeout) + reject(error) + } + + const handleStateUpdated = (event: BoxStateUpdatedEvent) => { + if (event.box.id !== boxId) { + return + } + + latestBox = event.box + + if (event.box.state === BoxState.STARTED) { + finish(event.box).catch(fail) + return + } + + try { + this.assertNotFailed(event.box.state, event.box.errorReason) + } catch (error) { + fail(error) + } + } + + this.callbacks.set(boxId, handleStateUpdated) + + this.boxService + .findOneByIdOrName(boxId, organizationId) + .then((box) => { + latestBox = box + if (box.state === BoxState.STARTED) { + return finish(box) + } + this.assertNotFailed(box.state, box.errorReason) + }) + .catch(fail) + + timeout = setTimeout(() => { + finish().catch(fail) + }, timeoutSeconds * 1000) + }) + } + + private assertNotFailed(state: BoxState, errorReason?: string | null) { + if (state === BoxState.ERROR) { + throw new BadRequestError(`Box failed to start: ${errorReason || 'Unknown error'}`) + } + } +} diff --git a/apps/api/src/box/services/box-warm-pool.service.ts b/apps/api/src/box/services/box-warm-pool.service.ts new file mode 100644 index 000000000..d92e32cb2 --- /dev/null +++ b/apps/api/src/box/services/box-warm-pool.service.ts @@ -0,0 +1,236 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Inject, Injectable, Logger } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Cron, CronExpression } from '@nestjs/schedule' +import { In, MoreThan, Not, Repository } from 'typeorm' +import { RedisLockProvider } from '../common/redis-lock.provider' +import { BoxRepository } from '../repositories/box.repository' +import { Box } from '../entities/box.entity' +import { BOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/box.constants' +import { WarmPool } from '../entities/warm-pool.entity' +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter' +import { BoxEvents } from '../constants/box-events.constants' +import { BoxOrganizationUpdatedEvent } from '../events/box-organization-updated.event' +import { ConfigService } from '@nestjs/config' +import { BoxClass } from '../enums/box-class.enum' +import { BoxState } from '../enums/box-state.enum' +import { Runner } from '../entities/runner.entity' +import { WarmPoolTopUpRequested } from '../events/warmpool-topup-requested.event' +import { WarmPoolEvents } from '../constants/warmpool-events.constants' +import { InjectRedis } from '@nestjs-modules/ioredis' +import { Redis } from 'ioredis' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { LogExecution } from '../../common/decorators/log-execution.decorator' +import { WithInstrumentation } from '../../common/decorators/otel.decorator' + +export type FetchWarmPoolBoxParams = { + image: string + target: string + class: BoxClass + cpu: number + mem: number + disk: number + gpu: number + osUser: string + env: { [key: string]: string } + organizationId: string + state: string +} + +@Injectable() +export class BoxWarmPoolService { + private readonly logger = new Logger(BoxWarmPoolService.name) + + constructor( + @InjectRepository(WarmPool) + private readonly warmPoolRepository: Repository, + private readonly boxRepository: BoxRepository, + @InjectRepository(Runner) + private readonly runnerRepository: Repository, + private readonly redisLockProvider: RedisLockProvider, + private readonly configService: ConfigService, + @Inject(EventEmitter2) + private eventEmitter: EventEmitter2, + @InjectRedis() private readonly redis: Redis, + ) {} + + // on init + async onApplicationBootstrap() { + // await this.adHocBackupCheck() + } + + async fetchWarmPoolBox(params: FetchWarmPoolBoxParams): Promise { + // check if box is warm pool + const warmPoolItem = await this.warmPoolRepository.findOne({ + where: { + image: params.image, + target: params.target, + class: params.class, + cpu: params.cpu, + mem: params.mem, + disk: params.disk, + gpu: params.gpu, + osUser: params.osUser, + env: params.env, + pool: MoreThan(0), + }, + }) + if (warmPoolItem) { + const availabilityScoreThreshold = this.configService.getOrThrow('runnerScore.thresholds.availability') + + // Build subquery to find excluded runners (unschedulable OR low score) + const excludedRunnersSubquery = this.runnerRepository + .createQueryBuilder('runner') + .select('runner.id') + .where('runner.region = :region') + .andWhere('(runner.unschedulable = true OR runner.availabilityScore < :scoreThreshold)') + + const queryBuilder = this.boxRepository + .createQueryBuilder('box') + .where('box.image = :image', { image: warmPoolItem.image }) + .andWhere('box.class = :class', { class: warmPoolItem.class }) + .andWhere('box.cpu = :cpu', { cpu: warmPoolItem.cpu }) + .andWhere('box.mem = :mem', { mem: warmPoolItem.mem }) + .andWhere('box.disk = :disk', { disk: warmPoolItem.disk }) + .andWhere('box.osUser = :osUser', { osUser: warmPoolItem.osUser }) + .andWhere('box.env = :env', { env: warmPoolItem.env }) + .andWhere('box.organizationId = :organizationId', { + organizationId: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + }) + .andWhere('box.region = :region', { region: warmPoolItem.target }) + .andWhere('box.state = :state', { state: BoxState.STARTED }) + .andWhere(`box.runnerId NOT IN (${excludedRunnersSubquery.getQuery()})`) + .setParameters({ + region: warmPoolItem.target, + scoreThreshold: availabilityScoreThreshold, + }) + + const candidateLimit = this.configService.getOrThrow('warmPool.candidateLimit') + const warmPoolBoxes = await queryBuilder.orderBy('RANDOM()').take(candidateLimit).getMany() + + // make sure we only release warm pool box once + let warmPoolBox: Box | null = null + for (const box of warmPoolBoxes) { + const lockKey = `box-warm-pool-${box.id}` + if (!(await this.redisLockProvider.lock(lockKey, 10))) { + continue + } + + warmPoolBox = box + break + } + + return warmPoolBox + } + + // no warm pool config exists for this image — cache it so callers can skip + await this.redis.set(`warm-pool:skip:${params.image}`, '1', 'EX', 60) + + return null + } + + // todo: make frequency configurable or more efficient + @Cron(CronExpression.EVERY_10_SECONDS, { name: 'warm-pool-check' }) + @LogExecution('warm-pool-check') + @WithInstrumentation() + async warmPoolCheck(): Promise { + const warmPoolItems = await this.warmPoolRepository.find() + + await Promise.all( + warmPoolItems.map(async (warmPoolItem) => { + const lockKey = `warm-pool-lock-${warmPoolItem.id}` + if (!(await this.redisLockProvider.lock(lockKey, 720))) { + return + } + + const boxCount = await this.boxRepository.count({ + where: { + organizationId: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + image: warmPoolItem.image, + class: warmPoolItem.class, + osUser: warmPoolItem.osUser, + env: warmPoolItem.env, + region: warmPoolItem.target, + cpu: warmPoolItem.cpu, + gpu: warmPoolItem.gpu, + mem: warmPoolItem.mem, + disk: warmPoolItem.disk, + desiredState: BoxDesiredState.STARTED, + state: Not(In([BoxState.ERROR])), + }, + }) + + const missingCount = warmPoolItem.pool - boxCount + if (missingCount > 0) { + const promises = [] + this.logger.debug(`Creating ${missingCount} boxes for warm pool id ${warmPoolItem.id}`) + + for (let i = 0; i < missingCount; i++) { + promises.push( + this.eventEmitter.emitAsync(WarmPoolEvents.TOPUP_REQUESTED, new WarmPoolTopUpRequested(warmPoolItem)), + ) + } + + // Wait for all promises to settle before releasing the lock. Otherwise, another worker could start creating boxes + await Promise.allSettled(promises) + } + + await this.redisLockProvider.unlock(lockKey) + }), + ) + } + + @OnEvent(BoxEvents.ORGANIZATION_UPDATED) + async handleBoxOrganizationUpdated(event: BoxOrganizationUpdatedEvent) { + if (event.newOrganizationId === BOX_WARM_POOL_UNASSIGNED_ORGANIZATION) { + return + } + const warmPoolItem = await this.warmPoolRepository.findOne({ + where: { + image: event.box.image, + class: event.box.class, + cpu: event.box.cpu, + mem: event.box.mem, + disk: event.box.disk, + target: event.box.region, + env: event.box.env, + gpu: event.box.gpu, + osUser: event.box.osUser, + }, + }) + + if (!warmPoolItem) { + return + } + + const boxCount = await this.boxRepository.count({ + where: { + organizationId: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + image: warmPoolItem.image, + class: warmPoolItem.class, + osUser: warmPoolItem.osUser, + env: warmPoolItem.env, + region: warmPoolItem.target, + cpu: warmPoolItem.cpu, + gpu: warmPoolItem.gpu, + mem: warmPoolItem.mem, + disk: warmPoolItem.disk, + desiredState: BoxDesiredState.STARTED, + state: Not(In([BoxState.ERROR])), + }, + }) + + if (warmPoolItem.pool <= boxCount) { + return + } + + if (warmPoolItem) { + this.eventEmitter.emit(WarmPoolEvents.TOPUP_REQUESTED, new WarmPoolTopUpRequested(warmPoolItem)) + } + } +} diff --git a/apps/api/src/box/services/box.service.box-id.spec.ts b/apps/api/src/box/services/box.service.box-id.spec.ts new file mode 100644 index 000000000..cd7e72062 --- /dev/null +++ b/apps/api/src/box/services/box.service.box-id.spec.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +jest.mock('uuid', () => ({ v4: () => '00000000-0000-4000-8000-000000000000' })) + +import { Not } from 'typeorm' +import { Box } from '../entities/box.entity' +import { BoxState } from '../enums/box-state.enum' +import { BoxService } from './box.service' + +function createService(findOne: jest.Mock): BoxService { + const service = Object.create(BoxService.prototype) as BoxService + ;(service as any).boxRepository = { findOne } + return service +} + +describe('BoxService public identity lookup', () => { + it('resolves the public id directly before falling back to name', async () => { + const organizationId = '057963b2-60ca-4356-81fc-11503e15f249' + const box = new Box('us', 'data-loader') + box.organizationId = organizationId + + const findOne = jest.fn().mockResolvedValueOnce(box) + const service = createService(findOne) + + await expect(service.findOneByIdOrName(box.id, organizationId)).resolves.toBe(box) + + expect(findOne).toHaveBeenCalledTimes(1) + expect(findOne).toHaveBeenCalledWith( + expect.objectContaining({ + where: { + id: box.id, + organizationId, + state: Not(BoxState.DESTROYED), + }, + }), + ) + }) +}) diff --git a/apps/api/src/box/services/box.service.quota.spec.ts b/apps/api/src/box/services/box.service.quota.spec.ts new file mode 100644 index 000000000..2660616c6 --- /dev/null +++ b/apps/api/src/box/services/box.service.quota.spec.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BadRequestException } from '@nestjs/common' +import { assertWithinPerBoxLimits } from './per-box-limits' + +const limits = { maxCpuPerBox: 4, maxMemoryPerBox: 8, maxDiskPerBox: 20 } + +describe('assertWithinPerBoxLimits', () => { + it('allows a request within the per-box limits', () => { + expect(() => assertWithinPerBoxLimits(4, 8, 20, limits)).not.toThrow() + expect(() => assertWithinPerBoxLimits(1, 1, 10, limits)).not.toThrow() + }) + + it('rejects cpu above the per-box limit (the cpu=999 leak)', () => { + expect(() => assertWithinPerBoxLimits(999, 8, 20, limits)).toThrow(BadRequestException) + }) + + it('rejects memory above the per-box limit (the 8 PiB leak)', () => { + // 8_192_000_000 MiB === the absurd value that used to be persisted. + expect(() => assertWithinPerBoxLimits(4, 8_192_000_000, 20, limits)).toThrow(BadRequestException) + }) + + it('rejects disk above the per-box limit', () => { + expect(() => assertWithinPerBoxLimits(4, 8, 9999, limits)).toThrow(BadRequestException) + }) + + it('reports every violated dimension in one message', () => { + expect(() => assertWithinPerBoxLimits(999, 999, 999, limits)).toThrow(/cpu .*memory .*disk/s) + }) + + it('treats a non-positive limit as unset (not enforced)', () => { + const unset = { maxCpuPerBox: 0, maxMemoryPerBox: 0, maxDiskPerBox: 0 } + expect(() => assertWithinPerBoxLimits(999, 999, 999, unset)).not.toThrow() + }) +}) diff --git a/apps/api/src/box/services/box.service.ts b/apps/api/src/box/services/box.service.ts new file mode 100644 index 000000000..63b8093a9 --- /dev/null +++ b/apps/api/src/box/services/box.service.ts @@ -0,0 +1,1481 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ForbiddenException, Injectable, Logger, NotFoundException, ConflictException } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Not, Repository, LessThan, In, JsonContains, FindOptionsWhere, ILike } from 'typeorm' +import { Box } from '../entities/box.entity' +import { CreateBoxDto } from '../dto/create-box.dto' +import { ResizeBoxDto } from '../dto/resize-box.dto' +import { BoxState } from '../enums/box-state.enum' +import { BoxClass } from '../enums/box-class.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { RunnerService } from './runner.service' +import { BoxError } from '../../exceptions/box-error.exception' +import { BadRequestError } from '../../exceptions/bad-request.exception' +import { Cron, CronExpression } from '@nestjs/schedule' +import { BOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/box.constants' +import { assertSupportedImage } from '../constants/curated-images.constant' +import { BoxWarmPoolService } from './box-warm-pool.service' +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter' +import { WarmPoolEvents } from '../constants/warmpool-events.constants' +import { WarmPoolTopUpRequested } from '../events/warmpool-topup-requested.event' +import { Runner } from '../entities/runner.entity' +import { Organization } from '../../organization/entities/organization.entity' +import { BoxEvents } from '../constants/box-events.constants' +import { BoxStateUpdatedEvent } from '../events/box-state-updated.event' +import { BoxDestroyedEvent } from '../events/box-destroyed.event' +import { BoxStartedEvent } from '../events/box-started.event' +import { BoxStoppedEvent } from '../events/box-stopped.event' +import { OrganizationService } from '../../organization/services/organization.service' +import { OrganizationEvents } from '../../organization/constants/organization-events.constant' +import { OrganizationSuspendedBoxStoppedEvent } from '../../organization/events/organization-suspended-box-stopped.event' +import { TypedConfigService } from '../../config/typed-config.service' +import { WarmPool } from '../entities/warm-pool.entity' +import { BoxDto, BoxVolume } from '../dto/box.dto' +import { RunnerAdapterFactory } from '../runner-adapter/runnerAdapter' +import { validateNetworkAllowList } from '../utils/network-validation.util' +import { SshAccess } from '../entities/ssh-access.entity' +import { SshAccessDto, SshAccessValidationDto } from '../dto/ssh-access.dto' +import { VolumeService } from './volume.service' +import { PaginatedList } from '../../common/interfaces/paginated-list.interface' +import { + BoxSortField, + BoxSortDirection, + DEFAULT_BOX_SORT_FIELD, + DEFAULT_BOX_SORT_DIRECTION, +} from '../dto/list-boxes-query.dto' +import { createRangeFilter } from '../../common/utils/range-filter' +import { LogExecution } from '../../common/decorators/log-execution.decorator' +import { RedisLockProvider } from '../common/redis-lock.provider' +import { customAlphabet as customNanoid, nanoid, urlAlphabet } from 'nanoid' +import { WithInstrumentation } from '../../common/decorators/otel.decorator' +import { validateMountPaths, validateSubpaths } from '../utils/volume-mount-path-validation.util' +import { BoxRepository } from '../repositories/box.repository' +import { PortPreviewUrlDto, SignedPortPreviewUrlDto } from '../dto/port-preview-url.dto' +import { RegionService } from '../../region/services/region.service' +import { BoxCreatedEvent } from '../events/box-create.event' +import { InjectRedis } from '@nestjs-modules/ioredis' +import { Redis } from 'ioredis' +import { + BOX_LOOKUP_CACHE_TTL_MS, + BOX_ORG_ID_CACHE_TTL_MS, + TOOLBOX_PROXY_URL_CACHE_TTL_S, + boxLookupCacheKeyById, + boxLookupCacheKeyByName, + boxOrgIdCacheKeyById, + boxOrgIdCacheKeyByName, + toolboxProxyUrlCacheKey, +} from '../utils/box-lookup-cache.util' +import { BoxLookupCacheInvalidationService } from './box-lookup-cache-invalidation.service' +import { Region } from '../../region/entities/region.entity' +import { BoxActivityService } from './box-activity.service' +import { assertWithinPerBoxLimits } from './per-box-limits' + +// TODO(image-rewrite): resource defaults previously came from the removed image subsystem; +// these mirror the Box entity column defaults until image resolution is rebuilt. +const DEFAULT_BOX_CPU = 1 +const DEFAULT_BOX_MEM = 1 +const DEFAULT_BOX_DISK = 10 +const DEFAULT_BOX_GPU = 0 + +@Injectable() +export class BoxService { + private readonly logger = new Logger(BoxService.name) + + constructor( + private readonly boxRepository: BoxRepository, + @InjectRepository(Runner) + private readonly runnerRepository: Repository, + @InjectRepository(SshAccess) + private readonly sshAccessRepository: Repository, + private readonly runnerService: RunnerService, + private readonly volumeService: VolumeService, + private readonly configService: TypedConfigService, + private readonly warmPoolService: BoxWarmPoolService, + private readonly eventEmitter: EventEmitter2, + private readonly organizationService: OrganizationService, + private readonly runnerAdapterFactory: RunnerAdapterFactory, + private readonly redisLockProvider: RedisLockProvider, + @InjectRedis() private readonly redis: Redis, + private readonly regionService: RegionService, + private readonly boxLookupCacheInvalidationService: BoxLookupCacheInvalidationService, + private readonly boxActivityService: BoxActivityService, + ) {} + + protected getLockKey(id: string): string { + return `box:${id}:state-change` + } + + private assertBoxNotErrored(box: Box): void { + if (box.state === BoxState.ERROR) { + throw new BoxError('Box is in an errored state') + } + } + + async createForWarmPool(warmPoolItem: WarmPool): Promise { + const box = new Box(warmPoolItem.target) + + box.organizationId = BOX_WARM_POOL_UNASSIGNED_ORGANIZATION + + box.class = warmPoolItem.class + box.image = warmPoolItem.image + // TODO: default user should be configurable + box.osUser = 'boxlite' + box.env = warmPoolItem.env || {} + + box.cpu = warmPoolItem.cpu + box.gpu = warmPoolItem.gpu + box.mem = warmPoolItem.mem + box.disk = warmPoolItem.disk + + // TODO(image-rewrite): box image resolution removed with the image subsystem; rebuild here. + const runner = await this.runnerService.getRandomAvailableRunner({ + regions: [box.region], + boxClass: box.class, + }) + + box.runnerId = runner.id + box.pending = true + + await this.boxRepository.insert(box) + return box + } + + async create(createBoxDto: CreateBoxDto, organization: Organization): Promise { + const region = await this.getValidatedOrDefaultRegion(organization, createBoxDto.target) + + try { + const boxClass = this.getValidatedOrDefaultClass(createBoxDto.class) + + // TODO(image-rewrite): image resolution removed; boxes can no + // longer resolve an image at create time. Resource sizing falls back to request values + // (or Box entity defaults). Rebuild image resolution here. + const cpu = createBoxDto.cpu ?? DEFAULT_BOX_CPU + const mem = createBoxDto.memory ?? DEFAULT_BOX_MEM + const disk = createBoxDto.disk ?? DEFAULT_BOX_DISK + const gpu = createBoxDto.gpu ?? DEFAULT_BOX_GPU + // Reject over-limit requests at the boundary (the "security option" + // per-box ceilings) rather than persisting out-of-range values. + assertWithinPerBoxLimits(cpu, mem, disk, organization) + // Restrict box creation to the supported pinned images; reject anything else + // at the request boundary (defaults undefined -> base image). + const image = assertSupportedImage(createBoxDto.image) + + this.organizationService.assertOrganizationIsNotSuspended(organization) + + if (createBoxDto.volumes && createBoxDto.volumes.length > 0) { + const volumeIdOrNames = createBoxDto.volumes.map((v) => v.volumeId) + await this.volumeService.validateVolumes(organization.id, volumeIdOrNames) + } else if (image) { + // No volumes requested — try to claim a pre-warmed box matching this image/spec + // before creating a fresh one. + const skipWarmPool = (await this.redis.exists(`warm-pool:skip:${image}`)) === 1 + if (!skipWarmPool) { + const warmPoolBox = await this.warmPoolService.fetchWarmPoolBox({ + organizationId: organization.id, + image, + target: region.id, + class: boxClass, + cpu, + mem, + disk, + gpu, + osUser: createBoxDto.user || 'boxlite', + env: createBoxDto.env || {}, + state: BoxState.STARTED, + }) + + if (warmPoolBox) { + return await this.assignWarmPoolBox(warmPoolBox, createBoxDto, organization) + } + } + } + + const runner = await this.runnerService.getRandomAvailableRunner({ + regions: [region.id], + boxClass, + }) + + const box = new Box(region.id, createBoxDto.name) + + box.organizationId = organization.id + + // TODO: make configurable + box.class = boxClass + // TODO: default user should be configurable + box.osUser = createBoxDto.user || 'boxlite' + box.env = createBoxDto.env || {} + box.labels = createBoxDto.labels || {} + + box.image = image + box.cpu = cpu + box.gpu = gpu + box.mem = mem + box.disk = disk + + box.public = createBoxDto.public || false + + if (createBoxDto.networkBlockAll !== undefined) { + box.networkBlockAll = createBoxDto.networkBlockAll + } + + if (createBoxDto.networkAllowList !== undefined) { + box.networkAllowList = this.resolveNetworkAllowList(createBoxDto.networkAllowList) + } + + if (createBoxDto.autoStopInterval !== undefined) { + box.autoStopInterval = this.resolveAutoStopInterval(createBoxDto.autoStopInterval) + } + + if (createBoxDto.autoDeleteInterval !== undefined) { + box.autoDeleteInterval = createBoxDto.autoDeleteInterval + } + + if (createBoxDto.volumes !== undefined) { + box.volumes = this.resolveVolumes(createBoxDto.volumes) + } + + box.runnerId = runner.id + box.pending = true + + const insertedBox = await this.boxRepository.insert(box) + + this.eventEmitter + .emitAsync(BoxEvents.CREATED, new BoxCreatedEvent(insertedBox)) + .catch((err) => this.logger.error('Failed to emit BoxCreatedEvent', err)) + + return this.toBoxDto(insertedBox) + } catch (error) { + if (error.code === '23505') { + throw new ConflictException(`Box with name ${createBoxDto.name} already exists`) + } + + throw error + } + } + + private async assignWarmPoolBox( + warmPoolBox: Box, + createBoxDto: CreateBoxDto, + organization: Organization, + ): Promise { + const now = new Date() + const updateData: Partial = { + public: createBoxDto.public || false, + labels: createBoxDto.labels || {}, + organizationId: organization.id, + createdAt: now, + } + + if (createBoxDto.name) { + updateData.name = createBoxDto.name + } + + if (createBoxDto.autoStopInterval !== undefined) { + updateData.autoStopInterval = this.resolveAutoStopInterval(createBoxDto.autoStopInterval) + } + + if (createBoxDto.autoDeleteInterval !== undefined) { + updateData.autoDeleteInterval = createBoxDto.autoDeleteInterval + } + + if (createBoxDto.networkBlockAll !== undefined) { + updateData.networkBlockAll = createBoxDto.networkBlockAll + } + + if (createBoxDto.networkAllowList !== undefined) { + updateData.networkAllowList = this.resolveNetworkAllowList(createBoxDto.networkAllowList) + } + + if (!warmPoolBox.runnerId) { + throw new BoxError('Runner not found for warm pool box') + } + + if ( + createBoxDto.networkBlockAll !== undefined || + createBoxDto.networkAllowList !== undefined || + organization.boxLimitedNetworkEgress + ) { + const runner = await this.runnerService.findOneOrFail(warmPoolBox.runnerId) + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + await runnerAdapter.updateNetworkSettings( + warmPoolBox.id, + createBoxDto.networkBlockAll, + createBoxDto.networkAllowList, + organization.boxLimitedNetworkEgress, + ) + } + + const updatedBox = await this.boxRepository.update(warmPoolBox.id, { + updateData, + entity: warmPoolBox, + }) + + // Defensive invalidation of orgId cache since the box moved from unassigned to a real organization + this.boxLookupCacheInvalidationService.invalidateOrgId({ + id: warmPoolBox.id, + organizationId: organization.id, + name: warmPoolBox.name, + previousOrganizationId: BOX_WARM_POOL_UNASSIGNED_ORGANIZATION, + }) + + // Treat this as a newly started box + this.eventEmitter.emit( + BoxEvents.STATE_UPDATED, + new BoxStateUpdatedEvent(updatedBox, BoxState.STARTED, BoxState.STARTED), + ) + return this.toBoxDto(updatedBox) + } + + async findAllDeprecated( + organizationId: string, + labels?: { [key: string]: string }, + includeErroredDestroyed?: boolean, + ): Promise { + const baseFindOptions: FindOptionsWhere = { + organizationId, + ...(labels ? { labels: JsonContains(labels) } : {}), + } + + const where: FindOptionsWhere[] = [ + { + ...baseFindOptions, + state: Not(In([BoxState.DESTROYED, BoxState.ERROR])), + }, + { + ...baseFindOptions, + state: BoxState.ERROR, + ...(includeErroredDestroyed ? {} : { desiredState: Not(BoxDesiredState.DESTROYED) }), + }, + ] + + return this.boxRepository.find({ where }) + } + + async findAll( + organizationId: string, + page = 1, + limit = 10, + filters?: { + id?: string + name?: string + labels?: { [key: string]: string } + includeErroredDestroyed?: boolean + states?: BoxState[] + regionIds?: string[] + minCpu?: number + maxCpu?: number + minMemoryGiB?: number + maxMemoryGiB?: number + minDiskGiB?: number + maxDiskGiB?: number + lastEventAfter?: Date + lastEventBefore?: Date + }, + sort?: { + field?: BoxSortField + direction?: BoxSortDirection + }, + ): Promise> { + const pageNum = Number(page) + const limitNum = Number(limit) + + const { + id, + name, + labels, + includeErroredDestroyed, + states, + regionIds, + minCpu, + maxCpu, + minMemoryGiB, + maxMemoryGiB, + minDiskGiB, + maxDiskGiB, + lastEventAfter, + lastEventBefore, + } = filters || {} + + const { field: sortField = DEFAULT_BOX_SORT_FIELD, direction: sortDirection = DEFAULT_BOX_SORT_DIRECTION } = + sort || {} + + const baseFindOptions: FindOptionsWhere = { + organizationId, + ...(labels ? { labels: JsonContains(labels) } : {}), + ...(regionIds ? { region: In(regionIds) } : {}), + } + + baseFindOptions.cpu = createRangeFilter(minCpu, maxCpu) + baseFindOptions.mem = createRangeFilter(minMemoryGiB, maxMemoryGiB) + baseFindOptions.disk = createRangeFilter(minDiskGiB, maxDiskGiB) + baseFindOptions.updatedAt = createRangeFilter(lastEventAfter, lastEventBefore) + + const statesToInclude = (states || Object.values(BoxState)).filter((state) => state !== BoxState.DESTROYED) + const errorStates = [BoxState.ERROR] + + const nonErrorStatesToInclude = statesToInclude.filter((state) => !errorStates.includes(state)) + const errorStatesToInclude = statesToInclude.filter((state) => errorStates.includes(state)) + + const where: FindOptionsWhere[] = [] + const searchFindOptions = this.getBoxSearchFindOptions(baseFindOptions, id, name) + + if (nonErrorStatesToInclude.length > 0) { + where.push( + ...searchFindOptions.map((findOptions) => ({ + ...findOptions, + state: In(nonErrorStatesToInclude), + })), + ) + } + + if (errorStatesToInclude.length > 0) { + where.push( + ...searchFindOptions.map((findOptions) => ({ + ...findOptions, + state: In(errorStatesToInclude), + ...(includeErroredDestroyed ? {} : { desiredState: Not(BoxDesiredState.DESTROYED) }), + })), + ) + } + + const [items, total] = await this.boxRepository.findAndCount({ + where, + order: { + [sortField]: { + direction: sortDirection, + nulls: 'LAST', + }, + ...(sortField !== BoxSortField.CREATED_AT && { createdAt: 'DESC' }), + }, + skip: (pageNum - 1) * limitNum, + take: limitNum, + }) + + return { + items, + total, + page: pageNum, + totalPages: Math.ceil(total / limitNum), + } + } + + private getBoxSearchFindOptions( + baseFindOptions: FindOptionsWhere, + id?: string, + name?: string, + ): FindOptionsWhere[] { + const nameFilter = name ? { name: ILike(`${name}%`) } : {} + + if (!id) { + return [ + { + ...baseFindOptions, + ...nameFilter, + }, + ] + } + + const idFilter = ILike(`${id}%`) + return [ + { + ...baseFindOptions, + ...nameFilter, + id: idFilter, + }, + { + ...baseFindOptions, + ...nameFilter, + name: idFilter, + }, + ] + } + + private getExpectedDesiredStateForState(state: BoxState): BoxDesiredState | undefined { + switch (state) { + case BoxState.STARTED: + return BoxDesiredState.STARTED + case BoxState.STOPPED: + return BoxDesiredState.STOPPED + case BoxState.DESTROYED: + return BoxDesiredState.DESTROYED + default: + return undefined + } + } + + private hasValidDesiredState(state: BoxState): boolean { + return this.getExpectedDesiredStateForState(state) !== undefined + } + + async findByRunnerId(runnerId: string, states?: BoxState[], skipReconcilingBoxes?: boolean): Promise { + const where: FindOptionsWhere = { runnerId } + if (states && states.length > 0) { + // Validate that all states have corresponding desired states + states.forEach((state) => { + if (!this.hasValidDesiredState(state)) { + throw new BadRequestError(`State ${state} does not have a corresponding desired state`) + } + }) + where.state = In(states) + } + + let boxes = await this.boxRepository.find({ where }) + + if (skipReconcilingBoxes) { + boxes = boxes.filter((box) => { + const expectedDesiredState = this.getExpectedDesiredStateForState(box.state) + return expectedDesiredState !== undefined && expectedDesiredState === box.desiredState + }) + } + + return boxes + } + + async findOneByIdOrName(boxIdOrName: string, organizationId?: string, returnDestroyed?: boolean): Promise { + const stateFilter = returnDestroyed ? {} : { state: Not(BoxState.DESTROYED) } + const organizationFilter = organizationId ? { organizationId } : {} + + // Public Box ID is the primary key. Name remains a user-facing fallback within an organization. + let box = await this.boxRepository.findOne({ + where: { + id: boxIdOrName, + ...organizationFilter, + ...stateFilter, + }, + cache: { + id: boxLookupCacheKeyById({ organizationId, returnDestroyed, id: boxIdOrName }), + milliseconds: BOX_LOOKUP_CACHE_TTL_MS, + }, + }) + + if (!box) { + box = await this.boxRepository.findOne({ + where: { + name: boxIdOrName, + ...organizationFilter, + ...stateFilter, + }, + cache: { + id: boxLookupCacheKeyByName({ organizationId, returnDestroyed, boxName: boxIdOrName }), + milliseconds: BOX_LOOKUP_CACHE_TTL_MS, + }, + }) + } + + if (!box || (!returnDestroyed && box.state === BoxState.ERROR && box.desiredState === BoxDesiredState.DESTROYED)) { + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + + return box + } + + async findOne(boxId: string, returnDestroyed?: boolean): Promise { + const box = await this.boxRepository.findOne({ + where: { + id: boxId, + ...(returnDestroyed ? {} : { state: Not(BoxState.DESTROYED) }), + }, + }) + + if (!box || (!returnDestroyed && box.state === BoxState.ERROR && box.desiredState === BoxDesiredState.DESTROYED)) { + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + return box + } + + async getOrganizationId(boxIdOrName: string, organizationId?: string): Promise { + const organizationFilter = organizationId ? { organizationId: organizationId } : {} + + let box = await this.boxRepository.findOne({ + where: { + id: boxIdOrName, + ...organizationFilter, + }, + select: ['organizationId'], + cache: { + id: boxOrgIdCacheKeyById({ organizationId, id: boxIdOrName }), + milliseconds: BOX_ORG_ID_CACHE_TTL_MS, + }, + }) + + if (!box && organizationId) { + box = await this.boxRepository.findOne({ + where: { + name: boxIdOrName, + organizationId: organizationId, + }, + select: ['organizationId'], + cache: { + id: boxOrgIdCacheKeyByName({ organizationId, boxName: boxIdOrName }), + milliseconds: BOX_ORG_ID_CACHE_TTL_MS, + }, + }) + } + + if (!box || !box.organizationId) { + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + + return box.organizationId + } + + async getRunnerId(boxIdOrName: string): Promise { + const box = await this.boxRepository.findOne({ + where: [{ id: boxIdOrName }, { name: boxIdOrName }], + select: ['runnerId'], + loadEagerRelations: false, + }) + + if (!box) { + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + + return box.runnerId || null + } + + async getRegionId(boxIdOrName: string): Promise { + const box = await this.boxRepository.findOne({ + where: [{ id: boxIdOrName }, { name: boxIdOrName }], + select: ['region'], + loadEagerRelations: false, + }) + + if (!box) { + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + + return box.region + } + + async getPortPreviewUrl(boxIdOrName: string, organizationId: string, port: number): Promise { + if (port < 1 || port > 65535) { + throw new BadRequestError('Invalid port') + } + + const proxyDomain = this.configService.getOrThrow('proxy.domain') + const proxyProtocol = this.configService.getOrThrow('proxy.protocol') + + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + let url = `${proxyProtocol}://${port}-${box.id}.${proxyDomain}` + + const region = await this.regionService.findOne(box.region, true) + if (region && region.proxyUrl) { + // Insert port and box.id into the custom proxy URL + url = region.proxyUrl.replace(/(https?:\/)(\/)/, `$1/${port}-${box.id}.`) + } + + return { + boxId: box.id, + url, + token: box.authToken, + } + } + + async getSignedPortPreviewUrl( + boxIdOrName: string, + organizationId: string, + port: number, + expiresInSeconds = 60, + ): Promise { + if (port < 1 || port > 65535) { + throw new BadRequestError('Invalid port') + } + + if (expiresInSeconds < 1 || expiresInSeconds > 60 * 60 * 24) { + throw new BadRequestError('expiresInSeconds must be between 1 second and 24 hours') + } + + const proxyDomain = this.configService.getOrThrow('proxy.domain') + const proxyProtocol = this.configService.getOrThrow('proxy.protocol') + + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + const token = customNanoid(urlAlphabet.replace('_', '').replace('-', ''))(16).toLocaleLowerCase() + + const lockKey = `box:signed-preview-url-token:${port}:${token}` + await this.redis.setex(lockKey, expiresInSeconds, box.id) + + let url = `${proxyProtocol}://${port}-${token}.${proxyDomain}` + + const region = await this.regionService.findOne(box.region, true) + if (region && region.proxyUrl) { + // Insert port and box.id into the custom proxy URL + url = region.proxyUrl.replace(/(https?:\/)(\/)/, `$1/${port}-${token}.`) + } + + return { + boxId: box.id, + port, + token, + url, + } + } + + async getBoxIdFromSignedPreviewUrlToken(token: string, port: number): Promise { + const lockKey = `box:signed-preview-url-token:${port}:${token}` + const boxId = await this.redis.get(lockKey) + if (!boxId) { + throw new ForbiddenException('Invalid or expired token') + } + return boxId + } + + async expireSignedPreviewUrlToken( + boxIdOrName: string, + organizationId: string, + token: string, + port: number, + ): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + if (!box) { + throw new NotFoundException(`Box with ID or name ${boxIdOrName} not found`) + } + + const lockKey = `box:signed-preview-url-token:${port}:${token}` + await this.redis.del(lockKey) + } + + async destroy(boxIdOrName: string, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + if (box.pending) { + throw new BoxError('Box state change in progress') + } + + const updateData = Box.getSoftDeleteUpdate(box) + + const updatedBox = await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: box.pending, state: box.state }, + }) + + this.eventEmitter.emit(BoxEvents.DESTROYED, new BoxDestroyedEvent(updatedBox)) + return updatedBox + } + + async start(boxIdOrName: string, organization: Organization): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organization.id) + + const region = await this.regionService.findOne(box.region) + if (!region) { + throw new NotFoundException(`Region with ID ${box.region} not found`) + } + + if (box.state === BoxState.STARTED && box.desiredState === BoxDesiredState.STARTED) { + return box + } + + this.assertBoxNotErrored(box) + + if (String(box.state) !== String(box.desiredState)) { + throw new BoxError('State change in progress') + } + + if (box.state !== BoxState.STOPPED) { + throw new BoxError('Box is not in valid state') + } + + if (box.pending) { + throw new BoxError('Box state change in progress') + } + + this.organizationService.assertOrganizationIsNotSuspended(organization) + + const updateData: Partial = { + pending: true, + desiredState: BoxDesiredState.STARTED, + authToken: nanoid(32).toLocaleLowerCase(), + } + + const updatedBox = await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: box.state }, + }) + + this.eventEmitter.emit(BoxEvents.STARTED, new BoxStartedEvent(updatedBox)) + + return updatedBox + } + + async stop(boxIdOrName: string, organizationId?: string, force?: boolean): Promise { + // Capture the JS call stack so we can identify the code path that hit + // boxService.stop() — the audit log only records the leaf endpoint, + // not which internal mechanism (cron / event handler / sync loop) routed + // here. Frames below the BoxService entry are the interesting ones. + const stack = new Error().stack?.split('\n').slice(2, 8).join(' | ') || '' + this.logger.warn( + `[stop-trace] box=${boxIdOrName} organizationId=${organizationId ?? 'undefined'} force=${force ?? false} caller=${stack}`, + ) + + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + this.assertBoxNotErrored(box) + + if (String(box.state) !== String(box.desiredState)) { + throw new BoxError('State change in progress') + } + + if (box.state !== BoxState.STARTED) { + throw new BoxError('Box is not started') + } + + if (box.pending) { + throw new BoxError('Box state change in progress') + } + + const updateData: Partial = { + pending: true, + desiredState: box.autoDeleteInterval === 0 ? BoxDesiredState.DESTROYED : BoxDesiredState.STOPPED, + } + + const updatedBox = await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: box.state }, + }) + + this.logger.warn( + `[stop-trace] box=${box.id} desiredState set to ${updateData.desiredState} (autoDeleteInterval=${box.autoDeleteInterval})`, + ) + + if (box.autoDeleteInterval === 0) { + this.eventEmitter.emit(BoxEvents.DESTROYED, new BoxDestroyedEvent(updatedBox)) + } else { + this.eventEmitter.emit(BoxEvents.STOPPED, new BoxStoppedEvent(updatedBox, force)) + } + + return updatedBox + } + + async recover(boxIdOrName: string, organization: Organization): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organization.id) + + if (box.state !== BoxState.ERROR) { + throw new BadRequestError('Box must be in error state to recover') + } + + if (box.pending) { + throw new BoxError('Box state change in progress') + } + + // Validate runner exists + if (!box.runnerId) { + throw new NotFoundException(`Box with ID ${box.id} does not have a runner`) + } + const runner = await this.runnerService.findOneOrFail(box.runnerId) + + if (runner.apiVersion === '2') { + // TODO: we need "recovering" state that can be set after calling recover + // Once in recovering, we abort further processing and let the manager/job handler take care of it + // (Also, since desiredState would be STARTED, we need to check the quota) + throw new ForbiddenException('Recovering boxes with runner API version 2 is not supported') + } + + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + try { + await runnerAdapter.recoverBox(box) + } catch (error) { + if (error instanceof Error && error.message.includes('storage cannot be further expanded')) { + const errorMsg = `Box storage cannot be further expanded. Maximum expansion of ${(box.disk * 0.1).toFixed(2)}GB (10% of original ${box.disk.toFixed(2)}GB) has been reached. Please contact support for further assistance.` + throw new ForbiddenException(errorMsg) + } + throw error + } + + const updateData: Partial = { + state: BoxState.STOPPED, + desiredState: BoxDesiredState.STOPPED, + errorReason: null, + recoverable: false, + } + + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { state: BoxState.ERROR }, + }) + + // Now that box is in STOPPED state, use the normal start flow + // This handles quota validation, pending usage, event emission, etc. + return await this.start(box.id, organization) + } + + async resize(boxIdOrName: string, resizeDto: ResizeBoxDto, organization: Organization): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organization.id) + + const region = await this.regionService.findOne(box.region) + if (!region) { + throw new NotFoundException(`Region with ID ${box.region} not found`) + } + + // Validate box is in a valid state for resize + if (box.state !== BoxState.STARTED && box.state !== BoxState.STOPPED) { + throw new BadRequestError('Box must be in started or stopped state to resize') + } + + if (box.pending) { + throw new BoxError('Box state change in progress') + } + + // If no resize parameters provided, throw error + if (resizeDto.cpu === undefined && resizeDto.memory === undefined && resizeDto.disk === undefined) { + throw new BadRequestError('No resource changes specified - box is already at the desired configuration') + } + + // Disk resize requires stopped box (cold resize only) + if (resizeDto.disk !== undefined && box.state !== BoxState.STOPPED) { + throw new BadRequestError('Disk resize can only be performed on a stopped box') + } + + // Hot resize (box is running): only CPU and memory can be increased + const isHotResize = box.state === BoxState.STARTED + + // Validate hot resize constraints + if (isHotResize) { + if (resizeDto.cpu !== undefined && resizeDto.cpu < box.cpu) { + throw new BadRequestError('Box must be in stopped state to decrease the number of CPU cores') + } + + if (resizeDto.memory !== undefined && resizeDto.memory < box.mem) { + throw new BadRequestError('Box must be in stopped state to decrease memory') + } + } + + // Disk can only be increased (never decreased) + if (resizeDto.disk !== undefined && resizeDto.disk < box.disk) { + throw new BadRequestError('Box disk size cannot be decreased') + } + + // Calculate new resource values + const newCpu = resizeDto.cpu ?? box.cpu + const newMem = resizeDto.memory ?? box.mem + const newDisk = resizeDto.disk ?? box.disk + + // Throw if nothing actually changes + if (newCpu === box.cpu && newMem === box.mem && newDisk === box.disk) { + throw new BadRequestError('No resource changes specified - box is already at the desired configuration') + } + + this.organizationService.assertOrganizationIsNotSuspended(organization) + + // Get runner and validate before changing state + if (!box.runnerId) { + throw new BadRequestError('Box has no runner assigned') + } + + const runner = await this.runnerService.findOneOrFail(box.runnerId) + + // Capture the previous state before transitioning to RESIZING (STARTED or STOPPED) + const previousState = + box.state === BoxState.STARTED ? BoxState.STARTED : box.state === BoxState.STOPPED ? BoxState.STOPPED : null + + if (!previousState) { + throw new BadRequestError('Box must be in started or stopped state to resize') + } + + // Now transition to RESIZING state + const updateData: Partial = { + state: BoxState.RESIZING, + } + + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: previousState }, + }) + + try { + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + + await runnerAdapter.resizeBox(box.id, resizeDto.cpu, resizeDto.memory, resizeDto.disk) + + // For V0 runners, update resources immediately (subscriber emits STATE_UPDATED) + // For V2 runners, job handler will update resources on completion + if (runner.apiVersion === '0') { + const updateData: Partial = { + cpu: newCpu, + mem: newMem, + disk: newDisk, + state: previousState, + } + + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { state: BoxState.RESIZING }, + }) + } + + return await this.findOneByIdOrName(box.id, organization.id) + } catch (error) { + // Return to previous state on error + const updateData: Partial = { + state: previousState, + } + + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { state: BoxState.RESIZING }, + }) + + throw error + } + } + + async updatePublicStatus(boxIdOrName: string, isPublic: boolean, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + const updateData: Partial = { + public: isPublic, + } + + return await this.boxRepository.update(box.id, { + updateData, + entity: box, + }) + } + + async updateLastActivityAt(boxId: string, lastActivityAt: Date): Promise { + await this.boxActivityService.updateLastActivityAt(boxId, lastActivityAt) + } + + async getToolboxProxyUrl(boxId: string): Promise { + const box = await this.findOne(boxId) + return this.resolveToolboxProxyUrl(box.region) + } + + async toBoxDto(box: Box): Promise { + const toolboxProxyUrl = await this.resolveToolboxProxyUrl(box.region) + return BoxDto.fromBox(box, toolboxProxyUrl) + } + + async toBoxDtos(boxes: Box[]): Promise { + const urlMap = await this.resolveToolboxProxyUrls(boxes.map((s) => s.region)) + return boxes.map((s) => { + const url = urlMap.get(s.region) + if (!url) { + throw new NotFoundException(`Toolbox proxy URL not resolved for region ${s.region}`) + } + return BoxDto.fromBox(s, url) + }) + } + + async resolveToolboxProxyUrl(regionId: string): Promise { + const cacheKey = toolboxProxyUrlCacheKey(regionId) + const cached = await this.redis.get(cacheKey) + if (cached) { + return cached + } + + const region = await this.regionService.findOne(regionId) + const url = region?.toolboxProxyUrl + ? region.toolboxProxyUrl.replace(/\/+$/, '') + '/toolbox' + : this.configService.getOrThrow('proxy.toolboxUrl') + + this.redis.setex(cacheKey, TOOLBOX_PROXY_URL_CACHE_TTL_S, url).catch((err) => { + this.logger.warn(`Failed to cache toolbox proxy URL for region ${regionId}: ${err.message}`) + }) + return url + } + + async resolveToolboxProxyUrls(regionIds: string[]): Promise> { + const unique = [...new Set(regionIds)] + const result = new Map() + + const pipeline = this.redis.pipeline() + for (const id of unique) { + pipeline.get(toolboxProxyUrlCacheKey(id)) + } + const cached = await pipeline.exec() + + const uncached: string[] = [] + for (let i = 0; i < unique.length; i++) { + const err = cached?.[i]?.[0] + if (err) { + this.logger.warn(`Failed to get cached toolbox proxy URL for region ${unique[i]}: ${err.message}`) + } + const val = cached?.[i]?.[1] as string | null + if (val) { + result.set(unique[i], val) + } else { + uncached.push(unique[i]) + } + } + + if (uncached.length > 0) { + const regions = await this.regionService.findByIds(uncached) + const regionMap = new Map(regions.map((r) => [r.id, r])) + const fallback = this.configService.getOrThrow('proxy.toolboxUrl') + const setPipeline = this.redis.pipeline() + for (const id of uncached) { + const region = regionMap.get(id) + const url = region?.toolboxProxyUrl ? region.toolboxProxyUrl.replace(/\/+$/, '') + '/toolbox' : fallback + result.set(id, url) + setPipeline.setex(toolboxProxyUrlCacheKey(id), TOOLBOX_PROXY_URL_CACHE_TTL_S, url) + } + const setResults = await setPipeline.exec() + setResults?.forEach(([err], i) => { + if (err) { + this.logger.warn(`Failed to cache toolbox proxy URL for region ${uncached[i]}: ${err.message}`) + } + }) + } + + return result + } + + private async getValidatedOrDefaultRegion(organization: Organization, regionIdOrName?: string): Promise { + regionIdOrName = regionIdOrName?.trim() + + if (!regionIdOrName) { + const defaultRegionId = organization.defaultRegionId || this.configService.getOrThrow('defaultRegion.id') + const region = await this.regionService.findOne(defaultRegionId) + if (!region) { + throw new NotFoundException('Default region not found') + } + return region + } + + const region = + (await this.regionService.findOneByName(regionIdOrName, organization.id)) ?? + (await this.regionService.findOneByName(regionIdOrName, null)) ?? + (await this.regionService.findOne(regionIdOrName)) + + if (!region) { + throw new NotFoundException('Region not found') + } + + return region + } + + private getValidatedOrDefaultClass(boxClass: BoxClass): BoxClass { + if (!boxClass) { + return BoxClass.SMALL + } + + if (Object.values(BoxClass).includes(boxClass)) { + return boxClass + } else { + throw new BadRequestError('Invalid class') + } + } + + async replaceLabels(boxIdOrName: string, labels: { [key: string]: string }, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + // Replace all labels + const updateData: Partial = { + labels, + } + + return await this.boxRepository.update(box.id, { updateData, entity: box }) + } + + @Cron(CronExpression.EVERY_SECOND, { name: 'cleanup-destroyed-boxes' }) + @LogExecution('cleanup-destroyed-boxes') + @WithInstrumentation() + async cleanupDestroyedBoxes() { + const twentyFourHoursAgo = new Date() + twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24) + + const destroyedBoxs = await this.boxRepository.delete({ + state: BoxState.DESTROYED, + updatedAt: LessThan(twentyFourHoursAgo), + }) + + if (destroyedBoxs.affected > 0) { + this.logger.debug(`Cleaned up ${destroyedBoxs.affected} destroyed boxes`) + } + } + + @Cron(CronExpression.EVERY_SECOND, { name: 'cleanup-stale-error-boxes' }) + @LogExecution('cleanup-stale-error-boxes') + @WithInstrumentation() + async cleanupStaleErrorBoxes() { + const sevenDaysAgo = new Date() + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7) + + const result = await this.boxRepository.delete({ + state: BoxState.ERROR, + desiredState: BoxDesiredState.DESTROYED, + updatedAt: LessThan(sevenDaysAgo), + }) + + if (result.affected > 0) { + this.logger.debug(`Cleaned up ${result.affected} stale error boxes`) + } + } + + async setAutostopInterval(boxIdOrName: string, interval: number, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + const updateData: Partial = { + autoStopInterval: this.resolveAutoStopInterval(interval), + } + + return await this.boxRepository.update(box.id, { updateData, entity: box }) + } + + async setAutoDeleteInterval(boxIdOrName: string, interval: number, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + const updateData: Partial = { + autoDeleteInterval: interval, + } + + return await this.boxRepository.update(box.id, { updateData, entity: box }) + } + + async updateNetworkSettings( + boxIdOrName: string, + networkBlockAll?: boolean, + networkAllowList?: string, + organizationId?: string, + ): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + const updateData: Partial = {} + + if (networkBlockAll !== undefined) { + updateData.networkBlockAll = networkBlockAll + } + + if (networkAllowList !== undefined) { + updateData.networkAllowList = this.resolveNetworkAllowList(networkAllowList) + } + + const updatedBox = await this.boxRepository.update(box.id, { updateData, entity: box }) + + // Update network settings on the runner + if (box.runnerId) { + const runner = await this.runnerService.findOne(box.runnerId) + if (runner) { + const runnerAdapter = await this.runnerAdapterFactory.create(runner) + await runnerAdapter.updateNetworkSettings(box.id, networkBlockAll, networkAllowList) + } + } + + return updatedBox + } + + // used by internal services to update the state of a box to resolve domain and runner state mismatch + // notably, when a box instance stops or errors on the runner, the domain state needs to be updated to reflect the actual state + async updateState(boxId: string, newState: BoxState, recoverable = false, errorReason?: string): Promise { + const box = await this.boxRepository.findOne({ + where: { id: boxId }, + }) + + if (!box) { + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + if (box.state === newState) { + this.logger.debug(`Box ${boxId} is already in state ${newState}`) + return + } + + // only allow updating the state of started | stopped boxes + if (![BoxState.STARTED, BoxState.STOPPED].includes(box.state)) { + throw new BadRequestError('Box is not in a valid state to be updated') + } + + if (box.desiredState == BoxDesiredState.DESTROYED) { + this.logger.debug(`Box ${boxId} is already DESTROYED, skipping state update`) + return + } + + const oldState = box.state + const oldDesiredState = box.desiredState + + const updateData: Partial = { + state: newState, + recoverable: false, + } + + if (errorReason !== undefined) { + updateData.errorReason = errorReason + if (newState === BoxState.ERROR) { + updateData.recoverable = recoverable + } + } + + // we need to update the desired state to match the new state + const desiredState = this.getExpectedDesiredStateForState(newState) + if (desiredState) { + updateData.desiredState = desiredState + } + + await this.boxRepository.updateWhere(box.id, { + updateData, + whereCondition: { pending: false, state: oldState, desiredState: oldDesiredState }, + }) + } + + @OnEvent(WarmPoolEvents.TOPUP_REQUESTED) + private async createWarmPoolBox(event: WarmPoolTopUpRequested) { + await this.createForWarmPool(event.warmPool) + } + + @Cron(CronExpression.EVERY_MINUTE, { name: 'handle-unschedulable-runners' }) + @LogExecution('handle-unschedulable-runners') + @WithInstrumentation() + private async handleUnschedulableRunners() { + const runners = await this.runnerRepository.find({ where: { unschedulable: true } }) + + if (runners.length === 0) { + return + } + + // find all boxes that are using the unschedulable runners and have organizationId = '00000000-0000-0000-0000-000000000000' + const boxes = await this.boxRepository.find({ + where: { + runnerId: In(runners.map((runner) => runner.id)), + organizationId: '00000000-0000-0000-0000-000000000000', + state: BoxState.STARTED, + desiredState: Not(BoxDesiredState.DESTROYED), + }, + }) + + if (boxes.length === 0) { + return + } + + const destroyPromises = boxes.map((box) => this.destroy(box.id)) + const results = await Promise.allSettled(destroyPromises) + + // Log any failed box destructions + results.forEach((result, index) => { + if (result.status === 'rejected') { + this.logger.error(`Failed to destroy box ${boxes[index].id}: ${result.reason}`) + } + }) + } + + async isBoxPublic(boxId: string): Promise { + const box = await this.boxRepository.findOne({ + where: { id: boxId }, + }) + + if (!box) { + throw new NotFoundException(`Box with ID ${boxId} not found`) + } + + return box.public + } + + @OnEvent(OrganizationEvents.SUSPENDED_BOX_STOPPED) + async handleSuspendedBoxStopped(event: OrganizationSuspendedBoxStoppedEvent) { + await this.stop(event.boxId).catch((error) => { + // log the error for now, but don't throw it as it will be retried + this.logger.error(`Error stopping box from suspended organization. BoxId: ${event.boxId}: `, error) + }) + } + + private resolveAutoStopInterval(autoStopInterval: number): number { + if (autoStopInterval < 0) { + throw new BadRequestError('Auto-stop interval must be non-negative') + } + + return autoStopInterval + } + + private resolveNetworkAllowList(networkAllowList: string): string { + try { + validateNetworkAllowList(networkAllowList) + } catch (error) { + throw new BadRequestError(error instanceof Error ? error.message : 'Invalid network allow list') + } + + return networkAllowList + } + + private resolveVolumes(volumes: BoxVolume[]): BoxVolume[] { + try { + validateMountPaths(volumes) + } catch (error) { + throw new BadRequestError(error instanceof Error ? error.message : 'Invalid volume mount configuration') + } + + try { + validateSubpaths(volumes) + } catch (error) { + throw new BadRequestError(error instanceof Error ? error.message : 'Invalid volume subpath configuration') + } + + return volumes + } + + async createSshAccess(boxIdOrName: string, expiresInMinutes = 60, organizationId?: string): Promise { + // check if box exists + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + // Revoke any existing SSH access for this box + await this.revokeSshAccess(box.id) + + const sshAccess = new SshAccess() + sshAccess.boxId = box.id + // Generate a safe token that can't doesn't have _ or - to avoid CLI issues + sshAccess.token = customNanoid(urlAlphabet.replace('_', '').replace('-', ''))(32) + sshAccess.expiresAt = new Date(Date.now() + expiresInMinutes * 60 * 1000) + + await this.sshAccessRepository.save(sshAccess) + + const region = await this.regionService.findOne(box.region, true) + if (region && region.sshGatewayUrl) { + return SshAccessDto.fromSshAccess(sshAccess, region.sshGatewayUrl) + } + + return SshAccessDto.fromSshAccess(sshAccess, this.configService.getOrThrow('sshGateway.url')) + } + + async revokeSshAccess(boxIdOrName: string, token?: string, organizationId?: string): Promise { + const box = await this.findOneByIdOrName(boxIdOrName, organizationId) + + if (token) { + // Revoke specific SSH access by token + await this.sshAccessRepository.delete({ boxId: box.id, token }) + } else { + // Revoke all SSH access for the box + await this.sshAccessRepository.delete({ boxId: box.id }) + } + + return box + } + + async validateSshAccess(token: string): Promise { + const sshAccess = await this.sshAccessRepository.findOne({ + where: { + token, + }, + relations: ['box'], + }) + + if (!sshAccess) { + return { valid: false, boxId: null } + } + + // Check if token is expired + const isExpired = sshAccess.expiresAt < new Date() + if (isExpired) { + return { valid: false, boxId: null } + } + + // Get runner information if box exists + if (sshAccess.box && sshAccess.box.runnerId) { + const runner = await this.runnerService.findOne(sshAccess.box.runnerId) + + if (runner) { + return { + valid: true, + boxId: sshAccess.box.id, + } + } + } + + return { valid: true, boxId: sshAccess.box.id } + } +} diff --git a/apps/api/src/box/services/job-state-handler.service.spec.ts b/apps/api/src/box/services/job-state-handler.service.spec.ts new file mode 100644 index 000000000..0d96183da --- /dev/null +++ b/apps/api/src/box/services/job-state-handler.service.spec.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { JobStateHandlerService } from './job-state-handler.service' +import { BoxState } from '../enums/box-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { JobStatus } from '../enums/job-status.enum' +import { JobType } from '../enums/job-type.enum' + +function buildHarness(box: { id: string; state: BoxState; desiredState: BoxDesiredState }) { + const updates: Array<{ id: string; updateData: any }> = [] + const boxRepository: any = { + findOne: jest.fn().mockResolvedValue(box), + update: jest.fn(async (id: string, opts: { updateData: any }) => { + updates.push({ id, updateData: opts.updateData }) + }), + } + const service = new JobStateHandlerService(boxRepository, {} as any) + return { service, boxRepository, updates } +} + +function failedCreateJob(errorMessage: string) { + return { + id: 'job-1', + type: JobType.CREATE_BOX, + status: JobStatus.FAILED, + resourceId: 'box-1', + errorMessage, + } as any +} + +describe('JobStateHandlerService CREATE_BOX failure handling', () => { + afterEach(() => jest.clearAllMocks()) + + it('converges a split-brain "already exists" CREATE failure to STOPPED, not ERROR', async () => { + // A prior CREATE_BOX already built this box on the runner; the API never recorded + // success, so the retry collides on the name and the runner reports "already exists". + // The box is present, not failed — it must not be stranded in ERROR. + const { service, updates } = buildHarness({ + id: 'box-1', + state: BoxState.CREATING, + desiredState: BoxDesiredState.STARTED, + }) + + await service.handleJobCompletion(failedCreateJob("failed to create box: box with name 'box-1' already exists")) + + expect(updates).toHaveLength(1) + expect(updates[0].updateData.state).toBe(BoxState.STOPPED) + expect(updates[0].updateData.errorReason).toBeNull() + }) + + it('also treats runner box ID collisions as split-brain create success', async () => { + const { service, updates } = buildHarness({ + id: 'box-1', + state: BoxState.CREATING, + desiredState: BoxDesiredState.STARTED, + }) + + await service.handleJobCompletion(failedCreateJob('failed to create box: box box-1 already exists')) + + expect(updates).toHaveLength(1) + expect(updates[0].updateData.state).toBe(BoxState.STOPPED) + expect(updates[0].updateData.errorReason).toBeNull() + }) + + it('does not treat unrelated already-exists CREATE errors as box split-brain', async () => { + const { service, updates } = buildHarness({ + id: 'box-1', + state: BoxState.CREATING, + desiredState: BoxDesiredState.STARTED, + }) + + await service.handleJobCompletion(failedCreateJob("failed to create box: volume with name 'box-1' already exists")) + + expect(updates).toHaveLength(1) + expect(updates[0].updateData.state).toBe(BoxState.ERROR) + expect(updates[0].updateData.errorReason).toContain('volume with name') + }) + + it('still marks a genuine CREATE failure as ERROR', async () => { + const { service, updates } = buildHarness({ + id: 'box-1', + state: BoxState.CREATING, + desiredState: BoxDesiredState.STARTED, + }) + + await service.handleJobCompletion(failedCreateJob('engine exited unexpectedly: signal SIGABRT')) + + expect(updates).toHaveLength(1) + expect(updates[0].updateData.state).toBe(BoxState.ERROR) + }) +}) diff --git a/apps/api/src/box/services/job-state-handler.service.ts b/apps/api/src/box/services/job-state-handler.service.ts new file mode 100644 index 000000000..f4139e66f --- /dev/null +++ b/apps/api/src/box/services/job-state-handler.service.ts @@ -0,0 +1,354 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Injectable, Logger } from '@nestjs/common' +import { BoxState } from '../enums/box-state.enum' +import { JobStatus } from '../enums/job-status.enum' +import { JobType } from '../enums/job-type.enum' +import { Job } from '../entities/job.entity' +import { BoxDesiredState } from '../enums/box-desired-state.enum' +import { sanitizeBoxError } from '../utils/sanitize-error.util' +import { BoxRepository } from '../repositories/box.repository' +import { Box } from '../entities/box.entity' +import { RedisLockProvider } from '../common/redis-lock.provider' +import { ResourceType } from '../enums/resource-type.enum' +import { getStateChangeLockKey } from '../utils/lock-key.util' + +// The runner reports box name/ID collisions as: +// - "box with name '' already exists" +// - "box already exists" +// - "box with this name already exists" +// The pattern intentionally avoids matching unrelated resource collisions. +const BOX_ALREADY_EXISTS_PATTERN = /\bbox (?:with name ['"][^'"]+['"]|with this name|\S+) already exists\b/i + +function isBoxAlreadyExistsError(errorReason: string | undefined | null): boolean { + return !!errorReason && BOX_ALREADY_EXISTS_PATTERN.test(errorReason) +} + +/** + * Service for handling entity state updates based on job completion (v2 runners only). + * This service listens to job status changes and updates entity states accordingly. + */ +@Injectable() +export class JobStateHandlerService { + private readonly logger = new Logger(JobStateHandlerService.name) + + constructor( + private readonly boxRepository: BoxRepository, + private readonly redisLockProvider: RedisLockProvider, + ) {} + + /** + * Handle job completion and update entity state accordingly. + * Called when a job status is updated to COMPLETED or FAILED. + */ + async handleJobCompletion(job: Job): Promise { + if (job.status !== JobStatus.COMPLETED && job.status !== JobStatus.FAILED) { + return + } + + if (!job.resourceId) { + return + } + + switch (job.type) { + case JobType.CREATE_BOX: + await this.handleCreateBoxJobCompletion(job) + break + case JobType.START_BOX: + await this.handleStartBoxJobCompletion(job) + break + case JobType.STOP_BOX: + await this.handleStopBoxJobCompletion(job) + break + case JobType.DESTROY_BOX: + await this.handleDestroyBoxJobCompletion(job) + break + case JobType.RESIZE_BOX: + await this.handleResizeBoxJobCompletion(job) + break + // TODO(image-rewrite): PULL_IMAGE / REMOVE_IMAGE job handling removed with + // the runner image subsystems; rebuild artifact lifecycle handling here. + case JobType.RECOVER_BOX: + await this.handleRecoverBoxJobCompletion(job) + break + default: + break + } + + switch (job.resourceType) { + case ResourceType.BOX: { + const lockKey = getStateChangeLockKey(job.resourceId) + this.redisLockProvider + .unlock(lockKey) + .catch((error) => this.logger.error(`Error unlocking Redis lock for box ${job.resourceId}:`, error)) // Clean up lock after job completion + break + } + default: + break + } + } + + private async handleCreateBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for CREATE_BOX job ${job.id}`) + return + } + + if (box.desiredState !== BoxDesiredState.STARTED) { + this.logger.error( + `Box ${boxId} is not in desired state STARTED for CREATE_BOX job ${job.id}. Desired state: ${box.desiredState}`, + ) + return + } + + const updateData: Partial = {} + + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`CREATE_BOX job ${job.id} completed successfully, marking box ${boxId} as STARTED`) + updateData.state = BoxState.STARTED + updateData.errorReason = null + const metadata = job.getResultMetadata() + if (metadata?.daemonVersion && typeof metadata.daemonVersion === 'string') { + updateData.daemonVersion = metadata.daemonVersion + } + } else if (job.status === JobStatus.FAILED) { + const { recoverable, errorReason } = sanitizeBoxError(job.errorMessage) + if (isBoxAlreadyExistsError(errorReason)) { + // Split-brain: a previous CREATE_BOX already built this box on the runner, but the + // API never recorded success (lost/stale job ack). Re-running CREATE collides on the + // box name and the runner reports "already exists". The box is present, not failed — + // so converge to STOPPED and let the start flow re-drive it with an idempotent + // START_BOX rather than stranding it in ERROR (and re-emitting the same collision). + this.logger.warn( + `CREATE_BOX job ${job.id} hit split-brain (box ${boxId} already exists on runner); converging to STOPPED for restart`, + ) + updateData.state = BoxState.STOPPED + updateData.errorReason = null + } else { + const sanitizedErrorReason = errorReason || 'Failed to create box' + this.logger.error(`CREATE_BOX job ${job.id} failed for box ${boxId}: ${sanitizedErrorReason}`) + updateData.state = BoxState.ERROR + updateData.errorReason = sanitizedErrorReason + updateData.recoverable = recoverable + } + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling CREATE_BOX job completion for box ${boxId}:`, error) + } + } + + private async handleStartBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for START_BOX job ${job.id}`) + return + } + + if (box.desiredState !== BoxDesiredState.STARTED) { + this.logger.error( + `Box ${boxId} is not in desired state STARTED for START_BOX job ${job.id}. Desired state: ${box.desiredState}`, + ) + return + } + + const updateData: Partial = {} + + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`START_BOX job ${job.id} completed successfully, marking box ${boxId} as STARTED`) + updateData.state = BoxState.STARTED + updateData.errorReason = null + const metadata = job.getResultMetadata() + if (metadata?.daemonVersion && typeof metadata.daemonVersion === 'string') { + updateData.daemonVersion = metadata.daemonVersion + } + } else if (job.status === JobStatus.FAILED) { + this.logger.error(`START_BOX job ${job.id} failed for box ${boxId}: ${job.errorMessage}`) + updateData.state = BoxState.ERROR + const { recoverable, errorReason } = sanitizeBoxError(job.errorMessage) + updateData.errorReason = errorReason || 'Failed to start box' + updateData.recoverable = recoverable + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling START_BOX job completion for box ${boxId}:`, error) + } + } + + private async handleStopBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for STOP_BOX job ${job.id}`) + return + } + + if (box.desiredState !== BoxDesiredState.STOPPED) { + this.logger.error( + `Box ${boxId} is not in desired state STOPPED for STOP_BOX job ${job.id}. Desired state: ${box.desiredState}`, + ) + return + } + + const updateData: Partial = {} + + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`STOP_BOX job ${job.id} completed successfully, marking box ${boxId} as STOPPED`) + updateData.state = BoxState.STOPPED + updateData.errorReason = null + } else if (job.status === JobStatus.FAILED) { + this.logger.error(`STOP_BOX job ${job.id} failed for box ${boxId}: ${job.errorMessage}`) + updateData.state = BoxState.ERROR + const { recoverable, errorReason } = sanitizeBoxError(job.errorMessage) + updateData.errorReason = errorReason || 'Failed to stop box' + updateData.recoverable = recoverable + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling STOP_BOX job completion for box ${boxId}:`, error) + } + } + + private async handleDestroyBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for DESTROY_BOX job ${job.id}`) + return + } + const updateData: Partial = {} + + if (box.desiredState === BoxDesiredState.DESTROYED) { + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`DESTROY_BOX job ${job.id} completed successfully, marking box ${boxId} as DESTROYED`) + updateData.state = BoxState.DESTROYED + updateData.errorReason = null + } else if (job.status === JobStatus.FAILED) { + this.logger.error(`DESTROY_BOX job ${job.id} failed for box ${boxId}: ${job.errorMessage}`) + updateData.state = BoxState.ERROR + const { recoverable, errorReason } = sanitizeBoxError(job.errorMessage) + updateData.errorReason = errorReason || 'Failed to destroy box' + updateData.recoverable = recoverable + } + } else { + return + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling DESTROY_BOX job completion for box ${boxId}:`, error) + } + } + + private async handleRecoverBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for RECOVER_BOX job ${job.id}`) + return + } + + if (box.desiredState !== BoxDesiredState.STARTED) { + this.logger.error( + `Box ${boxId} is not in desired state STARTED for RECOVER_BOX job ${job.id}. Desired state: ${box.desiredState}`, + ) + return + } + + const updateData: Partial = {} + + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`RECOVER_BOX job ${job.id} completed successfully, marking box ${boxId} as STARTED`) + updateData.state = BoxState.STARTED + updateData.errorReason = null + } else if (job.status === JobStatus.FAILED) { + this.logger.error(`RECOVER_BOX job ${job.id} failed for box ${boxId}: ${job.errorMessage}`) + updateData.state = BoxState.ERROR + updateData.errorReason = job.errorMessage || 'Failed to recover box' + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling RECOVER_BOX job completion for box ${boxId}:`, error) + } + } + + private async handleResizeBoxJobCompletion(job: Job): Promise { + const boxId = job.resourceId + if (!boxId) return + + try { + const box = await this.boxRepository.findOne({ where: { id: boxId } }) + if (!box) { + this.logger.warn(`Box ${boxId} not found for RESIZE_BOX job ${job.id}`) + return + } + + if (box.state !== BoxState.RESIZING) { + this.logger.warn(`Box ${boxId} is not in RESIZING state for RESIZE_BOX job ${job.id}. State: ${box.state}`) + return + } + + // Determine the previous state (STARTED or STOPPED based on desiredState) + const previousState = + box.desiredState === BoxDesiredState.STARTED + ? BoxState.STARTED + : box.desiredState === BoxDesiredState.STOPPED + ? BoxState.STOPPED + : null + + if (!previousState) { + this.logger.error(`Box ${boxId} has unexpected desiredState ${box.desiredState} for RESIZE_BOX job ${job.id}`) + return + } + + const payload = job.payload as { cpu?: number; memory?: number; disk?: number } + + const updateData: Partial = {} + + if (job.status === JobStatus.COMPLETED) { + this.logger.debug(`RESIZE_BOX job ${job.id} completed successfully for box ${boxId}`) + + // Update box resources + updateData.cpu = payload.cpu ?? box.cpu + updateData.mem = payload.memory ?? box.mem + updateData.disk = payload.disk ?? box.disk + updateData.state = previousState + return + } else if (job.status === JobStatus.FAILED) { + this.logger.error(`RESIZE_BOX job ${job.id} failed for box ${boxId}: ${job.errorMessage}`) + + updateData.state = previousState + } + + await this.boxRepository.update(boxId, { updateData, entity: box }) + } catch (error) { + this.logger.error(`Error handling RESIZE_BOX job completion for box ${boxId}:`, error) + } + } +} diff --git a/apps/api/src/sandbox/services/job.service.ts b/apps/api/src/box/services/job.service.ts similarity index 98% rename from apps/api/src/sandbox/services/job.service.ts rename to apps/api/src/box/services/job.service.ts index 88ea36983..5ad09c846 100644 --- a/apps/api/src/sandbox/services/job.service.ts +++ b/apps/api/src/box/services/job.service.ts @@ -27,8 +27,7 @@ const DEFAULT_STALE_TIMEOUT_MINUTES = 10 * Jobs not listed here use DEFAULT_STALE_TIMEOUT_MINUTES. */ const JOB_STALE_TIMEOUT_MINUTES: Partial> = { - [JobType.BUILD_SNAPSHOT]: 120, - [JobType.PULL_SNAPSHOT]: 120, + [JobType.PULL_ARTIFACT]: 120, } @Injectable() @@ -265,7 +264,7 @@ export class JobService { const updatedJob = await this.jobRepository.save(job) this.logger.debug(`Updated job ${jobId} status to ${status}`) - // Handle job completion for v2 runners - update sandbox/snapshot/backup state + // Handle job completion for v2 runners - update box, artifact, or backup state. if (status === JobStatus.COMPLETED || status === JobStatus.FAILED) { // Fire and forget - don't block the response this.jobStateHandlerService.handleJobCompletion(updatedJob).catch((error) => { @@ -313,8 +312,8 @@ export class JobService { } } - async findJobsBySandboxId(sandboxId: string): Promise { - return this.findJobsByResourceId(ResourceType.SANDBOX, sandboxId) + async findJobsByBoxId(boxId: string): Promise { + return this.findJobsByResourceId(ResourceType.BOX, boxId) } async findJobsByResourceId(resourceType: ResourceType, resourceId: string): Promise { diff --git a/apps/api/src/box/services/per-box-limits.ts b/apps/api/src/box/services/per-box-limits.ts new file mode 100644 index 000000000..14c6a6abd --- /dev/null +++ b/apps/api/src/box/services/per-box-limits.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BadRequestException } from '@nestjs/common' + +/** Per-box resource ceilings carried on the organization (the "security + * option" numbers). A value <= 0 means "unset" and is not enforced. */ +export interface PerBoxLimits { + maxCpuPerBox: number + maxMemoryPerBox: number + maxDiskPerBox: number +} + +/** + * Reject a box create whose requested cpu / memory / disk exceeds the org's + * per-box limits, instead of silently storing out-of-range values. Without + * this, the API accepted absurd inputs (e.g. cpu=999, memory past 4 GiB) and + * persisted them, which then poisoned every list_info round-trip for the + * whole org. Memory and disk are compared in GB (the CreateBoxDto unit). + */ +export function assertWithinPerBoxLimits(cpu: number, memoryGb: number, diskGb: number, limits: PerBoxLimits): void { + const violations: string[] = [] + if (limits.maxCpuPerBox > 0 && cpu > limits.maxCpuPerBox) { + violations.push(`cpu ${cpu} exceeds the per-box limit of ${limits.maxCpuPerBox}`) + } + if (limits.maxMemoryPerBox > 0 && memoryGb > limits.maxMemoryPerBox) { + violations.push(`memory ${memoryGb}GB exceeds the per-box limit of ${limits.maxMemoryPerBox}GB`) + } + if (limits.maxDiskPerBox > 0 && diskGb > limits.maxDiskPerBox) { + violations.push(`disk ${diskGb}GB exceeds the per-box limit of ${limits.maxDiskPerBox}GB`) + } + if (violations.length > 0) { + throw new BadRequestException(`Requested resources exceed per-box limits: ${violations.join('; ')}`) + } +} diff --git a/apps/api/src/box/services/proxy-cache-invalidation.service.ts b/apps/api/src/box/services/proxy-cache-invalidation.service.ts new file mode 100644 index 000000000..04809c09a --- /dev/null +++ b/apps/api/src/box/services/proxy-cache-invalidation.service.ts @@ -0,0 +1,35 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { InjectRedis } from '@nestjs-modules/ioredis' +import { Injectable, Logger } from '@nestjs/common' +import { OnEvent } from '@nestjs/event-emitter' +import Redis from 'ioredis' + +import { BoxEvents } from '../constants/box-events.constants' +import { BoxArchivedEvent } from '../events/box-archived.event' + +@Injectable() +export class ProxyCacheInvalidationService { + private readonly logger = new Logger(ProxyCacheInvalidationService.name) + private static readonly RUNNER_INFO_CACHE_PREFIX = 'proxy:box-runner-info:' + + constructor(@InjectRedis() private readonly redis: Redis) {} + + @OnEvent(BoxEvents.ARCHIVED) + async handleBoxArchived(event: BoxArchivedEvent): Promise { + await this.invalidateRunnerCache(event.box.id) + } + + private async invalidateRunnerCache(boxId: string): Promise { + try { + await this.redis.del(`${ProxyCacheInvalidationService.RUNNER_INFO_CACHE_PREFIX}${boxId}`) + this.logger.debug(`Invalidated box runner cache for ${boxId}`) + } catch (error) { + this.logger.warn(`Failed to invalidate runner cache for box ${boxId}: ${error.message}`) + } + } +} diff --git a/apps/api/src/sandbox/services/runner.service.ts b/apps/api/src/box/services/runner.service.ts similarity index 80% rename from apps/api/src/sandbox/services/runner.service.ts rename to apps/api/src/box/services/runner.service.ts index fa15f5879..650edc2c3 100644 --- a/apps/api/src/sandbox/services/runner.service.ts +++ b/apps/api/src/box/services/runner.service.ts @@ -19,14 +19,11 @@ import { Cron, CronExpression } from '@nestjs/schedule' import { DataSource, FindOptionsWhere, In, MoreThanOrEqual, Not, Repository, UpdateResult } from 'typeorm' import { Runner } from '../entities/runner.entity' import { CreateRunnerInternalDto } from '../dto/create-runner-internal.dto' -import { SandboxClass } from '../enums/sandbox-class.enum' +import { BoxClass } from '../enums/box-class.enum' import { RunnerState } from '../enums/runner-state.enum' import { BadRequestError } from '../../exceptions/bad-request.exception' import { EventEmitter2 } from '@nestjs/event-emitter' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SnapshotRunner } from '../entities/snapshot-runner.entity' -import { SnapshotRunnerState } from '../enums/snapshot-runner-state.enum' -import { RunnerSnapshotDto } from '../dto/runner-snapshot.dto' +import { BoxState } from '../enums/box-state.enum' import { RunnerAdapterFactory, RunnerInfo } from '../runner-adapter/runnerAdapter' import { RedisLockProvider } from '../common/redis-lock.provider' import { TypedConfigService } from '../../config/typed-config.service' @@ -41,12 +38,11 @@ import { RunnerStateUpdatedEvent } from '../events/runner-state-updated.event' import { RunnerDeletedEvent } from '../events/runner-deleted.event' import { generateApiKeyValue } from '../../common/utils/api-key' import { RunnerFullDto } from '../dto/runner-full.dto' -import { Snapshot } from '../entities/snapshot.entity' import { InjectRedis } from '@nestjs-modules/ioredis' import Redis from 'ioredis' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' +import { BoxDesiredState } from '../enums/box-desired-state.enum' import { runnerLookupCacheKeyById, RUNNER_LOOKUP_CACHE_TTL_MS } from '../utils/runner-lookup-cache.util' -import { SandboxRepository } from '../repositories/sandbox.repository' +import { BoxRepository } from '../repositories/box.repository' import { RunnerServiceInfo } from '../common/runner-service-info' @Injectable() @@ -59,14 +55,10 @@ export class RunnerService { @InjectRepository(Runner) private readonly runnerRepository: Repository, private readonly runnerAdapterFactory: RunnerAdapterFactory, - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(SnapshotRunner) - private readonly snapshotRunnerRepository: Repository, + private readonly boxRepository: BoxRepository, private readonly redisLockProvider: RedisLockProvider, private readonly configService: TypedConfigService, private readonly regionService: RegionService, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, @Inject(EventEmitter2) private eventEmitter: EventEmitter2, private readonly dataSource: DataSource, @@ -92,7 +84,7 @@ export class RunnerService { throw new BadRequestException('Runner name must be between 3 and 255 characters') } - const apiKey = createRunnerDto.apiKey ?? generateApiKeyValue() + const apiKey = createRunnerDto.apiKey ?? generateApiKeyValue(this.configService.getOrThrow('apiKey.prefix'), 'svc') let runner: Runner @@ -257,19 +249,19 @@ export class RunnerService { return this.runnerRepository.findOneBy({ apiKey }) } - async findBySandboxId(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId, state: Not(SandboxState.DESTROYED) }, + async findByBoxId(boxId: string): Promise { + const box = await this.boxRepository.findOne({ + where: { id: boxId, state: Not(BoxState.DESTROYED) }, select: ['runnerId'], }) - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) + if (!box) { + throw new NotFoundException(`Box with ID ${boxId} not found`) } - if (!sandbox.runnerId) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} does not have a runner`) + if (!box.runnerId) { + throw new NotFoundException(`Box with ID ${boxId} does not have a runner`) } - return this.findOne(sandbox.runnerId) + return this.findOne(box.runnerId) } async getRegionId(runnerId: string): Promise { @@ -302,26 +294,9 @@ export class RunnerService { ? params.excludedRunnerIds.filter((id) => !!id) : undefined - if (params.snapshotRef !== undefined) { - const snapshotRunners = await this.snapshotRunnerRepository.find({ - where: { - state: SnapshotRunnerState.READY, - snapshotRef: params.snapshotRef, - }, - }) - - let runnerIds = snapshotRunners.map((snapshotRunner) => snapshotRunner.runnerId) - - if (excludedRunnerIds?.length) { - runnerIds = runnerIds.filter((id) => !excludedRunnerIds.includes(id)) - } - - if (!runnerIds.length) { - return [] - } - - runnerFilter.id = In(runnerIds) - } else if (excludedRunnerIds?.length) { + // TODO(image-rewrite): artifact-cache aware runner selection removed with + // runner_artifact_cache; runners are no longer filtered by which artifact they have cached. + if (excludedRunnerIds?.length) { runnerFilter.id = Not(In(excludedRunnerIds)) } @@ -329,8 +304,8 @@ export class RunnerService { runnerFilter.region = In(params.regions) } - if (params.sandboxClass !== undefined) { - runnerFilter.class = params.sandboxClass + if (params.boxClass !== undefined) { + runnerFilter.class = params.boxClass } const runners = await this.runnerRepository.find({ @@ -343,7 +318,7 @@ export class RunnerService { /** * @throws {NotFoundException} If the runner is not found. * @throws {HttpException} If the runner is not unschedulable. - * @throws {HttpException} If the runner has sandboxes associated with it. + * @throws {HttpException} If the runner has boxes associated with it. */ async remove(id: string): Promise { const runner = await this.findOne(id) @@ -353,17 +328,17 @@ export class RunnerService { if (!runner.unschedulable) { throw new HttpException( - 'Cannot delete runner which is available for scheduling sandboxes', + 'Cannot delete runner which is available for scheduling boxes', HttpStatus.PRECONDITION_REQUIRED, ) } - const sandboxCount = await this.sandboxRepository.count({ - where: { runnerId: id, state: Not(In([SandboxState.ARCHIVED, SandboxState.DESTROYED])) }, + const boxCount = await this.boxRepository.count({ + where: { runnerId: id, state: Not(In([BoxState.ARCHIVED, BoxState.DESTROYED])) }, }) - if (sandboxCount > 0) { + if (boxCount > 0) { throw new HttpException( - 'Cannot delete runner which has sandboxes associated with it', + 'Cannot delete runner which has boxes associated with it', HttpStatus.PRECONDITION_REQUIRED, ) } @@ -389,8 +364,7 @@ export class RunnerService { currentAllocatedCpu?: number currentAllocatedMemoryGiB?: number currentAllocatedDiskGiB?: number - currentSnapshotCount?: number - currentStartedSandboxes?: number + currentStartedBoxes?: number cpu?: number memoryGiB?: number diskGiB?: number @@ -453,8 +427,7 @@ export class RunnerService { updateData.currentAllocatedCpu = metrics.currentAllocatedCpu || 0 updateData.currentAllocatedMemoryGiB = metrics.currentAllocatedMemoryGiB || 0 updateData.currentAllocatedDiskGiB = metrics.currentAllocatedDiskGiB || 0 - updateData.currentSnapshotCount = metrics.currentSnapshotCount || 0 - updateData.currentStartedSandboxes = metrics.currentStartedSandboxes || 0 + updateData.currentStartedBoxes = metrics.currentStartedBoxes || 0 updateData.cpu = metrics.cpu updateData.memoryGiB = metrics.memoryGiB updateData.diskGiB = metrics.diskGiB @@ -470,7 +443,7 @@ export class RunnerService { runnerCpu: updateData.cpu || runner.cpu, runnerMemoryGiB: updateData.memoryGiB || runner.memoryGiB, runnerDiskGiB: updateData.diskGiB || runner.diskGiB, - startedSandboxes: updateData.currentStartedSandboxes || 0, + startedBoxes: updateData.currentStartedBoxes || 0, }) } @@ -687,21 +660,21 @@ export class RunnerService { await Promise.allSettled( drainingRunners.map(async (runner) => { try { - // Check if runner has any sandboxes with desiredState != DESTROYED - const nonDestroyedSandboxCount = await this.sandboxRepository.count({ + // Check if runner has any boxes with desiredState != DESTROYED + const nonDestroyedBoxCount = await this.boxRepository.count({ where: { runnerId: runner.id, - desiredState: Not(SandboxDesiredState.DESTROYED), + desiredState: Not(BoxDesiredState.DESTROYED), }, }) const redisKey = `runner:draining-check:${runner.id}` - if (nonDestroyedSandboxCount > 0) { - // Reset counter if there are non-destroyed sandboxes + if (nonDestroyedBoxCount > 0) { + // Reset counter if there are non-destroyed boxes await this.redis.set(redisKey, '0', 'EX', 600) // 10 minute TTL this.logger.debug( - `Runner ${runner.id} has ${nonDestroyedSandboxCount} sandboxes with desiredState != DESTROYED, reset counter`, + `Runner ${runner.id} has ${nonDestroyedBoxCount} boxes with desiredState != DESTROYED, reset counter`, ) } else { // Increment counter @@ -718,7 +691,7 @@ export class RunnerService { } else { await this.redis.set(redisKey, count.toString(), 'EX', 600) // 10 minute TTL this.logger.debug( - `Runner ${runner.id} draining check passed (${count}/3), all sandboxes have desiredState = DESTROYED`, + `Runner ${runner.id} draining check passed (${count}/3), all boxes have desiredState = DESTROYED`, ) } } @@ -759,121 +732,8 @@ export class RunnerService { return availableRunners[randomIntFromInterval(0, availableRunners.length - 1)] } - async getSnapshotRunner(runnerId: string, snapshotRef: string): Promise { - return this.snapshotRunnerRepository.findOne({ - where: { - runnerId: runnerId, - snapshotRef: snapshotRef, - }, - }) - } - - async getSnapshotRunners(snapshotRef: string): Promise { - return this.snapshotRunnerRepository.find({ - where: { - snapshotRef, - }, - order: { - state: 'ASC', // Sorts state BUILDING_SNAPSHOT before ERROR - createdAt: 'ASC', // Sorts first runner to start building snapshot on top - }, - }) - } - - async createSnapshotRunnerEntry( - runnerId: string, - snapshotRef: string, - state?: SnapshotRunnerState, - errorReason?: string, - ): Promise { - try { - const snapshotRunner = new SnapshotRunner() - snapshotRunner.runnerId = runnerId - snapshotRunner.snapshotRef = snapshotRef - if (state) { - snapshotRunner.state = state - } - if (errorReason) { - snapshotRunner.errorReason = errorReason - } - await this.snapshotRunnerRepository.save(snapshotRunner) - } catch (error) { - if (error.code === '23505') { - // PostgreSQL unique violation error code - entry already exists, allow it - this.logger.debug( - `SnapshotRunner entry already exists for runnerId: ${runnerId}, snapshotRef: ${snapshotRef}. Continuing...`, - ) - return - } - throw error // Re-throw any other errors - } - } - - // TODO: combine getRunnersWithMultipleSnapshotsBuilding and getRunnersWithMultipleSnapshotsPulling? - - async getRunnersWithMultipleSnapshotsBuilding(maxSnapshotCount = 6): Promise { - const runners = await this.sandboxRepository - .createQueryBuilder('sandbox') - .select('sandbox.runnerId', 'runnerId') - .where('sandbox.state = :state', { state: SandboxState.BUILDING_SNAPSHOT }) - .andWhere('sandbox.buildInfoSnapshotRef IS NOT NULL') - .groupBy('sandbox.runnerId') - .having('COUNT(DISTINCT sandbox.buildInfoSnapshotRef) > :maxSnapshotCount', { maxSnapshotCount }) - .getRawMany() - - return runners.map((item) => item.runnerId) - } - - async getRunnersWithMultipleSnapshotsPulling(maxSnapshotCount = 6): Promise { - const runners = await this.snapshotRunnerRepository - .createQueryBuilder('snapshot_runner') - .select('snapshot_runner.runnerId') - .where('snapshot_runner.state = :state', { state: SnapshotRunnerState.PULLING_SNAPSHOT }) - .groupBy('snapshot_runner.runnerId') - .having('COUNT(*) > :maxSnapshotCount', { maxSnapshotCount }) - .getRawMany() - - return runners.map((item) => item.runnerId) - } - - async getRunnersBySnapshotRef(ref: string): Promise { - const snapshotRunners = await this.snapshotRunnerRepository.find({ - where: { - snapshotRef: ref, - state: Not(SnapshotRunnerState.ERROR), - }, - select: ['runnerId', 'id'], - }) - - // Extract distinct runnerIds from snapshot runners - const runnerIds = [...new Set(snapshotRunners.map((sr) => sr.runnerId))] - - // Find all runners with these IDs - const runners = await this.runnerRepository.find({ - where: { id: In(runnerIds) }, - select: ['id', 'domain'], - }) - - this.logger.debug(`Found ${runners.length} runners with IDs: ${runners.map((r) => r.id).join(', ')}`) - - // Map to DTO format, including the snapshot runner ID - return runners.map((runner) => { - const snapshotRunner = snapshotRunners.find((sr) => sr.runnerId === runner.id) - return new RunnerSnapshotDto(snapshotRunner.id, runner.id, runner.domain) - }) - } - - async getInitialRunnerBySnapshotId(snapshotId: string): Promise { - const snapshot = await this.snapshotRepository.findOne({ where: { id: snapshotId } }) - if (!snapshot) { - throw new NotFoundException('Snapshot runner not found') - } - if (!snapshot.initialRunnerId) { - throw new BadRequestException('Initial runner not found') - } - - return await this.findOneOrFail(snapshot.initialRunnerId) - } + // TODO(image-rewrite): the image based runner lookup helpers were + // removed with the image subsystem. async getRunnerApiVersion(runnerId: string): Promise { const result = await this.runnerRepository.findOneOrFail({ @@ -922,10 +782,10 @@ export class RunnerService { params.allocatedCpu < 0 || params.allocatedMemoryGiB < 0 || params.allocatedDiskGiB < 0 || - params.startedSandboxes < 0 + params.startedBoxes < 0 ) { this.logger.warn( - `Runner ${runnerId} has negative values for load, CPU, memory, disk, allocated CPU, allocated memory, allocated disk, or started sandboxes`, + `Runner ${runnerId} has negative values for load, CPU, memory, disk, allocated CPU, allocated memory, allocated disk, or started boxes`, ) return 0 } @@ -942,7 +802,7 @@ export class RunnerService { (params.allocatedCpu / params.runnerCpu) * 100, (params.allocatedMemoryGiB / params.runnerMemoryGiB) * 100, (params.allocatedDiskGiB / params.runnerDiskGiB) * 100, - params.startedSandboxes, // Raw count, will be normalized against its critical target value + params.startedBoxes, // Raw count, will be normalized against its critical target value ] // Calculate weighted Euclidean distances @@ -1009,7 +869,7 @@ export class RunnerService { this.configService.getOrThrow('runnerScore.weights.allocatedCpu'), this.configService.getOrThrow('runnerScore.weights.allocatedMemory'), this.configService.getOrThrow('runnerScore.weights.allocatedDisk'), - this.configService.getOrThrow('runnerScore.weights.startedSandboxes'), + this.configService.getOrThrow('runnerScore.weights.startedBoxes'), ], penalty: { exponents: { @@ -1033,7 +893,7 @@ export class RunnerService { this.configService.getOrThrow('runnerScore.targetValues.optimal.allocCpu'), this.configService.getOrThrow('runnerScore.targetValues.optimal.allocMem'), this.configService.getOrThrow('runnerScore.targetValues.optimal.allocDisk'), - this.configService.getOrThrow('runnerScore.targetValues.optimal.startedSandboxes'), + this.configService.getOrThrow('runnerScore.targetValues.optimal.startedBoxes'), ], critical: [ this.configService.getOrThrow('runnerScore.targetValues.critical.cpu'), @@ -1042,7 +902,7 @@ export class RunnerService { this.configService.getOrThrow('runnerScore.targetValues.critical.allocCpu'), this.configService.getOrThrow('runnerScore.targetValues.critical.allocMem'), this.configService.getOrThrow('runnerScore.targetValues.critical.allocDisk'), - this.configService.getOrThrow('runnerScore.targetValues.critical.startedSandboxes'), + this.configService.getOrThrow('runnerScore.targetValues.critical.startedBoxes'), ], }, } @@ -1051,8 +911,7 @@ export class RunnerService { export class GetRunnerParams { regions?: string[] - sandboxClass?: SandboxClass - snapshotRef?: string + boxClass?: BoxClass excludedRunnerIds?: string[] availabilityScoreThreshold?: number } @@ -1065,7 +924,7 @@ interface AvailabilityScoreParams { allocatedCpu: number allocatedMemoryGiB: number allocatedDiskGiB: number - startedSandboxes: number + startedBoxes: number runnerCpu: number runnerMemoryGiB: number runnerDiskGiB: number diff --git a/apps/api/src/box/services/volume.service.ts b/apps/api/src/box/services/volume.service.ts new file mode 100644 index 000000000..f776f1a86 --- /dev/null +++ b/apps/api/src/box/services/volume.service.ts @@ -0,0 +1,245 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ConflictException, Injectable, Logger, NotFoundException, ServiceUnavailableException } from '@nestjs/common' +import { InjectRepository } from '@nestjs/typeorm' +import { Repository, Not, In } from 'typeorm' +import { Volume } from '../entities/volume.entity' +import { VolumeState } from '../enums/volume-state.enum' +import { CreateVolumeDto } from '../dto/create-volume.dto' +import { v4 as uuidv4 } from 'uuid' +import { BadRequestError } from '../../exceptions/bad-request.exception' +import { Organization } from '../../organization/entities/organization.entity' +import { OnEvent } from '@nestjs/event-emitter' +import { BoxEvents } from '../constants/box-events.constants' +import { BoxCreatedEvent } from '../events/box-create.event' +import { OrganizationService } from '../../organization/services/organization.service' +import { TypedConfigService } from '../../config/typed-config.service' +import { RedisLockProvider } from '../common/redis-lock.provider' +import { BoxRepository } from '../repositories/box.repository' +import { BoxDesiredState } from '../enums/box-desired-state.enum' + +@Injectable() +export class VolumeService { + private readonly logger = new Logger(VolumeService.name) + + constructor( + @InjectRepository(Volume) + private readonly volumeRepository: Repository, + private readonly boxRepository: BoxRepository, + private readonly organizationService: OrganizationService, + private readonly configService: TypedConfigService, + private readonly redisLockProvider: RedisLockProvider, + ) {} + + async create(organization: Organization, createVolumeDto: CreateVolumeDto): Promise { + if (!this.configService.get('s3.endpoint')) { + throw new ServiceUnavailableException('Object storage is not configured') + } + + this.organizationService.assertOrganizationIsNotSuspended(organization) + + const volume = new Volume() + + // Generate ID + volume.id = uuidv4() + + // Set name from DTO or use ID as default + volume.name = createVolumeDto.name || volume.id + + // Check if volume with same name already exists for organization + const existingVolume = await this.volumeRepository.findOne({ + where: { + organizationId: organization.id, + name: volume.name, + state: Not(VolumeState.DELETED), + }, + }) + + if (existingVolume) { + throw new BadRequestError(`Volume with name ${volume.name} already exists`) + } + + volume.organizationId = organization.id + volume.state = VolumeState.PENDING_CREATE + + const savedVolume = await this.volumeRepository.save(volume) + this.logger.debug(`Created volume ${savedVolume.id} for organization ${organization.id}`) + return savedVolume + } + + async delete(volumeId: string): Promise { + const volume = await this.volumeRepository.findOne({ + where: { + id: volumeId, + }, + }) + + if (!volume) { + throw new NotFoundException(`Volume with ID ${volumeId} not found`) + } + + if (volume.state !== VolumeState.READY && volume.state !== VolumeState.ERROR) { + throw new BadRequestError( + `Volume must be in '${VolumeState.READY}' or '${VolumeState.ERROR}' state in order to be deleted`, + ) + } + + // Check if any non-destroyed boxes are using this volume + const boxUsingVolume = await this.boxRepository + .createQueryBuilder('box') + .where('box.organizationId = :organizationId', { + organizationId: volume.organizationId, + }) + .andWhere('box.volumes @> :volFilter::jsonb', { + volFilter: JSON.stringify([{ volumeId }]), + }) + .andWhere('box.desiredState != :destroyed', { + destroyed: BoxDesiredState.DESTROYED, + }) + .select(['box.id', 'box.name']) + .getOne() + + if (boxUsingVolume) { + throw new ConflictException( + `Volume cannot be deleted because it is in use by one or more boxes (e.g. ${boxUsingVolume.name})`, + ) + } + + // Update state to mark as deleting + volume.state = VolumeState.PENDING_DELETE + await this.volumeRepository.save(volume) + this.logger.debug(`Marked volume ${volumeId} for deletion`) + } + + async findOne(volumeId: string): Promise { + const volume = await this.volumeRepository.findOne({ + where: { id: volumeId }, + }) + + if (!volume) { + throw new NotFoundException(`Volume with ID ${volumeId} not found`) + } + + return volume + } + + async findAll(organizationId: string, includeDeleted = false): Promise { + return this.volumeRepository.find({ + where: { + organizationId, + ...(includeDeleted ? {} : { state: Not(VolumeState.DELETED) }), + }, + order: { + lastUsedAt: { + direction: 'DESC', + nulls: 'LAST', + }, + createdAt: 'DESC', + }, + }) + } + + async findByName(organizationId: string, name: string): Promise { + const volume = await this.volumeRepository.findOne({ + where: { + organizationId, + name, + state: Not(VolumeState.DELETED), + }, + }) + + if (!volume) { + throw new NotFoundException(`Volume with name ${name} not found`) + } + + return volume + } + + async validateVolumes(organizationId: string, volumeIdOrNames: string[]): Promise { + if (!volumeIdOrNames.length) { + return + } + + const volumes = await this.volumeRepository.find({ + where: [ + { id: In(volumeIdOrNames), organizationId, state: Not(VolumeState.DELETED) }, + { name: In(volumeIdOrNames), organizationId, state: Not(VolumeState.DELETED) }, + ], + }) + + // Check if all requested volumes were found and are in a READY state + const foundIds = new Set(volumes.map((v) => v.id)) + const foundNames = new Set(volumes.map((v) => v.name)) + + for (const idOrName of volumeIdOrNames) { + if (!foundIds.has(idOrName) && !foundNames.has(idOrName)) { + throw new NotFoundException(`Volume '${idOrName}' not found`) + } + } + + for (const volume of volumes) { + if (volume.state !== VolumeState.READY) { + throw new BadRequestError(`Volume '${volume.name}' is not in a ready state. Current state: ${volume.state}`) + } + } + } + + async getOrganizationId(params: { id: string } | { name: string; organizationId: string }): Promise { + if ('id' in params) { + const volume = await this.volumeRepository.findOneOrFail({ + where: { + id: params.id, + }, + select: ['organizationId'], + loadEagerRelations: false, + }) + return volume.organizationId + } + + const volume = await this.volumeRepository.findOneOrFail({ + where: { + name: params.name, + organizationId: params.organizationId, + }, + select: ['organizationId'], + loadEagerRelations: false, + }) + + return volume.organizationId + } + + @OnEvent(BoxEvents.CREATED) + private async handleBoxCreatedEvent(event: BoxCreatedEvent) { + if (!event.box.volumes.length) { + return + } + + try { + const volumeIds = event.box.volumes.map((vol) => vol.volumeId) + const volumes = await this.volumeRepository.find({ where: { id: In(volumeIds) } }) + + const results = await Promise.allSettled( + volumes.map(async (volume) => { + // Update once per minute at most + if (!(await this.redisLockProvider.lock(`volume:${volume.id}:update-last-used`, 60))) { + return + } + volume.lastUsedAt = event.box.createdAt + return this.volumeRepository.save(volume) + }), + ) + + results.forEach((result) => { + if (result.status === 'rejected') { + this.logger.error(`Failed to update volume lastUsedAt timestamp for box ${event.box.id}: ${result.reason}`) + } + }) + } catch (err) { + this.logger.error(err) + } + } +} diff --git a/apps/api/src/sandbox/subscribers/runner.subscriber.ts b/apps/api/src/box/subscribers/runner.subscriber.ts similarity index 100% rename from apps/api/src/sandbox/subscribers/runner.subscriber.ts rename to apps/api/src/box/subscribers/runner.subscriber.ts diff --git a/apps/api/src/sandbox/subscribers/volume.subscriber.ts b/apps/api/src/box/subscribers/volume.subscriber.ts similarity index 100% rename from apps/api/src/sandbox/subscribers/volume.subscriber.ts rename to apps/api/src/box/subscribers/volume.subscriber.ts diff --git a/apps/api/src/box/utils/box-id.util.spec.ts b/apps/api/src/box/utils/box-id.util.spec.ts new file mode 100644 index 000000000..03460c04d --- /dev/null +++ b/apps/api/src/box/utils/box-id.util.spec.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BOX_ID_LENGTH, BOX_ID_REGEX, generateBoxId } from './box-id.util' + +describe('box ID utilities', () => { + it('generates 12-character Base62 public box IDs', () => { + const boxId = generateBoxId() + + expect(boxId).toHaveLength(BOX_ID_LENGTH) + expect(boxId).toMatch(BOX_ID_REGEX) + }) +}) diff --git a/apps/api/src/box/utils/box-id.util.ts b/apps/api/src/box/utils/box-id.util.ts new file mode 100644 index 000000000..168b4f5d0 --- /dev/null +++ b/apps/api/src/box/utils/box-id.util.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { randomInt } from 'crypto' + +export const BOX_ID_LENGTH = 12 +export const BOX_ID_ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +export const BOX_ID_REGEX = /^[0-9A-Za-z]{12}$/ + +export function generateBoxId(): string { + let boxId = '' + for (let i = 0; i < BOX_ID_LENGTH; i += 1) { + boxId += BOX_ID_ALPHABET[randomInt(BOX_ID_ALPHABET.length)] + } + return boxId +} + +export function isBoxId(value: string): boolean { + return BOX_ID_REGEX.test(value) +} diff --git a/apps/api/src/box/utils/box-lookup-cache.util.ts b/apps/api/src/box/utils/box-lookup-cache.util.ts new file mode 100644 index 000000000..ac2d25318 --- /dev/null +++ b/apps/api/src/box/utils/box-lookup-cache.util.ts @@ -0,0 +1,47 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +export const BOX_LOOKUP_CACHE_TTL_MS = 10_000 +export const BOX_ORG_ID_CACHE_TTL_MS = 60_000 +export const TOOLBOX_PROXY_URL_CACHE_TTL_S = 30 * 60 // 30 minutes + +type BoxLookupCacheKeyArgs = { + organizationId?: string | null + returnDestroyed?: boolean +} + +export function boxLookupCacheKeyById(args: BoxLookupCacheKeyArgs & { id: string }): string { + const organizationId = args.organizationId ?? 'none' + const returnDestroyed = args.returnDestroyed ? 1 : 0 + return `box:lookup:by-id:org:${organizationId}:returnDestroyed:${returnDestroyed}:value:${args.id}` +} + +export function boxLookupCacheKeyByName(args: BoxLookupCacheKeyArgs & { boxName: string }): string { + const organizationId = args.organizationId ?? 'none' + const returnDestroyed = args.returnDestroyed ? 1 : 0 + return `box:lookup:by-name:org:${organizationId}:returnDestroyed:${returnDestroyed}:value:${args.boxName}` +} + +export function boxLookupCacheKeyByAuthToken(args: { authToken: string }): string { + return `box:lookup:by-authToken:${args.authToken}` +} + +type BoxOrgIdCacheKeyArgs = { + organizationId?: string +} + +export function boxOrgIdCacheKeyById(args: BoxOrgIdCacheKeyArgs & { id: string }): string { + const organizationId = args.organizationId ?? 'none' + return `box:orgId:by-id:org:${organizationId}:value:${args.id}` +} + +export function boxOrgIdCacheKeyByName(args: BoxOrgIdCacheKeyArgs & { boxName: string }): string { + const organizationId = args.organizationId ?? 'none' + return `box:orgId:by-name:org:${organizationId}:value:${args.boxName}` +} + +export function toolboxProxyUrlCacheKey(regionId: string): string { + return `toolbox-proxy-url:region:${regionId}` +} diff --git a/apps/api/src/sandbox/utils/lock-key.util.ts b/apps/api/src/box/utils/lock-key.util.ts similarity index 82% rename from apps/api/src/sandbox/utils/lock-key.util.ts rename to apps/api/src/box/utils/lock-key.util.ts index d5e9ecb1e..182554475 100644 --- a/apps/api/src/sandbox/utils/lock-key.util.ts +++ b/apps/api/src/box/utils/lock-key.util.ts @@ -5,5 +5,5 @@ */ export function getStateChangeLockKey(id: string): string { - return `sandbox:${id}:state-change` + return `box:${id}:state-change` } diff --git a/apps/api/src/box/utils/network-validation.util.spec.ts b/apps/api/src/box/utils/network-validation.util.spec.ts new file mode 100644 index 000000000..89ed3980d --- /dev/null +++ b/apps/api/src/box/utils/network-validation.util.spec.ts @@ -0,0 +1,37 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { isValidNetworkAllowEntry, validateNetworkAllowList } from './network-validation.util' + +describe('network allow list validation', () => { + it.each(['api.openai.com', '*.anthropic.com', '192.168.1.1', '10.0.0.0/8'])( + 'accepts supported allow_net entry %s', + (entry) => { + expect(isValidNetworkAllowEntry(entry)).toBe(true) + }, + ) + + it.each(['', 'https://api.openai.com', '*example.com', 'api..openai.com', '10.0.0.0/33', '999.0.0.1'])( + 'rejects invalid allow_net entry %s', + (entry) => { + expect(isValidNetworkAllowEntry(entry)).toBe(false) + }, + ) + + it('accepts comma-separated allow_net lists with hostnames, wildcards, IPs, and CIDRs', () => { + expect(() => validateNetworkAllowList('api.openai.com, *.anthropic.com, 192.168.1.1, 10.0.0.0/8')).not.toThrow() + }) + + it('rejects more than ten allow_net entries', () => { + const entries = Array.from({ length: 11 }, (_, index) => `api-${index}.example.com`).join(',') + + expect(() => validateNetworkAllowList(entries)).toThrow('more than 10 networks') + }) + + it('rejects invalid comma-separated allow_net entries', () => { + expect(() => validateNetworkAllowList('api.openai.com,10.0.0.0/33')).toThrow('Invalid network allow list entry') + }) +}) diff --git a/apps/api/src/box/utils/network-validation.util.ts b/apps/api/src/box/utils/network-validation.util.ts new file mode 100644 index 000000000..f98ec9aee --- /dev/null +++ b/apps/api/src/box/utils/network-validation.util.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ +import { isIPv4 } from 'net' + +export const MAX_NETWORK_ALLOW_LIST_ENTRIES = 10 + +const IPV4_LIKE_PATTERN = /^(\d{1,3}\.){3}\d{1,3}$/ +const HOSTNAME_LABEL_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/ + +/** + * Validates network allow list entries accepted by the Box API/core contract: + * exact IPv4, IPv4 CIDR, exact hostname, and wildcard hostname. + * @param networkAllowList - Comma-separated string of network addresses + * @returns null if valid, error message string if invalid + */ +export function validateNetworkAllowList(networkAllowList: string): void { + const networks = networkAllowList + .split(',') + .map((net: string) => net.trim()) + .filter(Boolean) + + if (networks.length > MAX_NETWORK_ALLOW_LIST_ENTRIES) { + throw new Error(`Network allow list cannot contain more than ${MAX_NETWORK_ALLOW_LIST_ENTRIES} networks`) + } + + for (const network of networks) { + if (!isValidNetworkAllowEntry(network)) { + throw new Error( + `Invalid network allow list entry: "${network}". Must be an IPv4 address, IPv4 CIDR, hostname, or wildcard hostname`, + ) + } + } +} + +export function isValidNetworkAllowEntry(entry: string): boolean { + const network = entry.trim() + if (!network) { + return false + } + + if (isIPv4(network)) { + return true + } + + if (network.includes('/')) { + const [ipAddress, prefixLength, extra] = network.split('/') + if (extra !== undefined || !isIPv4(ipAddress) || !/^\d+$/.test(prefixLength)) { + return false + } + + const prefix = Number(prefixLength) + return prefix >= 0 && prefix <= 32 + } + + if (IPV4_LIKE_PATTERN.test(network)) { + return false + } + + if (network.startsWith('*.')) { + return isValidHostname(network.slice(2)) + } + + return isValidHostname(network) +} + +function isValidHostname(hostname: string): boolean { + if (hostname.length === 0 || hostname.length > 253 || hostname.includes('..')) { + return false + } + + return hostname.split('.').every((label) => HOSTNAME_LABEL_PATTERN.test(label)) +} diff --git a/apps/api/src/sandbox/utils/runner-lookup-cache.util.ts b/apps/api/src/box/utils/runner-lookup-cache.util.ts similarity index 100% rename from apps/api/src/sandbox/utils/runner-lookup-cache.util.ts rename to apps/api/src/box/utils/runner-lookup-cache.util.ts diff --git a/apps/api/src/sandbox/utils/sanitize-error.util.ts b/apps/api/src/box/utils/sanitize-error.util.ts similarity index 83% rename from apps/api/src/sandbox/utils/sanitize-error.util.ts rename to apps/api/src/box/utils/sanitize-error.util.ts index 308f04cd9..90200a2e7 100644 --- a/apps/api/src/sandbox/utils/sanitize-error.util.ts +++ b/apps/api/src/box/utils/sanitize-error.util.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0 */ -export function sanitizeSandboxError(error: any): { recoverable: boolean; errorReason: string } { +export function sanitizeBoxError(error: any): { recoverable: boolean; errorReason: string } { if (typeof error === 'string') { try { const errObj = JSON.parse(error) as { recoverable: boolean; errorReason: string } @@ -15,7 +15,7 @@ export function sanitizeSandboxError(error: any): { recoverable: boolean; errorR } else if (typeof error === 'object' && error !== null && 'recoverable' in error && 'errorReason' in error) { return { recoverable: error.recoverable, errorReason: error.errorReason } } else if (typeof error === 'object' && error.message) { - return sanitizeSandboxError(error.message) + return sanitizeBoxError(error.message) } return { recoverable: false, errorReason: String(error) } diff --git a/apps/api/src/sandbox/utils/volume-mount-path-validation.util.ts b/apps/api/src/box/utils/volume-mount-path-validation.util.ts similarity index 84% rename from apps/api/src/sandbox/utils/volume-mount-path-validation.util.ts rename to apps/api/src/box/utils/volume-mount-path-validation.util.ts index 182e27639..50e06d80f 100644 --- a/apps/api/src/sandbox/utils/volume-mount-path-validation.util.ts +++ b/apps/api/src/box/utils/volume-mount-path-validation.util.ts @@ -4,14 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { SandboxVolume } from '../dto/sandbox.dto' +import { BoxVolume } from '../dto/box.dto' /** - * Validates mount paths for sandbox volumes to ensure they are safe and valid - * @param volumes - Array of SandboxVolume objects to validate + * Validates mount paths for box volumes to ensure they are safe and valid + * @param volumes - Array of BoxVolume objects to validate * @throws Error with descriptive message if any mount path is invalid */ -export function validateMountPaths(volumes: SandboxVolume[]): void { +export function validateMountPaths(volumes: BoxVolume[]): void { const errors: string[] = [] for (const volume of volumes) { @@ -55,11 +55,11 @@ export function validateMountPaths(volumes: SandboxVolume[]): void { } /** - * Validates subpaths for sandbox volumes to ensure they are safe S3 key prefixes - * @param volumes - Array of SandboxVolume objects to validate + * Validates subpaths for box volumes to ensure they are safe S3 key prefixes + * @param volumes - Array of BoxVolume objects to validate * @throws Error with descriptive message if any subpath is invalid */ -export function validateSubpaths(volumes: SandboxVolume[]): void { +export function validateSubpaths(volumes: BoxVolume[]): void { const errors: string[] = [] for (const volume of volumes) { diff --git a/apps/api/src/boxlite-rest/boxlite-auth.controller.ts b/apps/api/src/boxlite-rest/boxlite-auth.controller.ts deleted file mode 100644 index 2278a94b1..000000000 --- a/apps/api/src/boxlite-rest/boxlite-auth.controller.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Controller, Post, Body, HttpCode, BadRequestException, Inject } from '@nestjs/common' -import { ApiTags } from '@nestjs/swagger' -import { ApiKeyService } from '../api-key/api-key.service' - -@ApiTags('BoxLite REST') -@Controller('v1') -export class BoxliteAuthController { - constructor( - @Inject(ApiKeyService) - private readonly apiKeyService: ApiKeyService, - ) {} - - @Post('oauth/tokens') - @HttpCode(200) - async getToken( - @Body() body: { grant_type?: string; client_id?: string; client_secret?: string }, - ) { - if (body.grant_type !== 'client_credentials') { - throw new BadRequestException('Only client_credentials grant type is supported') - } - - if (!body.client_secret) { - throw new BadRequestException('client_secret (API key) is required') - } - - try { - await this.apiKeyService.getApiKeyByValue(body.client_secret) - } catch { - throw new BadRequestException('Invalid API key') - } - - return { - access_token: body.client_secret, - token_type: 'bearer', - expires_in: 86400, - } - } -} diff --git a/apps/api/src/boxlite-rest/boxlite-box.controller.ts b/apps/api/src/boxlite-rest/boxlite-box.controller.ts index 53226bf4d..c95de4340 100644 --- a/apps/api/src/boxlite-rest/boxlite-box.controller.ts +++ b/apps/api/src/boxlite-rest/boxlite-box.controller.ts @@ -16,75 +16,114 @@ import { HttpCode, UseGuards, Logger, - NotFoundException, Res, } from '@nestjs/common' -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger' +import { ApiTags, ApiBearerAuth, ApiResponse, ApiExcludeController } from '@nestjs/swagger' import { Response } from 'express' import { CombinedAuthGuard } from '../auth/combined-auth.guard' import { OrganizationResourceActionGuard } from '../organization/guards/organization-resource-action.guard' import { AuthContext } from '../common/decorators/auth-context.decorator' import { OrganizationAuthContext } from '../common/interfaces/auth-context.interface' -import { SandboxService } from '../sandbox/services/sandbox.service' -import { SandboxStateWaiterService } from '../sandbox/services/sandbox-state-waiter.service' -import { Sandbox } from '../sandbox/entities/sandbox.entity' -import { SandboxState } from '../sandbox/enums/sandbox-state.enum' -import { SandboxDesiredState } from '../sandbox/enums/sandbox-desired-state.enum' +import { BoxService } from '../box/services/box.service' +import { BoxStateWaiterService } from '../box/services/box-state-waiter.service' +import { Box } from '../box/entities/box.entity' +import { BoxState } from '../box/enums/box-state.enum' +import { BoxDesiredState } from '../box/enums/box-desired-state.enum' import { BoxResponseDto, ListBoxesResponseDto } from './dto/box-response.dto' import { CreateBoxDto } from './dto/create-box.dto' -import { sandboxToBoxResponse, createBoxToCreateSandbox } from './mappers/sandbox-to-box.mapper' - +import { boxToBoxResponse, createBoxToCreateBox } from './mappers/box-to-box.mapper' +import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../audit/decorators/audit.decorator' +import { AuditAction } from '../audit/enums/audit-action.enum' +import { AuditTarget } from '../audit/enums/audit-target.enum' +// Spec-first surface: the contract is openapi/box.openapi.yaml, not the +// generated product spec (which `:prefix` routes would render invalid). +@ApiExcludeController() @ApiTags('BoxLite REST') -@Controller('v1/:prefix/boxes') +@Controller(['v1/boxes', 'v1/:prefix/boxes']) @UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard) @ApiBearerAuth() export class BoxliteBoxController { private readonly logger = new Logger(BoxliteBoxController.name) constructor( - private readonly sandboxService: SandboxService, - private readonly sandboxStateWaiter: SandboxStateWaiterService, + private readonly boxService: BoxService, + private readonly boxStateWaiter: BoxStateWaiterService, ) {} @Post() @HttpCode(201) + @ApiResponse({ + status: 201, + description: 'Box created', + type: BoxResponseDto, + }) + @Audit({ + action: AuditAction.CREATE, + targetType: AuditTarget.BOX, + targetIdFromResult: (result: BoxResponseDto) => result?.box_id, + requestMetadata: { + body: (req: TypedRequest) => ({ + name: req.body?.name, + image: req.body?.image, + user: req.body?.user, + env: req.body?.env + ? Object.fromEntries(Object.keys(req.body?.env).map((key) => [key, MASKED_AUDIT_VALUE])) + : undefined, + cpus: req.body?.cpus, + memory_mib: req.body?.memory_mib, + disk_size_gb: req.body?.disk_size_gb, + working_dir: req.body?.working_dir, + entrypoint: req.body?.entrypoint, + cmd: req.body?.cmd, + auto_remove: req.body?.auto_remove, + detach: req.body?.detach, + }), + }, + }) async createBox( @AuthContext() authContext: OrganizationAuthContext, @Body() dto: CreateBoxDto, ): Promise { const organization = authContext.organization - const createSandboxDto = createBoxToCreateSandbox(dto) - let sandbox = await this.sandboxService.createFromSnapshot(createSandboxDto, organization) - if (sandbox.state !== SandboxState.STARTED) { - sandbox = await this.sandboxStateWaiter.waitForStarted( - sandbox.id, - organization.id, - 30, - ) + const createBoxDto = createBoxToCreateBox(dto) + + let box = await this.boxService.create(createBoxDto, organization) + if (box.state !== BoxState.STARTED) { + box = await this.boxStateWaiter.waitForStarted(box.id, organization.id, 30) } - return sandboxToBoxResponse(sandbox) + return boxToBoxResponse(box) } @Get() + @ApiResponse({ + status: 200, + description: 'List boxes', + type: ListBoxesResponseDto, + }) async listBoxes( @AuthContext() authContext: OrganizationAuthContext, @Query('pageSize') pageSize?: string, ): Promise { - const sandboxes = await this.sandboxService.findAllDeprecated(authContext.organizationId) - const dtos = await this.sandboxService.toSandboxDtos(sandboxes) + const boxes = await this.boxService.findAllDeprecated(authContext.organizationId) + const dtos = await this.boxService.toBoxDtos(boxes) return { - boxes: dtos.map(sandboxToBoxResponse), + boxes: dtos.map(boxToBoxResponse), } } @Get(':boxId') + @ApiResponse({ + status: 200, + description: 'Box details', + type: BoxResponseDto, + }) async getBox( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, ): Promise { - const sandbox = await this.sandboxService.findOneByIdOrName(boxId, authContext.organizationId) - const dto = await this.sandboxService.toSandboxDto(sandbox) - return sandboxToBoxResponse(dto) + const box = await this.boxService.findOneByIdOrName(boxId, authContext.organizationId) + const dto = await this.boxService.toBoxDto(box) + return boxToBoxResponse(dto) } @Head(':boxId') @@ -94,7 +133,7 @@ export class BoxliteBoxController { @Res() res: Response, ) { try { - await this.sandboxService.findOneByIdOrName(boxId, authContext.organizationId) + await this.boxService.findOneByIdOrName(boxId, authContext.organizationId) res.status(204).end() } catch { res.status(404).end() @@ -103,66 +142,71 @@ export class BoxliteBoxController { @Delete(':boxId') @HttpCode(204) - async removeBox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('boxId') boxId: string, - ) { - await this.sandboxService.destroy(boxId, authContext.organizationId) + @Audit({ + action: AuditAction.DELETE, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxId, + }) + async removeBox(@AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string) { + await this.boxService.destroy(boxId, authContext.organizationId) } @Post(':boxId/start') + @ApiResponse({ + status: 201, + description: 'Box start requested', + type: BoxResponseDto, + }) + @Audit({ + action: AuditAction.START, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxId, + targetIdFromResult: (result: BoxResponseDto) => result?.box_id, + }) async startBox( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, ): Promise { - let sandbox = await this.sandboxService.findOneByIdOrName( - boxId, - authContext.organizationId, - ) + let box = await this.boxService.findOneByIdOrName(boxId, authContext.organizationId) - if (this.isStartAlreadyInProgress(sandbox)) { - const dto = await this.sandboxStateWaiter.waitForStarted( - sandbox.id, - authContext.organizationId, - 30, - ) - return sandboxToBoxResponse(dto) + if (this.isStartAlreadyInProgress(box)) { + const dto = await this.boxStateWaiter.waitForStarted(box.id, authContext.organizationId, 30) + return boxToBoxResponse(dto) } - sandbox = await this.sandboxService.start(boxId, authContext.organization) - let dto = await this.sandboxService.toSandboxDto(sandbox) - if (dto.state !== SandboxState.STARTED) { - dto = await this.sandboxStateWaiter.waitForStarted( - sandbox.id, - authContext.organizationId, - 30, - ) + box = await this.boxService.start(boxId, authContext.organization) + let dto = await this.boxService.toBoxDto(box) + if (dto.state !== BoxState.STARTED) { + dto = await this.boxStateWaiter.waitForStarted(box.id, authContext.organizationId, 30) } - return sandboxToBoxResponse(dto) + return boxToBoxResponse(dto) } @Post(':boxId/stop') + @ApiResponse({ + status: 201, + description: 'Box stop requested', + type: BoxResponseDto, + }) + @Audit({ + action: AuditAction.STOP, + targetType: AuditTarget.BOX, + targetIdFromRequest: (req) => req.params.boxId, + targetIdFromResult: (result: BoxResponseDto) => result?.box_id, + }) async stopBox( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, ): Promise { - const sandbox = await this.sandboxService.stop(boxId, authContext.organizationId) - const dto = await this.sandboxService.toSandboxDto(sandbox) - return sandboxToBoxResponse(dto) + const box = await this.boxService.stop(boxId, authContext.organizationId) + const dto = await this.boxService.toBoxDto(box) + return boxToBoxResponse(dto) } - private isStartAlreadyInProgress(sandbox: Sandbox): boolean { + private isStartAlreadyInProgress(box: Box): boolean { return ( - sandbox.desiredState === SandboxDesiredState.STARTED && - [ - SandboxState.UNKNOWN, - SandboxState.CREATING, - SandboxState.STARTING, - SandboxState.RESTORING, - SandboxState.PULLING_SNAPSHOT, - SandboxState.BUILDING_SNAPSHOT, - SandboxState.PENDING_BUILD, - ].includes(sandbox.state) + box.desiredState === BoxDesiredState.STARTED && + [BoxState.UNKNOWN, BoxState.CREATING, BoxState.STARTING, BoxState.RESTORING].includes(box.state) ) } } diff --git a/apps/api/src/boxlite-rest/boxlite-config.controller.ts b/apps/api/src/boxlite-rest/boxlite-config.controller.ts index 13db62383..96e8a030f 100644 --- a/apps/api/src/boxlite-rest/boxlite-config.controller.ts +++ b/apps/api/src/boxlite-rest/boxlite-config.controller.ts @@ -5,8 +5,10 @@ */ import { Controller, Get } from '@nestjs/common' -import { ApiTags } from '@nestjs/swagger' +import { ApiTags, ApiExcludeController } from '@nestjs/swagger' +// Spec-first surface: the contract is openapi/box.openapi.yaml. +@ApiExcludeController() @ApiTags('BoxLite REST') @Controller('v1') export class BoxliteConfigController { diff --git a/apps/api/src/boxlite-rest/boxlite-me.controller.spec.ts b/apps/api/src/boxlite-rest/boxlite-me.controller.spec.ts new file mode 100644 index 000000000..e48cd58ee --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-me.controller.spec.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiKey } from '../api-key/api-key.entity' +import { AuthContext } from '../common/interfaces/auth-context.interface' +import { OrganizationService } from '../organization/services/organization.service' +import { BoxliteMeController } from './boxlite-me.controller' + +describe('BoxliteMeController', () => { + // resolvePathPrefix only touches OrganizationService on the user-session path; + // API-key contexts short-circuit on apiKey.organizationId. + function makeController(orgFindByUser: jest.Mock = jest.fn()): BoxliteMeController { + const organizationService = { findByUserWithDefaultFlag: orgFindByUser } as unknown as OrganizationService + return new BoxliteMeController(organizationService) + } + + function apiKeyContext(expiresAt?: Date): AuthContext { + const apiKey = { + organizationId: '11111111-1111-1111-1111-111111111111', + userId: 'user-1', + name: 'key-531', + expiresAt, + } as ApiKey + return { + userId: 'user-1', + email: 'svc@example.com', + role: 'user' as AuthContext['role'], + apiKey, + organizationId: apiKey.organizationId, + } + } + + describe('GET /v1/me — expires_at is sourced from ApiKey.expiresAt', () => { + it('returns the API key expiry as an ISO string (same field the dashboard renders)', async () => { + const expiresAt = new Date('2026-06-07T12:00:00.000Z') + const result = await makeController().getMe(apiKeyContext(expiresAt)) + + expect(result.principal_type).toBe('service_account') + expect(result.expires_at).toBe(expiresAt.toISOString()) + }) + + it('returns null for a non-expiring API key (expiresAt unset)', async () => { + const result = await makeController().getMe(apiKeyContext(undefined)) + + expect(result.expires_at).toBeNull() + }) + + it('returns null for an interactive user session (no API key)', async () => { + const findByUser = jest.fn().mockResolvedValue([]) + const ctx: AuthContext = { + userId: 'user-2', + email: 'human@example.com', + role: 'user' as AuthContext['role'], + } + + const result = await makeController(findByUser).getMe(ctx) + + expect(result.principal_type).toBe('user') + expect(result.expires_at).toBeNull() + }) + }) +}) diff --git a/apps/api/src/boxlite-rest/boxlite-me.controller.ts b/apps/api/src/boxlite-rest/boxlite-me.controller.ts new file mode 100644 index 000000000..5235e4945 --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-me.controller.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Controller, Get, UseGuards } from '@nestjs/common' +import { ApiBearerAuth, ApiTags, ApiExcludeController } from '@nestjs/swagger' +import { CombinedAuthGuard } from '../auth/combined-auth.guard' +import { AuthContext } from '../common/decorators/auth-context.decorator' +import { AuthContext as AuthCtx } from '../common/interfaces/auth-context.interface' +import { OrganizationService } from '../organization/services/organization.service' +import { PrincipalDto } from './dto/principal.dto' + +/** + * `GET /v1/me` — identity for the calling credential. + * + * Returns a [`PrincipalDto`] regardless of how the Bearer token was issued + * (API key, OAuth device-flow access_token, or future federated source). + * The CLI uses this to validate freshly-pasted keys and to render the + * `Logged in as` banner. + * + * Spec: `openapi/box.openapi.yaml` § GET /me. + */ +@ApiExcludeController() +@ApiTags('BoxLite REST') +@Controller('v1') +@UseGuards(CombinedAuthGuard) +@ApiBearerAuth() +export class BoxliteMeController { + constructor(private readonly organizationService: OrganizationService) {} + + @Get('me') + async getMe(@AuthContext() ctx: AuthCtx): Promise { + const pathPrefix = await this.resolvePathPrefix(ctx) + + const principalType: 'user' | 'service_account' = ctx.apiKey ? 'service_account' : 'user' + + return { + sub: ctx.userId, + principal_type: principalType, + email: ctx.email || undefined, + display_name: undefined, + path_prefix: pathPrefix, + // TODO: source scopes from ctx.apiKey?.scopes once the ApiKey entity + // has a `scopes` column. For now grant the full set used by the OpenAPI + // spec's documented scope vocabulary. + scopes: ['box:read', 'box:write', 'box:exec', 'box:delete', 'image:read', 'image:write', 'me:read'], + // Source of truth for key expiry is ApiKey.expiresAt — the same column the + // dashboard's `/api-keys` list renders. Returning a hardcoded null here let + // clients believe a soon-to-expire key was permanent (P1-2). `null` stays + // correct for non-expiring keys and for interactive user sessions (no apiKey). + expires_at: ctx.apiKey?.expiresAt ? ctx.apiKey.expiresAt.toISOString() : null, + } + } + + /** + * Resolve the routing-slot value for the calling credential. + * + * API keys carry their org binding directly. OIDC tokens carry no + * org claim, so we look it up: prefer the user's default org; + * otherwise the first membership; otherwise `null` (no scope yet — + * the field stays present in the response envelope with explicit + * `null` per the OpenAPI contract). + */ + private async resolvePathPrefix(ctx: AuthCtx): Promise { + if (ctx.apiKey?.organizationId) { + return ctx.apiKey.organizationId + } + const memberships = await this.organizationService.findByUserWithDefaultFlag(ctx.userId) + if (memberships.length === 0) { + return null + } + return (memberships.find((membership) => membership.isDefaultForAuthenticatedUser) ?? memberships[0]).organization + .id + } +} diff --git a/apps/api/src/boxlite-rest/boxlite-proxy.controller.spec.ts b/apps/api/src/boxlite-rest/boxlite-proxy.controller.spec.ts new file mode 100644 index 000000000..61f5f9256 --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-proxy.controller.spec.ts @@ -0,0 +1,55 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { createProxyMiddleware } from 'http-proxy-middleware' +import { BoxliteProxyController } from './boxlite-proxy.controller' + +jest.mock('http-proxy-middleware', () => ({ + createProxyMiddleware: jest.fn(), + fixRequestBody: jest.fn(), +})) +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +describe('BoxliteProxyController', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + it('rewrites public box ids to internal box ids before proxying exec requests to the runner', async () => { + const proxyHandler = jest.fn() + jest.mocked(createProxyMiddleware).mockReturnValue(proxyHandler as never) + + const boxService = { + findOneByIdOrName: jest.fn().mockResolvedValue({ + id: 'box-uuid', + runnerId: 'runner-1', + }), + updateLastActivityAt: jest.fn().mockResolvedValue(undefined), + } + const runnerService = { + findOne: jest.fn().mockResolvedValue({ + apiUrl: 'http://runner.local', + apiKey: 'runner-key', + }), + } + + const controller = new BoxliteProxyController(boxService as never, runnerService as never) + const req = { url: '/api/v1/boxes/public-box/exec' } + const res = {} + const next = jest.fn() + + await controller.proxyExec({ organizationId: 'org-1' } as never, 'public-box', req as never, res as never, next) + + const proxyOptions = jest.mocked(createProxyMiddleware).mock.calls[0][0] + const pathRewrite = proxyOptions.pathRewrite as (path: string, req: unknown) => string + expect(pathRewrite('/api/v1/boxes/public-box/exec', req)).toBe('/v1/boxes/box-uuid/exec') + expect(boxService.findOneByIdOrName).toHaveBeenCalledWith('public-box', 'org-1') + expect(proxyHandler).toHaveBeenCalledWith(req, res, next) + }) +}) diff --git a/apps/api/src/boxlite-rest/boxlite-proxy.controller.ts b/apps/api/src/boxlite-rest/boxlite-proxy.controller.ts index 98cab10cc..b978b509a 100644 --- a/apps/api/src/boxlite-rest/boxlite-proxy.controller.ts +++ b/apps/api/src/boxlite-rest/boxlite-proxy.controller.ts @@ -7,6 +7,8 @@ import { Controller, All, + Get, + Delete, Param, Req, Res, @@ -15,29 +17,28 @@ import { Logger, NotFoundException, } from '@nestjs/common' -import { ApiTags, ApiBearerAuth } from '@nestjs/swagger' -import { - createProxyMiddleware, - fixRequestBody, - Options, -} from 'http-proxy-middleware' +import { ApiTags, ApiBearerAuth, ApiExcludeController } from '@nestjs/swagger' +import { createProxyMiddleware, fixRequestBody, Options } from 'http-proxy-middleware' import { Request, Response, NextFunction } from 'express' import { CombinedAuthGuard } from '../auth/combined-auth.guard' import { OrganizationResourceActionGuard } from '../organization/guards/organization-resource-action.guard' import { AuthContext } from '../common/decorators/auth-context.decorator' import { OrganizationAuthContext } from '../common/interfaces/auth-context.interface' -import { SandboxService } from '../sandbox/services/sandbox.service' -import { RunnerService } from '../sandbox/services/runner.service' +import { BoxService } from '../box/services/box.service' +import { RunnerService } from '../box/services/runner.service' +// Spec-first surface (openapi/box.openapi.yaml). Must stay out of the product +// spec: @All() expands to the SEARCH verb, which OpenAPI 3.0 cannot express. +@ApiExcludeController() @ApiTags('BoxLite REST') -@Controller('v1/:prefix/boxes') +@Controller(['v1/boxes', 'v1/:prefix/boxes']) @UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard) @ApiBearerAuth() export class BoxliteProxyController { private readonly logger = new Logger(BoxliteProxyController.name) constructor( - private readonly sandboxService: SandboxService, + private readonly boxService: BoxService, private readonly runnerService: RunnerService, ) {} @@ -49,18 +50,11 @@ export class BoxliteProxyController { @Res() res: Response, @Next() next: NextFunction, ) { - return this.proxyToRunner( - authContext, - boxId, - `/v1/boxes/${boxId}/exec`, - req, - res, - next, - ) + return this.proxyToRunner(authContext, boxId, (runnerBoxId) => `/v1/boxes/${runnerBoxId}/exec`, req, res, next) } - @All(':boxId/executions/:execId/output') - async proxyExecOutput( + @All(':boxId/executions/:execId/signal') + async proxyExecSignal( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, @Param('execId') execId: string, @@ -68,18 +62,18 @@ export class BoxliteProxyController { @Res() res: Response, @Next() next: NextFunction, ) { - return this.streamFromRunner( + return this.proxyToRunner( authContext, boxId, - `/v1/boxes/${boxId}/executions/${execId}/output`, + (runnerBoxId) => `/v1/boxes/${runnerBoxId}/executions/${execId}/signal`, req, res, next, ) } - @All(':boxId/executions/:execId/input') - async proxyExecInput( + @All(':boxId/executions/:execId/resize') + async proxyExecResize( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, @Param('execId') execId: string, @@ -90,15 +84,15 @@ export class BoxliteProxyController { return this.proxyToRunner( authContext, boxId, - `/v1/boxes/${boxId}/executions/${execId}/input`, + (runnerBoxId) => `/v1/boxes/${runnerBoxId}/executions/${execId}/resize`, req, res, next, ) } - @All(':boxId/executions/:execId/signal') - async proxyExecSignal( + @Get(':boxId/executions/:execId') + async proxyExecStatus( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, @Param('execId') execId: string, @@ -109,15 +103,15 @@ export class BoxliteProxyController { return this.proxyToRunner( authContext, boxId, - `/v1/boxes/${boxId}/executions/${execId}/signal`, + (runnerBoxId) => `/v1/boxes/${runnerBoxId}/executions/${execId}`, req, res, next, ) } - @All(':boxId/executions/:execId/resize') - async proxyExecResize( + @Delete(':boxId/executions/:execId') + async proxyExecKill( @AuthContext() authContext: OrganizationAuthContext, @Param('boxId') boxId: string, @Param('execId') execId: string, @@ -128,13 +122,19 @@ export class BoxliteProxyController { return this.proxyToRunner( authContext, boxId, - `/v1/boxes/${boxId}/executions/${execId}/resize`, + (runnerBoxId) => `/v1/boxes/${runnerBoxId}/executions/${execId}`, req, res, next, ) } + // /executions/:execId/attach is a WebSocket-only route. Real WS upgrades + // bypass Express entirely and are handled by BoxliteWsProxyService via the + // `server.on('upgrade', ...)` hook registered in main.ts. Plain HTTP GETs + // to this path (callers that forgot the Upgrade headers) fall through to + // a NestJS 404, which is the correct answer. + @All(':boxId/files') async proxyFiles( @AuthContext() authContext: OrganizationAuthContext, @@ -143,13 +143,11 @@ export class BoxliteProxyController { @Res() res: Response, @Next() next: NextFunction, ) { - const query = req.url.includes('?') - ? req.url.substring(req.url.indexOf('?')) - : '' + const query = req.url.includes('?') ? req.url.substring(req.url.indexOf('?')) : '' return this.proxyToRunner( authContext, boxId, - `/v1/boxes/${boxId}/files${query}`, + (runnerBoxId) => `/v1/boxes/${runnerBoxId}/files${query}`, req, res, next, @@ -164,33 +162,31 @@ export class BoxliteProxyController { @Res() res: Response, @Next() next: NextFunction, ) { - return this.proxyToRunner( - authContext, - boxId, - `/v1/boxes/${boxId}/metrics`, - req, - res, - next, - ) + return this.proxyToRunner(authContext, boxId, (runnerBoxId) => `/v1/boxes/${runnerBoxId}/metrics`, req, res, next) } private async proxyToRunner( authContext: OrganizationAuthContext, boxId: string, - targetPath: string, + targetPathForRunnerBox: (runnerBoxId: string) => string, req: Request, res: Response, next: NextFunction, + opts?: { ws?: boolean }, ) { - const sandbox = await this.sandboxService.findOneByIdOrName( - boxId, - authContext.organizationId, - ) - if (!sandbox) { + const box = await this.boxService.findOneByIdOrName(boxId, authContext.organizationId) + if (!box) { throw new NotFoundException(`Box ${boxId} not found`) } - const runner = await this.runnerService.findOne(sandbox.runnerId) + // Any SDK-initiated proxy + // call counts as user activity, so the autostop cron does not reap an + // actively used box. Best-effort: never block the proxy on this. + this.boxService + .updateLastActivityAt(box.id, new Date()) + .catch((err) => this.logger.warn(`updateLastActivityAt failed for ${box.id}: ${err}`)) + + const runner = await this.runnerService.findOne(box.runnerId) if (!runner) { throw new NotFoundException(`Runner for box ${boxId} not found`) } @@ -205,7 +201,8 @@ export class BoxliteProxyController { secure: false, changeOrigin: true, autoRewrite: true, - pathRewrite: () => targetPath, + ws: opts?.ws ?? false, + pathRewrite: () => targetPathForRunnerBox(box.id), on: { proxyReq: (proxyReq: any, originalReq: any) => { proxyReq.setHeader('Authorization', `Bearer ${runner.apiKey}`) @@ -217,87 +214,4 @@ export class BoxliteProxyController { return createProxyMiddleware(proxyOptions)(req, res, next) } - - private async streamFromRunner( - authContext: OrganizationAuthContext, - boxId: string, - targetPath: string, - req: Request, - res: Response, - next: NextFunction, - ) { - try { - const sandbox = await this.sandboxService.findOneByIdOrName( - boxId, - authContext.organizationId, - ) - if (!sandbox) { - throw new NotFoundException(`Box ${boxId} not found`) - } - - const runner = await this.runnerService.findOne(sandbox.runnerId) - if (!runner) { - throw new NotFoundException(`Runner for box ${boxId} not found`) - } - - const targetBaseUrl = runner.apiUrl || runner.proxyUrl - if (!targetBaseUrl) { - throw new NotFoundException( - `Runner endpoint for box ${boxId} not found`, - ) - } - - const targetUrl = new URL(targetPath, targetBaseUrl) - const runnerResponse = await fetch(targetUrl, { - method: req.method, - headers: { - Authorization: `Bearer ${runner.apiKey}`, - Accept: req.header('accept') || 'text/event-stream', - }, - }) - - res.status(runnerResponse.status) - runnerResponse.headers.forEach((value, key) => { - if ( - !['connection', 'content-length', 'transfer-encoding'].includes( - key.toLowerCase(), - ) - ) { - res.setHeader(key, value) - } - }) - - if (!runnerResponse.body) { - res.end() - return - } - - const reader = runnerResponse.body.getReader() - try { - while (true) { - const { done, value } = await reader.read() - if (done) { - break - } - if (value && !res.write(Buffer.from(value))) { - await new Promise((resolve) => res.once('drain', resolve)) - } - } - } finally { - reader.releaseLock() - res.end() - } - } catch (error) { - if (res.headersSent) { - this.logger.error( - `Runner stream failed after response started for box ${boxId}: ${error}`, - ) - if (!res.writableEnded) { - res.end() - } - return - } - next(error) - } - } } diff --git a/apps/api/src/boxlite-rest/boxlite-rest-routing.spec.ts b/apps/api/src/boxlite-rest/boxlite-rest-routing.spec.ts new file mode 100644 index 000000000..1236c10d8 --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-rest-routing.spec.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { PATH_METADATA } from '@nestjs/common/constants' +import { Test } from '@nestjs/testing' +import type { INestApplication } from '@nestjs/common' +import type { AddressInfo } from 'net' +import { CombinedAuthGuard } from '../auth/combined-auth.guard' +import { OrganizationResourceActionGuard } from '../organization/guards/organization-resource-action.guard' +import { BoxService } from '../box/services/box.service' +import { BoxStateWaiterService } from '../box/services/box-state-waiter.service' +import { BoxliteBoxController } from './boxlite-box.controller' +import { BoxliteProxyController } from './boxlite-proxy.controller' +import { BoxliteWsProxyService } from './boxlite-ws-proxy.service' + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +describe('BoxLite REST routing', () => { + let app: INestApplication + + async function startRoutingTestApp() { + const moduleRef = await Test.createTestingModule({ + controllers: [BoxliteBoxController], + providers: [ + { + provide: BoxService, + useValue: { + findAllDeprecated: jest.fn().mockResolvedValue([]), + toBoxDtos: jest.fn().mockResolvedValue([]), + }, + }, + { + provide: BoxStateWaiterService, + useValue: {}, + }, + ], + }) + .overrideGuard(CombinedAuthGuard) + .useValue({ + canActivate: (context: any) => { + context.switchToHttp().getRequest().user = { + organizationId: 'org-123', + organization: { id: 'org-123' }, + } + return true + }, + }) + .overrideGuard(OrganizationResourceActionGuard) + .useValue({ canActivate: () => true }) + .compile() + + app = moduleRef.createNestApplication() + app.setGlobalPrefix('api') + await app.listen(0) + } + + async function get(path: string): Promise { + const address = app.getHttpServer().address() as AddressInfo + return fetch(`http://127.0.0.1:${address.port}${path}`) + } + + afterEach(async () => { + await app?.close() + }) + + it('mounts box controllers at canonical and legacy default-prefix routes', () => { + expect(Reflect.getMetadata(PATH_METADATA, BoxliteBoxController)).toEqual(['v1/boxes', 'v1/:prefix/boxes']) + expect(Reflect.getMetadata(PATH_METADATA, BoxliteProxyController)).toEqual(['v1/boxes', 'v1/:prefix/boxes']) + }) + + it('registers canonical and legacy default-prefix routes in the Nest HTTP router', async () => { + await startRoutingTestApp() + + const canonical = await get('/api/v1/boxes') + const legacy = await get('/api/v1/default/boxes') + + expect(canonical.status).toBe(200) + expect(await canonical.json()).toEqual({ boxes: [] }) + expect(legacy.status).toBe(200) + expect(await legacy.json()).toEqual({ boxes: [] }) + }) + + it('matches websocket attach upgrades with or without a routing prefix', () => { + const service = new BoxliteWsProxyService({} as any, {} as any, {} as any, {} as any, {} as any) + + expect(service.matchAttachPath('/api/v1/boxes/box-1/executions/exec-1/attach')).toEqual({ boxId: 'box-1' }) + expect(service.matchAttachPath('/api/v1/default/boxes/box-1/executions/exec-1/attach')).toEqual({ + boxId: 'box-1', + tenant: 'default', + }) + }) +}) diff --git a/apps/api/src/boxlite-rest/boxlite-rest.module.ts b/apps/api/src/boxlite-rest/boxlite-rest.module.ts index 9c12735be..10233276e 100644 --- a/apps/api/src/boxlite-rest/boxlite-rest.module.ts +++ b/apps/api/src/boxlite-rest/boxlite-rest.module.ts @@ -5,22 +5,20 @@ */ import { Module } from '@nestjs/common' -import { SandboxModule } from '../sandbox/sandbox.module' +import { BoxModule } from '../box/box.module' import { AuthModule } from '../auth/auth.module' import { ApiKeyModule } from '../api-key/api-key.module' import { OrganizationModule } from '../organization/organization.module' -import { BoxliteAuthController } from './boxlite-auth.controller' +import { BoxliteMeController } from './boxlite-me.controller' import { BoxliteConfigController } from './boxlite-config.controller' import { BoxliteBoxController } from './boxlite-box.controller' import { BoxliteProxyController } from './boxlite-proxy.controller' +import { BoxliteWsProxyService } from './boxlite-ws-proxy.service' @Module({ - imports: [SandboxModule, AuthModule, ApiKeyModule, OrganizationModule], - controllers: [ - BoxliteAuthController, - BoxliteConfigController, - BoxliteBoxController, - BoxliteProxyController, - ], + imports: [BoxModule, AuthModule, ApiKeyModule, OrganizationModule], + controllers: [BoxliteMeController, BoxliteConfigController, BoxliteBoxController, BoxliteProxyController], + providers: [BoxliteWsProxyService], + exports: [BoxliteWsProxyService], }) export class BoxliteRestModule {} diff --git a/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.spec.ts b/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.spec.ts new file mode 100644 index 000000000..430f2850a --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.spec.ts @@ -0,0 +1,116 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { createProxyMiddleware } from 'http-proxy-middleware' +import type { IncomingMessage } from 'http' +import { BoxliteWsProxyService } from './boxlite-ws-proxy.service' + +jest.mock('http-proxy-middleware', () => ({ + createProxyMiddleware: jest.fn(() => ({ + upgrade: jest.fn(), + })), +})) +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +describe('BoxliteWsProxyService', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + function authRequest(token: string, url = '/api/v1/org-1/boxes/public-box/executions/exec-1/attach') { + return { + url, + headers: { + authorization: `Bearer ${token}`, + }, + } as IncomingMessage + } + + function buildAuthHarness() { + const apiKeyService = { + getApiKeyByValue: jest.fn().mockRejectedValue(new Error('api key not found')), + } + const organizationUserService = { + findOne: jest.fn(), + } + const jwtStrategy = { + verifyToken: jest.fn(), + } + const service = new BoxliteWsProxyService( + apiKeyService as never, + organizationUserService as never, + {} as never, + {} as never, + jwtStrategy as never, + ) as unknown as { + authenticate: (req: IncomingMessage, urlTenant?: string) => Promise<{ organizationId: string } | null> + } + + return { service, apiKeyService, organizationUserService, jwtStrategy } + } + + it('rewrites public box ids to internal box ids before proxying attach upgrades to the runner', () => { + new BoxliteWsProxyService({} as never, {} as never, {} as never, {} as never, {} as never) + + const proxyOptions = jest.mocked(createProxyMiddleware).mock.calls[0][0] + const pathRewrite = proxyOptions.pathRewrite as (path: string, req: unknown) => string + const req = { __boxliteRunnerBoxId: 'box-uuid' } + + expect(pathRewrite('/api/v1/boxes/public-box/executions/exec-1/attach', req)).toBe( + '/v1/boxes/box-uuid/executions/exec-1/attach', + ) + expect(pathRewrite('/api/v1/default/boxes/public-box/executions/exec-1/attach?x=1', req)).toBe( + '/v1/boxes/box-uuid/executions/exec-1/attach?x=1', + ) + }) + + it('authenticates API key bearer tokens for websocket attach', async () => { + const { service, apiKeyService, organizationUserService, jwtStrategy } = buildAuthHarness() + apiKeyService.getApiKeyByValue.mockResolvedValue({ + organizationId: 'org-1', + userId: 'user-1', + expiresAt: null, + }) + organizationUserService.findOne.mockResolvedValue({ organizationId: 'org-1', userId: 'user-1' }) + + await expect(service.authenticate(authRequest('blk_live_test'))).resolves.toEqual({ organizationId: 'org-1' }) + expect(organizationUserService.findOne).toHaveBeenCalledWith('org-1', 'user-1') + expect(jwtStrategy.verifyToken).not.toHaveBeenCalled() + }) + + it('authenticates JWT bearer tokens for websocket attach', async () => { + const { service, organizationUserService, jwtStrategy } = buildAuthHarness() + const jwt = 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEifQ.signature' + jwtStrategy.verifyToken.mockResolvedValue({ sub: 'user-1', email: 'dev@acme.test' }) + organizationUserService.findOne.mockResolvedValue({ organizationId: 'org-1', userId: 'user-1' }) + + await expect(service.authenticate(authRequest(jwt), 'org-1')).resolves.toEqual({ organizationId: 'org-1' }) + expect(jwtStrategy.verifyToken).toHaveBeenCalledWith(jwt) + expect(organizationUserService.findOne).toHaveBeenCalledWith('org-1', 'user-1') + }) + + it('rejects invalid JWT bearer tokens for websocket attach', async () => { + const { service, organizationUserService, jwtStrategy } = buildAuthHarness() + const jwt = 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEifQ.signature' + jwtStrategy.verifyToken.mockRejectedValue(new Error('bad jwt')) + + await expect(service.authenticate(authRequest(jwt), 'org-1')).resolves.toBeNull() + expect(organizationUserService.findOne).not.toHaveBeenCalled() + }) + + it('rejects JWT attach when organization membership has been removed', async () => { + const { service, organizationUserService, jwtStrategy } = buildAuthHarness() + const jwt = 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEifQ.signature' + jwtStrategy.verifyToken.mockResolvedValue({ sub: 'user-1', email: 'dev@acme.test' }) + organizationUserService.findOne.mockResolvedValue(null) + + await expect(service.authenticate(authRequest(jwt), 'org-1')).resolves.toBeNull() + expect(organizationUserService.findOne).toHaveBeenCalledWith('org-1', 'user-1') + }) +}) diff --git a/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.ts b/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.ts new file mode 100644 index 000000000..7994a6532 --- /dev/null +++ b/apps/api/src/boxlite-rest/boxlite-ws-proxy.service.ts @@ -0,0 +1,214 @@ +/* + * SPDX-License-Identifier: AGPL-3.0 + * Copyright (c) 2025 BoxLite AI + */ + +import { Inject, Injectable, Logger } from '@nestjs/common' +import type { IncomingMessage } from 'http' +import type { Socket } from 'net' +import { createProxyMiddleware, type RequestHandler } from 'http-proxy-middleware' +import { ApiKeyService } from '../api-key/api-key.service' +import { JwtStrategy } from '../auth/jwt.strategy' +import { OrganizationUserService } from '../organization/services/organization-user.service' +import { BoxService } from '../box/services/box.service' +import { RunnerService } from '../box/services/runner.service' +import type { Runner } from '../box/entities/runner.entity' + +type RunnerUpgradeRequest = IncomingMessage & { + __boxliteRunner?: Runner + __boxliteRunnerBoxId?: string +} + +// Matches /api/v1/boxes//executions//attach and the +// /api/v1//boxes//executions//attach shape with optional query string. +// Named groups: `tenant` (optional org id / path prefix) and `boxId`. +const ATTACH_PATH = /^\/api\/v1\/(?:(?[^/]+)\/)?boxes\/(?[^/]+)\/executions\/[^/]+\/attach(?:\?.*)?$/ + +/** + * Singleton WebSocket proxy for `/attach` upgrades. + * + * Express middleware/guards don't run on Node's `upgrade` event, so the + * NestJS controller `@Get(':boxId/executions/:execId/attach')` route never + * fires for actual WS upgrade requests — it's HTTP-only and gets bypassed. + * Main.ts registers `server.on('upgrade', wsProxy.upgrade)` and routes + * matching paths through this service, which mirrors the API-key half of + * CombinedAuthGuard inline, resolves the runner, and hands off to a + * shared `createProxyMiddleware({ ws: true, ... })` instance. + */ +@Injectable() +export class BoxliteWsProxyService { + private readonly logger = new Logger(BoxliteWsProxyService.name) + private readonly proxy: RequestHandler + + constructor( + private readonly apiKeyService: ApiKeyService, + private readonly organizationUserService: OrganizationUserService, + private readonly boxService: BoxService, + private readonly runnerService: RunnerService, + // Exported by AuthModule (already imported here). Resolves to `undefined` + // when `skipConnections` is set, so the JWT path guards on it. + @Inject(JwtStrategy) private readonly jwtStrategy: JwtStrategy | undefined, + ) { + this.proxy = createProxyMiddleware({ + ws: true, + changeOrigin: true, + // Drop the public `/api/v1/` or `/api/v1//` prefix; runner mounts routes at `/v1/...`. + pathRewrite: (path: string, req: IncomingMessage) => { + const runnerBoxId = (req as RunnerUpgradeRequest).__boxliteRunnerBoxId + if (!runnerBoxId) { + throw new Error('ws proxy: runner box id not resolved before upgrade — bug in caller') + } + return path.replace(/^\/api\/v1\/(?:[^/]+\/)?boxes\/[^/]+/, `/v1/boxes/${runnerBoxId}`) + }, + // Target is resolved per-upgrade and stashed on the request before + // delegating into the proxy. + router: (req: IncomingMessage) => { + const runner = (req as RunnerUpgradeRequest).__boxliteRunner + if (!runner) { + throw new Error('ws proxy: runner not resolved before upgrade — bug in caller') + } + return runner.apiUrl || (runner as Runner & { proxyUrl?: string }).proxyUrl || '' + }, + on: { + proxyReqWs: (proxyReq: { setHeader: (name: string, value: string) => void }, req: IncomingMessage) => { + const runner = (req as RunnerUpgradeRequest).__boxliteRunner + if (runner?.apiKey) { + proxyReq.setHeader('Authorization', `Bearer ${runner.apiKey}`) + } + }, + }, + }) + } + + /** + * Box id (+ optional tenant/org id) when the URL is an `/attach` WS upgrade. + * The tenant is the organization for JWT auth (an API key carries its own org). + */ + matchAttachPath(url: string | undefined): { boxId: string; tenant?: string } | null { + if (!url) return null + const groups = url.match(ATTACH_PATH)?.groups as { boxId: string; tenant?: string } | undefined + if (!groups) return null + return { boxId: groups.boxId, tenant: groups.tenant } + } + + /** + * Resolve auth + box + runner, then hand the upgrade to the shared + * proxy middleware. Closes the socket cleanly on any failure. + */ + async upgrade(req: IncomingMessage, socket: Socket, head: Buffer): Promise { + const match = this.matchAttachPath(req.url) + if (!match) { + socket.destroy() + return + } + + const auth = await this.authenticate(req, match.tenant) + if (!auth) { + this.respondAndClose(socket, 401, 'Unauthorized') + return + } + + try { + const box = await this.boxService.findOneByIdOrName(match.boxId, auth.organizationId) + if (!box?.runnerId) { + this.respondAndClose(socket, 404, 'Not Found') + return + } + // Mirror legacy toolbox path — opening a WS attach is user activity, + // so the autostop cron does not reap a session that's still connected. + // Best-effort: do not fail the upgrade if this errors. + this.boxService + .updateLastActivityAt(box.id, new Date()) + .catch((err) => this.logger.warn(`updateLastActivityAt failed for ${box.id}: ${err}`)) + const runner = await this.runnerService.findOne(box.runnerId) + if (!runner) { + this.respondAndClose(socket, 404, 'Not Found') + return + } + ;(req as RunnerUpgradeRequest).__boxliteRunner = runner + ;(req as RunnerUpgradeRequest).__boxliteRunnerBoxId = box.id + ;( + this.proxy as unknown as { + upgrade: (req: IncomingMessage, socket: Socket, head: Buffer) => void + } + ).upgrade(req, socket, head) + } catch (err) { + this.logger.warn(`upgrade failed for ${req.url}: ${(err as Error).message}`) + this.respondAndClose(socket, 404, 'Not Found') + } + } + + /** + * Inline authentication for WS upgrades, mirroring the API-key + JWT halves of + * CombinedAuthGuard. Membership-only, like the HTTP exec/attach routes + * (`BoxliteProxyController`), which carry no resource-permission decorator. + * Never throws — any failure resolves to `null` (a 401), so the fire-and-forget + * `upgrade` caller can't leak a hung socket. + * + * API key: the bearer must be a non-expired API key whose user is still a + * member of the key's organization. The membership check is critical — + * removing a user from an org deletes the OrganizationUser row but does not + * cascade to ApiKey rows, so without it a removed member's surviving key could + * still attach to boxes in that org. The URL tenant is ignored (key is scoped). + * + * JWT (OIDC): when the bearer is not an API key, verify it via `JwtStrategy` + * (same JWKS/issuer/audience as the HTTP path). A JWT carries no org, so the + * organization is taken from the URL `{prefix}` (`urlTenant`); a request with + * no tenant (or the legacy `default`) is rejected since the org is ambiguous + * for a multi-org user. Membership in that org is then required, identically to + * the API-key path. `jwtStrategy` is absent when `skipConnections` is set. + * + * Unlike the HTTP path, this does not consult the Redis cache used by + * ApiKeyStrategy / OrganizationAccessGuard. Upgrade frequency is low; if + * upgrade latency becomes a concern, add caching as a follow-up. + */ + private async authenticate(req: IncomingMessage, urlTenant?: string): Promise<{ organizationId: string } | null> { + try { + const header = req.headers['authorization'] + const headerValue = Array.isArray(header) ? header[0] : header + if (!headerValue || !/^bearer\s+/i.test(headerValue)) return null + const token = headerValue.replace(/^bearer\s+/i, '').trim() + if (!token) return null + + // 1. API key — org comes from the key itself; the URL tenant is ignored, as + // before. A *throw* means "not an API key", so it is caught here to fall + // through to JWT rather than being rejected by the outer guard. + const apiKey = await this.apiKeyService.getApiKeyByValue(token).catch(() => null) + if (apiKey) { + if (apiKey.expiresAt && apiKey.expiresAt < new Date()) return null + const membership = await this.organizationUserService.findOne(apiKey.organizationId, apiKey.userId) + if (!membership) return null + return { organizationId: apiKey.organizationId } + } + + // 2. JWT (OIDC) — org comes from the URL tenant; membership required. + if (!this.jwtStrategy) return null + if (!urlTenant || urlTenant === 'default') return null + const payload = await this.jwtStrategy.verifyToken(token) + // Mirror JwtStrategy.validate's sub/uid handling (OKTA carries userId in `uid`). + const claims = payload as { sub?: string; cid?: unknown; uid?: string } + let userId = claims.sub + if (claims.cid && claims.uid) userId = claims.uid + if (!userId) return null + + const membership = await this.organizationUserService.findOne(urlTenant, userId) + if (!membership) return null + return { organizationId: urlTenant } + } catch { + // Any failure (invalid JWT signature, a DB error, …) → 401. This single + // guard is why authenticate never throws — `upgrade` calls it before its + // own try-block and main.ts runs `void upgrade(...)`, so an escaped throw + // would leak a hung socket. + return null + } + } + + private respondAndClose(socket: Socket, status: number, reason: string): void { + try { + socket.write(`HTTP/1.1 ${status} ${reason}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`) + } catch { + // Socket may already be torn down — ignore. + } + socket.destroy() + } +} diff --git a/apps/api/src/boxlite-rest/dto/box-response.dto.ts b/apps/api/src/boxlite-rest/dto/box-response.dto.ts index ed7a9db61..0d3b8fac6 100644 --- a/apps/api/src/boxlite-rest/dto/box-response.dto.ts +++ b/apps/api/src/boxlite-rest/dto/box-response.dto.ts @@ -4,28 +4,107 @@ * SPDX-License-Identifier: AGPL-3.0 */ +import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' + +@ApiSchema({ name: 'Box' }) export class BoxResponseDto { + @ApiProperty({ + description: 'The public 12-character Box ID shown to users and SDK clients', + example: 'aB3cD4eF5gH6', + }) box_id: string + + @ApiPropertyOptional({ + description: 'User-provided box name', + example: 'notebook-worker', + }) name?: string + + @ApiProperty({ + description: 'Box runtime status', + example: 'running', + }) status: string + + @ApiProperty({ + description: 'Creation timestamp', + example: '2026-06-04T12:00:00.000Z', + }) created_at: string + + @ApiProperty({ + description: 'Last update timestamp', + example: '2026-06-04T12:05:00.000Z', + }) updated_at: string + + @ApiPropertyOptional({ + description: 'Runtime process ID when available', + example: 12345, + }) pid?: number + + @ApiProperty({ + description: 'Approved image used for the box', + example: 'boxlite/base', + }) image: string + + @ApiProperty({ + description: 'Allocated CPU count', + example: 2, + }) cpus: number + + @ApiProperty({ + description: 'Allocated memory in MiB', + example: 4096, + }) memory_mib: number + + @ApiProperty({ + description: 'Labels attached to the box', + type: 'object', + additionalProperties: { type: 'string' }, + example: { 'boxlite.io/public': 'true' }, + }) labels: Record } +@ApiSchema({ name: 'ListBoxesResponse' }) export class ListBoxesResponseDto { + @ApiProperty({ type: [BoxResponseDto] }) boxes: BoxResponseDto[] + + @ApiPropertyOptional({ + description: 'Token for the next page when pagination is available', + example: 'eyJwYWdlIjoyfQ', + }) next_page_token?: string } +class ErrorDetailDto { + @ApiProperty({ + description: 'Human-readable error message', + example: 'Box not found', + }) + message: string + + @ApiProperty({ + description: 'Machine-readable error type', + example: 'not_found', + }) + type: string + + @ApiProperty({ + description: 'HTTP status code', + example: 404, + }) + code: number +} + +@ApiSchema({ name: 'BoxLiteRestError' }) export class ErrorResponseDto { - error: { - message: string - type: string - code: number - } + @ApiProperty({ type: ErrorDetailDto }) + error: ErrorDetailDto } diff --git a/apps/api/src/boxlite-rest/dto/create-box.dto.spec.ts b/apps/api/src/boxlite-rest/dto/create-box.dto.spec.ts new file mode 100644 index 000000000..d501a07a8 --- /dev/null +++ b/apps/api/src/boxlite-rest/dto/create-box.dto.spec.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import 'reflect-metadata' +import { validate } from 'class-validator' +import { plainToInstance } from 'class-transformer' +import { CreateBoxDto } from './create-box.dto' + +// A box with 0 vCPUs can never boot (libkrun set_vm_config(0, ...) -> EINVAL), +// so the create endpoint must reject undersized resources at the request +// boundary. These assert the @Min constraints stay wired on CreateBoxDto — +// drop a decorator and the matching case goes red. (The global ValidationPipe +// in main.ts turns these constraint violations into HTTP 400s; that wiring is +// verified live, not here.) +describe('CreateBoxDto resource minimums', () => { + it.each([ + ['cpus', { cpus: 0 }], + ['memory_mib', { memory_mib: 128 }], + ['disk_size_gb', { disk_size_gb: 0 }], + ])('rejects undersized %s with a min constraint', async (field, body) => { + const errors = await validate(plainToInstance(CreateBoxDto, body)) + + const fieldError = errors.find((e) => e.property === field) + expect(fieldError?.constraints).toHaveProperty('min') + }) + + it('accepts values exactly at the minimum boundary', async () => { + const errors = await validate(plainToInstance(CreateBoxDto, { cpus: 1, memory_mib: 256, disk_size_gb: 1 })) + + expect(errors).toHaveLength(0) + }) + + it('accepts a request that omits resource fields (engine defaults)', async () => { + const errors = await validate(plainToInstance(CreateBoxDto, { image: 'alpine:3.23' })) + + expect(errors).toHaveLength(0) + }) +}) + +describe('CreateBoxDto network validation', () => { + it('accepts supported allow_net entry types', async () => { + const errors = await validate( + plainToInstance(CreateBoxDto, { + network: { + mode: 'enabled', + allow_net: ['api.openai.com', '*.anthropic.com', '192.168.1.1', '10.0.0.0/8'], + }, + }), + ) + + expect(errors).toHaveLength(0) + }) + + it.each(['', 'https://api.openai.com', '*example.com', 'api..openai.com', '10.0.0.0/33', '999.0.0.1'])( + 'rejects invalid allow_net entry %s', + async (entry) => { + const errors = await validate( + plainToInstance(CreateBoxDto, { + network: { + mode: 'enabled', + allow_net: [entry], + }, + }), + ) + + expect(JSON.stringify(errors)).toContain('isNetworkAllowEntry') + }, + ) + + it('rejects more than ten allow_net entries', async () => { + const errors = await validate( + plainToInstance(CreateBoxDto, { + network: { + mode: 'enabled', + allow_net: Array.from({ length: 11 }, (_, index) => `api-${index}.example.com`), + }, + }), + ) + + expect(JSON.stringify(errors)).toContain('arrayMaxSize') + }) + + it('rejects unsupported network modes', async () => { + const errors = await validate( + plainToInstance(CreateBoxDto, { + network: { mode: 'public' }, + }), + ) + + expect(JSON.stringify(errors)).toContain('isIn') + }) +}) diff --git a/apps/api/src/boxlite-rest/dto/create-box.dto.ts b/apps/api/src/boxlite-rest/dto/create-box.dto.ts index 45907b401..70ca0f361 100644 --- a/apps/api/src/boxlite-rest/dto/create-box.dto.ts +++ b/apps/api/src/boxlite-rest/dto/create-box.dto.ts @@ -4,7 +4,46 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { IsOptional, IsString, IsNumber, IsBoolean, IsObject, IsArray } from 'class-validator' +import { Type } from 'class-transformer' +import { + ArrayMaxSize, + IsOptional, + IsString, + IsNumber, + IsBoolean, + IsObject, + IsArray, + Min, + IsIn, + Validate, + ValidateNested, + ValidatorConstraint, + ValidatorConstraintInterface, +} from 'class-validator' +import { isValidNetworkAllowEntry, MAX_NETWORK_ALLOW_LIST_ENTRIES } from '../../box/utils/network-validation.util' + +@ValidatorConstraint({ name: 'isNetworkAllowEntry', async: false }) +class IsNetworkAllowEntryConstraint implements ValidatorConstraintInterface { + validate(value: unknown): boolean { + return typeof value === 'string' && isValidNetworkAllowEntry(value) + } + + defaultMessage(): string { + return 'each allow_net entry must be an IPv4 address, IPv4 CIDR, hostname, or wildcard hostname' + } +} + +export class NetworkSpecDto { + @IsIn(['enabled', 'disabled']) + mode: 'enabled' | 'disabled' + + @IsOptional() + @IsArray() + @ArrayMaxSize(MAX_NETWORK_ALLOW_LIST_ENTRIES) + @IsString({ each: true }) + @Validate(IsNetworkAllowEntryConstraint, { each: true }) + allow_net?: string[] +} export class CreateBoxDto { @IsOptional() @@ -15,16 +54,22 @@ export class CreateBoxDto { @IsString() image?: string + // A box with 0 vCPUs can never boot (libkrun set_vm_config(0, ...) → EINVAL), + // so reject undersized resources at the request boundary instead of accepting + // a box that fails to start. @IsOptional() @IsNumber() + @Min(1) cpus?: number @IsOptional() @IsNumber() + @Min(256) memory_mib?: number @IsOptional() @IsNumber() + @Min(1) disk_size_gb?: number @IsOptional() @@ -54,4 +99,9 @@ export class CreateBoxDto { @IsOptional() @IsBoolean() detach?: boolean + + @IsOptional() + @ValidateNested() + @Type(() => NetworkSpecDto) + network?: NetworkSpecDto } diff --git a/apps/api/src/boxlite-rest/dto/exec.dto.ts b/apps/api/src/boxlite-rest/dto/exec.dto.ts deleted file mode 100644 index 28c0c5be2..000000000 --- a/apps/api/src/boxlite-rest/dto/exec.dto.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsString, IsOptional, IsNumber, IsBoolean, IsArray, IsObject } from 'class-validator' - -export class ExecRequestDto { - @IsString() - command: string - - @IsOptional() - @IsArray() - args?: string[] - - @IsOptional() - @IsObject() - env?: Record - - @IsOptional() - @IsNumber() - timeout_seconds?: number - - @IsOptional() - @IsString() - working_dir?: string - - @IsOptional() - @IsBoolean() - tty?: boolean -} - -export class ExecResponseDto { - execution_id: string -} diff --git a/apps/api/src/boxlite-rest/dto/principal.dto.ts b/apps/api/src/boxlite-rest/dto/principal.dto.ts new file mode 100644 index 000000000..f934d2189 --- /dev/null +++ b/apps/api/src/boxlite-rest/dto/principal.dto.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +/** + * Identity payload returned by `GET /v1/me`. Wire shape matches the + * `Principal` schema in `openapi/box.openapi.yaml`. + */ +export class PrincipalDto { + /** Stable opaque principal identifier; treat as opaque. */ + sub: string + + /** `user` for interactive logins; `service_account` for automation keys. */ + principal_type: 'user' | 'service_account' + + /** Optional email; absent for service accounts. */ + email?: string + + /** Optional human-readable label. */ + display_name?: string + + /** + * Routing-slot value the client substitutes into the `{prefix}` + * URL segment on box-scoped requests. + * + * `null` when the credential has no scope assigned yet (mid- + * provisioning service accounts, first OIDC login before org + * assignment). The field is always present in the response + * envelope per the OpenAPI contract (`nullable: true`); spec- + * strict clients (Rust, Java, Go generated code) treat a missing + * key vs. an explicit `null` as different shapes. + */ + path_prefix: string | null + + /** Granted scope strings (`box:read`, `box:write`, etc.). */ + scopes: string[] + + /** Optional expiry; `null` for long-lived dashboard keys. */ + expires_at?: string | null +} diff --git a/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.spec.ts b/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.spec.ts new file mode 100644 index 000000000..5adb918fd --- /dev/null +++ b/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.spec.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxDto } from '../../box/dto/box.dto' +import { boxToBoxResponse, createBoxToCreateBox } from './box-to-box.mapper' + +describe('box-to-box mapper', () => { + it('maps REST box_id from the canonical box id', () => { + const response = boxToBoxResponse({ + id: 'aB3cD4eF5gH6', + organizationId: '057963b2-60ca-4356-81fc-11503e15f249', + name: 'data-loader', + state: 'started', + createdAt: '2026-06-04T00:00:00.000Z', + updatedAt: '2026-06-04T00:00:00.000Z', + target: 'us', + user: 'boxlite', + env: {}, + cpu: 1, + gpu: 0, + memory: 1, + disk: 3, + public: false, + networkBlockAll: false, + labels: {}, + toolboxProxyUrl: 'https://proxy.boxlite.dev/toolbox', + } as BoxDto) + + expect(response.box_id).toBe('aB3cD4eF5gH6') + }) + + it('maps SDK resource settings to box create overrides', () => { + const dto = createBoxToCreateBox({ + cpus: 2, + memory_mib: 1536, + disk_size_gb: 8, + }) + + expect(dto.cpu).toBe(2) + expect(dto.memory).toBe(2) + expect(dto.disk).toBe(8) + }) + + it('maps disabled network onto the internal create dto', () => { + const dto = createBoxToCreateBox({ + network: { mode: 'disabled' }, + }) + + expect(dto.networkBlockAll).toBe(true) + expect(dto.networkAllowList).toBeUndefined() + }) + + it('maps enabled network allowlist onto the internal create dto', () => { + const dto = createBoxToCreateBox({ + network: { mode: 'enabled', allow_net: [' api.openai.com ', 'github.com'] }, + }) + + expect(dto.networkBlockAll).toBe(false) + expect(dto.networkAllowList).toBe('api.openai.com,github.com') + }) +}) diff --git a/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts b/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts new file mode 100644 index 000000000..bc3a194be --- /dev/null +++ b/apps/api/src/boxlite-rest/mappers/box-to-box.mapper.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxDto } from '../../box/dto/box.dto' +import { BoxState } from '../../box/enums/box-state.enum' +import { BoxResponseDto } from '../dto/box-response.dto' +import { CreateBoxDto as RestCreateBoxDto } from '../dto/create-box.dto' +import { CreateBoxDto } from '../../box/dto/create-box.dto' + +export function boxToBoxResponse(box: BoxDto): BoxResponseDto { + return { + box_id: box.id, + name: box.name, + status: mapState(box.state), + created_at: box.createdAt || new Date().toISOString(), + updated_at: box.updatedAt || new Date().toISOString(), + image: box.image || '', + cpus: box.cpu || 1, + memory_mib: (box.memory || 1) * 1024, + labels: box.labels || {}, + } +} + +export function createBoxToCreateBox(dto: RestCreateBoxDto, target?: string): CreateBoxDto { + const createDto = new CreateBoxDto() + createDto.name = dto.name + createDto.image = dto.image + createDto.user = dto.user + createDto.env = dto.env + createDto.cpu = dto.cpus + createDto.memory = dto.memory_mib ? Math.ceil(dto.memory_mib / 1024) : undefined + createDto.disk = dto.disk_size_gb + createDto.target = target + if (dto.network) { + const allowNet = dto.network.allow_net?.map((entry) => entry.trim()).filter(Boolean) + createDto.networkBlockAll = dto.network.mode === 'disabled' + createDto.networkAllowList = dto.network.mode === 'enabled' && allowNet?.length ? allowNet.join(',') : undefined + } + return createDto +} + +function mapState(state: string | BoxState | undefined): string { + switch (state) { + case BoxState.STARTED: + return 'running' + case BoxState.STOPPED: + case BoxState.ARCHIVED: + return 'stopped' + case BoxState.CREATING: + case BoxState.STARTING: + case BoxState.RESTORING: + return 'configured' + case BoxState.STOPPING: + case BoxState.DESTROYING: + case BoxState.ARCHIVING: + return 'stopping' + case BoxState.ERROR: + case BoxState.UNKNOWN: + default: + return 'unknown' + } +} diff --git a/apps/api/src/boxlite-rest/mappers/sandbox-to-box.mapper.ts b/apps/api/src/boxlite-rest/mappers/sandbox-to-box.mapper.ts deleted file mode 100644 index 768343320..000000000 --- a/apps/api/src/boxlite-rest/mappers/sandbox-to-box.mapper.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxDto } from '../../sandbox/dto/sandbox.dto' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { BoxResponseDto } from '../dto/box-response.dto' -import { CreateBoxDto } from '../dto/create-box.dto' -import { CreateSandboxDto } from '../../sandbox/dto/create-sandbox.dto' - -export function sandboxToBoxResponse(sandbox: SandboxDto): BoxResponseDto { - return { - box_id: sandbox.id, - name: sandbox.name, - status: mapState(sandbox.state), - created_at: sandbox.createdAt || new Date().toISOString(), - updated_at: sandbox.updatedAt || new Date().toISOString(), - image: sandbox.snapshot || '', - cpus: sandbox.cpu || 1, - memory_mib: (sandbox.memory || 1) * 1024, - labels: sandbox.labels || {}, - } -} - -export function createBoxToCreateSandbox(dto: CreateBoxDto, target?: string): CreateSandboxDto { - const createDto = new CreateSandboxDto() - createDto.name = dto.name - createDto.snapshot = dto.image - createDto.user = dto.user - createDto.env = dto.env - createDto.cpu = dto.cpus - createDto.memory = dto.memory_mib ? Math.ceil(dto.memory_mib / 1024) : undefined - createDto.disk = dto.disk_size_gb - createDto.target = target - return createDto -} - -function mapState(state: string | SandboxState | undefined): string { - switch (state) { - case SandboxState.STARTED: - return 'running' - case SandboxState.STOPPED: - case SandboxState.ARCHIVED: - return 'stopped' - case SandboxState.CREATING: - case SandboxState.STARTING: - case SandboxState.RESTORING: - case SandboxState.PULLING_SNAPSHOT: - case SandboxState.BUILDING_SNAPSHOT: - case SandboxState.PENDING_BUILD: - return 'configured' - case SandboxState.STOPPING: - case SandboxState.DESTROYING: - case SandboxState.ARCHIVING: - return 'stopping' - case SandboxState.ERROR: - case SandboxState.BUILD_FAILED: - case SandboxState.UNKNOWN: - default: - return 'unknown' - } -} diff --git a/apps/api/src/common/constants/constants.ts b/apps/api/src/common/constants/constants.ts index 8ba3220de..4d6da329c 100644 --- a/apps/api/src/common/constants/constants.ts +++ b/apps/api/src/common/constants/constants.ts @@ -4,4 +4,4 @@ * SPDX-License-Identifier: AGPL-3.0 */ -export const SANDBOX_EVENT_CHANNEL = 'sandbox.event.channel' +export const BOX_EVENT_CHANNEL = 'box.event.channel' diff --git a/apps/api/src/common/constants/error-messages.ts b/apps/api/src/common/constants/error-messages.ts index ab7715bcb..dd755133b 100644 --- a/apps/api/src/common/constants/error-messages.ts +++ b/apps/api/src/common/constants/error-messages.ts @@ -7,7 +7,7 @@ export const UPGRADE_TIER_MESSAGE = (dashboardUrl: string) => `To increase concurrency limits, upgrade your organization's Tier by visiting ${dashboardUrl}/limits.` -export const ARCHIVE_SANDBOXES_MESSAGE = 'Consider archiving your unused Sandboxes to free up available storage.' +export const STORAGE_LIMIT_MESSAGE = 'Consider deleting unused Boxes or increasing your storage limit.' -export const PER_SANDBOX_LIMIT_MESSAGE = - 'Need higher resource limits per-sandbox? Contact us at support@boxlite.io and let us know about your use case.' +export const PER_BOX_LIMIT_MESSAGE = + 'Need higher resource limits per-box? Contact us at support@boxlite.io and let us know about your use case.' diff --git a/apps/api/src/common/constants/feature-flags.ts b/apps/api/src/common/constants/feature-flags.ts index 18d3f1389..64ae04ce1 100644 --- a/apps/api/src/common/constants/feature-flags.ts +++ b/apps/api/src/common/constants/feature-flags.ts @@ -6,5 +6,5 @@ export const FeatureFlags = { ORGANIZATION_INFRASTRUCTURE: 'organization_infrastructure', - SANDBOX_RESIZE: 'sandbox_resize', + BOX_RESIZE: 'box_resize', } as const diff --git a/apps/api/src/common/decorators/distributed-lock.decorator.ts b/apps/api/src/common/decorators/distributed-lock.decorator.ts index 20a000d81..e31cbda0e 100644 --- a/apps/api/src/common/decorators/distributed-lock.decorator.ts +++ b/apps/api/src/common/decorators/distributed-lock.decorator.ts @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { RedisLockProvider } from '../../sandbox/common/redis-lock.provider' +import { RedisLockProvider } from '../../box/common/redis-lock.provider' type DistributedLockOptions = { lockKey?: string diff --git a/apps/api/src/common/decorators/throttler-scope.decorator.ts b/apps/api/src/common/decorators/throttler-scope.decorator.ts index eb82a5958..3a6188797 100644 --- a/apps/api/src/common/decorators/throttler-scope.decorator.ts +++ b/apps/api/src/common/decorators/throttler-scope.decorator.ts @@ -14,14 +14,14 @@ export const THROTTLER_SCOPE_KEY = 'throttler:scope' * The 'authenticated' throttler always applies to authenticated routes. * * @example - * // Apply sandbox-create throttler - * @ThrottlerScope('sandbox-create') + * // Apply box-create throttler + * @ThrottlerScope('box-create') * @Post() - * createSandbox() {} + * createBox() {} * * @example * // Apply multiple throttlers - * @ThrottlerScope('sandbox-create', 'sandbox-lifecycle') + * @ThrottlerScope('box-create', 'box-lifecycle') * @Post() * createAndStart() {} */ diff --git a/apps/api/src/common/dto/url.dto.ts b/apps/api/src/common/dto/url.dto.ts deleted file mode 100644 index 805146dcd..000000000 --- a/apps/api/src/common/dto/url.dto.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { IsString } from 'class-validator' - -@ApiSchema({ name: 'Url' }) -export class UrlDto { - @ApiProperty({ - description: 'URL response', - }) - @IsString() - url: string - - constructor(url: string) { - this.url = url - } -} diff --git a/apps/api/src/common/guards/authenticated-rate-limit.guard.ts b/apps/api/src/common/guards/authenticated-rate-limit.guard.ts index ea840b5cf..b2798b39e 100644 --- a/apps/api/src/common/guards/authenticated-rate-limit.guard.ts +++ b/apps/api/src/common/guards/authenticated-rate-limit.guard.ts @@ -12,6 +12,16 @@ import { getRedisConnectionToken } from '@nestjs-modules/ioredis' import { Redis } from 'ioredis' import { OrganizationService } from '../../organization/services/organization.service' import { THROTTLER_SCOPE_KEY } from '../decorators/throttler-scope.decorator' +import { AuthContextType } from '../interfaces/auth-context.interface' + +type RateLimitAuthContext = AuthContextType & { + organizationId?: string + userId?: string +} + +type AuthenticatedRequest = Request & { + user?: RateLimitAuthContext +} @Injectable() export class AuthenticatedRateLimitGuard extends ThrottlerGuard { @@ -28,7 +38,7 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { } protected async getTracker(req: Request): Promise { - const user = req.user as any + const user = (req as AuthenticatedRequest).user // Track by organization ID when available (shared quota per org) if (user?.organizationId) { @@ -47,13 +57,13 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { protected generateKey(context: ExecutionContext, suffix: string, name: string): string { // Override to make rate limiting per-rate-limit-type, not per-route - // This ensures all routes share the same counter per rate limit type (authenticated, sandbox-create, sandbox-lifecycle) + // This ensures all routes share the same counter per rate limit type (authenticated, box-create, box-lifecycle) return `${name}-${suffix}` } async handleRequest(requestProps: ThrottlerRequest): Promise { const { context, throttler } = requestProps - const request = context.switchToHttp().getRequest() + const request = context.switchToHttp().getRequest() const isAuthenticated = request.user && this.isValidAuthContext(request.user) // Skip rate limiting for M2M system roles (checked AFTER auth runs) @@ -72,15 +82,15 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { } // Check authenticated throttlers - const authenticatedThrottlers = ['authenticated', 'sandbox-create', 'sandbox-lifecycle'] + const authenticatedThrottlers = ['authenticated', 'box-create', 'box-lifecycle'] if (authenticatedThrottlers.includes(throttler.name)) { if (isAuthenticated) { // Only 'authenticated' applies to all routes by default - // 'sandbox-create' and 'sandbox-lifecycle' only apply if explicitly configured via @SkipThrottle or @Throttle + // 'box-create' and 'box-lifecycle' only apply if explicitly configured via @SkipThrottle or @Throttle const isDefaultThrottler = throttler.name === 'authenticated' if (!isDefaultThrottler) { - // Sandbox throttlers (sandbox-create, sandbox-lifecycle) are opt-in only + // Box throttlers (box-create, box-lifecycle) are opt-in only // Check if this route declares this throttler scope via @ThrottlerScope() decorator const scopes = this.reflector.getAllAndOverride(THROTTLER_SCOPE_KEY, [ context.getHandler(), @@ -93,7 +103,7 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { } } - const user = request.user as any + const user = request.user const orgId = user?.organizationId if (orgId) { const orgLimits = await this.getCachedOrganizationRateLimits(orgId) @@ -101,19 +111,19 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { const customLimit = throttler.name === 'authenticated' ? orgLimits.authenticated - : throttler.name === 'sandbox-create' - ? orgLimits.sandboxCreate - : throttler.name === 'sandbox-lifecycle' - ? orgLimits.sandboxLifecycle + : throttler.name === 'box-create' + ? orgLimits.boxCreate + : throttler.name === 'box-lifecycle' + ? orgLimits.boxLifecycle : undefined const customTtlSeconds = throttler.name === 'authenticated' ? orgLimits.authenticatedTtlSeconds - : throttler.name === 'sandbox-create' - ? orgLimits.sandboxCreateTtlSeconds - : throttler.name === 'sandbox-lifecycle' - ? orgLimits.sandboxLifecycleTtlSeconds + : throttler.name === 'box-create' + ? orgLimits.boxCreateTtlSeconds + : throttler.name === 'box-lifecycle' + ? orgLimits.boxLifecycleTtlSeconds : undefined if (customLimit != null || customTtlSeconds != null) { @@ -141,22 +151,22 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { return true } - private isValidAuthContext(user: any): boolean { - return user && (user.userId || user.role) + private isValidAuthContext(user: RateLimitAuthContext | undefined): boolean { + return Boolean(user && (user.userId || user.role)) } - private isSystemRole(user: any): boolean { + private isSystemRole(user: RateLimitAuthContext | undefined): boolean { // Skip rate limiting for M2M system roles (proxy, runner, ssh-gateway) return user?.role === 'ssh-gateway' || user?.role === 'proxy' || user?.role === 'runner' } private async getCachedOrganizationRateLimits(organizationId: string): Promise<{ authenticated: number | null - sandboxCreate: number | null - sandboxLifecycle: number | null + boxCreate: number | null + boxLifecycle: number | null authenticatedTtlSeconds: number | null - sandboxCreateTtlSeconds: number | null - sandboxLifecycleTtlSeconds: number | null + boxCreateTtlSeconds: number | null + boxLifecycleTtlSeconds: number | null } | null> { // If OrganizationService is not available (e.g., in UserModule), use default rate limits if (!this.organizationService) { @@ -175,11 +185,11 @@ export class AuthenticatedRateLimitGuard extends ThrottlerGuard { if (organization) { const limits = { authenticated: organization.authenticatedRateLimit, - sandboxCreate: organization.sandboxCreateRateLimit, - sandboxLifecycle: organization.sandboxLifecycleRateLimit, + boxCreate: organization.boxCreateRateLimit, + boxLifecycle: organization.boxLifecycleRateLimit, authenticatedTtlSeconds: organization.authenticatedRateLimitTtlSeconds, - sandboxCreateTtlSeconds: organization.sandboxCreateRateLimitTtlSeconds, - sandboxLifecycleTtlSeconds: organization.sandboxLifecycleRateLimitTtlSeconds, + boxCreateTtlSeconds: organization.boxCreateRateLimitTtlSeconds, + boxLifecycleTtlSeconds: organization.boxLifecycleRateLimitTtlSeconds, } await this.redis.set(cacheKey, JSON.stringify(limits), 'EX', 60) return limits diff --git a/apps/api/src/common/interfaces/runner-context.interface.ts b/apps/api/src/common/interfaces/runner-context.interface.ts index 01a64fab0..a69974f8f 100644 --- a/apps/api/src/common/interfaces/runner-context.interface.ts +++ b/apps/api/src/common/interfaces/runner-context.interface.ts @@ -5,7 +5,7 @@ */ import { BaseAuthContext } from './auth-context.interface' -import { Runner } from '../../sandbox/entities/runner.entity' +import { Runner } from '../../box/entities/runner.entity' export interface RunnerContext extends BaseAuthContext { role: 'runner' @@ -13,6 +13,6 @@ export interface RunnerContext extends BaseAuthContext { runner: Runner } -export function isRunnerContext(user: BaseAuthContext): user is RunnerContext { - return 'role' in user && user.role === 'runner' +export function isRunnerContext(user: BaseAuthContext | null | undefined): user is RunnerContext { + return Boolean(user) && user.role === 'runner' } diff --git a/apps/api/src/common/utils/api-key.spec.ts b/apps/api/src/common/utils/api-key.spec.ts new file mode 100644 index 000000000..6bc212e02 --- /dev/null +++ b/apps/api/src/common/utils/api-key.spec.ts @@ -0,0 +1,83 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { crc32 } from 'node:zlib' +import { createHash } from 'crypto' +import { extractKeyDisplayPrefix, generateApiKeyHash, generateApiKeyValue } from './api-key' + +const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + +// Independent base62(uint32) — deliberately NOT the production helper, so the +// checksum assertion crosses a real boundary instead of being tautological. +function base62Uint32(n: number): string { + if (n === 0) { + return '0' + } + let out = '' + while (n > 0) { + out = BASE62[n % 62] + out + n = Math.floor(n / 62) + } + return out +} + +describe('api-key', () => { + describe('generateApiKeyValue', () => { + it('produces {prefix}_{class}_{base62 body}{crc6} for each class', () => { + for (const cls of ['live', 'test', 'svc'] as const) { + expect(generateApiKeyValue('blk', cls)).toMatch(new RegExp(`^blk_${cls}_[0-9A-Za-z]{40,}$`)) + } + }) + + it('honors an overridden brand prefix', () => { + expect(generateApiKeyValue('acme', 'live').startsWith('acme_live_')).toBe(true) + }) + + it('rejects an invalid prefix at the boundary', () => { + expect(() => generateApiKeyValue('Blk', 'live')).toThrow(/Invalid API key prefix/) + expect(() => generateApiKeyValue('b', 'live')).toThrow(/Invalid API key prefix/) + expect(() => generateApiKeyValue('has_underscore', 'live')).toThrow(/Invalid API key prefix/) + expect(() => generateApiKeyValue('toolongprefixvalue', 'live')).toThrow(/Invalid API key prefix/) + }) + + it('appends a CRC32 a secret scanner can verify offline', () => { + const key = generateApiKeyValue('blk', 'live') + const rest = key.slice('blk_live_'.length) + const body = rest.slice(0, -6) + const checksum = rest.slice(-6) + expect(checksum).toBe(base62Uint32(crc32(Buffer.from(body)) >>> 0).padStart(6, '0')) + }) + + it('is unique per call (256-bit body entropy)', () => { + expect(generateApiKeyValue('blk', 'live')).not.toBe(generateApiKeyValue('blk', 'live')) + }) + }) + + describe('extractKeyDisplayPrefix', () => { + it('returns the {prefix}_{class}_ head for current keys', () => { + expect(extractKeyDisplayPrefix(generateApiKeyValue('blk', 'live'))).toBe('blk_live_') + expect(extractKeyDisplayPrefix(generateApiKeyValue('acme', 'svc'))).toBe('acme_svc_') + }) + + it('falls back to the first 3 chars for legacy Daytona values (display unchanged)', () => { + expect(extractKeyDisplayPrefix('dtn_0000000000000000000000000000000000000000000000000000000000000000')).toBe( + 'dtn', + ) + expect(extractKeyDisplayPrefix('abcdef')).toBe('abc') + }) + }) + + describe('generateApiKeyHash', () => { + // Auth resolves keys by SHA-256 of the whole value. Pinning the digest of + // a fixed legacy-format key makes any hashing change that would silently + // invalidate every already-issued key fail loudly here. + it('hashes a legacy dtn_ value to its known SHA-256 (proves zero auth break)', () => { + const legacy = 'dtn_0000000000000000000000000000000000000000000000000000000000000000' + expect(generateApiKeyHash(legacy)).toBe('f5d6c9e2d6502b5e547419370d8b0cdddf8659241fc6c15eb2ca78fc3430c84c') + expect(generateApiKeyHash(legacy)).toBe(createHash('sha256').update(legacy).digest('hex')) + }) + }) +}) diff --git a/apps/api/src/common/utils/api-key.ts b/apps/api/src/common/utils/api-key.ts index f387fbbb7..7668b1648 100644 --- a/apps/api/src/common/utils/api-key.ts +++ b/apps/api/src/common/utils/api-key.ts @@ -5,15 +5,111 @@ */ import * as crypto from 'crypto' +import { crc32 } from 'node:zlib' + +/** + * Key class segment. `live`/`test` = user dashboard keys (Stripe-style + * environment split); `svc` = internal machine keys (runner / proxy / + * ssh-gateway). Cosmetic — for humans, masked display, and secret scanners; + * never consulted during authentication. + */ +export type ApiKeyClass = 'live' | 'test' | 'svc' + +const BASE62 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + +// The brand prefix is operator-overridable (self-host / white-label) and ends +// up in every issued key and in secret-scanner regexes, so it is validated at +// this boundary rather than trusted. +const PREFIX_RE = /^[a-z][a-z0-9]{1,15}$/ export function generateRandomString(size: number): string { return crypto.randomBytes(size).toString('hex') } -export function generateApiKeyValue(): string { - return `dtn_${generateRandomString(32)}` +function base62FromBytes(buf: Buffer): string { + let n = BigInt('0x' + buf.toString('hex')) + if (n === 0n) { + return '0' + } + let out = '' + while (n > 0n) { + out = BASE62[Number(n % 62n)] + out + n /= 62n + } + return out +} + +function base62FromUint32(n: number): string { + if (n === 0) { + return '0' + } + let out = '' + while (n > 0) { + out = BASE62[n % 62] + out + n = Math.floor(n / 62) + } + return out +} + +/** + * 6-char base62 CRC32 of the random body, appended to the key so secret + * scanners can reject malformed / fabricated tokens offline without a DB + * lookup (GitHub's token-format scheme). Purely a leak-detection aid — + * authentication is still SHA-256 of the whole value. 62^6 > 2^32, so 6 chars + * always hold a uint32 CRC. + */ +function checksum(body: string): string { + const c = crc32(Buffer.from(body)) >>> 0 + return base62FromUint32(c).padStart(6, '0') +} + +/** + * Mint an opaque API key `{prefix}_{class}_{base62(256-bit)}{crc6}` + * (e.g. `blk_live_<~43 base62><6>`). Structure aligns with + * Stripe/GitHub/Doppler/AWS; the prefix and class carry no security — the + * 256-bit body plus the server-side hash do. + */ +export function generateApiKeyValue(prefix: string, keyClass: ApiKeyClass): string { + if (!PREFIX_RE.test(prefix)) { + throw new Error( + `Invalid API key prefix "${prefix}": must be lowercase alphanumeric, 2-16 chars, starting with a letter (check API_KEY_PREFIX)`, + ) + } + const body = base62FromBytes(crypto.randomBytes(32)) + return `${prefix}_${keyClass}_${body}${checksum(body)}` } +/** + * Hash an API key for at-rest storage / lookup. + * + * Uses SHA-256 (not a slow KDF like bcrypt/scrypt/argon2) deliberately: + * API keys produced by {@link generateApiKeyValue} carry 256 bits of + * CSPRNG entropy (`crypto.randomBytes(32)`) -- the keyspace is + * computationally infeasible to brute-force regardless of hash speed. + * Slow KDFs exist to defend *low-entropy human passwords*; they impose + * latency on every auth request without adding security for random + * high-entropy tokens. Matches the upstream `daytonaio/daytona` + * implementation byte-for-byte and the documented pattern used by + * Stripe, GitHub, Doppler, and other API-key providers. + */ export function generateApiKeyHash(value: string): string { return crypto.createHash('sha256').update(value).digest('hex') } + +/** + * Non-secret prefix used for masked display (`blk_live_••••abc`). Returns the + * `{prefix}_{class}_` head for current keys; for legacy single-segment values + * (Daytona `dtn_…`) falls back to the first 3 characters, matching the + * pre-existing stored `keyPrefix` so already-issued rows render unchanged. + */ +export function extractKeyDisplayPrefix(value: string): string { + const firstUnderscore = value.indexOf('_') + if (firstUnderscore === -1) { + return value.substring(0, 3) + } + const secondUnderscore = value.indexOf('_', firstUnderscore + 1) + if (secondUnderscore === -1) { + return value.substring(0, 3) + } + return value.substring(0, secondUnderscore + 1) +} diff --git a/apps/api/src/common/utils/docker-image.util.spec.ts b/apps/api/src/common/utils/docker-image.util.spec.ts new file mode 100644 index 000000000..249bf2414 --- /dev/null +++ b/apps/api/src/common/utils/docker-image.util.spec.ts @@ -0,0 +1,16 @@ +import { parseDockerImage } from './docker-image.util' + +describe('parseDockerImage', () => { + it('preserves digest image references when rebuilding the full name', () => { + const digest = '1db4c9815a3353bf2e5730b62c24c364b53d53ca0a52722c5d79f32df935b6fd' + + const image = parseDockerImage(`ghcr.io/boxlite-ai/boxlite-agent-base@sha256:${digest}`) + + expect(image.registry).toBe('ghcr.io') + expect(image.project).toBe('boxlite-ai') + expect(image.repository).toBe('boxlite-agent-base') + expect(image.tag).toBeUndefined() + expect(image.digest).toBe(`sha256:${digest}`) + expect(image.getFullName()).toBe(`ghcr.io/boxlite-ai/boxlite-agent-base@sha256:${digest}`) + }) +}) diff --git a/apps/api/src/common/utils/docker-image.util.ts b/apps/api/src/common/utils/docker-image.util.ts index 58ae2e018..9d9495218 100644 --- a/apps/api/src/common/utils/docker-image.util.ts +++ b/apps/api/src/common/utils/docker-image.util.ts @@ -16,6 +16,8 @@ export interface DockerImageInfo { repository: string /** The tag or digest (e.g. 'latest' or 'sha256:123...') */ tag?: string + /** The digest (e.g. 'sha256:123...') */ + digest?: string /** The full original image name */ originalName: string } @@ -25,6 +27,7 @@ export class DockerImage implements DockerImageInfo { project?: string repository: string tag?: string + digest?: string originalName: string constructor(info: DockerImageInfo) { @@ -32,6 +35,7 @@ export class DockerImage implements DockerImageInfo { this.project = info.project this.repository = info.repository this.tag = info.tag + this.digest = info.digest this.originalName = info.originalName } @@ -46,6 +50,9 @@ export class DockerImage implements DockerImageInfo { if (this.tag) { name = `${name}:${this.tag}` } + if (this.digest) { + name = `${name}@${this.digest}` + } return name } } @@ -58,6 +65,7 @@ export class DockerImage implements DockerImageInfo { * * Examples: * - registry:5000/test/image:latest -> { registry: 'registry:5000', project: 'test', repository: 'image', tag: 'latest' } + * - ghcr.io/test/image@sha256:abc... -> { registry: 'ghcr.io', project: 'test', repository: 'image', digest: 'sha256:abc...' } * - docker.io/library/ubuntu:20.04 -> { registry: 'docker.io', project: 'library', repository: 'ubuntu', tag: '20.04' } * - ubuntu:20.04 -> { registry: undefined, project: undefined, repository: 'ubuntu', tag: '20.04' } * - ubuntu -> { registry: undefined, project: undefined, repository: 'ubuntu', tag: undefined } @@ -80,7 +88,7 @@ export function parseDockerImage(imageName: string): DockerImage { if (!nameWithoutDigest || !digest || !/^[a-f0-9]{64}$/.test(digest)) { throw new Error('Invalid digest format. Must be image@sha256:64_hex_characters') } - result.tag = `sha256:${digest}` + result.digest = `sha256:${digest}` // Split remaining parts parts = nameWithoutDigest.split('/') diff --git a/apps/api/src/config/config.controller.ts b/apps/api/src/config/config.controller.ts index 723418fba..7cf630de7 100644 --- a/apps/api/src/config/config.controller.ts +++ b/apps/api/src/config/config.controller.ts @@ -8,11 +8,15 @@ import { Controller, Get } from '@nestjs/common' import { TypedConfigService } from './typed-config.service' import { ApiOperation, ApiTags, ApiResponse } from '@nestjs/swagger' import { ConfigurationDto } from './dto/configuration.dto' +import { OidcMetadataService } from './oidc-metadata.service' @ApiTags('config') @Controller('config') export class ConfigController { - constructor(private readonly configService: TypedConfigService) {} + constructor( + private readonly configService: TypedConfigService, + private readonly oidcMetadataService: OidcMetadataService, + ) {} @Get() @ApiOperation({ summary: 'Get config' }) @@ -21,7 +25,8 @@ export class ConfigController { description: 'BoxLite configuration', type: ConfigurationDto, }) - getConfig() { - return new ConfigurationDto(this.configService) + async getConfig() { + const endSessionState = await this.oidcMetadataService.getEndSessionState() + return new ConfigurationDto(this.configService, { endSessionState }) } } diff --git a/apps/api/src/config/configuration.ts b/apps/api/src/config/configuration.ts index 976d7724e..3b2f733fd 100644 --- a/apps/api/src/config/configuration.ts +++ b/apps/api/src/config/configuration.ts @@ -4,6 +4,13 @@ * SPDX-License-Identifier: AGPL-3.0 */ +function csvEnv(value?: string): string[] { + return (value ?? '') + .split(',') + .map((item) => item.trim()) + .filter(Boolean) +} + const configuration = { production: process.env.NODE_ENV === 'production', version: process.env.VERSION || '0.0.0-dev', @@ -46,6 +53,8 @@ const configuration = { issuer: process.env.OIDC_ISSUER_BASE_URL || process.env.OID_ISSUER_BASE_URL, publicIssuer: process.env.PUBLIC_OIDC_DOMAIN, audience: process.env.OIDC_AUDIENCE || process.env.OID_AUDIENCE, + endSessionEndpoint: process.env.OIDC_END_SESSION_ENDPOINT, + postLogoutRedirectAllowlist: process.env.OIDC_POST_LOGOUT_REDIRECT_ALLOWLIST, managementApi: { enabled: process.env.OIDC_MANAGEMENT_API_ENABLED === 'true', clientId: process.env.OIDC_MANAGEMENT_API_CLIENT_ID, @@ -61,21 +70,19 @@ const configuration = { secure: process.env.SMTP_SECURE === 'true', from: process.env.SMTP_EMAIL_FROM || 'noreply@mail.boxlite.io', }, - defaultSnapshot: process.env.DEFAULT_SNAPSHOT, dashboardUrl: process.env.DASHBOARD_URL, // Default to empty string - dashboard will then hit '/api' dashboardBaseApiUrl: process.env.DASHBOARD_BASE_API_URL || '', - transientRegistry: { - url: process.env.TRANSIENT_REGISTRY_URL, - admin: process.env.TRANSIENT_REGISTRY_ADMIN, - password: process.env.TRANSIENT_REGISTRY_PASSWORD, - projectId: process.env.TRANSIENT_REGISTRY_PROJECT_ID, - }, - internalRegistry: { - url: process.env.INTERNAL_REGISTRY_URL, - admin: process.env.INTERNAL_REGISTRY_ADMIN, - password: process.env.INTERNAL_REGISTRY_PASSWORD, - projectId: process.env.INTERNAL_REGISTRY_PROJECT_ID, + // Currently unconsumed (Daytona-port residue): nothing reads `systemSourceRegistry`. + // Box images are a fixed curated set of digest-pinned ghcr.io refs pulled directly by + // the runner (see box/constants/curated-images.constant.ts), not mirrored from a source + // registry. Kept as a reserved surface for a future per-org custom-image path. + systemSourceRegistry: { + name: process.env.BOXLITE_SYSTEM_SOURCE_REGISTRY_NAME || 'BoxLite System Source Registry', + url: process.env.BOXLITE_SYSTEM_SOURCE_REGISTRY_URL, + username: process.env.BOXLITE_SYSTEM_SOURCE_REGISTRY_USERNAME, + password: process.env.BOXLITE_SYSTEM_SOURCE_REGISTRY_PASSWORD, + projectId: process.env.BOXLITE_SYSTEM_SOURCE_REGISTRY_PROJECT_ID || '', }, s3: { endpoint: process.env.S3_ENDPOINT, @@ -89,7 +96,6 @@ const configuration = { }, notificationGatewayDisabled: process.env.NOTIFICATION_GATEWAY_DISABLED === 'true', skipConnections: process.env.SKIP_CONNECTIONS === 'true', - maxAutoArchiveInterval: parseInt(process.env.MAX_AUTO_ARCHIVE_INTERVAL || '43200', 10), maintananceMode: process.env.MAINTENANCE_MODE === 'true', disableCronJobs: process.env.DISABLE_CRON_JOBS === 'true', appRole: process.env.APP_ROLE || 'all', @@ -157,8 +163,7 @@ const configuration = { publicKey: process.env.SSH_GATEWAY_PUBLIC_KEY, url: process.env.SSH_GATEWAY_URL, }, - organizationSandboxDefaultLimitedNetworkEgress: - process.env.ORGANIZATION_SANDBOX_DEFAULT_LIMITED_NETWORK_EGRESS === 'true', + organizationBoxDefaultLimitedNetworkEgress: process.env.ORGANIZATION_BOX_DEFAULT_LIMITED_NETWORK_EGRESS === 'true', pylonAppId: process.env.PYLON_APP_ID, billingApiUrl: process.env.BILLING_API_URL, analyticsApiUrl: process.env.ANALYTICS_API_URL, @@ -186,7 +191,7 @@ const configuration = { allocatedCpu: parseFloat(process.env.RUNNER_ALLOCATED_CPU_WEIGHT || '0.03'), allocatedMemory: parseFloat(process.env.RUNNER_ALLOCATED_MEMORY_WEIGHT || '0.03'), allocatedDisk: parseFloat(process.env.RUNNER_ALLOCATED_DISK_WEIGHT || '0.03'), - startedSandboxes: parseFloat(process.env.RUNNER_STARTED_SANDBOXES_WEIGHT || '0.1'), + startedBoxes: parseFloat(process.env.RUNNER_STARTED_BOXES_WEIGHT || '0.1'), }, penalty: { exponents: { @@ -211,7 +216,7 @@ const configuration = { allocCpu: parseInt(process.env.RUNNER_OPTIMAL_ALLOC_CPU || '100', 10), allocMem: parseInt(process.env.RUNNER_OPTIMAL_ALLOC_MEM || '100', 10), allocDisk: parseInt(process.env.RUNNER_OPTIMAL_ALLOC_DISK || '100', 10), - startedSandboxes: parseInt(process.env.RUNNER_OPTIMAL_STARTED_SANDBOXES || '0', 10), + startedBoxes: parseInt(process.env.RUNNER_OPTIMAL_STARTED_BOXES || '0', 10), }, critical: { cpu: parseInt(process.env.RUNNER_CRITICAL_CPU || '100', 10), @@ -220,7 +225,7 @@ const configuration = { allocCpu: parseInt(process.env.RUNNER_CRITICAL_ALLOC_CPU || '500', 10), allocMem: parseInt(process.env.RUNNER_CRITICAL_ALLOC_MEM || '500', 10), allocDisk: parseInt(process.env.RUNNER_CRITICAL_ALLOC_DISK || '500', 10), - startedSandboxes: parseInt(process.env.RUNNER_CRITICAL_STARTED_SANDBOXES || '100', 10), + startedBoxes: parseInt(process.env.RUNNER_CRITICAL_STARTED_BOXES || '100', 10), }, }, }, @@ -243,20 +248,18 @@ const configuration = { ? parseInt(process.env.RATE_LIMIT_AUTHENTICATED_LIMIT, 10) : undefined, }, - sandboxCreate: { - ttl: process.env.RATE_LIMIT_SANDBOX_CREATE_TTL - ? parseInt(process.env.RATE_LIMIT_SANDBOX_CREATE_TTL, 10) - : undefined, - limit: process.env.RATE_LIMIT_SANDBOX_CREATE_LIMIT - ? parseInt(process.env.RATE_LIMIT_SANDBOX_CREATE_LIMIT, 10) + boxCreate: { + ttl: process.env.RATE_LIMIT_BOX_CREATE_TTL ? parseInt(process.env.RATE_LIMIT_BOX_CREATE_TTL, 10) : undefined, + limit: process.env.RATE_LIMIT_BOX_CREATE_LIMIT + ? parseInt(process.env.RATE_LIMIT_BOX_CREATE_LIMIT, 10) : undefined, }, - sandboxLifecycle: { - ttl: process.env.RATE_LIMIT_SANDBOX_LIFECYCLE_TTL - ? parseInt(process.env.RATE_LIMIT_SANDBOX_LIFECYCLE_TTL, 10) + boxLifecycle: { + ttl: process.env.RATE_LIMIT_BOX_LIFECYCLE_TTL + ? parseInt(process.env.RATE_LIMIT_BOX_LIFECYCLE_TTL, 10) : undefined, - limit: process.env.RATE_LIMIT_SANDBOX_LIFECYCLE_LIMIT - ? parseInt(process.env.RATE_LIMIT_SANDBOX_LIFECYCLE_LIMIT, 10) + limit: process.env.RATE_LIMIT_BOX_LIFECYCLE_LIMIT + ? parseInt(process.env.RATE_LIMIT_BOX_LIFECYCLE_LIMIT, 10) : undefined, }, }, @@ -269,17 +272,6 @@ const configuration = { enabled: process.env.LOG_REQUESTS_ENABLED === 'true', }, }, - defaultOrganizationQuota: { - totalCpuQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_TOTAL_CPU_QUOTA || '10', 10), - totalMemoryQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_TOTAL_MEMORY_QUOTA || '10', 10), - totalDiskQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_TOTAL_DISK_QUOTA || '30', 10), - maxCpuPerSandbox: parseInt(process.env.DEFAULT_ORG_QUOTA_MAX_CPU_PER_SANDBOX || '4', 10), - maxMemoryPerSandbox: parseInt(process.env.DEFAULT_ORG_QUOTA_MAX_MEMORY_PER_SANDBOX || '8', 10), - maxDiskPerSandbox: parseInt(process.env.DEFAULT_ORG_QUOTA_MAX_DISK_PER_SANDBOX || '10', 10), - snapshotQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_SNAPSHOT_QUOTA || '100', 10), - maxSnapshotSize: parseInt(process.env.DEFAULT_ORG_QUOTA_MAX_SNAPSHOT_SIZE || '20', 10), - volumeQuota: parseInt(process.env.DEFAULT_ORG_QUOTA_VOLUME_QUOTA || '100', 10), - }, defaultRegion: { id: process.env.DEFAULT_REGION_ID || 'us', name: process.env.DEFAULT_REGION_NAME || 'us', @@ -287,18 +279,10 @@ const configuration = { }, admin: { apiKey: process.env.ADMIN_API_KEY, - totalCpuQuota: parseInt(process.env.ADMIN_TOTAL_CPU_QUOTA || '0', 10), - totalMemoryQuota: parseInt(process.env.ADMIN_TOTAL_MEMORY_QUOTA || '0', 10), - totalDiskQuota: parseInt(process.env.ADMIN_TOTAL_DISK_QUOTA || '0', 10), - maxCpuPerSandbox: parseInt(process.env.ADMIN_MAX_CPU_PER_SANDBOX || '0', 10), - maxMemoryPerSandbox: parseInt(process.env.ADMIN_MAX_MEMORY_PER_SANDBOX || '0', 10), - maxDiskPerSandbox: parseInt(process.env.ADMIN_MAX_DISK_PER_SANDBOX || '0', 10), - snapshotQuota: parseInt(process.env.ADMIN_SNAPSHOT_QUOTA || '100', 10), - maxSnapshotSize: parseInt(process.env.ADMIN_MAX_SNAPSHOT_SIZE || '100', 10), - volumeQuota: parseInt(process.env.ADMIN_VOLUME_QUOTA || '0', 10), }, skipUserEmailVerification: process.env.SKIP_USER_EMAIL_VERIFICATION === 'true', apiKey: { + prefix: process.env.API_KEY_PREFIX || 'blk', validationCacheTtlSeconds: parseInt(process.env.API_KEY_VALIDATION_CACHE_TTL_SECONDS || '10', 10), userCacheTtlSeconds: parseInt(process.env.API_KEY_USER_CACHE_TTL_SECONDS || '60', 10), }, @@ -306,13 +290,14 @@ const configuration = { warmPool: { candidateLimit: parseInt(process.env.WARM_POOL_CANDIDATE_LIMIT || '300', 10), }, - sandboxOtel: { - endpointUrl: process.env.SANDBOX_OTEL_ENDPOINT_URL, + boxOtel: { + endpointUrl: process.env.BOX_OTEL_ENDPOINT_URL, }, otelCollector: { apiKey: process.env.OTEL_COLLECTOR_API_KEY, }, clickhouse: { + url: process.env.CLICKHOUSE_READER_URL || process.env.CLICKHOUSE_URL, host: process.env.CLICKHOUSE_HOST, port: parseInt(process.env.CLICKHOUSE_PORT || '8123', 10), database: process.env.CLICKHOUSE_DATABASE || 'otel', @@ -320,16 +305,39 @@ const configuration = { password: process.env.CLICKHOUSE_PASSWORD, protocol: process.env.CLICKHOUSE_PROTOCOL || 'https', }, - sandboxActivity: { - throttleTtlSeconds: parseInt(process.env.SANDBOX_ACTIVITY_THROTTLE_TTL_SECONDS || '5', 10), - flushBatchSize: parseInt(process.env.SANDBOX_ACTIVITY_FLUSH_BATCH_SIZE || '1000', 10), + adminObservability: { + cloudwatch: { + region: process.env.ADMIN_OBSERVABILITY_CLOUDWATCH_REGION || process.env.AWS_REGION || process.env.S3_REGION, + logGroups: csvEnv(process.env.ADMIN_OBSERVABILITY_CLOUDWATCH_LOG_GROUPS), + logGroupPrefix: process.env.ADMIN_OBSERVABILITY_CLOUDWATCH_LOG_GROUP_PREFIX, + maxLogGroups: parseInt(process.env.ADMIN_OBSERVABILITY_CLOUDWATCH_MAX_LOG_GROUPS || '20', 10), + limitPerGroup: parseInt(process.env.ADMIN_OBSERVABILITY_CLOUDWATCH_LIMIT_PER_GROUP || '25', 10), + }, + s3: { + region: process.env.ADMIN_OBSERVABILITY_S3_REGION || process.env.S3_REGION, + endpoint: process.env.ADMIN_OBSERVABILITY_S3_ENDPOINT || process.env.S3_ENDPOINT, + accessKey: process.env.ADMIN_OBSERVABILITY_S3_ACCESS_KEY || process.env.S3_ACCESS_KEY, + secretKey: process.env.ADMIN_OBSERVABILITY_S3_SECRET_KEY || process.env.S3_SECRET_KEY, + buckets: csvEnv(process.env.ADMIN_OBSERVABILITY_S3_BUCKETS || process.env.S3_DEFAULT_BUCKET), + prefixes: csvEnv(process.env.ADMIN_OBSERVABILITY_S3_PREFIXES), + maxObjects: parseInt(process.env.ADMIN_OBSERVABILITY_S3_MAX_OBJECTS || '25', 10), + }, + }, + observability: { + clickstackBaseUrl: process.env.ADMIN_OBSERVABILITY_CLICKSTACK_URL, + clickstackDashboardUrl: process.env.ADMIN_OBSERVABILITY_CLICKSTACK_DASHBOARD_URL, + clickstackLogSourceId: process.env.ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID, + clickstackTraceSourceId: process.env.ADMIN_OBSERVABILITY_CLICKSTACK_TRACE_SOURCE_ID, + clickstackMetricSourceId: process.env.ADMIN_OBSERVABILITY_CLICKSTACK_METRIC_SOURCE_ID, + }, + boxActivity: { + throttleTtlSeconds: parseInt(process.env.BOX_ACTIVITY_THROTTLE_TTL_SECONDS || '5', 10), + flushBatchSize: parseInt(process.env.BOX_ACTIVITY_FLUSH_BATCH_SIZE || '1000', 10), }, encryption: { key: process.env.ENCRYPTION_KEY, salt: process.env.ENCRYPTION_SALT, }, - failedSnapshotRunnerRetentionHours: parseInt(process.env.FAILED_SNAPSHOT_RUNNER_RETENTION_HOURS || '3', 10), - buildInfoSnapshotRunnerStalenessDays: parseInt(process.env.BUILDINFO_SNAPSHOT_RUNNER_STALENESS_DAYS || '7', 10), } export { configuration } diff --git a/apps/api/src/config/dto/configuration.dto.ts b/apps/api/src/config/dto/configuration.dto.ts index 2df2000b6..126d031e3 100644 --- a/apps/api/src/config/dto/configuration.dto.ts +++ b/apps/api/src/config/dto/configuration.dto.ts @@ -7,6 +7,7 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional, ApiSchema, getSchemaPath } from '@nestjs/swagger' import { IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator' import { TypedConfigService } from '../typed-config.service' +import { EndSessionState, isValidHttpUrl } from '../oidc-metadata.service' @ApiSchema({ name: 'Announcement' }) export class Announcement { @@ -79,18 +80,18 @@ export class RateLimitConfig { authenticated?: RateLimitEntry @ApiPropertyOptional({ - description: 'Sandbox create rate limit', + description: 'Box create rate limit', type: RateLimitEntry, }) @IsOptional() - sandboxCreate?: RateLimitEntry + boxCreate?: RateLimitEntry @ApiPropertyOptional({ - description: 'Sandbox lifecycle rate limit', + description: 'Box lifecycle rate limit', type: RateLimitEntry, }) @IsOptional() - sandboxLifecycle?: RateLimitEntry + boxLifecycle?: RateLimitEntry } @ApiSchema({ name: 'OidcConfig' }) @@ -115,6 +116,15 @@ export class OidcConfig { }) @IsString() audience: string + + @ApiPropertyOptional({ + description: + 'OIDC end-session endpoint. Set when the IdP does not advertise one via discovery (e.g. Dex) and BoxLite hosts a compatible logout endpoint.', + example: 'https://api.example.com/api/auth/end-session', + }) + @IsString() + @IsOptional() + endSessionEndpoint?: string } @ApiExtraModels(Announcement) @@ -164,7 +174,7 @@ export class ConfigurationDto { @ApiProperty({ description: 'Proxy template URL', - example: 'https://{{PORT}}-{{sandboxId}}.proxy.example.com', + example: 'https://{{PORT}}-{{boxId}}.proxy.example.com', }) @IsString() proxyTemplateUrl: string @@ -176,13 +186,6 @@ export class ConfigurationDto { @IsString() proxyToolboxUrl: string - @ApiProperty({ - description: 'Default snapshot for sandboxes', - example: 'ubuntu:22.04', - }) - @IsString() - defaultSnapshot: string - @ApiProperty({ description: 'Dashboard URL', example: 'https://dashboard.example.com', @@ -190,13 +193,6 @@ export class ConfigurationDto { @IsString() dashboardUrl: string - @ApiProperty({ - description: 'Maximum auto-archive interval in minutes', - example: 43200, - }) - @IsNumber() - maxAutoArchiveInterval: number - @ApiProperty({ description: 'Whether maintenance mode is enabled', example: false, @@ -250,20 +246,28 @@ export class ConfigurationDto { @IsOptional() rateLimit?: RateLimitConfig - constructor(configService: TypedConfigService) { + constructor(configService: TypedConfigService, options: { endSessionState: EndSessionState }) { this.version = configService.getOrThrow('version') this.oidc = { issuer: configService.get('oidc.publicIssuer') || configService.getOrThrow('oidc.issuer'), clientId: configService.getOrThrow('oidc.clientId'), audience: configService.getOrThrow('oidc.audience'), + // Only expose the BoxLite-hosted fallback when discovery proved the IdP + // lacks end_session_endpoint. 'present' → trust the IdP; 'unknown' → + // fail closed (don't override Auth0/Okta's real endpoint on a probe + // blip). Only 'absent' is a definitive answer that justifies the seed. + // Even then we validate the operator-set env: a typo'd OIDC_END_SESSION_ENDPOINT + // must not silently propagate as a metadataSeed value. + endSessionEndpoint: + options.endSessionState === 'absent' + ? validatedEndSessionEndpoint(configService.get('oidc.endSessionEndpoint')) + : undefined, } this.linkedAccountsEnabled = configService.get('oidc.managementApi.enabled') this.proxyTemplateUrl = configService.getOrThrow('proxy.templateUrl') this.proxyToolboxUrl = configService.getOrThrow('proxy.toolboxUrl') - this.defaultSnapshot = configService.getOrThrow('defaultSnapshot') this.dashboardUrl = configService.getOrThrow('dashboardUrl') - this.maxAutoArchiveInterval = configService.getOrThrow('maxAutoArchiveInterval') this.maintananceMode = configService.getOrThrow('maintananceMode') this.environment = configService.getOrThrow('environment') @@ -295,14 +299,19 @@ export class ConfigurationDto { ttl: configService.get('rateLimit.authenticated.ttl'), limit: configService.get('rateLimit.authenticated.limit'), }, - sandboxCreate: { - ttl: configService.get('rateLimit.sandboxCreate.ttl'), - limit: configService.get('rateLimit.sandboxCreate.limit'), + boxCreate: { + ttl: configService.get('rateLimit.boxCreate.ttl'), + limit: configService.get('rateLimit.boxCreate.limit'), }, - sandboxLifecycle: { - ttl: configService.get('rateLimit.sandboxLifecycle.ttl'), - limit: configService.get('rateLimit.sandboxLifecycle.limit'), + boxLifecycle: { + ttl: configService.get('rateLimit.boxLifecycle.ttl'), + limit: configService.get('rateLimit.boxLifecycle.limit'), }, } } } + +function validatedEndSessionEndpoint(value: string | undefined): string | undefined { + if (!value) return undefined + return isValidHttpUrl(value) ? value : undefined +} diff --git a/apps/api/src/config/oidc-metadata.service.ts b/apps/api/src/config/oidc-metadata.service.ts new file mode 100644 index 000000000..b40605c69 --- /dev/null +++ b/apps/api/src/config/oidc-metadata.service.ts @@ -0,0 +1,110 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { HttpService } from '@nestjs/axios' +import { Injectable, Logger } from '@nestjs/common' +import { OidcMetadata } from 'oidc-client-ts' +import { catchError, firstValueFrom, map } from 'rxjs' +import { TypedConfigService } from './typed-config.service' + +export type EndSessionState = 'present' | 'absent' | 'unknown' + +/** + * Parse a value as an http(s) URL with sanity-tight rules. Returns the parsed + * `URL` or `undefined` if any check fails. Shared by every boundary that + * receives a URL from a source we don't fully trust (IdP discovery response, + * operator env, SPA query param). + * + * Rejects: non-strings, non-http(s) schemes (blocks javascript:, data:, file:), + * empty hostnames, and URLs with userinfo (`user:pass@host`) because those + * leak credentials into logs and the address bar. + */ +export function parseHttpUrl(value: unknown): URL | undefined { + if (typeof value !== 'string' || value.trim() === '') return undefined + let u: URL + try { + u = new URL(value) + } catch { + return undefined + } + if (u.protocol !== 'https:' && u.protocol !== 'http:') return undefined + if (!u.hostname) return undefined + if (u.username || u.password) return undefined + return u +} + +export function isValidHttpUrl(value: unknown): value is string { + return parseHttpUrl(value) !== undefined +} + +@Injectable() +export class OidcMetadataService { + private readonly logger = new Logger(OidcMetadataService.name) + private cached?: Promise + + constructor( + private readonly httpService: HttpService, + private readonly configService: TypedConfigService, + ) { + const configured = this.configService.get('oidc.endSessionEndpoint') + if (configured && !isValidHttpUrl(configured)) { + this.logger.warn( + `OIDC_END_SESSION_ENDPOINT is set but not a valid http(s) URL; it will be ignored: ${configured}`, + ) + } + } + + getMetadata(): Promise { + if (!this.cached) { + const inflight = this.fetchOnce() + this.cached = inflight + inflight.catch(() => { + if (this.cached === inflight) { + this.cached = undefined + } + }) + } + return this.cached + } + + async getEndSessionState(): Promise { + let metadata: OidcMetadata + try { + metadata = await this.getMetadata() + } catch (err) { + this.logger.warn( + `OIDC discovery probe failed; treating as 'unknown' (fail-closed): ${err instanceof Error ? err.message : String(err)}`, + ) + return 'unknown' + } + if (typeof metadata !== 'object' || metadata === null) { + // Server returned non-JSON or empty body; the type cast at fetch time lied. + return 'unknown' + } + const endpoint = metadata.end_session_endpoint + if (endpoint === undefined) { + // Canonically missing — the IdP intentionally doesn't advertise it. + return 'absent' + } + // Field is present in the doc; only call it "valid" if it's a real URL. + return isValidHttpUrl(endpoint) ? 'present' : 'unknown' + } + + private async fetchOnce(): Promise { + // Strip trailing slash so the composed URL doesn't get a `//` segment some + // IdPs reject (Keycloak in strict mode, certain Auth0 tenants). + const issuer = this.configService.getOrThrow('oidc.issuer').replace(/\/+$/, '') + const discoveryUrl = `${issuer}/.well-known/openid-configuration` + return firstValueFrom( + this.httpService.get(discoveryUrl).pipe( + map((response) => response.data as OidcMetadata), + catchError((error) => { + throw new Error(`Failed to fetch OpenID configuration from ${discoveryUrl}: ${error.message}`) + }), + ), + ) + } +} diff --git a/apps/api/src/config/typed-config.module.ts b/apps/api/src/config/typed-config.module.ts index 5210d001c..8f0263d76 100644 --- a/apps/api/src/config/typed-config.module.ts +++ b/apps/api/src/config/typed-config.module.ts @@ -4,11 +4,13 @@ * SPDX-License-Identifier: AGPL-3.0 */ +import { HttpModule } from '@nestjs/axios' import { Global, Module, DynamicModule } from '@nestjs/common' import { ConfigModule as NestConfigModule, ConfigModuleOptions } from '@nestjs/config' import { TypedConfigService } from './typed-config.service' import { configuration } from './configuration' import { ConfigController } from './config.controller' +import { OidcMetadataService } from './oidc-metadata.service' @Global() @Module({ @@ -17,10 +19,11 @@ import { ConfigController } from './config.controller' isGlobal: true, load: [() => configuration], }), + HttpModule, ], controllers: [ConfigController], - providers: [TypedConfigService], - exports: [TypedConfigService], + providers: [TypedConfigService, OidcMetadataService], + exports: [TypedConfigService, OidcMetadataService], }) export class TypedConfigModule { static forRoot(options: Partial = {}): DynamicModule { @@ -30,9 +33,10 @@ export class TypedConfigModule { NestConfigModule.forRoot({ ...options, }), + HttpModule, ], - providers: [TypedConfigService], - exports: [TypedConfigService], + providers: [TypedConfigService, OidcMetadataService], + exports: [TypedConfigService, OidcMetadataService], } } } diff --git a/apps/api/src/config/typed-config.service.spec.ts b/apps/api/src/config/typed-config.service.spec.ts new file mode 100644 index 000000000..61e50a68a --- /dev/null +++ b/apps/api/src/config/typed-config.service.spec.ts @@ -0,0 +1,63 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { TypedConfigService } from './typed-config.service' + +describe('TypedConfigService', () => { + function buildService(values: Record) { + const configService = { + get: jest.fn((key: string) => values[key]), + } + return new TypedConfigService(configService as any) + } + + it('returns null when ClickHouse is not configured', () => { + const service = buildService({ + 'clickhouse.url': undefined, + 'clickhouse.host': undefined, + }) + + expect(service.getClickHouseConfig()).toBeNull() + }) + + it('uses a full ClickHouse URL when configured', () => { + const service = buildService({ + 'clickhouse.url': 'https://abc123.us-east-1.aws.clickhouse.cloud:8443', + 'clickhouse.host': 'ignored-host', + 'clickhouse.port': 443, + 'clickhouse.protocol': 'https', + 'clickhouse.username': 'reader', + 'clickhouse.password': 'redacted', + 'clickhouse.database': 'otel', + }) + + expect(service.getClickHouseConfig()).toEqual({ + url: 'https://abc123.us-east-1.aws.clickhouse.cloud:8443', + username: 'reader', + password: 'redacted', + database: 'otel', + }) + }) + + it('keeps the existing host, port, and protocol fallback', () => { + const service = buildService({ + 'clickhouse.url': undefined, + 'clickhouse.host': 'abc123.us-east-1.aws.clickhouse.cloud', + 'clickhouse.port': 8443, + 'clickhouse.protocol': 'https', + 'clickhouse.username': 'reader', + 'clickhouse.password': 'redacted', + 'clickhouse.database': 'otel', + }) + + expect(service.getClickHouseConfig()).toEqual({ + url: 'https://abc123.us-east-1.aws.clickhouse.cloud:8443', + username: 'reader', + password: 'redacted', + database: 'otel', + }) + }) +}) diff --git a/apps/api/src/config/typed-config.service.ts b/apps/api/src/config/typed-config.service.ts index 2fad7b236..d7334b66f 100644 --- a/apps/api/src/config/typed-config.service.ts +++ b/apps/api/src/config/typed-config.service.ts @@ -184,6 +184,16 @@ export class TypedConfigService { * @returns The ClickHouse configuration */ getClickHouseConfig() { + const url = this.get('clickhouse.url') + if (url) { + return { + url, + username: this.get('clickhouse.username'), + password: this.get('clickhouse.password'), + database: this.get('clickhouse.database'), + } + } + const host = this.get('clickhouse.host') if (!host) { return null diff --git a/apps/api/src/docker-registry/controllers/docker-registry.controller.ts b/apps/api/src/docker-registry/controllers/docker-registry.controller.ts deleted file mode 100644 index 2c7373d95..000000000 --- a/apps/api/src/docker-registry/controllers/docker-registry.controller.ts +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Controller, - Get, - Post, - Body, - Patch, - Param, - Delete, - UseGuards, - HttpCode, - ForbiddenException, - Query, -} from '@nestjs/common' -import { - ApiTags, - ApiOperation, - ApiResponse, - ApiOAuth2, - ApiHeader, - ApiParam, - ApiBearerAuth, - ApiQuery, -} from '@nestjs/swagger' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { DockerRegistryService } from '../services/docker-registry.service' -import { CreateDockerRegistryDto } from '../dto/create-docker-registry.dto' -import { UpdateDockerRegistryDto } from '../dto/update-docker-registry.dto' -import { DockerRegistryDto } from '../dto/docker-registry.dto' -import { RegistryPushAccessDto } from '../../sandbox/dto/registry-push-access-dto' -import { DockerRegistryAccessGuard } from '../guards/docker-registry-access.guard' -import { DockerRegistry } from '../decorators/docker-registry.decorator' -import { DockerRegistry as DockerRegistryEntity } from '../entities/docker-registry.entity' -import { CustomHeaders } from '../../common/constants/header.constants' -import { AuthContext } from '../../common/decorators/auth-context.decorator' -import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' -import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { SystemActionGuard } from '../../auth/system-action.guard' -import { RequiredSystemRole } from '../../common/decorators/required-role.decorator' -import { SystemRole } from '../../user/enums/system-role.enum' -import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../../audit/decorators/audit.decorator' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -import { RegistryType } from '../enums/registry-type.enum' -import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' - -@ApiTags('docker-registry') -@Controller('docker-registry') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@UseGuards(CombinedAuthGuard, SystemActionGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class DockerRegistryController { - constructor(private readonly dockerRegistryService: DockerRegistryService) {} - - @Post() - @ApiOperation({ - summary: 'Create registry', - operationId: 'createRegistry', - }) - @ApiResponse({ - status: 201, - description: 'The docker registry has been successfully created.', - type: DockerRegistryDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_REGISTRIES]) - @Audit({ - action: AuditAction.CREATE, - targetType: AuditTarget.DOCKER_REGISTRY, - targetIdFromResult: (result: DockerRegistryDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - name: req.body?.name, - username: req.body?.username, - password: req.body?.password ? MASKED_AUDIT_VALUE : undefined, - url: req.body?.url, - project: req.body?.project, - registryType: req.body?.registryType, - isDefault: req.body?.isDefault, - }), - }, - }) - async create( - @AuthContext() authContext: OrganizationAuthContext, - @Body() createDockerRegistryDto: CreateDockerRegistryDto, - ): Promise { - if (createDockerRegistryDto.registryType !== RegistryType.ORGANIZATION && authContext.role !== SystemRole.ADMIN) { - throw new ForbiddenException( - `Insufficient permissions for creating ${createDockerRegistryDto.registryType} registries`, - ) - } - - if (createDockerRegistryDto.isDefault && authContext.role !== SystemRole.ADMIN) { - throw new ForbiddenException('Insufficient permissions for setting a default registry') - } - - const dockerRegistry = await this.dockerRegistryService.create(createDockerRegistryDto, authContext.organizationId) - return DockerRegistryDto.fromDockerRegistry(dockerRegistry) - } - - @Get() - @ApiOperation({ - summary: 'List registries', - operationId: 'listRegistries', - }) - @ApiResponse({ - status: 200, - description: 'List of all docker registries', - type: [DockerRegistryDto], - }) - async findAll(@AuthContext() authContext: OrganizationAuthContext): Promise { - const dockerRegistries = await this.dockerRegistryService.findAll( - authContext.organizationId, - // only include registries manually created by the organization - RegistryType.ORGANIZATION, - ) - return dockerRegistries.map(DockerRegistryDto.fromDockerRegistry) - } - - @Get('registry-push-access') - @HttpCode(200) - @ApiOperation({ - summary: 'Get temporary registry access for pushing snapshots', - operationId: 'getTransientPushAccess', - }) - @ApiQuery({ - name: 'regionId', - required: false, - description: 'ID of the region where the snapshot will be available (defaults to organization default region)', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Temporary registry access has been generated', - type: RegistryPushAccessDto, - }) - async getTransientPushAccess( - @AuthContext() authContext: OrganizationAuthContext, - @Query('regionId') regionId?: string, - ): Promise { - return this.dockerRegistryService.getRegistryPushAccess(authContext.organizationId, authContext.userId, regionId) - } - - @Get(':id') - @ApiOperation({ - summary: 'Get registry', - operationId: 'getRegistry', - }) - @ApiParam({ - name: 'id', - description: 'ID of the docker registry', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'The docker registry', - type: DockerRegistryDto, - }) - @UseGuards(DockerRegistryAccessGuard) - async findOne(@DockerRegistry() registry: DockerRegistryEntity): Promise { - return DockerRegistryDto.fromDockerRegistry(registry) - } - - @Patch(':id') - @ApiOperation({ - summary: 'Update registry', - operationId: 'updateRegistry', - }) - @ApiParam({ - name: 'id', - description: 'ID of the docker registry', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'The docker registry has been successfully updated.', - type: DockerRegistryDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_REGISTRIES]) - @UseGuards(DockerRegistryAccessGuard) - @Audit({ - action: AuditAction.UPDATE, - targetType: AuditTarget.DOCKER_REGISTRY, - targetIdFromRequest: (req) => req.params.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - name: req.body?.name, - url: req.body?.url, - username: req.body?.username, - password: req.body?.password ? MASKED_AUDIT_VALUE : undefined, - project: req.body?.project, - }), - }, - }) - async update( - @Param('id') registryId: string, - @Body() updateDockerRegistryDto: UpdateDockerRegistryDto, - ): Promise { - const dockerRegistry = await this.dockerRegistryService.update(registryId, updateDockerRegistryDto) - return DockerRegistryDto.fromDockerRegistry(dockerRegistry) - } - - @Delete(':id') - @ApiOperation({ - summary: 'Delete registry', - operationId: 'deleteRegistry', - }) - @ApiParam({ - name: 'id', - description: 'ID of the docker registry', - type: 'string', - }) - @ApiResponse({ - status: 204, - description: 'The docker registry has been successfully deleted.', - }) - @HttpCode(204) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.DELETE_REGISTRIES]) - @UseGuards(DockerRegistryAccessGuard) - @Audit({ - action: AuditAction.DELETE, - targetType: AuditTarget.DOCKER_REGISTRY, - targetIdFromRequest: (req) => req.params.id, - }) - async remove(@Param('id') registryId: string): Promise { - return this.dockerRegistryService.remove(registryId) - } - - @Post(':id/set-default') - @ApiOperation({ - summary: 'Set default registry', - operationId: 'setDefaultRegistry', - }) - @ApiParam({ - name: 'id', - description: 'ID of the docker registry', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'The docker registry has been set as default.', - type: DockerRegistryDto, - }) - @RequiredSystemRole(SystemRole.ADMIN) - @UseGuards(DockerRegistryAccessGuard) - @Audit({ - action: AuditAction.SET_DEFAULT, - targetType: AuditTarget.DOCKER_REGISTRY, - targetIdFromRequest: (req) => req.params.id, - }) - async setDefault(@Param('id') registryId: string): Promise { - const dockerRegistry = await this.dockerRegistryService.setDefault(registryId) - return DockerRegistryDto.fromDockerRegistry(dockerRegistry) - } -} diff --git a/apps/api/src/docker-registry/decorators/docker-registry.decorator.ts b/apps/api/src/docker-registry/decorators/docker-registry.decorator.ts deleted file mode 100644 index ebdf9bd52..000000000 --- a/apps/api/src/docker-registry/decorators/docker-registry.decorator.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { createParamDecorator, ExecutionContext } from '@nestjs/common' - -export const DockerRegistry = createParamDecorator((data: unknown, ctx: ExecutionContext) => { - const request = ctx.switchToHttp().getRequest() - return request.dockerRegistry -}) diff --git a/apps/api/src/docker-registry/docker-registry.module.ts b/apps/api/src/docker-registry/docker-registry.module.ts deleted file mode 100644 index 03673a037..000000000 --- a/apps/api/src/docker-registry/docker-registry.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' -import { DockerRegistry } from './entities/docker-registry.entity' -import { DockerRegistryService } from './services/docker-registry.service' -import { DockerRegistryController } from './controllers/docker-registry.controller' -import { HttpModule } from '@nestjs/axios' -import { DockerRegistryProvider } from './providers/docker-registry.provider' -import { DOCKER_REGISTRY_PROVIDER } from './providers/docker-registry.provider.interface' -import { OrganizationModule } from '../organization/organization.module' -import { RegionModule } from '../region/region.module' - -@Module({ - imports: [OrganizationModule, RegionModule, TypeOrmModule.forFeature([DockerRegistry]), HttpModule], - controllers: [DockerRegistryController], - providers: [ - { - provide: DOCKER_REGISTRY_PROVIDER, - useClass: DockerRegistryProvider, - }, - DockerRegistryService, - ], - exports: [DockerRegistryService], -}) -export class DockerRegistryModule {} diff --git a/apps/api/src/docker-registry/dto/create-docker-registry-internal.dto.ts b/apps/api/src/docker-registry/dto/create-docker-registry-internal.dto.ts deleted file mode 100644 index 8922c7f34..000000000 --- a/apps/api/src/docker-registry/dto/create-docker-registry-internal.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { RegistryType } from '../enums/registry-type.enum' - -export class CreateDockerRegistryInternalDto { - name: string - url: string - username: string - password: string - project?: string - registryType: RegistryType - isDefault?: boolean - regionId?: string | null -} diff --git a/apps/api/src/docker-registry/dto/create-docker-registry.dto.ts b/apps/api/src/docker-registry/dto/create-docker-registry.dto.ts deleted file mode 100644 index c14ac7e19..000000000 --- a/apps/api/src/docker-registry/dto/create-docker-registry.dto.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsString, IsUrl, IsEnum, IsOptional, IsBoolean } from 'class-validator' -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { RegistryType } from './../../docker-registry/enums/registry-type.enum' - -@ApiSchema({ name: 'CreateDockerRegistry' }) -export class CreateDockerRegistryDto { - @ApiProperty({ description: 'Registry name' }) - @IsString() - name: string - - @ApiProperty({ description: 'Registry URL' }) - @IsUrl() - url: string - - @ApiProperty({ description: 'Registry username' }) - @IsString() - username: string - - @ApiProperty({ description: 'Registry password' }) - @IsString() - password: string - - @ApiPropertyOptional({ description: 'Registry project' }) - @IsString() - @IsOptional() - project?: string - - @ApiProperty({ - description: 'Registry type', - enum: RegistryType, - default: RegistryType.ORGANIZATION, - }) - @IsEnum(RegistryType) - registryType: RegistryType - - @ApiPropertyOptional({ description: 'Set as default registry' }) - @IsBoolean() - @IsOptional() - isDefault?: boolean -} diff --git a/apps/api/src/docker-registry/dto/docker-registry.dto.ts b/apps/api/src/docker-registry/dto/docker-registry.dto.ts deleted file mode 100644 index 76b2b726b..000000000 --- a/apps/api/src/docker-registry/dto/docker-registry.dto.ts +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { RegistryType } from './../../docker-registry/enums/registry-type.enum' -import { DockerRegistry } from '../entities/docker-registry.entity' - -@ApiSchema({ name: 'DockerRegistry' }) -export class DockerRegistryDto { - @ApiProperty({ - description: 'Registry ID', - example: '123e4567-e89b-12d3-a456-426614174000', - }) - id: string - - @ApiProperty({ - description: 'Registry name', - example: 'My Docker Hub', - }) - name: string - - @ApiProperty({ - description: 'Registry URL', - example: 'https://registry.hub.docker.com', - }) - url: string - - @ApiProperty({ - description: 'Registry username', - example: 'username', - }) - username: string - - @ApiProperty({ - description: 'Registry project', - example: 'my-project', - }) - project: string - - @ApiProperty({ - description: 'Registry type', - enum: RegistryType, - example: RegistryType.INTERNAL, - }) - registryType: RegistryType - - @ApiProperty({ - description: 'Creation timestamp', - example: '2024-01-31T12:00:00Z', - }) - createdAt: Date - - @ApiProperty({ - description: 'Last update timestamp', - example: '2024-01-31T12:00:00Z', - }) - updatedAt: Date - - static fromDockerRegistry(dockerRegistry: DockerRegistry): DockerRegistryDto { - const dto: DockerRegistryDto = { - id: dockerRegistry.id, - name: dockerRegistry.name, - url: dockerRegistry.url, - username: dockerRegistry.username, - project: dockerRegistry.project, - registryType: dockerRegistry.registryType, - createdAt: dockerRegistry.createdAt, - updatedAt: dockerRegistry.updatedAt, - } - - return dto - } -} diff --git a/apps/api/src/docker-registry/dto/update-docker-registry.dto.ts b/apps/api/src/docker-registry/dto/update-docker-registry.dto.ts deleted file mode 100644 index 8c1a636f1..000000000 --- a/apps/api/src/docker-registry/dto/update-docker-registry.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsString, IsOptional, IsUrl } from 'class-validator' -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'UpdateDockerRegistry' }) -export class UpdateDockerRegistryDto { - @ApiProperty({ description: 'Registry name' }) - @IsString() - name: string - - @ApiProperty({ description: 'Registry URL' }) - @IsUrl() - url: string - - @ApiProperty({ description: 'Registry username' }) - @IsString() - username: string - - @ApiPropertyOptional({ description: 'Registry password' }) - @IsString() - @IsOptional() - password?: string - - @ApiPropertyOptional({ description: 'Registry project' }) - @IsString() - @IsOptional() - project?: string -} diff --git a/apps/api/src/docker-registry/entities/docker-registry.entity.ts b/apps/api/src/docker-registry/entities/docker-registry.entity.ts deleted file mode 100644 index 4fb441329..000000000 --- a/apps/api/src/docker-registry/entities/docker-registry.entity.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { RegistryType } from './../../docker-registry/enums/registry-type.enum' -import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' - -@Entity() -@Index(['organizationId', 'registryType']) -@Index(['region', 'registryType']) -@Index(['registryType', 'isDefault']) -export class DockerRegistry { - @PrimaryGeneratedColumn('uuid') - id: string - - @Column() - name: string - - @Column() - url: string - - @Column() - username: string - - @Column() - password: string - - @Column({ default: false }) - isDefault: boolean - - @Column({ default: false }) - isFallback: boolean - - @Column({ default: '' }) - project: string - - @Column({ nullable: true, type: 'uuid' }) - organizationId?: string - - @Column({ nullable: true }) - region: string | null - - @Column({ - type: 'enum', - enum: RegistryType, - default: RegistryType.INTERNAL, - }) - registryType: RegistryType - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date -} diff --git a/apps/api/src/docker-registry/enums/registry-type.enum.ts b/apps/api/src/docker-registry/enums/registry-type.enum.ts deleted file mode 100644 index d85aa229c..000000000 --- a/apps/api/src/docker-registry/enums/registry-type.enum.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum RegistryType { - INTERNAL = 'internal', // Used for internal snapshots - ORGANIZATION = 'organization', - TRANSIENT = 'transient', - BACKUP = 'backup', -} diff --git a/apps/api/src/docker-registry/guards/docker-registry-access.guard.ts b/apps/api/src/docker-registry/guards/docker-registry-access.guard.ts deleted file mode 100644 index cfab1e045..000000000 --- a/apps/api/src/docker-registry/guards/docker-registry-access.guard.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, CanActivate, ExecutionContext, NotFoundException, ForbiddenException } from '@nestjs/common' -import { DockerRegistryService } from '../services/docker-registry.service' -import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { SystemRole } from '../../user/enums/system-role.enum' -import { RegistryType } from '../enums/registry-type.enum' - -@Injectable() -export class DockerRegistryAccessGuard implements CanActivate { - constructor(private readonly dockerRegistryService: DockerRegistryService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() - const dockerRegistryId: string = request.params.dockerRegistryId || request.params.registryId || request.params.id - - // TODO: initialize authContext safely - const authContext: OrganizationAuthContext = request.user - - try { - const dockerRegistry = await this.dockerRegistryService.findOneOrFail(dockerRegistryId) - if (authContext.role !== SystemRole.ADMIN && dockerRegistry.organizationId !== authContext.organizationId) { - throw new ForbiddenException('Request organization ID does not match resource organization ID') - } - if (authContext.role !== SystemRole.ADMIN && dockerRegistry.registryType !== RegistryType.ORGANIZATION) { - // only allow access to registries manually created by the organization - throw new ForbiddenException(`Requested registry is not type "${RegistryType.ORGANIZATION}"`) - } - request.dockerRegistry = dockerRegistry - return true - } catch (error) { - throw new NotFoundException(`Docker registry with ID ${dockerRegistryId} not found`) - } - } -} diff --git a/apps/api/src/docker-registry/providers/docker-registry.provider.interface.ts b/apps/api/src/docker-registry/providers/docker-registry.provider.interface.ts deleted file mode 100644 index 7e2b7b0fb..000000000 --- a/apps/api/src/docker-registry/providers/docker-registry.provider.interface.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const DOCKER_REGISTRY_PROVIDER = 'DOCKER_REGISTRY_PROVIDER' - -export interface IDockerRegistryProvider { - createRobotAccount( - url: string, - auth: { username: string; password: string }, - robotConfig: { - name: string - description: string - duration: number - level: string - permissions: Array<{ - kind: string - namespace: string - access: Array<{ resource: string; action: string }> - }> - }, - ): Promise<{ name: string; secret: string }> - - deleteArtifact( - baseUrl: string, - auth: { username: string; password: string }, - params: { project: string; repository: string; tag: string }, - ): Promise -} diff --git a/apps/api/src/docker-registry/providers/docker-registry.provider.ts b/apps/api/src/docker-registry/providers/docker-registry.provider.ts deleted file mode 100644 index aeb0f6b19..000000000 --- a/apps/api/src/docker-registry/providers/docker-registry.provider.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable } from '@nestjs/common' -import { HttpService } from '@nestjs/axios' -import { firstValueFrom } from 'rxjs' -import { IDockerRegistryProvider } from './docker-registry.provider.interface' - -@Injectable() -export class DockerRegistryProvider implements IDockerRegistryProvider { - constructor(private readonly httpService: HttpService) {} - - async createRobotAccount( - url: string, - auth: { username: string; password: string }, - robotConfig: { - name: string - description: string - duration: number - level: string - permissions: Array<{ - kind: string - namespace: string - access: Array<{ resource: string; action: string }> - }> - }, - ): Promise<{ name: string; secret: string }> { - const response = await firstValueFrom(this.httpService.post(url, robotConfig, { auth })) - return { - name: response.data.name, - secret: response.data.secret, - } - } - - async deleteArtifact( - baseUrl: string, - auth: { username: string; password: string }, - params: { project: string; repository: string; tag: string }, - ): Promise { - const url = `${baseUrl}/api/v2.0/projects/${params.project}/repositories/${params.repository}/artifacts/${params.tag}` - - try { - await firstValueFrom(this.httpService.delete(url, { auth })) - } catch (error) { - if (error.response?.status === 404) { - return // Artifact not found, consider it a success - } - throw error - } - } -} diff --git a/apps/api/src/docker-registry/providers/mock-docker-registry.provider.ts b/apps/api/src/docker-registry/providers/mock-docker-registry.provider.ts deleted file mode 100644 index f7ad2b1c6..000000000 --- a/apps/api/src/docker-registry/providers/mock-docker-registry.provider.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IDockerRegistryProvider } from './docker-registry.provider.interface' - -export class MockDockerRegistryProvider implements IDockerRegistryProvider { - async createRobotAccount(): Promise<{ name: string; secret: string }> { - return { - name: 'mock-robot', - secret: 'mock-secret', - } - } - - async deleteArtifact(): Promise { - return Promise.resolve() - } -} diff --git a/apps/api/src/docker-registry/services/docker-registry.service.ts b/apps/api/src/docker-registry/services/docker-registry.service.ts deleted file mode 100644 index 8ecf9c083..000000000 --- a/apps/api/src/docker-registry/services/docker-registry.service.ts +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ForbiddenException, Inject, Injectable, Logger, NotFoundException } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { EntityManager, FindOptionsWhere, IsNull, Repository } from 'typeorm' -import { DockerRegistry } from '../entities/docker-registry.entity' -import { CreateDockerRegistryInternalDto } from '../dto/create-docker-registry-internal.dto' -import { UpdateDockerRegistryDto } from '../dto/update-docker-registry.dto' -import { ApiOAuth2 } from '@nestjs/swagger' -import { RegistryPushAccessDto } from '../../sandbox/dto/registry-push-access-dto' -import { - DOCKER_REGISTRY_PROVIDER, - IDockerRegistryProvider, -} from './../../docker-registry/providers/docker-registry.provider.interface' -import { RegistryType } from './../../docker-registry/enums/registry-type.enum' -import { parseDockerImage, checkDockerfileHasRegistryPrefix } from '../../common/utils/docker-image.util' -import axios from 'axios' -import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' -import { RegionEvents } from '../../region/constants/region-events.constant' -import { RegionCreatedEvent } from '../../region/events/region-created.event' -import { RegionDeletedEvent } from '../../region/events/region-deleted.event' -import { RegionService } from '../../region/services/region.service' -import { - RegionSnapshotManagerCredsRegeneratedEvent, - RegionSnapshotManagerUpdatedEvent, -} from '../../region/events/region-snapshot-manager-creds.event' - -const AXIOS_TIMEOUT_MS = 3000 -const DOCKER_HUB_REGISTRY = 'registry-1.docker.io' -const DOCKER_HUB_URL = 'docker.io' - -/** - * Normalizes Docker Hub URLs to 'docker.io' for storage. - * Empty URLs are assumed to be Docker Hub. - */ -function normalizeRegistryUrl(url: string): string { - if (!url || url.trim() === '' || url.toLowerCase().includes('docker.io')) { - return DOCKER_HUB_URL - } - // Strip trailing slashes for consistent matching - return url.trim().replace(/\/+$/, '') -} - -export interface ImageDetails { - digest: string - sizeGB: number - entrypoint: string[] - cmd: string[] - env: string[] - workingDir?: string - user?: string -} - -@Injectable() -@ApiOAuth2(['openid', 'profile', 'email']) -export class DockerRegistryService { - private readonly logger = new Logger(DockerRegistryService.name) - - constructor( - @InjectRepository(DockerRegistry) - private readonly dockerRegistryRepository: Repository, - @Inject(DOCKER_REGISTRY_PROVIDER) - private readonly dockerRegistryProvider: IDockerRegistryProvider, - private readonly regionService: RegionService, - ) {} - - async create( - createDto: CreateDockerRegistryInternalDto, - organizationId?: string, - isFallback = false, - entityManager?: EntityManager, - ): Promise { - const repository = entityManager ? entityManager.getRepository(DockerRegistry) : this.dockerRegistryRepository - - // set some limit to the number of registries - if (organizationId) { - const registries = await repository.find({ - where: { organizationId }, - }) - if (registries.length >= 100) { - throw new ForbiddenException('You have reached the maximum number of registries') - } - } - - const registry = repository.create({ - ...createDto, - url: normalizeRegistryUrl(createDto.url), - region: createDto.regionId, - organizationId, - isFallback, - }) - return repository.save(registry) - } - - async findAll(organizationId: string, registryType: RegistryType): Promise { - return this.dockerRegistryRepository.find({ - where: { organizationId, registryType }, - order: { - createdAt: 'DESC', - }, - }) - } - - async findOne(registryId: string): Promise { - return this.dockerRegistryRepository.findOne({ - where: { id: registryId }, - }) - } - - async findOneOrFail(registryId: string): Promise { - return this.dockerRegistryRepository.findOneOrFail({ - where: { id: registryId }, - }) - } - - async update(registryId: string, updateDto: UpdateDockerRegistryDto): Promise { - const registry = await this.dockerRegistryRepository.findOne({ - where: { id: registryId }, - }) - - if (!registry) { - throw new NotFoundException(`Docker registry with ID ${registryId} not found`) - } - - registry.name = updateDto.name - registry.url = normalizeRegistryUrl(updateDto.url) - registry.username = updateDto.username - if (updateDto.password) { - registry.password = updateDto.password - } - registry.project = updateDto.project - - return this.dockerRegistryRepository.save(registry) - } - - async remove(registryId: string): Promise { - const registry = await this.dockerRegistryRepository.findOne({ - where: { id: registryId }, - }) - - if (!registry) { - throw new NotFoundException(`Docker registry with ID ${registryId} not found`) - } - - await this.dockerRegistryRepository.remove(registry) - } - - async setDefault(registryId: string): Promise { - const registry = await this.dockerRegistryRepository.findOne({ - where: { id: registryId }, - }) - - if (!registry) { - throw new NotFoundException(`Docker registry with ID ${registryId} not found`) - } - - await this.unsetDefaultRegistry() - - registry.isDefault = true - return this.dockerRegistryRepository.save(registry) - } - - private async unsetDefaultRegistry(): Promise { - await this.dockerRegistryRepository.update({ isDefault: true }, { isDefault: false }) - } - - /** - * Returns an available internal registry for storing snapshots. - * - * If a snapshot manager _is_ configured for the region identified by the provided _regionId_, only an internal registry that matches the region snapshot manager can be returned. - * If no matching internal registry is found, _null_ will be returned. - * - * If a snapshot manager _is not_ configured for the provided region, the default internal registry will be returned (if available). - * If no default internal registry is found, _null_ will be returned. - * - * @param regionId - The ID of the region. - */ - async getAvailableInternalRegistry(regionId: string): Promise { - const region = await this.regionService.findOne(regionId) - if (!region) { - return null - } - - if (region.snapshotManagerUrl) { - return this.dockerRegistryRepository.findOne({ - where: { region: regionId, registryType: RegistryType.INTERNAL }, - }) - } - - return this.dockerRegistryRepository.findOne({ - where: { isDefault: true, registryType: RegistryType.INTERNAL }, - }) - } - - /** - * Returns an available transient registry for pushing snapshots. - * - * If a snapshot manager _is_ configured for the region identified by the provided _regionId_, only a transient registry that matches the region snapshot manager can be returned. - * If no matching transient registry is found, _null_ will be returned. - * - * If a snapshot manager _is not_ configured for the provided region or no region is provided, the default transient registry will be returned (if available). - * If no default transient registry is found, _null_ will be returned. - * - * @param regionId - (Optional) The ID of the region. - */ - async getAvailableTransientRegistry(regionId?: string): Promise { - if (regionId) { - const region = await this.regionService.findOne(regionId) - if (!region) { - return null - } - - if (region.snapshotManagerUrl) { - return this.dockerRegistryRepository.findOne({ - where: { region: regionId, registryType: RegistryType.TRANSIENT }, - }) - } - } - - return this.dockerRegistryRepository.findOne({ - where: { isDefault: true, registryType: RegistryType.TRANSIENT }, - }) - } - - async getDefaultDockerHubRegistry(): Promise { - return this.dockerRegistryRepository.findOne({ - where: { - organizationId: IsNull(), - registryType: RegistryType.INTERNAL, - url: DOCKER_HUB_URL, - project: '', - }, - }) - } - - /** - * Returns an available backup registry for storing snapshots. - * - * If a snapshot manager _is_ configured for the region identified by the provided _preferredRegionId_, only a backup registry that matches the region snapshot manager can be returned. - * If no matching backup registry is found, _null_ will be returned. - * - * If a snapshot manager _is not_ configured for the provided region, a backup registry in the preferred region will be returned (if available). - * If no backup registry is found in the preferred region, a fallback backup registry will be returned (if available). - * If no fallback backup registry is found, _null_ will be returned. - * - * @param preferredRegionId - The ID of the preferred region. - */ - async getAvailableBackupRegistry(preferredRegionId: string): Promise { - const region = await this.regionService.findOne(preferredRegionId) - if (!region) { - return null - } - - if (region.snapshotManagerUrl) { - return this.dockerRegistryRepository.findOne({ - where: { region: preferredRegionId, registryType: RegistryType.BACKUP }, - }) - } - - const registries = await this.dockerRegistryRepository.find({ - where: { registryType: RegistryType.BACKUP, isDefault: true }, - }) - - if (registries.length === 0) { - return null - } - - // Filter registries by preferred region - const preferredRegionRegistries = registries.filter((registry) => registry.region === preferredRegionId) - - // If we have registries in the preferred region, randomly select one - if (preferredRegionRegistries.length > 0) { - const randomIndex = Math.floor(Math.random() * preferredRegionRegistries.length) - return preferredRegionRegistries[randomIndex] - } - - // If no registry found in preferred region, try to find a fallback registry - const fallbackRegistries = registries.filter((registry) => registry.isFallback === true) - - if (fallbackRegistries.length > 0) { - const randomIndex = Math.floor(Math.random() * fallbackRegistries.length) - return fallbackRegistries[randomIndex] - } - - // If no fallback registry found either, throw an error - throw new Error('No backup registry available') - } - - /** - * Returns an internal registry that matches the snapshot ref. - * - * If no matching internal registry is found, _null_ will be returned. - * - * @param ref - The snapshot ref. - * @param regionId - The ID of the region which needs access to the internal registry. - */ - async findInternalRegistryBySnapshotRef(ref: string, regionId: string): Promise { - const region = await this.regionService.findOne(regionId) - if (!region) { - return null - } - - let registries: DockerRegistry[] - - if (region.snapshotManagerUrl) { - registries = await this.dockerRegistryRepository.find({ - where: { - region: regionId, - registryType: RegistryType.INTERNAL, - }, - }) - } else { - registries = await this.dockerRegistryRepository.find({ - where: { - organizationId: IsNull(), - registryType: RegistryType.INTERNAL, - }, - }) - } - - return this.findRegistryByUrlMatch(registries, ref) - } - - /** - * Returns a source registry that matches the snapshot image name and can be used to pull the image. - * - * If no matching source registry is found, _null_ will be returned. - * - * @param imageName - The user-provided image. - * @param regionId - The ID of the region which needs access to the source registry. - */ - async findSourceRegistryBySnapshotImageName( - imageName: string, - regionId: string, - organizationId?: string, - ): Promise { - const region = await this.regionService.findOne(regionId) - if (!region) { - return null - } - - const whereCondition: FindOptionsWhere[] = [] - - if (region.organizationId) { - // registries manually added by the organization - whereCondition.push({ - organizationId: region.organizationId, - registryType: RegistryType.ORGANIZATION, - }) - } - - if (organizationId) { - whereCondition.push({ - organizationId: organizationId, - registryType: RegistryType.ORGANIZATION, - }) - } - - if (region.snapshotManagerUrl) { - // internal registry associated with region snapshot manager - whereCondition.push({ - region: regionId, - registryType: RegistryType.INTERNAL, - }) - } else { - // shared internal registries - whereCondition.push({ - organizationId: IsNull(), - registryType: RegistryType.INTERNAL, - }) - } - - const registries = await this.dockerRegistryRepository.find({ - where: whereCondition, - }) - - // Prioritize ORGANIZATION registries over others - // This ensures user-configured credentials take precedence over shared internal ones - const priority: Partial> = { - [RegistryType.ORGANIZATION]: 0, - } - const sortedRegistries = [...registries].sort( - (a, b) => (priority[a.registryType] ?? 1) - (priority[b.registryType] ?? 1), - ) - - return this.findRegistryByUrlMatch(sortedRegistries, imageName) - } - - /** - * Returns a transient registry that matches the snapshot image name. - * - * If no matching transient registry is found, _null_ will be returned. - * - * @param imageName - The user-provided image. - * @param regionId - The ID of the region which needs access to the transient registry. - */ - async findTransientRegistryBySnapshotImageName(imageName: string, regionId: string): Promise { - const region = await this.regionService.findOne(regionId) - if (!region) { - return null - } - - let registries: DockerRegistry[] - - if (region.snapshotManagerUrl) { - registries = await this.dockerRegistryRepository.find({ - where: { - region: regionId, - registryType: RegistryType.TRANSIENT, - }, - }) - } else { - registries = await this.dockerRegistryRepository.find({ - where: { - organizationId: IsNull(), - registryType: RegistryType.TRANSIENT, - }, - }) - } - - return this.findRegistryByUrlMatch(registries, imageName) - } - - async getRegistryPushAccess( - organizationId: string, - userId: string, - regionId?: string, - ): Promise { - const transientRegistry = await this.getAvailableTransientRegistry(regionId) - if (!transientRegistry) { - throw new Error('No default transient registry configured') - } - - const uniqueId = crypto.randomUUID().replace(/-/g, '').slice(0, 12) - const robotName = `temp-push-robot-${uniqueId}` - const expiresAt = new Date() - expiresAt.setHours(expiresAt.getHours() + 1) // Token valid for 1 hour - - const url = this.getRegistryUrl(transientRegistry) + '/api/v2.0/robots' - - try { - const response = await this.dockerRegistryProvider.createRobotAccount( - url, - { - username: transientRegistry.username, - password: transientRegistry.password, - }, - { - name: robotName, - description: `Temporary push access for user ${userId} in organization ${organizationId}`, - duration: 3600, - level: 'project', - permissions: [ - { - kind: 'project', - namespace: transientRegistry.project, - access: [{ resource: 'repository', action: 'push' }], - }, - ], - }, - ) - - return { - username: response.name, - secret: response.secret, - registryId: transientRegistry.id, - registryUrl: new URL(url).host, - project: transientRegistry.project, - expiresAt: expiresAt.toISOString(), - } - } catch (error) { - let errorMessage = `Failed to generate push token: ${error.message}` - if (error.response) { - errorMessage += ` - ${error.response.data.message || error.response.statusText}` - } - throw new Error(errorMessage) - } - } - - async removeImage(imageName: string, registryId: string): Promise { - const registry = await this.findOne(registryId) - if (!registry) { - throw new Error('Registry not found') - } - - const parsedImage = parseDockerImage(imageName) - if (!parsedImage.project) { - throw new Error('Invalid image name format. Expected: [registry]/project/repository[:tag]') - } - - try { - await this.dockerRegistryProvider.deleteArtifact( - this.getRegistryUrl(registry), - { - username: registry.username, - password: registry.password, - }, - { - project: parsedImage.project, - repository: parsedImage.repository, - tag: parsedImage.tag, - }, - ) - } catch (error) { - const message = error.response?.data?.message || error.message - throw new Error(`Failed to remove image ${imageName}: ${message}`) - } - } - - getRegistryUrl(registry: DockerRegistry): string { - // Dev mode - if (registry.url.startsWith('localhost:') || registry.url.startsWith('registry:')) { - return `http://${registry.url}` - } - - if (registry.url.startsWith('localhost') || registry.url.startsWith('127.0.0.1')) { - return `http://${registry.url}` - } - - return registry.url.startsWith('http') ? registry.url : `https://${registry.url}` - } - - public async findRegistryByImageName( - imageName: string, - regionId: string, - organizationId?: string, - ): Promise { - // Parse the image to extract potential registry hostname - const parsedImage = parseDockerImage(imageName) - - if (parsedImage.registry) { - // Image has registry prefix, try to find matching registry in database first - const registry = await this.findSourceRegistryBySnapshotImageName(imageName, regionId, organizationId) - if (registry) { - return registry - } - // Not found in database, create temporary registry config for public access - return this.createTemporaryRegistryConfig(parsedImage.registry) - } else { - // Image has no registry prefix (e.g., "alpine:3.21") - // Create temporary Docker Hub config - return this.createTemporaryRegistryConfig('docker.io') - } - } - - /** - * Finds a registry with a URL that matches the start of the target string. - * - * @param registries - The list of registries to search. - * @param targetString - The string to match against registry URLs. - * @returns The matching registry, or null if no match is found. - */ - private findRegistryByUrlMatch(registries: DockerRegistry[], targetString: string): DockerRegistry | null { - for (const registry of registries) { - const strippedUrl = registry.url.replace(/^(https?:\/\/)/, '').replace(/\/+$/, '') - if (targetString.startsWith(strippedUrl)) { - // Ensure match is at a proper boundary (followed by '/', ':', or end-of-string) - // to prevent "registry.depot.dev" from matching "registry.depot.dev-evil.com/..." - const nextChar = targetString[strippedUrl.length] - if (nextChar === undefined || nextChar === '/' || nextChar === ':') { - return registry - } - } - } - return null - } - - private createTemporaryRegistryConfig(registryOrigin: string): DockerRegistry { - const registry = new DockerRegistry() - registry.id = `temp-${registryOrigin}` - registry.name = `Temporary ${registryOrigin}` - registryOrigin = registryOrigin.replace(/^(https?:\/\/)/, '') - registry.url = `https://${registryOrigin}` - registry.username = '' - registry.password = '' - registry.project = '' - registry.isDefault = false - registry.registryType = RegistryType.INTERNAL - return registry - } - - private async getDockerHubToken(repository: string): Promise { - try { - const tokenUrl = `https://auth.docker.io/token?service=${DOCKER_HUB_REGISTRY}&scope=repository:${repository}:pull` - const response = await axios.get(tokenUrl, { timeout: 10000 }) - return response.data.token - } catch (error) { - this.logger.warn(`Failed to get Docker Hub token: ${error.message}`) - return null - } - } - - private async deleteRepositoryWithPrefix( - repository: string, - prefix: string, - registry: DockerRegistry, - ): Promise { - const registryUrl = this.getRegistryUrl(registry) - const encodedCredentials = Buffer.from(`${registry.username}:${registry.password}`).toString('base64') - const repoPath = `${registry.project}/${prefix}${repository}` - - try { - // Step 1: List all tags in the repository - const tagsUrl = `${registryUrl}/v2/${repoPath}/tags/list` - - const tagsResponse = await axios({ - method: 'get', - url: tagsUrl, - headers: { - Authorization: `Basic ${encodedCredentials}`, - }, - validateStatus: (status) => status < 500, - timeout: AXIOS_TIMEOUT_MS, - }) - - if (tagsResponse.status === 404) { - return - } - - if (tagsResponse.status >= 300) { - this.logger.error(`Error listing tags in repository ${repoPath}: ${tagsResponse.statusText}`) - throw new Error(`Failed to list tags in repository ${repoPath}: ${tagsResponse.statusText}`) - } - - const tags = tagsResponse.data.tags || [] - - if (tags.length === 0) { - this.logger.debug(`Repository ${repoPath} has no tags to delete`) - return - } - - if (tags.length > 500) { - this.logger.warn(`Repository ${repoPath} has more than 500 tags, skipping cleanup`) - return - } - - // Step 2: Delete each tag - for (const tag of tags) { - try { - // Get the digest for this tag - const manifestUrl = `${registryUrl}/v2/${repoPath}/manifests/${tag}` - - const manifestResponse = await axios({ - method: 'head', - url: manifestUrl, - headers: { - Authorization: `Basic ${encodedCredentials}`, - Accept: 'application/vnd.docker.distribution.manifest.v2+json', - }, - validateStatus: (status) => status < 500, - timeout: AXIOS_TIMEOUT_MS, - }) - - if (manifestResponse.status >= 300) { - this.logger.warn(`Couldn't get manifest for tag ${tag}: ${manifestResponse.statusText}`) - continue - } - - const digest = manifestResponse.headers['docker-content-digest'] - if (!digest) { - this.logger.warn(`Docker content digest not found for tag ${tag}`) - continue - } - - // Delete the manifest - const deleteUrl = `${registryUrl}/v2/${repoPath}/manifests/${digest}` - - const deleteResponse = await axios({ - method: 'delete', - url: deleteUrl, - headers: { - Authorization: `Basic ${encodedCredentials}`, - }, - validateStatus: (status) => status < 500, - timeout: AXIOS_TIMEOUT_MS, - }) - - if (deleteResponse.status < 300) { - this.logger.debug(`Deleted tag ${tag} from repository ${repoPath}`) - } else { - this.logger.warn(`Failed to delete tag ${tag}: ${deleteResponse.statusText}`) - } - } catch (error) { - this.logger.warn(`Exception when deleting tag ${tag}: ${error.message}`) - // Continue with other tags - } - } - - this.logger.debug(`Repository ${repoPath} cleanup completed`) - } catch (error) { - this.logger.error(`Exception when deleting repository ${repoPath}: ${error.message}`) - throw error - } - } - - async deleteSandboxRepository(repository: string, registry: DockerRegistry): Promise { - try { - // Delete both backup and snapshot repositories - necessary due to renaming - await this.deleteRepositoryWithPrefix(repository, 'backup-', registry) - await this.deleteRepositoryWithPrefix(repository, 'snapshot-', registry) - } catch (error) { - this.logger.error(`Failed to delete repositories for ${repository}: ${error.message}`) - throw error - } - } - - async deleteBackupImageFromRegistry(imageName: string, registry: DockerRegistry): Promise { - const parsedImage = parseDockerImage(imageName) - if (!parsedImage.project || !parsedImage.tag) { - throw new Error('Invalid image name format. Expected: [registry]/project/repository:tag') - } - - const registryUrl = this.getRegistryUrl(registry) - const repoPath = `${parsedImage.project}/${parsedImage.repository}` - - // First, get the digest for the tag using the manifests endpoint - const manifestUrl = `${registryUrl}/v2/${repoPath}/manifests/${parsedImage.tag}` - const encodedCredentials = Buffer.from(`${registry.username}:${registry.password}`).toString('base64') - - try { - // Get the digest from the headers - const manifestResponse = await axios({ - method: 'head', // Using HEAD request to only fetch headers - url: manifestUrl, - headers: { - Authorization: `Basic ${encodedCredentials}`, - Accept: 'application/vnd.docker.distribution.manifest.v2+json', - }, - validateStatus: (status) => status < 500, - timeout: AXIOS_TIMEOUT_MS, - }) - - if (manifestResponse.status >= 300) { - this.logger.error(`Error getting manifest for image ${imageName}: ${manifestResponse.statusText}`) - throw new Error(`Failed to get manifest for image ${imageName}: ${manifestResponse.statusText}`) - } - - // Extract the digest from headers - const digest = manifestResponse.headers['docker-content-digest'] - if (!digest) { - throw new Error(`Docker content digest not found for image ${imageName}`) - } - - // Now delete the image using the digest - const deleteUrl = `${registryUrl}/v2/${repoPath}/manifests/${digest}` - - const deleteResponse = await axios({ - method: 'delete', - url: deleteUrl, - headers: { - Authorization: `Basic ${encodedCredentials}`, - }, - validateStatus: (status) => status < 500, - timeout: AXIOS_TIMEOUT_MS, - }) - - if (deleteResponse.status < 300) { - this.logger.debug(`Image ${imageName} removed from the registry`) - return - } - - this.logger.error(`Error removing image ${imageName} from registry: ${deleteResponse.statusText}`) - throw new Error(`Failed to remove image ${imageName} from registry: ${deleteResponse.statusText}`) - } catch (error) { - this.logger.error(`Exception when deleting image ${imageName}: ${error.message}`) - throw error - } - } - - /** - * Gets source registries for building a Docker image from a Dockerfile - * If the Dockerfile has images with registry prefixes, returns all user registries - * - * @param dockerfileContent - The Dockerfile content - * @param organizationId - The organization ID - * @returns Array of source registries (private registries + default Docker Hub) - */ - async getSourceRegistriesForDockerfile(dockerfileContent: string, organizationId: string): Promise { - const sourceRegistries: DockerRegistry[] = [] - - // Check if Dockerfile has any images with a registry prefix - // If so, include all user's registries (we can't reliably match specific registries) - if (checkDockerfileHasRegistryPrefix(dockerfileContent)) { - const userRegistries = await this.findAll(organizationId, RegistryType.ORGANIZATION) - sourceRegistries.push(...userRegistries) - } - - // Add default Docker Hub registry only if user doesn't have their own Docker Hub credentials - // The auth configs map is keyed by URL, so adding the default last would override user credentials - const userHasDockerHubCreds = sourceRegistries.some((registry) => registry.url.includes('docker.io')) - - if (!userHasDockerHubCreds) { - const defaultDockerHubRegistry = await this.getDefaultDockerHubRegistry() - if (defaultDockerHubRegistry) { - sourceRegistries.push(defaultDockerHubRegistry) - } - } - - return sourceRegistries - } - - @OnAsyncEvent({ - event: RegionEvents.SNAPSHOT_MANAGER_CREDENTIALS_REGENERATED, - }) - private async _handleRegionSnapshotManagerCredsRegenerated( - payload: RegionSnapshotManagerCredsRegeneratedEvent, - ): Promise { - const { regionId, snapshotManagerUrl, username, password, entityManager } = payload - - const em = entityManager ?? this.dockerRegistryRepository.manager - - const registries = await em.count(DockerRegistry, { - where: { region: regionId, url: snapshotManagerUrl }, - }) - - if (registries === 0) { - throw new NotFoundException(`No registries found for region ${regionId} with URL ${snapshotManagerUrl}`) - } - - await em.update(DockerRegistry, { region: regionId, url: snapshotManagerUrl }, { username, password }) - } - - @OnAsyncEvent({ - event: RegionEvents.SNAPSHOT_MANAGER_UPDATED, - }) - private async _handleRegionSnapshotManagerUpdated(payload: RegionSnapshotManagerUpdatedEvent): Promise { - const { - region, - organizationId, - snapshotManagerUrl, - prevSnapshotManagerUrl, - entityManager, - newUsername, - newPassword, - } = payload - - const em = entityManager ?? this.dockerRegistryRepository.manager - - if (prevSnapshotManagerUrl) { - // Update old registries associated with previous snapshot manager URL - if (snapshotManagerUrl) { - await em.update( - DockerRegistry, - { - region: region.id, - url: prevSnapshotManagerUrl, - }, - { - url: snapshotManagerUrl, - username: newUsername, - password: newPassword, - }, - ) - } else { - // If snapshot manager URL is removed, delete associated registries - await em.delete(DockerRegistry, { - region: region.id, - url: prevSnapshotManagerUrl, - }) - } - - return - } - - const registries = await em.count(DockerRegistry, { - where: { region: region.id, url: snapshotManagerUrl }, - }) - - if (registries === 0) { - await this._handleRegionCreatedEvent( - new RegionCreatedEvent(entityManager, region, organizationId, newUsername, newPassword), - ) - } - } - - @OnAsyncEvent({ - event: RegionEvents.CREATED, - }) - private async _handleRegionCreatedEvent(payload: RegionCreatedEvent): Promise { - const { entityManager, region, organizationId, snapshotManagerUsername, snapshotManagerPassword } = payload - - if (!region.snapshotManagerUrl || !snapshotManagerUsername || !snapshotManagerPassword) { - return - } - - await this.create( - { - name: `${region.name}-backup`, - url: region.snapshotManagerUrl, - username: snapshotManagerUsername, - password: snapshotManagerPassword, - registryType: RegistryType.BACKUP, - regionId: region.id, - }, - organizationId ?? undefined, - false, - entityManager, - ) - - await this.create( - { - name: `${region.name}-internal`, - url: region.snapshotManagerUrl, - username: snapshotManagerUsername, - password: snapshotManagerPassword, - registryType: RegistryType.INTERNAL, - regionId: region.id, - }, - organizationId ?? undefined, - false, - entityManager, - ) - - await this.create( - { - name: `${region.name}-transient`, - url: region.snapshotManagerUrl, - username: snapshotManagerUsername, - password: snapshotManagerPassword, - registryType: RegistryType.TRANSIENT, - regionId: region.id, - }, - organizationId ?? undefined, - false, - entityManager, - ) - } - - @OnAsyncEvent({ - event: RegionEvents.DELETED, - }) - async handleRegionDeletedEvent(payload: RegionDeletedEvent): Promise { - const { entityManager, region } = payload - - if (!region.snapshotManagerUrl) { - return - } - - const repository = entityManager.getRepository(DockerRegistry) - await repository.delete({ region: region.id }) - } -} diff --git a/apps/api/src/exceptions/box-error.exception.ts b/apps/api/src/exceptions/box-error.exception.ts new file mode 100644 index 000000000..c709c6c27 --- /dev/null +++ b/apps/api/src/exceptions/box-error.exception.ts @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { HttpException, HttpStatus } from '@nestjs/common' + +export class BoxError extends HttpException { + constructor(message: string) { + super(message, HttpStatus.BAD_REQUEST) + } +} diff --git a/apps/api/src/exceptions/sandbox-error.exception.ts b/apps/api/src/exceptions/sandbox-error.exception.ts deleted file mode 100644 index dd8cde691..000000000 --- a/apps/api/src/exceptions/sandbox-error.exception.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { HttpException, HttpStatus } from '@nestjs/common' - -export class SandboxError extends HttpException { - constructor(message: string) { - super(message, HttpStatus.BAD_REQUEST) - } -} diff --git a/apps/api/src/filters/all-exceptions.filter.spec.ts b/apps/api/src/filters/all-exceptions.filter.spec.ts new file mode 100644 index 000000000..eefbc5a4c --- /dev/null +++ b/apps/api/src/filters/all-exceptions.filter.spec.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ArgumentsHost, HttpStatus } from '@nestjs/common' +import { AllExceptionsFilter } from './all-exceptions.filter' +import { RunnerApiError } from '../box/errors/runner-api-error' + +describe('AllExceptionsFilter', () => { + it('serializes HttpException code fields into the JSON response', async () => { + const json = jest.fn() + const status = jest.fn().mockReturnValue({ json }) + const filter = new AllExceptionsFilter({ incrementFailedAuth: jest.fn() } as never) + const host = { + switchToHttp: () => ({ + getResponse: () => ({ status }), + getRequest: () => ({ path: '/api/v1/org/boxes', url: '/api/v1/org/boxes' }), + }), + } as ArgumentsHost + + await filter.catch( + new RunnerApiError('Runner API returned a non-JSON error response', 503, 'runner_non_json_error'), + host, + ) + + expect(status).toHaveBeenCalledWith(HttpStatus.BAD_GATEWAY) + expect(json).toHaveBeenCalledWith( + expect.objectContaining({ + path: '/api/v1/org/boxes', + statusCode: HttpStatus.BAD_GATEWAY, + error: 'Bad Gateway', + message: 'Runner API returned a non-JSON error response', + code: 'runner_non_json_error', + }), + ) + }) +}) diff --git a/apps/api/src/filters/all-exceptions.filter.ts b/apps/api/src/filters/all-exceptions.filter.ts index 81f67c4a7..145880bfc 100644 --- a/apps/api/src/filters/all-exceptions.filter.ts +++ b/apps/api/src/filters/all-exceptions.filter.ts @@ -34,6 +34,7 @@ export class AllExceptionsFilter implements ExceptionFilter { let statusCode: number let error: string let message: string + let code: string | undefined // If the exception is a NotFoundException and the request path is not an API request, serve the dashboard index.html file if (exception instanceof NotFoundException && !request.path.startsWith('/api/')) { @@ -66,6 +67,8 @@ export class AllExceptionsFilter implements ExceptionFilter { message = Array.isArray(responseMessage) ? responseMessage.join(', ') : (responseMessage as string) || exception.message + const responseCode = (exceptionResponse as Record).code + code = typeof responseCode === 'string' ? responseCode : undefined } } else { this.logger.error(exception) @@ -80,6 +83,7 @@ export class AllExceptionsFilter implements ExceptionFilter { statusCode, error, message, + ...(code ? { code } : {}), }) } } diff --git a/apps/api/src/generate-openapi.ts b/apps/api/src/generate-openapi.ts index b72403733..8293c63d2 100644 --- a/apps/api/src/generate-openapi.ts +++ b/apps/api/src/generate-openapi.ts @@ -7,11 +7,8 @@ import { SwaggerModule } from '@nestjs/swagger' import { getOpenApiConfig } from './openapi.config' import { addWebhookDocumentation } from './openapi-webhooks' import { - SandboxCreatedWebhookDto, - SandboxStateUpdatedWebhookDto, - SnapshotCreatedWebhookDto, - SnapshotStateUpdatedWebhookDto, - SnapshotRemovedWebhookDto, + BoxCreatedWebhookDto, + BoxStateUpdatedWebhookDto, VolumeCreatedWebhookDto, VolumeStateUpdatedWebhookDto, } from './webhook/dto/webhook-event-payloads.dto' @@ -36,11 +33,8 @@ async function generateOpenAPI() { const document_3_1_0 = { ...SwaggerModule.createDocument(app, config, { extraModels: [ - SandboxCreatedWebhookDto, - SandboxStateUpdatedWebhookDto, - SnapshotCreatedWebhookDto, - SnapshotStateUpdatedWebhookDto, - SnapshotRemovedWebhookDto, + BoxCreatedWebhookDto, + BoxStateUpdatedWebhookDto, VolumeCreatedWebhookDto, VolumeStateUpdatedWebhookDto, ], diff --git a/apps/api/src/interceptors/metrics.interceptor.ts b/apps/api/src/interceptors/metrics.interceptor.ts index 404103af4..1dfce4033 100644 --- a/apps/api/src/interceptors/metrics.interceptor.ts +++ b/apps/api/src/interceptors/metrics.interceptor.ts @@ -15,14 +15,8 @@ import { import { Observable } from 'rxjs' import { tap } from 'rxjs/operators' import { PostHog } from 'posthog-node' -import { SandboxDto } from '../sandbox/dto/sandbox.dto' -import { DockerRegistryDto } from '../docker-registry/dto/docker-registry.dto' -import { CreateSandboxDto } from '../sandbox/dto/create-sandbox.dto' import { Request } from 'express' -import { CreateSnapshotDto } from '../sandbox/dto/create-snapshot.dto' -import { SnapshotDto } from '../sandbox/dto/snapshot.dto' import { CreateOrganizationDto } from '../organization/dto/create-organization.dto' -import { UpdateOrganizationQuotaDto } from '../organization/dto/update-organization-quota.dto' import { OrganizationDto } from '../organization/dto/organization.dto' import { UpdateOrganizationMemberAccessDto } from '../organization/dto/update-organization-member-access.dto' import { CreateOrganizationRoleDto } from '../organization/dto/create-organization-role.dto' @@ -30,15 +24,17 @@ import { UpdateOrganizationRoleDto } from '../organization/dto/update-organizati import { CreateOrganizationInvitationDto } from '../organization/dto/create-organization-invitation.dto' import { UpdateOrganizationInvitationDto } from '../organization/dto/update-organization-invitation.dto' import { CustomHeaders } from '../common/constants/header.constants' -import { CreateVolumeDto } from '../sandbox/dto/create-volume.dto' -import { VolumeDto } from '../sandbox/dto/volume.dto' -import { CreateWorkspaceDto } from '../sandbox/dto/create-workspace.deprecated.dto' -import { WorkspaceDto } from '../sandbox/dto/workspace.deprecated.dto' +import { CreateVolumeDto } from '../box/dto/create-volume.dto' +import { VolumeDto } from '../box/dto/volume.dto' +import { CreateBoxDto as RestCreateBoxDto } from '../boxlite-rest/dto/create-box.dto' +import { BoxResponseDto } from '../boxlite-rest/dto/box-response.dto' import { TypedConfigService } from '../config/typed-config.service' -import { UpdateOrganizationRegionQuotaDto } from '../organization/dto/update-organization-region-quota.dto' import { UpdateOrganizationDefaultRegionDto } from '../organization/dto/update-organization-default-region.dto' -type RequestWithUser = Request & { user?: { userId: string; organizationId: string } } +type RequestWithUser = Request & { + user?: { userId: string; organizationId: string } + params: Record +} type CommonCaptureProps = { organizationId?: string distinctId: string @@ -121,7 +117,7 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow userAgent, error, source: Array.isArray(source) ? source[0] : source, - isDeprecated: request.route.path.includes('/workspace') || request.route.path.includes('/images'), + isDeprecated: request.route.path.includes('/images'), sdkVersion, environment: this.configService.get('posthog.environment'), } @@ -132,72 +128,34 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow case '/api/api-keys': this.captureCreateApiKey(props) break - case '/api/snapshots': - this.captureCreateSnapshot(props, request.body, response) - break - case '/api/snapshots/:snapshotId/activate': - this.captureActivateSnapshot(props, request.params.snapshotId) - break - case '/api/snapshots/:snapshotId/deactivate': - this.captureDeactivateSnapshot(props, request.params.snapshotId) - break - case '/api/docker-registry': - this.captureCreateDockerRegistry(props, response) - break - case '/api/sandbox': - this.captureCreateSandbox(props, request.body, response) + // TODO(image-rewrite): /api/templates metrics removed with box_template. + case '/api/v1/boxes': + case '/api/v1/:prefix/boxes': + this.captureCreateBox(props, request.body, response) break - case '/api/workspace': - this.captureCreateWorkspace_deprecated(props, request.body, response) + case '/api/v1/boxes/:boxId/start': + case '/api/v1/:prefix/boxes/:boxId/start': + this.captureStartBox(props, request.params.boxIdOrName || request.params.boxId) break - case '/api/sandbox/:sandboxIdOrName/start': - case '/api/workspace/:workspaceId/start': - this.captureStartSandbox(props, request.params.sandboxIdOrName || request.params.workspaceId) - break - case '/api/sandbox/:sandboxIdOrName/stop': - case '/api/workspace/:workspaceId/stop': - this.captureStopSandbox( + case '/api/v1/boxes/:boxId/stop': + case '/api/v1/:prefix/boxes/:boxId/stop': + this.captureStopBox( props, - request.params.sandboxIdOrName || request.params.workspaceId, + request.params.boxIdOrName || request.params.boxId, request.query?.force === 'true', ) break - case '/api/sandbox/:sandboxIdOrName/resize': - this.captureResizeSandbox(props, request.params.sandboxIdOrName, request.body) - break - case '/api/sandbox/:sandboxIdOrName/archive': - case '/api/workspace/:workspaceId/archive': - this.captureArchiveSandbox(props, request.params.sandboxIdOrName || request.params.workspaceId) - break - case '/api/sandbox/:sandboxIdOrName/backup': - this.captureCreateBackup(props, request.params.sandboxIdOrName) - break - case '/api/sandbox/:sandboxIdOrName/public/:isPublic': - case '/api/workspace/:workspaceId/public/:isPublic': - this.captureUpdatePublicStatus( - props, - request.params.sandboxIdOrName || request.params.workspaceId, - request.params.isPublic === 'true', - ) + case '/api/box/:boxIdOrName/resize': + this.captureResizeBox(props, request.params.boxIdOrName, request.body) break - case '/api/sandbox/:sandboxIdOrName/autostop/:interval': - case '/api/workspace/:workspaceId/autostop/:interval': - this.captureSetAutostopInterval( - props, - request.params.sandboxIdOrName || request.params.workspaceId, - parseInt(request.params.interval), - ) + case '/api/box/:boxIdOrName/public/:isPublic': + this.captureUpdatePublicStatus(props, request.params.boxIdOrName, request.params.isPublic === 'true') break - case '/api/sandbox/:sandboxIdOrName/autoarchive/:interval': - case '/api/workspace/:workspaceId/autoarchive/:interval': - this.captureSetAutoArchiveInterval( - props, - request.params.sandboxIdOrName || request.params.workspaceId, - parseInt(request.params.interval), - ) + case '/api/box/:boxIdOrName/autostop/:interval': + this.captureSetAutostopInterval(props, request.params.boxIdOrName, parseInt(request.params.interval)) break - case '/api/sandbox/:sandboxIdOrName/autodelete/:interval': - this.captureSetAutoDeleteInterval(props, request.params.sandboxIdOrName, parseInt(request.params.interval)) + case '/api/box/:boxIdOrName/autodelete/:interval': + this.captureSetAutoDeleteInterval(props, request.params.boxIdOrName, parseInt(request.params.interval)) break case '/api/organizations/invitations/:invitationId/accept': this.captureAcceptInvitation(props, request.params.invitationId) @@ -235,13 +193,11 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow break case 'DELETE': switch (request.route.path) { - case '/api/sandbox/:sandboxIdOrName': - case '/api/workspace/:workspaceId': - this.captureDeleteSandbox(props, request.params.sandboxIdOrName || request.params.workspaceId) - break - case '/api/snapshots/:snapshotId': - this.captureDeleteSnapshot(props, request.params.snapshotId) + case '/api/v1/boxes/:boxId': + case '/api/v1/:prefix/boxes/:boxId': + this.captureDeleteBox(props, request.params.boxIdOrName || request.params.boxId) break + // TODO(image-rewrite): /api/templates delete metrics removed with box_template. case '/api/organizations/:organizationId': this.captureDeleteOrganization(props, request.params.organizationId) break @@ -258,9 +214,8 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow break case 'PUT': switch (request.route.path) { - case '/api/sandbox/:sandboxIdOrName/labels': - case '/api/workspace/:workspaceId/labels': - this.captureUpdateSandboxLabels(props, request.params.sandboxIdOrName || request.params.workspaceId) + case '/api/box/:boxIdOrName/labels': + this.captureUpdateBoxLabels(props, request.params.boxIdOrName) break case '/api/organizations/:organizationId/roles/:roleId': this.captureUpdateOrganizationRole( @@ -288,101 +243,90 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow case '/api/organizations/:organizationId/default-region': this.captureSetOrganizationDefaultRegion(props, request.params.organizationId, request.body) break - case '/api/organizations/:organizationId/quota': - this.captureUpdateOrganizationQuota(props, request.params.organizationId, request.body) - break - case '/api/organizations/:organizationId/quota/:regionId': - this.captureUpdateOrganizationRegionQuota( - props, - request.params.organizationId, - request.params.regionId, - request.body, - ) - break } break } - if (!request.route.path.startsWith('/api/toolbox/:sandboxId/toolbox')) { + if (!request.route.path.startsWith('/api/toolbox/:boxId/toolbox')) { return } - const path = request.route.path.replace('/api/toolbox/:sandboxId/toolbox', '') + const path = request.route.path.replace('/api/toolbox/:boxId/toolbox', '') switch (path) { case '/project-dir': - this.captureToolboxCommand(props, request.params.sandboxId, 'project-dir_get') + this.captureToolboxCommand(props, request.params.boxId, 'project-dir_get') break case '/files': switch (request.method) { case 'GET': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_list') + this.captureToolboxCommand(props, request.params.boxId, 'files_list') break case 'DELETE': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_delete') + this.captureToolboxCommand(props, request.params.boxId, 'files_delete') break } break case '/files/download': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_download') + this.captureToolboxCommand(props, request.params.boxId, 'files_download') break case '/files/find': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_find') + this.captureToolboxCommand(props, request.params.boxId, 'files_find') break case '/files/folder': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_folder_create') + this.captureToolboxCommand(props, request.params.boxId, 'files_folder_create') break case '/files/info': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_info') + this.captureToolboxCommand(props, request.params.boxId, 'files_info') break case '/files/move': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_move') + this.captureToolboxCommand(props, request.params.boxId, 'files_move') break case '/files/permissions': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_permissions') + this.captureToolboxCommand(props, request.params.boxId, 'files_permissions') break case '/files/replace': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_replace') + this.captureToolboxCommand(props, request.params.boxId, 'files_replace') break case '/files/search': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_search') + this.captureToolboxCommand(props, request.params.boxId, 'files_search') break case '/files/upload': - this.captureToolboxCommand(props, request.params.sandboxId, 'files_upload') + this.captureToolboxCommand(props, request.params.boxId, 'files_upload') break case '/git/add': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_add') + this.captureToolboxCommand(props, request.params.boxId, 'git_add') break case '/git/branches': switch (request.method) { case 'GET': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_branches_list') + this.captureToolboxCommand(props, request.params.boxId, 'git_branches_list') break case 'POST': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_branches_create') + this.captureToolboxCommand(props, request.params.boxId, 'git_branches_create') break } break case '/git/clone': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_clone') + this.captureToolboxCommand(props, request.params.boxId, 'git_clone') break case '/git/commit': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_commit') + this.captureToolboxCommand(props, request.params.boxId, 'git_commit') break case '/git/history': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_history') + this.captureToolboxCommand(props, request.params.boxId, 'git_history') break case '/git/pull': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_pull') + this.captureToolboxCommand(props, request.params.boxId, 'git_pull') break case '/git/push': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_push') + this.captureToolboxCommand(props, request.params.boxId, 'git_push') break case '/git/status': - this.captureToolboxCommand(props, request.params.sandboxId, 'git_status') + this.captureToolboxCommand(props, request.params.boxId, 'git_status') break case '/process/execute': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_execute', { + this.captureToolboxCommand(props, request.params.boxId, 'process_execute', { command: request.body.command, cwd: request.body.cwd, exit_code: response.exitCode, @@ -392,10 +336,10 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow case '/process/session': switch (request.method) { case 'GET': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_list') + this.captureToolboxCommand(props, request.params.boxId, 'process_session_list') break case 'POST': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_create', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_create', { session_id: request.body.sessionId, }) break @@ -404,59 +348,59 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow case '/process/session/:sessionId': switch (request.method) { case 'GET': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_get', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_get', { session_id: request.params.sessionId, }) break case 'DELETE': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_delete', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_delete', { session_id: request.params.sessionId, }) break } break case '/process/session/:sessionId/exec': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_execute', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_execute', { session_id: request.params.sessionId, command: request.body.command, }) break case '/process/session/:sessionId/command/:commandId': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_command_get', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_command_get', { session_id: request.params.sessionId, command_id: request.params.commandId, }) break case '/process/session/:sessionId/command/:commandId/logs': - this.captureToolboxCommand(props, request.params.sandboxId, 'process_session_command_logs', { + this.captureToolboxCommand(props, request.params.boxId, 'process_session_command_logs', { session_id: request.params.sessionId, command_id: request.params.commandId, }) break case '/lsp/completions': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_completions') + this.captureToolboxCommand(props, request.params.boxId, 'lsp_completions') break case '/lsp/did-close': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_did_close') + this.captureToolboxCommand(props, request.params.boxId, 'lsp_did_close') break case '/lsp/did-open': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_did_open') + this.captureToolboxCommand(props, request.params.boxId, 'lsp_did_open') break case '/lsp/document-symbols': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_document_symbols') + this.captureToolboxCommand(props, request.params.boxId, 'lsp_document_symbols') break case '/lsp/start': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_start', { + this.captureToolboxCommand(props, request.params.boxId, 'lsp_start', { language_id: request.body.languageId, }) break case '/lsp/stop': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_stop', { + this.captureToolboxCommand(props, request.params.boxId, 'lsp_stop', { language_id: request.body.languageId, }) break - case '/lsp/sandbox-symbols': - this.captureToolboxCommand(props, request.params.sandboxId, 'lsp_sandbox_symbols', { + case '/lsp/box-symbols': + this.captureToolboxCommand(props, request.params.boxId, 'lsp_box_symbols', { language_id: request.query.languageId, path_to_project: request.query.pathToProject, query: request.query.query, @@ -469,213 +413,83 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow this.capture('api_api_key_created', props, 'api_api_key_creation_failed') } - private captureCreateDockerRegistry(props: CommonCaptureProps, response: DockerRegistryDto) { - this.capture('api_docker_registry_created', props, 'api_docker_registry_creation_failed', { - registry_name: response.name, - registry_url: response.url, - }) - } - - private captureCreateSnapshot(props: CommonCaptureProps, request: CreateSnapshotDto, response: SnapshotDto) { - this.capture('api_snapshot_created', props, 'api_snapshot_creation_failed', { - snapshot_id: response.id, - snapshot_name: request.name, - snapshot_image_name: request.imageName, - snapshot_entrypoint: request.entrypoint, - snapshot_cpu: request.cpu, - snapshot_gpu: request.gpu, - snapshot_memory: request.memory, - snapshot_disk: request.disk, - snapshot_is_build: request.buildInfo ? true : false, - snapshot_build_info_context_hashes_length: request.buildInfo?.contextHashes?.length, - }) - } - - private captureActivateSnapshot(props: CommonCaptureProps, snapshotId: string) { - this.capture('api_snapshot_activated', props, 'api_snapshot_activation_failed', { - snapshot_id: snapshotId, - }) - } - - private captureDeactivateSnapshot(props: CommonCaptureProps, snapshotId: string) { - this.capture('api_snapshot_deactivated', props, 'api_snapshot_deactivation_failed', { - snapshot_id: snapshotId, - }) - } - - private captureDeleteSnapshot(props: CommonCaptureProps, snapshotId: string) { - this.capture('api_snapshot_deleted', props, 'api_snapshot_deletion_failed', { - snapshot_id: snapshotId, - }) - } - - private captureCreateSandbox(props: CommonCaptureProps, request: CreateSandboxDto, response: SandboxDto) { - const envVarsLength = request.env ? Object.keys(request.env).length : 0 - - const records = { - sandbox_id: response.id, - sandbox_name_request: request.name, - sandbox_name: response.name, - sandbox_snapshot_request: request.snapshot, - sandbox_snapshot: response.snapshot, - sandbox_user_request: request.user, - sandbox_user: response.user, - sandbox_cpu_request: request.cpu, - sandbox_cpu: response.cpu, - sandbox_gpu_request: request.gpu, - sandbox_gpu: response.gpu, - sandbox_memory_mb_request: request.memory * 1024, - sandbox_memory_mb: response.memory * 1024, - sandbox_disk_gb_request: request.disk, - sandbox_disk_gb: response.disk, - sandbox_target_request: request.target, - sandbox_target: response.target, - sandbox_auto_stop_interval_min_request: request.autoStopInterval, - sandbox_auto_stop_interval_min: response.autoStopInterval, - sandbox_auto_archive_interval_min_request: request.autoArchiveInterval, - sandbox_auto_archive_interval_min: response.autoArchiveInterval, - sandbox_auto_delete_interval_min_request: request.autoDeleteInterval, - sandbox_auto_delete_interval_min: response.autoDeleteInterval, - sandbox_public_request: request.public, - sandbox_public: response.public, - sandbox_labels_request: request.labels, - sandbox_labels: response.labels, - sandbox_env_vars_length_request: envVarsLength, - sandbox_volumes_length_request: request.volumes?.length, - sandbox_daemon_version: response.daemonVersion, - sandbox_network_block_all_request: request.networkBlockAll, - sandbox_network_block_all: response.networkBlockAll, - sandbox_network_allow_list_set_request: !!request.networkAllowList, - sandbox_network_allow_list_set: !!response.networkAllowList, - } - - if (request.buildInfo) { - records['sandbox_is_dynamic_build'] = true - records['sandbox_build_info_context_hashes_length'] = request.buildInfo.contextHashes?.length - } - - this.capture('api_sandbox_created', props, 'api_sandbox_creation_failed', records) - } + // TODO(image-rewrite): template create/activate/deactivate/delete metrics removed with box_template. - private captureCreateWorkspace_deprecated( - props: CommonCaptureProps, - request: CreateWorkspaceDto, - response: WorkspaceDto, - ) { + private captureCreateBox(props: CommonCaptureProps, request: RestCreateBoxDto, response: BoxResponseDto) { const envVarsLength = request.env ? Object.keys(request.env).length : 0 const records = { - sandbox_id: response.id, - sandbox_snapshot_request: request.image, - sandbox_snapshot: response.snapshot, - sandbox_user_request: request.user, - sandbox_user: response.user, - sandbox_cpu_request: request.cpu, - sandbox_cpu: response.cpu, - sandbox_gpu_request: request.gpu, - sandbox_gpu: response.gpu, - sandbox_memory_mb_request: request.memory * 1024, - sandbox_memory_mb: response.memory * 1024, - sandbox_disk_gb_request: request.disk, - sandbox_disk_gb: response.disk, - sandbox_target_request: request.target, - sandbox_target: response.target, - sandbox_auto_stop_interval_min_request: request.autoStopInterval, - sandbox_auto_stop_interval_min: response.autoStopInterval, - sandbox_auto_archive_interval_min_request: request.autoArchiveInterval, - sandbox_auto_archive_interval_min: response.autoArchiveInterval, - sandbox_public_request: request.public, - sandbox_public: response.public, - sandbox_labels_request: request.labels, - sandbox_labels: response.labels, - sandbox_env_vars_length_request: envVarsLength, - sandbox_volumes_length_request: request.volumes?.length, - sandbox_daemon_version: response.daemonVersion, + box_id: response.box_id, + box_name_request: request.name, + box_name: response.name, + box_user_request: request.user, + box_cpu_request: request.cpus, + box_cpu: response.cpus, + box_memory_mb_request: request.memory_mib, + box_memory_mb: response.memory_mib, + box_disk_gb_request: request.disk_size_gb, + box_env_vars_length_request: envVarsLength, } - if (request.buildInfo) { - records['sandbox_is_dynamic_build'] = true - records['sandbox_build_info_context_hashes_length'] = request.buildInfo.contextHashes?.length - } - - this.capture('api_sandbox_created', props, 'api_sandbox_creation_failed', records) + this.capture('api_box_created', props, 'api_box_creation_failed', records) } - private captureDeleteSandbox(props: CommonCaptureProps, sandboxId: string) { - this.capture('api_sandbox_deleted', props, 'api_sandbox_deletion_failed', { - sandbox_id: sandboxId, + private captureDeleteBox(props: CommonCaptureProps, boxId: string) { + this.capture('api_box_deleted', props, 'api_box_deletion_failed', { + box_id: boxId, }) } - private captureStartSandbox(props: CommonCaptureProps, sandboxId: string) { - this.capture('api_sandbox_started', props, 'api_sandbox_start_failed', { - sandbox_id: sandboxId, + private captureStartBox(props: CommonCaptureProps, boxId: string) { + this.capture('api_box_started', props, 'api_box_start_failed', { + box_id: boxId, }) } - private captureStopSandbox(props: CommonCaptureProps, sandboxId: string, force: boolean) { - this.capture('api_sandbox_stopped', props, 'api_sandbox_stop_failed', { - sandbox_id: sandboxId, + private captureStopBox(props: CommonCaptureProps, boxId: string, force: boolean) { + this.capture('api_box_stopped', props, 'api_box_stop_failed', { + box_id: boxId, force, }) } - private captureResizeSandbox( + private captureResizeBox( props: CommonCaptureProps, - sandboxId: string, + boxId: string, body: { cpu?: number; memory?: number; disk?: number }, ) { - this.capture('api_sandbox_resized', props, 'api_sandbox_resize_failed', { - sandbox_id: sandboxId, + this.capture('api_box_resized', props, 'api_box_resize_failed', { + box_id: boxId, cpu: body?.cpu, memory: body?.memory, disk: body?.disk, }) } - private captureArchiveSandbox(props: CommonCaptureProps, sandboxId: string) { - this.capture('api_sandbox_archived', props, 'api_sandbox_archive_failed', { - sandbox_id: sandboxId, - }) - } - - private captureCreateBackup(props: CommonCaptureProps, sandboxId: string) { - this.capture('api_sandbox_backup_created', props, 'api_sandbox_backup_creation_failed', { - sandbox_id: sandboxId, - }) - } - - private captureUpdatePublicStatus(props: CommonCaptureProps, sandboxId: string, isPublic: boolean) { - this.capture('api_sandbox_public_status_updated', props, 'api_sandbox_public_status_update_failed', { - sandbox_id: sandboxId, - sandbox_public: isPublic, + private captureUpdatePublicStatus(props: CommonCaptureProps, boxId: string, isPublic: boolean) { + this.capture('api_box_public_status_updated', props, 'api_box_public_status_update_failed', { + box_id: boxId, + box_public: isPublic, }) } - private captureSetAutostopInterval(props: CommonCaptureProps, sandboxId: string, interval: number) { - this.capture('api_sandbox_autostop_interval_updated', props, 'api_sandbox_autostop_interval_update_failed', { - sandbox_id: sandboxId, - sandbox_autostop_interval: interval, + private captureSetAutostopInterval(props: CommonCaptureProps, boxId: string, interval: number) { + this.capture('api_box_autostop_interval_updated', props, 'api_box_autostop_interval_update_failed', { + box_id: boxId, + box_autostop_interval: interval, }) } - private captureSetAutoArchiveInterval(props: CommonCaptureProps, sandboxId: string, interval: number) { - this.capture('api_sandbox_autoarchive_interval_updated', props, 'api_sandbox_autoarchive_interval_update_failed', { - sandbox_id: sandboxId, - sandbox_autoarchive_interval: interval, + private captureSetAutoDeleteInterval(props: CommonCaptureProps, boxId: string, interval: number) { + this.capture('api_box_autodelete_interval_updated', props, 'api_box_autodelete_interval_update_failed', { + box_id: boxId, + box_autodelete_interval: interval, }) } - private captureSetAutoDeleteInterval(props: CommonCaptureProps, sandboxId: string, interval: number) { - this.capture('api_sandbox_autodelete_interval_updated', props, 'api_sandbox_autodelete_interval_update_failed', { - sandbox_id: sandboxId, - sandbox_autodelete_interval: interval, - }) - } - - private captureUpdateSandboxLabels(props: CommonCaptureProps, sandboxId: string) { - this.capture('api_sandbox_labels_update', props, 'api_sandbox_labels_update_failed', { - sandbox_id: sandboxId, + private captureUpdateBoxLabels(props: CommonCaptureProps, boxId: string) { + this.capture('api_box_labels_update', props, 'api_box_labels_update_failed', { + box_id: boxId, }) } @@ -707,7 +521,7 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow name: request.name, created_at: response.createdAt, created_by: response.createdBy, - personal: response.personal, + is_default_for_authenticated_user: response.isDefaultForAuthenticatedUser, environment: this.configService.get('posthog.environment'), }, }) @@ -736,37 +550,6 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow }) } - private captureUpdateOrganizationQuota( - props: CommonCaptureProps, - organizationId: string, - request: UpdateOrganizationQuotaDto, - ) { - this.capture('api_organization_quota_updated', props, 'api_organization_quota_update_failed', { - organization_id: organizationId, - organization_max_cpu_per_sandbox: request.maxCpuPerSandbox, - organization_max_memory_per_sandbox_mb: request.maxMemoryPerSandbox ? request.maxMemoryPerSandbox * 1024 : null, - organization_max_disk_per_sandbox_gb: request.maxDiskPerSandbox, - organization_snapshot_quota: request.snapshotQuota, - organization_max_snapshot_size_mb: request.maxSnapshotSize ? request.maxSnapshotSize * 1024 : null, - organization_volume_quota: request.volumeQuota, - }) - } - - private captureUpdateOrganizationRegionQuota( - props: CommonCaptureProps, - organizationId: string, - regionId: string, - request: UpdateOrganizationRegionQuotaDto, - ) { - this.capture('api_organization_region_quota_updated', props, 'api_organization_region_quota_update_failed', { - organization_id: organizationId, - organization_region_id: regionId, - organization_region_total_cpu_quota: request.totalCpuQuota, - organization_region_total_memory_quota_mb: request.totalMemoryQuota ? request.totalMemoryQuota * 1024 : null, - organization_region_total_disk_quota_gb: request.totalDiskQuota, - }) - } - private captureDeleteOrganization(props: CommonCaptureProps, organizationId: string) { this.capture('api_organization_deleted', props, 'api_organization_deletion_failed', { organization_id: organizationId, @@ -895,12 +678,12 @@ export class MetricsInterceptor implements NestInterceptor, OnApplicationShutdow private captureToolboxCommand( props: CommonCaptureProps, - sandboxId: string, + boxId: string, command: string, extraProps?: Record, ) { this.capture('api_toolbox_command', props, 'api_toolbox_command_failed', { - sandbox_id: sandboxId, + box_id: boxId, toolbox_command: command, ...extraProps, }) diff --git a/apps/api/src/interceptors/observability-context.interceptor.spec.ts b/apps/api/src/interceptors/observability-context.interceptor.spec.ts new file mode 100644 index 000000000..da73fde93 --- /dev/null +++ b/apps/api/src/interceptors/observability-context.interceptor.spec.ts @@ -0,0 +1,148 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CallHandler, ExecutionContext } from '@nestjs/common' +import { of } from 'rxjs' +import { trace } from '@opentelemetry/api' +import { CustomHeaders } from '../common/constants/header.constants' +import { ObservabilityContextInterceptor } from './observability-context.interceptor' + +jest.mock('@opentelemetry/api', () => ({ + trace: { + getActiveSpan: jest.fn(), + }, +})) + +describe('ObservabilityContextInterceptor', () => { + const getActiveSpan = trace.getActiveSpan as jest.Mock + + afterEach(() => { + jest.clearAllMocks() + }) + + function httpContext(request: Record): ExecutionContext { + return { + switchToHttp: () => ({ + getRequest: () => request, + }), + } as unknown as ExecutionContext + } + + it('adds allowed resource identifiers and request source to the active span', (done) => { + const span = { setAttributes: jest.fn() } + getActiveSpan.mockReturnValue(span) + const request = { + params: { + traceId: 'trace-path-1', + }, + query: { + orgId: 'org-query-1', + userId: 'user-query-1', + boxId: 'box-query-1', + runnerId: 'runner-query-1', + machineId: 'machine-query-1', + executionId: 'exec-query-1', + jobId: 'job-query-1', + requestId: 'req-query-1', + operationId: 'op-query-1', + search: 'do not record this', + }, + get: (name: string) => (name === CustomHeaders.SOURCE.name ? 'agent' : undefined), + } + const interceptor = new ObservabilityContextInterceptor() + const next: CallHandler = { handle: () => of('ok') } + + interceptor.intercept(httpContext(request), next).subscribe({ + next: () => { + expect(span.setAttributes).toHaveBeenCalledWith({ + 'boxlite.trace_id': 'trace-path-1', + 'boxlite.org_id': 'org-query-1', + 'boxlite.user_id': 'user-query-1', + 'boxlite.box_id': 'box-query-1', + 'boxlite.runner_id': 'runner-query-1', + 'boxlite.machine_id': 'machine-query-1', + 'boxlite.execution_id': 'exec-query-1', + 'boxlite.job_id': 'job-query-1', + 'boxlite.request_id': 'req-query-1', + 'boxlite.operation_id': 'op-query-1', + 'boxlite.source': 'agent', + }) + expect(JSON.stringify(span.setAttributes.mock.calls[0][0])).not.toContain('do not record this') + done() + }, + error: done, + }) + }) + + it('maps boxIdOrName route params to box correlation attributes', (done) => { + const span = { setAttributes: jest.fn() } + getActiveSpan.mockReturnValue(span) + const request = { + params: { + boxIdOrName: 'box-or-name-1', + }, + query: {}, + get: () => undefined, + } + const interceptor = new ObservabilityContextInterceptor() + const next: CallHandler = { handle: () => of('ok') } + + interceptor.intercept(httpContext(request), next).subscribe({ + next: () => { + try { + expect(span.setAttributes).toHaveBeenCalledWith({ + 'boxlite.box_id': 'box-or-name-1', + }) + done() + } catch (error) { + done(error as Error) + } + }, + error: done, + }) + }) + + it('falls back to authenticated user context when query identifiers are absent', (done) => { + const span = { setAttributes: jest.fn() } + getActiveSpan.mockReturnValue(span) + const request = { + params: {}, + query: {}, + user: { + userId: 'auth-user-1', + organizationId: 'auth-org-1', + }, + get: () => undefined, + } + const interceptor = new ObservabilityContextInterceptor() + const next: CallHandler = { handle: () => of('ok') } + + interceptor.intercept(httpContext(request), next).subscribe({ + next: () => { + expect(span.setAttributes).toHaveBeenCalledWith({ + 'boxlite.user_id': 'auth-user-1', + 'boxlite.org_id': 'auth-org-1', + }) + done() + }, + error: done, + }) + }) + + it('does nothing when no active span is available', (done) => { + getActiveSpan.mockReturnValue(undefined) + const interceptor = new ObservabilityContextInterceptor() + const next: CallHandler = { handle: () => of('ok') } + + interceptor.intercept(httpContext({ params: { boxId: 'box-1' } }), next).subscribe({ + next: (value) => { + expect(value).toBe('ok') + done() + }, + error: done, + }) + }) +}) diff --git a/apps/api/src/interceptors/observability-context.interceptor.ts b/apps/api/src/interceptors/observability-context.interceptor.ts new file mode 100644 index 000000000..eea5909f7 --- /dev/null +++ b/apps/api/src/interceptors/observability-context.interceptor.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common' +import { trace, Attributes } from '@opentelemetry/api' +import { Observable } from 'rxjs' +import { Request } from 'express' +import { CustomHeaders } from '../common/constants/header.constants' + +type RequestWithContext = Request & { + params?: Record + query?: Record + user?: { + userId?: unknown + organizationId?: unknown + } +} + +const REQUEST_ATTRIBUTE_KEYS: Array<{ keys: string[]; attribute: string }> = [ + { keys: ['traceId'], attribute: 'boxlite.trace_id' }, + { keys: ['orgId', 'organizationId'], attribute: 'boxlite.org_id' }, + { keys: ['userId'], attribute: 'boxlite.user_id' }, + { keys: ['boxId', 'boxIdOrName'], attribute: 'boxlite.box_id' }, + { keys: ['runnerId'], attribute: 'boxlite.runner_id' }, + { keys: ['machineId'], attribute: 'boxlite.machine_id' }, + { keys: ['executionId'], attribute: 'boxlite.execution_id' }, + { keys: ['jobId'], attribute: 'boxlite.job_id' }, + { keys: ['requestId'], attribute: 'boxlite.request_id' }, + { keys: ['operationId'], attribute: 'boxlite.operation_id' }, +] + +@Injectable() +export class ObservabilityContextInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const span = trace.getActiveSpan() + if (!span) { + return next.handle() + } + + const request = context.switchToHttp().getRequest() + const attributes = this.buildAttributes(request) + if (Object.keys(attributes).length > 0) { + span.setAttributes(attributes) + } + + return next.handle() + } + + private buildAttributes(request: RequestWithContext): Attributes { + const attributes: Attributes = {} + const params = request.params ?? {} + const query = request.query ?? {} + + for (const { keys, attribute } of REQUEST_ATTRIBUTE_KEYS) { + const value = this.firstDefinedValue(keys, params) ?? this.firstDefinedValue(keys, query) + if (value) { + attributes[attribute] = value + } + } + + const authenticatedUserId = this.firstString(request.user?.userId) + if (!attributes['boxlite.user_id'] && authenticatedUserId) { + attributes['boxlite.user_id'] = authenticatedUserId + } + const authenticatedOrgId = this.firstString(request.user?.organizationId) + if (!attributes['boxlite.org_id'] && authenticatedOrgId) { + attributes['boxlite.org_id'] = authenticatedOrgId + } + + const source = this.firstString(request.get?.(CustomHeaders.SOURCE.name)) + if (source) { + attributes['boxlite.source'] = source + } + + return attributes + } + + private firstDefinedValue(keys: string[], values: Record): string | undefined { + for (const key of keys) { + const value = this.firstString(values[key]) + if (value) { + return value + } + } + return undefined + } + + private firstString(value: unknown): string | undefined { + const resolved = Array.isArray(value) ? value[0] : value + if (typeof resolved !== 'string') { + return undefined + } + const trimmed = resolved.trim() + return trimmed || undefined + } +} diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts index 5d63d960f..402786630 100644 --- a/apps/api/src/main.ts +++ b/apps/api/src/main.ts @@ -19,7 +19,7 @@ import { FailedAuthTrackerService } from './auth/failed-auth-tracker.service' import { DataSource, MigrationExecutor } from 'typeorm' import { getOpenApiConfig } from './openapi.config' import { AuditInterceptor } from './audit/interceptors/audit.interceptor' -import { join } from 'node:path' +import { extname, join } from 'node:path' import { ApiKeyService } from './api-key/api-key.service' import { BOXLITE_ADMIN_USER_ID } from './app.service' import { OrganizationService } from './organization/services/organization.service' @@ -27,7 +27,11 @@ import { MicroserviceOptions, Transport } from '@nestjs/microservices' import { Partitioners } from 'kafkajs' import { isApiEnabled, isWorkerEnabled } from './common/utils/app-mode' import cluster from 'node:cluster' +import type { IncomingMessage } from 'http' +import type { Socket } from 'net' import { Logger as PinoLogger, LoggerErrorInterceptor } from 'nestjs-pino' +import { BoxliteWsProxyService } from './boxlite-rest/boxlite-ws-proxy.service' +import { ObservabilityContextInterceptor } from './interceptors/observability-context.interceptor' // https options const httpsEnabled = process.env.CERT_PATH && process.env.CERT_KEY_PATH @@ -46,8 +50,32 @@ async function bootstrap() { }) app.useLogger(app.get(PinoLogger)) app.flushLogs() + // Pin CORS to known first-party origins rather than reflecting any origin. + // With `credentials: true`, `origin: true` would let any site make + // credentialed cross-origin calls to the API. The dashboard SPA (served at + // DASHBOARD_URL) is the only legitimate cross-origin caller; CORS_ALLOWED_ORIGINS + // (comma-separated) adds extras (e.g. local dev). If nothing is configured we + // fall back to reflecting the origin and warn — so an unconfigured deployment + // is never silently broken, while configured stacks (SST sets DASHBOARD_URL) + // are locked down. + // Gather configured origins, trim them, drop unset/empty entries (the + // `is string` guard also narrows the array to string[]), then dedupe. + const allowedOrigins = [ + process.env.DASHBOARD_URL, + process.env.APP_URL, + ...(process.env.CORS_ALLOWED_ORIGINS?.split(',') ?? []), + ] + .map((origin) => origin?.trim()) + .filter((origin): origin is string => !!origin) + const uniqueAllowedOrigins = [...new Set(allowedOrigins)] + if (uniqueAllowedOrigins.length === 0) { + Logger.warn( + 'CORS: no DASHBOARD_URL / APP_URL / CORS_ALLOWED_ORIGINS set; reflecting request origin. Set one to restrict cross-origin access.', + 'Bootstrap', + ) + } app.enableCors({ - origin: true, + origin: uniqueAllowedOrigins.length > 0 ? uniqueAllowedOrigins : true, methods: 'GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS', credentials: true, }) @@ -57,6 +85,7 @@ async function bootstrap() { app.set('trust proxy', true) app.useGlobalFilters(new AllExceptionsFilter(failedAuthTracker)) app.useGlobalInterceptors(new LoggerErrorInterceptor()) + app.useGlobalInterceptors(new ObservabilityContextInterceptor()) app.useGlobalInterceptors(new MetricsInterceptor(configService)) app.useGlobalInterceptors(app.get(AuditInterceptor)) app.useGlobalPipes( @@ -117,13 +146,15 @@ async function bootstrap() { // Replace dashboard api url before serving if (configService.get('production')) { const dashboardDir = join(__dirname, '..', 'dashboard') + const dashboardTextExtensions = new Set(['.html', '.js', '.css']) const replaceInDirectory = (dir: string) => { for (const file of readdirSync(dir)) { const filePath = join(dir, file) if (statSync(filePath).isDirectory()) { - if (file === 'assets') { - replaceInDirectory(filePath) - } + replaceInDirectory(filePath) + continue + } + if (!dashboardTextExtensions.has(extname(filePath))) { continue } Logger.log(`Replacing %BOXLITE_BASE_API_URL% in ${filePath}`) @@ -147,6 +178,44 @@ async function bootstrap() { if (isApiEnabled()) { await app.listen(port, host) Logger.log(`🚀 BoxLite API is running on: http://${host}:${port}/${globalPrefix}`) + + // Node's http.Server keep-alive must outlast the ALB's idle_timeout. Per + // AWS ALB User Guide HTTP 502 troubleshooting: "Check whether the keep-alive + // duration of the target is shorter than the idle timeout value of the load + // balancer." Node 18+ defaults keepAliveTimeout to 5s; we set ALB idle to + // "1 hour" (sst.config.ts Api service.loadBalancer). 65 min keepalive and + // 66 min headersTimeout (which must be >= keepAliveTimeout) cover the gap. + const httpServer = app.getHttpServer() + httpServer.keepAliveTimeout = 65 * 60 * 1000 + httpServer.headersTimeout = 66 * 60 * 1000 + + // WebSocket upgrade routing for the BoxLite REST `/attach` endpoint. + // NestJS controllers only fire on Express's `request` event; WS upgrades + // arrive as `upgrade` events on the underlying Node http server and + // bypass middleware/guards. http-proxy-middleware's `ws: true` per-request + // pattern in BoxliteProxyController doesn't catch these — http-proxy + // requires `server.on('upgrade', proxy.upgrade)` to be wired at bootstrap + // (see its README "External WebSocket upgrade" section). Without this, + // Node defaults to closing the socket and the upstream ALB returns 502. + // + // The notification gateway (apps/api/src/notification/gateways/notification.gateway.ts) + // registers `@WebSocketGateway({ path: '/api/socket.io/' })` and attaches + // its own upgrade listener to the same http.Server. We must let those + // paths fall through to socket.io rather than destroying the socket out + // from under it; everything else is unauthenticated traffic and gets + // closed here. + const wsProxy = app.get(BoxliteWsProxyService) + httpServer.on('upgrade', (req: IncomingMessage, socket: Socket, head: Buffer) => { + if (wsProxy.matchAttachPath(req.url)) { + void wsProxy.upgrade(req, socket, head) + return + } + if (req.url?.startsWith('/api/socket.io/')) { + // Handled by NotificationGateway's socket.io upgrade listener. + return + } + socket.destroy() + }) } else { await app.init() app.flushLogs() @@ -187,8 +256,8 @@ async function createAdminApiKey(app: INestApplication, apiKeyName: string) { const apiKeyService = app.get(ApiKeyService) const organizationService = app.get(OrganizationService) - const personalOrg = await organizationService.findPersonal(BOXLITE_ADMIN_USER_ID) - const { value } = await apiKeyService.createApiKey(personalOrg.id, BOXLITE_ADMIN_USER_ID, apiKeyName, []) + const defaultOrg = await organizationService.findDefaultForUser(BOXLITE_ADMIN_USER_ID) + const { value } = await apiKeyService.createApiKey(defaultOrg.id, BOXLITE_ADMIN_USER_ID, apiKeyName, []) Logger.log( ` ========================================= diff --git a/apps/api/src/migrations/1741087887225-migration.ts b/apps/api/src/migrations/1741087887225-migration.ts index 541f93745..8c7671e3d 100644 --- a/apps/api/src/migrations/1741087887225-migration.ts +++ b/apps/api/src/migrations/1741087887225-migration.ts @@ -1,86 +1,237 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - import { MigrationInterface, QueryRunner } from 'typeorm' export class Migration1741087887225 implements MigrationInterface { name = 'Migration1741087887225' public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE SCHEMA IF NOT EXISTS "public"`) + await queryRunner.query(`CREATE EXTENSION IF NOT EXISTS "uuid-ossp"`) + + await queryRunner.query(`CREATE TYPE "public"."user_role_enum" AS ENUM('admin', 'user')`) + await queryRunner.query( + `CREATE TYPE "public"."api_key_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:templates', 'delete:templates', 'write:boxes', 'delete:boxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'write:regions', 'delete:regions', 'read:runners', 'write:runners', 'delete:runners', 'read:audit_logs')`, + ) + await queryRunner.query(`CREATE TYPE "public"."region_regiontype_enum" AS ENUM('shared', 'dedicated', 'custom')`) + await queryRunner.query(`CREATE TYPE "public"."organization_invitation_role_enum" AS ENUM('owner', 'member')`) + await queryRunner.query( + `CREATE TYPE "public"."organization_invitation_status_enum" AS ENUM('pending', 'accepted', 'declined', 'cancelled')`, + ) + await queryRunner.query( + `CREATE TYPE "public"."organization_role_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:templates', 'delete:templates', 'write:boxes', 'delete:boxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'write:regions', 'delete:regions', 'read:runners', 'write:runners', 'delete:runners', 'read:audit_logs')`, + ) + await queryRunner.query(`CREATE TYPE "public"."organization_user_role_enum" AS ENUM('owner', 'member')`) + await queryRunner.query( + `CREATE TYPE "public"."volume_state_enum" AS ENUM('creating', 'ready', 'pending_create', 'pending_delete', 'deleting', 'deleted', 'error')`, + ) + await queryRunner.query(`CREATE TYPE "public"."warm_pool_class_enum" AS ENUM('small', 'medium', 'large')`) + await queryRunner.query(`CREATE TYPE "public"."box_class_enum" AS ENUM('small', 'medium', 'large')`) + await queryRunner.query( + `CREATE TYPE "public"."box_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'error', 'unknown', 'archived', 'archiving', 'resizing')`, + ) + await queryRunner.query( + `CREATE TYPE "public"."box_desiredstate_enum" AS ENUM('destroyed', 'started', 'stopped', 'resized')`, + ) + await queryRunner.query(`CREATE TYPE "public"."runner_class_enum" AS ENUM('small', 'medium', 'large')`) + await queryRunner.query( + `CREATE TYPE "public"."runner_state_enum" AS ENUM('initializing', 'ready', 'disabled', 'decommissioned', 'unresponsive')`, + ) + await queryRunner.query( + `CREATE TYPE "public"."job_status_enum" AS ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED')`, + ) + await queryRunner.query(`CREATE TYPE "public"."job_resourcetype_enum" AS ENUM('BOX', 'ARTIFACT', 'BACKUP')`) + + await queryRunner.query( + `CREATE TABLE "user" ("id" character varying NOT NULL, "name" character varying NOT NULL, "email" character varying NOT NULL DEFAULT '', "emailVerified" boolean NOT NULL DEFAULT false, "keyPair" text, "publicKeys" text NOT NULL, "role" "public"."user_role_enum" NOT NULL DEFAULT 'user', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "user_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "api_key" ("organizationId" uuid NOT NULL, "userId" character varying NOT NULL, "name" character varying NOT NULL, "keyHash" character varying NOT NULL DEFAULT '', "keyPrefix" character varying NOT NULL DEFAULT '', "keySuffix" character varying NOT NULL DEFAULT '', "permissions" "public"."api_key_permissions_enum" array NOT NULL, "createdAt" TIMESTAMP NOT NULL, "lastUsedAt" TIMESTAMP, "expiresAt" TIMESTAMP, CONSTRAINT "api_key_keyHash_unique" UNIQUE ("keyHash"), CONSTRAINT "api_key_organizationId_userId_name_pk" PRIMARY KEY ("organizationId", "userId", "name"))`, + ) + await queryRunner.query( + `CREATE TABLE "webhook_initialization" ("organizationId" character varying NOT NULL, "svixApplicationId" character varying, "lastError" text, "retryCount" integer NOT NULL DEFAULT '0', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "webhook_initialization_organizationId_pk" PRIMARY KEY ("organizationId"))`, + ) + await queryRunner.query( + `CREATE TABLE "region" ("id" character varying NOT NULL, "name" character varying NOT NULL, "organizationId" uuid, "regionType" "public"."region_regiontype_enum" NOT NULL, "enforceQuotas" boolean NOT NULL DEFAULT true, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "proxyUrl" character varying, "toolboxProxyUrl" character varying, "proxyApiKeyHash" character varying, "sshGatewayUrl" character varying, "sshGatewayApiKeyHash" character varying, CONSTRAINT "region_not_custom" CHECK ("organizationId" IS NOT NULL OR "regionType" != 'custom'), CONSTRAINT "region_not_shared" CHECK ("organizationId" IS NULL OR "regionType" != 'shared'), CONSTRAINT "region_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "organization" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "createdBy" character varying NOT NULL, "telemetryEnabled" boolean NOT NULL DEFAULT true, "defaultRegionId" character varying, "max_cpu_per_box" integer NOT NULL DEFAULT '4', "max_memory_per_box" integer NOT NULL DEFAULT '8', "max_disk_per_box" integer NOT NULL DEFAULT '10', "authenticated_rate_limit" integer, "box_create_rate_limit" integer, "box_lifecycle_rate_limit" integer, "authenticated_rate_limit_ttl_seconds" integer, "box_create_rate_limit_ttl_seconds" integer, "box_lifecycle_rate_limit_ttl_seconds" integer, "suspended" boolean NOT NULL DEFAULT false, "suspendedAt" TIMESTAMP WITH TIME ZONE, "suspensionReason" character varying, "suspensionCleanupGracePeriodHours" integer NOT NULL DEFAULT '24', "suspendedUntil" TIMESTAMP WITH TIME ZONE, "template_deactivation_timeout_minutes" integer NOT NULL DEFAULT '20160', "boxLimitedNetworkEgress" boolean NOT NULL DEFAULT false, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "experimentalConfig" jsonb, CONSTRAINT "organization_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "organization_role" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "description" character varying NOT NULL, "permissions" "public"."organization_role_permissions_enum" array NOT NULL, "isGlobal" boolean NOT NULL DEFAULT false, "organizationId" uuid, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "organization_role_id_pk" PRIMARY KEY ("id"), CONSTRAINT "organization_role_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "organization_invitation" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "organizationId" uuid NOT NULL, "email" character varying NOT NULL, "invitedBy" character varying NOT NULL DEFAULT '', "role" "public"."organization_invitation_role_enum" NOT NULL DEFAULT 'member', "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "status" "public"."organization_invitation_status_enum" NOT NULL DEFAULT 'pending', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "organization_invitation_id_pk" PRIMARY KEY ("id"), CONSTRAINT "organization_invitation_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "organization_user" ("organizationId" uuid NOT NULL, "userId" character varying NOT NULL, "role" "public"."organization_user_role_enum" NOT NULL DEFAULT 'member', "isDefaultForUser" boolean NOT NULL DEFAULT false, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "organization_user_organizationId_userId_pk" PRIMARY KEY ("organizationId", "userId"), CONSTRAINT "organization_user_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "volume" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "organizationId" uuid, "name" character varying NOT NULL, "state" "public"."volume_state_enum" NOT NULL DEFAULT 'pending_create', "errorReason" character varying, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "lastUsedAt" TIMESTAMP, CONSTRAINT "volume_organizationId_name_unique" UNIQUE ("organizationId", "name"), CONSTRAINT "volume_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "warm_pool" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "pool" integer NOT NULL, "image" character varying NOT NULL, "target" character varying NOT NULL, "cpu" integer NOT NULL, "mem" integer NOT NULL, "disk" integer NOT NULL, "gpu" integer NOT NULL, "gpuType" character varying NOT NULL, "class" "public"."warm_pool_class_enum" NOT NULL DEFAULT 'small', "osUser" character varying NOT NULL, "errorReason" character varying, "env" text NOT NULL DEFAULT '{}', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "warm_pool_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "box" ("id" character varying(12) NOT NULL, "organizationId" uuid NOT NULL, "name" character varying NOT NULL, "region" character varying NOT NULL, "image" character varying, "runnerId" uuid, "prevRunnerId" uuid, "class" "public"."box_class_enum" NOT NULL DEFAULT 'small', "state" "public"."box_state_enum" NOT NULL DEFAULT 'unknown', "desiredState" "public"."box_desiredstate_enum" NOT NULL DEFAULT 'started', "osUser" character varying NOT NULL, "errorReason" character varying, "recoverable" boolean NOT NULL DEFAULT false, "env" jsonb NOT NULL DEFAULT '{}', "public" boolean NOT NULL DEFAULT false, "networkBlockAll" boolean NOT NULL DEFAULT false, "networkAllowList" character varying, "labels" jsonb, "cpu" integer NOT NULL DEFAULT '2', "gpu" integer NOT NULL DEFAULT '0', "mem" integer NOT NULL DEFAULT '4', "disk" integer NOT NULL DEFAULT '10', "volumes" jsonb NOT NULL DEFAULT '[]', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "autoStopInterval" integer NOT NULL DEFAULT '15', "autoDeleteInterval" integer NOT NULL DEFAULT '-1', "pending" boolean NOT NULL DEFAULT false, "authToken" character varying NOT NULL, "daemonVersion" character varying, CONSTRAINT "box_organizationId_name_unique" UNIQUE ("organizationId", "name"), CONSTRAINT "box_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "box_last_activity" ("boxId" character varying NOT NULL, "lastActivityAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "box_last_activity_boxId_pk" PRIMARY KEY ("boxId"), CONSTRAINT "box_last_activity_boxId_fk" FOREIGN KEY ("boxId") REFERENCES "box"("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "ssh_access" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "boxId" character varying NOT NULL, "token" text NOT NULL, "expiresAt" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "ssh_access_id_pk" PRIMARY KEY ("id"), CONSTRAINT "ssh_access_boxId_fk" FOREIGN KEY ("boxId") REFERENCES "box"("id") ON DELETE CASCADE ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "runner" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "domain" character varying, "apiUrl" character varying, "proxyUrl" character varying, "apiKey" character varying NOT NULL, "cpu" double precision NOT NULL DEFAULT '0', "memoryGiB" double precision NOT NULL DEFAULT '0', "diskGiB" double precision NOT NULL DEFAULT '0', "gpu" integer, "gpuType" character varying, "class" "public"."runner_class_enum" NOT NULL DEFAULT 'small', "currentCpuLoadAverage" double precision NOT NULL DEFAULT '0', "currentCpuUsagePercentage" double precision NOT NULL DEFAULT '0', "currentMemoryUsagePercentage" double precision NOT NULL DEFAULT '0', "currentDiskUsagePercentage" double precision NOT NULL DEFAULT '0', "currentAllocatedCpu" double precision NOT NULL DEFAULT '0', "currentAllocatedMemoryGiB" double precision NOT NULL DEFAULT '0', "currentAllocatedDiskGiB" double precision NOT NULL DEFAULT '0', "currentStartedBoxes" integer NOT NULL DEFAULT '0', "availabilityScore" integer NOT NULL DEFAULT '0', "region" character varying NOT NULL, "name" character varying NOT NULL, "state" "public"."runner_state_enum" NOT NULL DEFAULT 'initializing', "appVersion" character varying DEFAULT 'v0.0.0-dev', "apiVersion" character varying NOT NULL DEFAULT '0', "lastChecked" TIMESTAMP WITH TIME ZONE, "unschedulable" boolean NOT NULL DEFAULT false, "draining" boolean NOT NULL DEFAULT false, "serviceHealth" jsonb, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "runner_region_name_unique" UNIQUE ("region", "name"), CONSTRAINT "runner_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "job" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "version" integer NOT NULL, "type" character varying NOT NULL, "status" "public"."job_status_enum" NOT NULL DEFAULT 'PENDING', "runnerId" character varying NOT NULL, "resourceType" "public"."job_resourcetype_enum" NOT NULL, "resourceId" character varying NOT NULL, "payload" character varying, "resultMetadata" character varying, "traceContext" jsonb, "errorMessage" text, "startedAt" TIMESTAMP WITH TIME ZONE, "completedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "job_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "audit_log" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "actorId" character varying NOT NULL, "actorEmail" character varying NOT NULL DEFAULT '', "organizationId" character varying, "action" character varying NOT NULL, "targetType" character varying, "targetId" character varying, "statusCode" integer, "errorMessage" character varying, "ipAddress" character varying, "userAgent" text, "source" character varying, "metadata" jsonb, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "audit_log_id_pk" PRIMARY KEY ("id"))`, + ) + await queryRunner.query( + `CREATE TABLE "organization_role_assignment_invitation" ("invitationId" uuid NOT NULL, "roleId" uuid NOT NULL, CONSTRAINT "organization_role_assignment_invitation_invitationId_roleId_pk" PRIMARY KEY ("invitationId", "roleId"), CONSTRAINT "organization_role_assignment_invitation_invitationId_fk" FOREIGN KEY ("invitationId") REFERENCES "organization_invitation"("id") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, + ) + await queryRunner.query( + `CREATE TABLE "organization_role_assignment" ("organizationId" uuid NOT NULL, "userId" character varying NOT NULL, "roleId" uuid NOT NULL, CONSTRAINT "organization_role_assignment_organizationId_userId_roleId_pk" PRIMARY KEY ("organizationId", "userId", "roleId"), CONSTRAINT "organization_role_assignment_organizationId_userId_fk" FOREIGN KEY ("organizationId", "userId") REFERENCES "organization_user"("organizationId","userId") ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION)`, + ) + + await queryRunner.query(`CREATE INDEX "api_key_org_user_idx" ON "api_key" ("organizationId", "userId")`) + await queryRunner.query( + `CREATE INDEX "idx_region_custom" ON "region" ("organizationId") WHERE "regionType" = 'custom'`, + ) + await queryRunner.query( + `CREATE UNIQUE INDEX "region_sshGatewayApiKeyHash_unique" ON "region" ("sshGatewayApiKeyHash") WHERE "sshGatewayApiKeyHash" IS NOT NULL`, + ) await queryRunner.query( - `CREATE TABLE "user" ("id" character varying NOT NULL, "name" character varying NOT NULL, "keyPair" text, "publicKeys" text NOT NULL, "total_cpu_quota" integer NOT NULL DEFAULT '10', "total_memory_quota" integer NOT NULL DEFAULT '40', "total_disk_quota" integer NOT NULL DEFAULT '100', "max_cpu_per_workspace" integer NOT NULL DEFAULT '2', "max_memory_per_workspace" integer NOT NULL DEFAULT '4', "max_disk_per_workspace" integer NOT NULL DEFAULT '10', "max_concurrent_workspaces" integer NOT NULL DEFAULT '10', "workspace_quota" integer NOT NULL DEFAULT '0', "image_quota" integer NOT NULL DEFAULT '5', "max_image_size" integer NOT NULL DEFAULT '2', "total_image_size" integer NOT NULL DEFAULT '5', CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`, + `CREATE UNIQUE INDEX "region_proxyApiKeyHash_unique" ON "region" ("proxyApiKeyHash") WHERE "proxyApiKeyHash" IS NOT NULL`, ) await queryRunner.query( - `CREATE TABLE "team" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, CONSTRAINT "PK_f57d8293406df4af348402e4b74" PRIMARY KEY ("id"))`, + `CREATE UNIQUE INDEX "region_organizationId_null_name_unique" ON "region" ("name") WHERE "organizationId" IS NULL`, ) await queryRunner.query( - `CREATE TABLE "workspace_usage_periods" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "workspaceId" character varying NOT NULL, "startAt" TIMESTAMP NOT NULL, "endAt" TIMESTAMP, "cpu" double precision NOT NULL, "gpu" double precision NOT NULL, "mem" double precision NOT NULL, "disk" double precision NOT NULL, "storage" double precision NOT NULL, "region" character varying NOT NULL, CONSTRAINT "PK_b8d71f79ee638064397f678e877" PRIMARY KEY ("id"))`, + `CREATE UNIQUE INDEX "region_organizationId_name_unique" ON "region" ("organizationId", "name") WHERE "organizationId" IS NOT NULL`, ) - await queryRunner.query(`CREATE TYPE "node_class_enum" AS ENUM('small', 'medium', 'large')`) - await queryRunner.query(`CREATE TYPE "node_region_enum" AS ENUM('eu', 'us', 'asia')`) await queryRunner.query( - `CREATE TYPE "node_state_enum" AS ENUM('initializing', 'ready', 'disabled', 'decommissioned', 'unresponsive')`, + `CREATE UNIQUE INDEX "organization_user_default_user_unique" ON "organization_user" ("userId") WHERE "isDefaultForUser" = true`, ) await queryRunner.query( - `CREATE TABLE "node" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "domain" character varying NOT NULL, "apiUrl" character varying NOT NULL, "apiKey" character varying NOT NULL, "cpu" integer NOT NULL, "memory" integer NOT NULL, "disk" integer NOT NULL, "gpu" integer NOT NULL, "gpuType" character varying NOT NULL, "class" "node_class_enum" NOT NULL DEFAULT 'small', "used" integer NOT NULL DEFAULT '0', "capacity" integer NOT NULL, "region" "node_region_enum" NOT NULL, "state" "node_state_enum" NOT NULL DEFAULT 'initializing', "lastChecked" TIMESTAMP, "unschedulable" boolean NOT NULL DEFAULT false, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_330d74ac3d0e349b4c73c62ad6d" UNIQUE ("domain"), CONSTRAINT "PK_8c8caf5f29d25264abe9eaf94dd" PRIMARY KEY ("id"))`, + `CREATE INDEX "warm_pool_find_idx" ON "warm_pool" ("image", "target", "class", "cpu", "mem", "disk", "gpu", "osUser", "env")`, ) + await queryRunner.query(`CREATE INDEX "idx_box_authtoken" ON "box" ("authToken")`) + await queryRunner.query(`CREATE INDEX "box_image_idx" ON "box" ("image")`) + await queryRunner.query(`CREATE INDEX "box_pending_idx" ON "box" ("id") WHERE "pending" = true`) await queryRunner.query( - `CREATE TYPE "image_node_state_enum" AS ENUM('pulling_image', 'ready', 'error', 'removing')`, + `CREATE INDEX "box_active_only_idx" ON "box" ("id") WHERE "state" <> ALL (ARRAY['destroyed'::box_state_enum, 'archived'::box_state_enum])`, ) await queryRunner.query( - `CREATE TABLE "image_node" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "state" "image_node_state_enum" NOT NULL DEFAULT 'pulling_image', "errorReason" character varying, "image" character varying NOT NULL, "internalImageName" character varying NOT NULL DEFAULT '', "nodeId" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_6c66fc8bd2b9fb41362a50fddd0" PRIMARY KEY ("id"))`, + `CREATE INDEX "box_runner_state_desired_idx" ON "box" ("runnerId", "state", "desiredState") WHERE "pending" = false`, ) + await queryRunner.query(`CREATE INDEX "box_resources_idx" ON "box" ("cpu", "mem", "disk", "gpu")`) + await queryRunner.query(`CREATE INDEX "box_region_idx" ON "box" ("region")`) + await queryRunner.query(`CREATE INDEX "box_organizationid_idx" ON "box" ("organizationId")`) + await queryRunner.query(`CREATE INDEX "box_runner_state_idx" ON "box" ("runnerId", "state")`) + await queryRunner.query(`CREATE INDEX "box_runnerid_idx" ON "box" ("runnerId")`) + await queryRunner.query(`CREATE INDEX "box_desiredstate_idx" ON "box" ("desiredState")`) + await queryRunner.query(`CREATE INDEX "box_state_idx" ON "box" ("state")`) + await queryRunner.query(`CREATE INDEX "box_labels_gin_full_idx" ON "box" USING gin ("labels" jsonb_path_ops)`) + await queryRunner.query(`CREATE INDEX "idx_box_volumes_gin" ON "box" USING gin ("volumes" jsonb_path_ops)`) await queryRunner.query( - `CREATE TYPE "image_state_enum" AS ENUM('pending', 'pulling_image', 'pending_validation', 'validating', 'active', 'error', 'removing')`, + `CREATE INDEX "runner_state_unschedulable_region_index" ON "runner" ("state", "unschedulable", "region")`, ) await queryRunner.query( - `CREATE TABLE "image" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "userId" character varying NOT NULL, "general" boolean NOT NULL DEFAULT false, "name" character varying NOT NULL, "internalName" character varying, "enabled" boolean NOT NULL DEFAULT true, "state" "image_state_enum" NOT NULL DEFAULT 'pending', "errorReason" character varying, "size" double precision, "entrypoint" character varying, "internalRegistryId" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "lastUsedAt" TIMESTAMP, CONSTRAINT "UQ_9db6fbe71409d80375c32826db3" UNIQUE ("userId", "name"), CONSTRAINT "PK_d6db1ab4ee9ad9dbe86c64e4cc3" PRIMARY KEY ("id"))`, + `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_BACKUP_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL AND "type" = 'CREATE_BACKUP'`, ) - await queryRunner.query(`CREATE TYPE "workspace_region_enum" AS ENUM('eu', 'us', 'asia')`) - await queryRunner.query(`CREATE TYPE "workspace_class_enum" AS ENUM('small', 'medium', 'large')`) await queryRunner.query( - `CREATE TYPE "workspace_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'resizing', 'error', 'unknown', 'pulling_image', 'archiving', 'archived')`, + `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL AND "type" != 'CREATE_BACKUP'`, ) + await queryRunner.query(`CREATE INDEX "job_resourceType_resourceId_index" ON "job" ("resourceType", "resourceId")`) + await queryRunner.query(`CREATE INDEX "job_status_createdAt_index" ON "job" ("status", "createdAt")`) + await queryRunner.query(`CREATE INDEX "job_runnerId_status_index" ON "job" ("runnerId", "status")`) await queryRunner.query( - `CREATE TYPE "workspace_desiredstate_enum" AS ENUM('destroyed', 'started', 'stopped', 'resized', 'archived')`, + `CREATE INDEX "audit_log_organizationId_createdAt_index" ON "audit_log" ("organizationId", "createdAt")`, ) + await queryRunner.query(`CREATE INDEX "audit_log_createdAt_index" ON "audit_log" ("createdAt")`) await queryRunner.query( - `CREATE TYPE "workspace_snapshotstate_enum" AS ENUM('None', 'Pending', 'InProgress', 'Completed', 'Error')`, + `CREATE INDEX "organization_role_assignment_invitation_invitationId_index" ON "organization_role_assignment_invitation" ("invitationId")`, ) await queryRunner.query( - `CREATE TABLE "workspace" ("id" character varying NOT NULL, "name" character varying NOT NULL, "userId" character varying NOT NULL, "region" "workspace_region_enum" NOT NULL DEFAULT 'eu', "nodeId" uuid, "prevNodeId" uuid, "class" "workspace_class_enum" NOT NULL DEFAULT 'small', "state" "workspace_state_enum" NOT NULL DEFAULT 'unknown', "desiredState" "workspace_desiredstate_enum" NOT NULL DEFAULT 'started', "image" character varying NOT NULL, "osUser" character varying NOT NULL, "errorReason" character varying, "env" text NOT NULL DEFAULT '{}', "public" boolean NOT NULL DEFAULT false, "labels" jsonb, "snapshotRegistryId" character varying, "snapshotImage" character varying, "lastSnapshotAt" TIMESTAMP, "snapshotState" "workspace_snapshotstate_enum" NOT NULL DEFAULT 'None', "existingSnapshotImages" jsonb NOT NULL DEFAULT '[]', "cpu" integer NOT NULL DEFAULT '2', "gpu" integer NOT NULL DEFAULT '0', "mem" integer NOT NULL DEFAULT '4', "disk" integer NOT NULL DEFAULT '10', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "lastActivityAt" TIMESTAMP, "autoStopInterval" integer NOT NULL DEFAULT '15', CONSTRAINT "PK_ca86b6f9b3be5fe26d307d09b49" PRIMARY KEY ("id"))`, + `CREATE INDEX "organization_role_assignment_invitation_roleId_index" ON "organization_role_assignment_invitation" ("roleId")`, ) await queryRunner.query( - `CREATE TABLE "docker_registry" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "url" character varying NOT NULL, "username" character varying NOT NULL, "password" character varying NOT NULL, "isDefault" boolean NOT NULL DEFAULT false, "project" character varying NOT NULL, "userId" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_4ad72294240279415eb57799798" PRIMARY KEY ("id"))`, + `CREATE INDEX "organization_role_assignment_organizationId_userId_index" ON "organization_role_assignment" ("organizationId", "userId")`, ) await queryRunner.query( - `CREATE TABLE "api_key" ("userId" character varying NOT NULL, "name" character varying NOT NULL, "value" character varying NOT NULL, "createdAt" TIMESTAMP NOT NULL, CONSTRAINT "UQ_4b0873b633484d5de20b2d8f852" UNIQUE ("value"), CONSTRAINT "PK_1df0337a701df00e9b2a16c8a0b" PRIMARY KEY ("userId", "name"))`, + `CREATE INDEX "organization_role_assignment_roleId_index" ON "organization_role_assignment" ("roleId")`, ) } public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX "public"."organization_role_assignment_roleId_index"`) + await queryRunner.query(`DROP INDEX "public"."organization_role_assignment_organizationId_userId_index"`) + await queryRunner.query(`DROP TABLE "organization_role_assignment"`) + await queryRunner.query(`DROP INDEX "public"."organization_role_assignment_invitation_roleId_index"`) + await queryRunner.query(`DROP INDEX "public"."organization_role_assignment_invitation_invitationId_index"`) + await queryRunner.query(`DROP TABLE "organization_role_assignment_invitation"`) + await queryRunner.query(`DROP INDEX "public"."audit_log_createdAt_index"`) + await queryRunner.query(`DROP INDEX "public"."audit_log_organizationId_createdAt_index"`) + await queryRunner.query(`DROP TABLE "audit_log"`) + await queryRunner.query(`DROP INDEX "public"."job_runnerId_status_index"`) + await queryRunner.query(`DROP INDEX "public"."job_status_createdAt_index"`) + await queryRunner.query(`DROP INDEX "public"."job_resourceType_resourceId_index"`) + await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_JOB"`) + await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_BACKUP_JOB"`) + await queryRunner.query(`DROP TABLE "job"`) + await queryRunner.query(`DROP TYPE "public"."job_resourcetype_enum"`) + await queryRunner.query(`DROP TYPE "public"."job_status_enum"`) + await queryRunner.query(`DROP INDEX "public"."runner_state_unschedulable_region_index"`) + await queryRunner.query(`DROP TABLE "runner"`) + await queryRunner.query(`DROP TYPE "public"."runner_state_enum"`) + await queryRunner.query(`DROP TYPE "public"."runner_class_enum"`) + await queryRunner.query(`DROP TABLE "ssh_access"`) + await queryRunner.query(`DROP TABLE "box_last_activity"`) + await queryRunner.query(`DROP INDEX "public"."idx_box_volumes_gin"`) + await queryRunner.query(`DROP INDEX "public"."box_labels_gin_full_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_state_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_desiredstate_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_runnerid_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_runner_state_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_organizationid_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_region_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_resources_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_runner_state_desired_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_active_only_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_pending_idx"`) + await queryRunner.query(`DROP INDEX "public"."box_image_idx"`) + await queryRunner.query(`DROP INDEX "public"."idx_box_authtoken"`) + await queryRunner.query(`DROP TABLE "box"`) + await queryRunner.query(`DROP TYPE "public"."box_desiredstate_enum"`) + await queryRunner.query(`DROP TYPE "public"."box_state_enum"`) + await queryRunner.query(`DROP TYPE "public"."box_class_enum"`) + await queryRunner.query(`DROP INDEX "public"."warm_pool_find_idx"`) + await queryRunner.query(`DROP TABLE "warm_pool"`) + await queryRunner.query(`DROP TYPE "public"."warm_pool_class_enum"`) + await queryRunner.query(`DROP TABLE "volume"`) + await queryRunner.query(`DROP TYPE "public"."volume_state_enum"`) + await queryRunner.query(`DROP INDEX "public"."organization_user_default_user_unique"`) + await queryRunner.query(`DROP TABLE "organization_user"`) + await queryRunner.query(`DROP TYPE "public"."organization_user_role_enum"`) + await queryRunner.query(`DROP TABLE "organization_invitation"`) + await queryRunner.query(`DROP TYPE "public"."organization_invitation_status_enum"`) + await queryRunner.query(`DROP TYPE "public"."organization_invitation_role_enum"`) + await queryRunner.query(`DROP TABLE "organization_role"`) + await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum"`) + await queryRunner.query(`DROP TABLE "organization"`) + await queryRunner.query(`DROP INDEX "public"."region_organizationId_name_unique"`) + await queryRunner.query(`DROP INDEX "public"."region_organizationId_null_name_unique"`) + await queryRunner.query(`DROP INDEX "public"."region_proxyApiKeyHash_unique"`) + await queryRunner.query(`DROP INDEX "public"."region_sshGatewayApiKeyHash_unique"`) + await queryRunner.query(`DROP INDEX "public"."idx_region_custom"`) + await queryRunner.query(`DROP TABLE "region"`) + await queryRunner.query(`DROP TYPE "public"."region_regiontype_enum"`) + await queryRunner.query(`DROP TABLE "webhook_initialization"`) + await queryRunner.query(`DROP INDEX "public"."api_key_org_user_idx"`) await queryRunner.query(`DROP TABLE "api_key"`) - await queryRunner.query(`DROP TABLE "docker_registry"`) - await queryRunner.query(`DROP TABLE "workspace"`) - await queryRunner.query(`DROP TYPE "workspace_snapshotstate_enum"`) - await queryRunner.query(`DROP TYPE "workspace_desiredstate_enum"`) - await queryRunner.query(`DROP TYPE "workspace_state_enum"`) - await queryRunner.query(`DROP TYPE "workspace_class_enum"`) - await queryRunner.query(`DROP TYPE "workspace_region_enum"`) - await queryRunner.query(`DROP TABLE "image"`) - await queryRunner.query(`DROP TYPE "image_state_enum"`) - await queryRunner.query(`DROP TABLE "image_node"`) - await queryRunner.query(`DROP TYPE "image_node_state_enum"`) - await queryRunner.query(`DROP TABLE "node"`) - await queryRunner.query(`DROP TYPE "node_state_enum"`) - await queryRunner.query(`DROP TYPE "node_region_enum"`) - await queryRunner.query(`DROP TYPE "node_class_enum"`) - await queryRunner.query(`DROP TABLE "workspace_usage_periods"`) - await queryRunner.query(`DROP TABLE "team"`) + await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum"`) await queryRunner.query(`DROP TABLE "user"`) + await queryRunner.query(`DROP TYPE "public"."user_role_enum"`) } } diff --git a/apps/api/src/migrations/1741088165704-migration.ts b/apps/api/src/migrations/1741088165704-migration.ts deleted file mode 100644 index 9de051ebb..000000000 --- a/apps/api/src/migrations/1741088165704-migration.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1741088165704 implements MigrationInterface { - name = 'Migration1741088165704' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "internalRegistryId"`) - await queryRunner.query( - `CREATE TYPE "public"."docker_registry_registrytype_enum" AS ENUM('internal', 'user', 'public', 'transient')`, - ) - await queryRunner.query( - `ALTER TABLE "docker_registry" ADD "registryType" "public"."docker_registry_registrytype_enum" NOT NULL DEFAULT 'internal'`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" DROP COLUMN "registryType"`) - await queryRunner.query(`DROP TYPE "public"."docker_registry_registrytype_enum"`) - await queryRunner.query(`ALTER TABLE "image" ADD "internalRegistryId" character varying`) - } -} diff --git a/apps/api/src/migrations/1741088883000-migration.ts b/apps/api/src/migrations/1741088883000-migration.ts deleted file mode 100644 index ddca09ca8..000000000 --- a/apps/api/src/migrations/1741088883000-migration.ts +++ /dev/null @@ -1,487 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1741088883000 implements MigrationInterface { - name = 'Migration1741088883000' - - public async up(queryRunner: QueryRunner): Promise { - // organizations - await queryRunner.query( - `CREATE TABLE "organization" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "createdBy" character varying NOT NULL, "personal" boolean NOT NULL DEFAULT false, "telemetryEnabled" boolean NOT NULL DEFAULT true, "total_cpu_quota" integer NOT NULL DEFAULT '10', "total_memory_quota" integer NOT NULL DEFAULT '40', "total_disk_quota" integer NOT NULL DEFAULT '100', "max_cpu_per_workspace" integer NOT NULL DEFAULT '2', "max_memory_per_workspace" integer NOT NULL DEFAULT '4', "max_disk_per_workspace" integer NOT NULL DEFAULT '10', "max_concurrent_workspaces" integer NOT NULL DEFAULT '10', "workspace_quota" integer NOT NULL DEFAULT '0', "image_quota" integer NOT NULL DEFAULT '5', "max_image_size" integer NOT NULL DEFAULT '2', "total_image_size" integer NOT NULL DEFAULT '5', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "organization_id_pk" PRIMARY KEY ("id"))`, - ) - - // organization users - await queryRunner.query(`CREATE TYPE "public"."organization_user_role_enum" AS ENUM('owner', 'member')`) - await queryRunner.query( - `CREATE TABLE "organization_user" ("organizationId" uuid NOT NULL, "userId" character varying NOT NULL, "role" "public"."organization_user_role_enum" NOT NULL DEFAULT 'member', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "organization_user_organizationId_userId_pk" PRIMARY KEY ("organizationId", "userId"))`, - ) - await queryRunner.query( - `ALTER TABLE "organization_user" ADD CONSTRAINT "organization_user_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - - // organization invitations - await queryRunner.query(`CREATE TYPE "public"."organization_invitation_role_enum" AS ENUM('owner', 'member')`) - await queryRunner.query( - `CREATE TYPE "public"."organization_invitation_status_enum" AS ENUM('pending', 'accepted', 'declined', 'cancelled')`, - ) - await queryRunner.query( - `CREATE TABLE "organization_invitation" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "organizationId" uuid NOT NULL, "email" character varying NOT NULL, "role" "public"."organization_invitation_role_enum" NOT NULL DEFAULT 'member', "expiresAt" TIMESTAMP NOT NULL, "status" "public"."organization_invitation_status_enum" NOT NULL DEFAULT 'pending', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "organization_invitation_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query( - `ALTER TABLE "organization_invitation" ADD CONSTRAINT "organization_invitation_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - - // organization roles - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes')`, - ) - await queryRunner.query( - `CREATE TABLE "organization_role" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "description" character varying NOT NULL, "permissions" "public"."organization_role_permissions_enum" array NOT NULL, "isGlobal" boolean NOT NULL DEFAULT false, "organizationId" uuid, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "organization_role_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ADD CONSTRAINT "organization_role_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - - // organization role assignments for members - await queryRunner.query( - `CREATE TABLE "organization_role_assignment" ("organizationId" uuid NOT NULL, "userId" character varying NOT NULL, "roleId" uuid NOT NULL, CONSTRAINT "organization_role_assignment_organizationId_userId_roleId_pk" PRIMARY KEY ("organizationId", "userId", "roleId"))`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_organizationId_userId_fk" FOREIGN KEY ("organizationId", "userId") REFERENCES "organization_user"("organizationId","userId") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `CREATE INDEX "organization_role_assignment_organizationId_userId_index" ON "organization_role_assignment" ("organizationId", "userId") `, - ) - await queryRunner.query( - `CREATE INDEX "organization_role_assignment_roleId_index" ON "organization_role_assignment" ("roleId") `, - ) - - // organization role assignments for invitations - await queryRunner.query( - `CREATE TABLE "organization_role_assignment_invitation" ("invitationId" uuid NOT NULL, "roleId" uuid NOT NULL, CONSTRAINT "organization_role_assignment_invitation_invitationId_roleId_pk" PRIMARY KEY ("invitationId", "roleId"))`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_invitationId_fk" FOREIGN KEY ("invitationId") REFERENCES "organization_invitation"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `CREATE INDEX "organization_role_assignment_invitation_invitationId_index" ON "organization_role_assignment_invitation" ("invitationId") `, - ) - await queryRunner.query( - `CREATE INDEX "organization_role_assignment_invitation_roleId_index" ON "organization_role_assignment_invitation" ("roleId") `, - ) - - // create personal organizations - await queryRunner.query(` - INSERT INTO "organization" ( - name, - personal, - "createdBy", - total_cpu_quota, - total_memory_quota, - total_disk_quota, - max_cpu_per_workspace, - max_memory_per_workspace, - max_disk_per_workspace, - max_concurrent_workspaces, - workspace_quota, - image_quota, - max_image_size, - total_image_size - ) - SELECT - 'Personal', - true, - u.id, - u.total_cpu_quota, - u.total_memory_quota, - u.total_disk_quota, - u.max_cpu_per_workspace, - u.max_memory_per_workspace, - u.max_disk_per_workspace, - u.max_concurrent_workspaces, - u.workspace_quota, - u.image_quota, - u.max_image_size, - u.total_image_size - FROM "user" u - `) - await queryRunner.query(` - INSERT INTO "organization_user" ("organizationId", "userId", role) - SELECT - o.id, - o."createdBy", - 'owner' - FROM "organization" o - WHERE o.personal = true - `) - - // drop user quotas - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "total_cpu_quota"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "total_memory_quota"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "total_disk_quota"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "max_cpu_per_workspace"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "max_memory_per_workspace"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "max_disk_per_workspace"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "max_concurrent_workspaces"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "workspace_quota"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "image_quota"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "max_image_size"`) - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "total_image_size"`) - - // move existing api keys to corresponding personal organizations - await queryRunner.query(`ALTER TABLE "api_key" ADD "organizationId" uuid NULL`) - await queryRunner.query(` - UPDATE "api_key" ak - SET "organizationId" = ( - SELECT o.id - FROM "organization" o - WHERE o."createdBy" = ak."userId" - AND o.personal = true - LIMIT 1 - ) - `) - await queryRunner.query(`ALTER TABLE "api_key" ALTER COLUMN "organizationId" SET NOT NULL`) - - // update api key primary key - await queryRunner.query(` - DO $$ - DECLARE - constraint_name text; - BEGIN - SELECT tc.constraint_name INTO constraint_name - FROM information_schema.table_constraints tc - WHERE tc.table_name = 'api_key' - AND tc.constraint_type = 'PRIMARY KEY'; - IF constraint_name IS NOT NULL THEN - EXECUTE format('ALTER TABLE "api_key" DROP CONSTRAINT "%s"', constraint_name); - END IF; - END $$; - `) - await queryRunner.query( - `ALTER TABLE "api_key" ADD CONSTRAINT "api_key_userId_name_organizationId_pk" PRIMARY KEY ("userId", "name", "organizationId")`, - ) - - // api key permissions - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes')`, - ) - await queryRunner.query(`ALTER TABLE "api_key" ADD "permissions" "public"."api_key_permissions_enum" array NULL`) - await queryRunner.query(` - UPDATE api_key - SET permissions = ARRAY[ - 'write:registries', - 'delete:registries', - 'write:images', - 'delete:images', - 'write:sandboxes', - 'delete:sandboxes' - ]::api_key_permissions_enum[] - `) - await queryRunner.query(`ALTER TABLE "api_key" ALTER COLUMN "permissions" SET NOT NULL`) - - // modify docker registry type enum - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" DROP DEFAULT`) - await queryRunner.query( - `ALTER TYPE "public"."docker_registry_registrytype_enum" RENAME TO "docker_registry_registrytype_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."docker_registry_registrytype_enum" AS ENUM('internal', 'organization', 'public', 'transient')`, - ) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION migrate_registry_type(old_type text) - RETURNS "public"."docker_registry_registrytype_enum" AS $$ - BEGIN - IF old_type = 'user' THEN - RETURN 'organization'::"public"."docker_registry_registrytype_enum"; - ELSE - RETURN old_type::"public"."docker_registry_registrytype_enum"; - END IF; - END; - $$ LANGUAGE plpgsql; - `) - await queryRunner.query(` - ALTER TABLE "docker_registry" - ALTER COLUMN "registryType" TYPE "public"."docker_registry_registrytype_enum" - USING migrate_registry_type("registryType"::text) - `) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" SET DEFAULT 'internal'`) - await queryRunner.query(`DROP TYPE "public"."docker_registry_registrytype_enum_old"`) - await queryRunner.query(`DROP FUNCTION migrate_registry_type`) - - // move existing docker registries to corresponding personal organizations - await queryRunner.query(`ALTER TABLE "docker_registry" ADD "organizationId" uuid NULL`) - await queryRunner.query(`UPDATE "docker_registry" SET "organizationId" = NULL WHERE "userId" = 'system'`) - await queryRunner.query(` - UPDATE "docker_registry" dr - SET "organizationId" = ( - SELECT o.id - FROM "organization" o - WHERE o."createdBy" = dr."userId" - AND o.personal = true - LIMIT 1 - ) - `) - await queryRunner.query(`ALTER TABLE "docker_registry" DROP COLUMN "userId"`) - - // move existing images to corresponding personal organizations - await queryRunner.query(`ALTER TABLE "image" ADD "organizationId" uuid NULL`) - await queryRunner.query(` - UPDATE "image" i - SET "organizationId" = ( - SELECT o.id - FROM "organization" o - WHERE o."createdBy" = i."userId" - AND o.personal = true - LIMIT 1 - ) - `) - - // update image unique constraint - await queryRunner.query(` - DO $$ - DECLARE - constraint_name text; - BEGIN - SELECT tc.constraint_name INTO constraint_name - FROM information_schema.table_constraints tc - WHERE tc.table_name = 'image' - AND tc.constraint_type = 'UNIQUE' - AND tc.constraint_name LIKE '%name%'; - - IF constraint_name IS NOT NULL THEN - EXECUTE format('ALTER TABLE "image" DROP CONSTRAINT "%s"', constraint_name); - END IF; - END $$; - `) - await queryRunner.query( - `ALTER TABLE "image" ADD CONSTRAINT "image_organizationId_name_unique" UNIQUE ("organizationId", "name")`, - ) - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "userId"`) - - // move existing workspaces to corresponding personal organizations - await queryRunner.query(`ALTER TABLE "workspace" ADD "organizationId" uuid NULL`) - await queryRunner.query(` - UPDATE "workspace" w - SET "organizationId" = ( - SELECT o.id - FROM "organization" o - WHERE o."createdBy" = w."userId" - AND o.personal = true - LIMIT 1 - ) - WHERE w."userId" != 'unassigned' - `) - await queryRunner.query(` - UPDATE "workspace" w - SET "organizationId" = '00000000-0000-0000-0000-000000000000' - WHERE w."userId" = 'unassigned' - `) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "organizationId" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "userId"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // workspaces - await queryRunner.query(`ALTER TABLE "workspace" ADD "userId" character varying NULL`) - await queryRunner.query(` - UPDATE "workspace" w - SET "userId" = 'unassigned' - WHERE w."organizationId" = '00000000-0000-0000-0000-000000000000' - `) - await queryRunner.query(` - UPDATE "workspace" w - SET "userId" = ( - SELECT o."createdBy" - FROM "organization" o - WHERE o.id = w."organizationId" - ) - WHERE w."organizationId" != '00000000-0000-0000-0000-000000000000' - `) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "userId" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "organizationId"`) - - // images - await queryRunner.query(`ALTER TABLE "image" ADD "userId" character varying NULL`) - await queryRunner.query(` - UPDATE "image" i - SET "userId" = ( - SELECT o."createdBy" - FROM "organization" o - WHERE o.id = i."organizationId" - ) - `) - await queryRunner.query(`ALTER TABLE "image" ALTER COLUMN "userId" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "image" DROP CONSTRAINT "image_organizationId_name_unique"`) - await queryRunner.query(`ALTER TABLE "image" ADD CONSTRAINT "image_userId_name_unique" UNIQUE ("userId", "name")`) - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "organizationId"`) - - // docker registries - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" DROP DEFAULT`) - await queryRunner.query( - `ALTER TYPE "public"."docker_registry_registrytype_enum" RENAME TO "docker_registry_registrytype_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."docker_registry_registrytype_enum" AS ENUM('internal', 'user', 'public', 'transient')`, - ) - await queryRunner.query(` - CREATE OR REPLACE FUNCTION rollback_registry_type(new_type text) - RETURNS "public"."docker_registry_registrytype_enum" AS $$ - BEGIN - IF new_type = 'organization' THEN - RETURN 'user'::"public"."docker_registry_registrytype_enum"; - ELSE - RETURN new_type::"public"."docker_registry_registrytype_enum"; - END IF; - END; - $$ LANGUAGE plpgsql; - `) - await queryRunner.query(` - ALTER TABLE "docker_registry" - ALTER COLUMN "registryType" TYPE "public"."docker_registry_registrytype_enum" - USING rollback_registry_type("registryType"::text) - `) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" SET DEFAULT 'internal'`) - await queryRunner.query(`DROP TYPE "public"."docker_registry_registrytype_enum_old"`) - await queryRunner.query(`DROP FUNCTION rollback_registry_type`) - await queryRunner.query(`ALTER TABLE "docker_registry" ADD "userId" character varying NULL`) - await queryRunner.query(` - UPDATE "docker_registry" dr - SET "userId" = ( - SELECT o."createdBy" - FROM "organization" o - WHERE o.id = dr."organizationId" - ) - `) - await queryRunner.query(`ALTER TABLE "docker_registry" DROP COLUMN "organizationId"`) - - // api keys - await queryRunner.query(`ALTER TABLE "api_key" DROP CONSTRAINT "api_key_userId_name_organizationId_pk"`) - await queryRunner.query( - `ALTER TABLE "api_key" ADD CONSTRAINT "api_key_userId_name_pk" PRIMARY KEY ("userId", "name")`, - ) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "organizationId"`) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "permissions"`) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum"`) - - // user quotas - await queryRunner.query(`ALTER TABLE "user" ADD "total_cpu_quota" integer NOT NULL DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "user" ADD "total_memory_quota" integer NOT NULL DEFAULT '40'`) - await queryRunner.query(`ALTER TABLE "user" ADD "total_disk_quota" integer NOT NULL DEFAULT '100'`) - await queryRunner.query(`ALTER TABLE "user" ADD "max_cpu_per_workspace" integer NOT NULL DEFAULT '2'`) - await queryRunner.query(`ALTER TABLE "user" ADD "max_memory_per_workspace" integer NOT NULL DEFAULT '4'`) - await queryRunner.query(`ALTER TABLE "user" ADD "max_disk_per_workspace" integer NOT NULL DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "user" ADD "max_concurrent_workspaces" integer NOT NULL DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "user" ADD "workspace_quota" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "user" ADD "image_quota" integer NOT NULL DEFAULT '5'`) - await queryRunner.query(`ALTER TABLE "user" ADD "max_image_size" integer NOT NULL DEFAULT '2'`) - await queryRunner.query(`ALTER TABLE "user" ADD "total_image_size" integer NOT NULL DEFAULT '5'`) - await queryRunner.query(` - UPDATE "user" u - SET - total_cpu_quota = ( - SELECT o.total_cpu_quota - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - total_memory_quota = ( - SELECT o.total_memory_quota - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - total_disk_quota = ( - SELECT o.total_disk_quota - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - max_cpu_per_workspace = ( - SELECT o.max_cpu_per_workspace - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - max_memory_per_workspace = ( - SELECT o.max_memory_per_workspace - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - max_disk_per_workspace = ( - SELECT o.max_disk_per_workspace - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - max_concurrent_workspaces = ( - SELECT o.max_concurrent_workspaces - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - workspace_quota = ( - SELECT o.workspace_quota - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - image_quota = ( - SELECT o.image_quota - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - max_image_size = ( - SELECT o.max_image_size - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ), - total_image_size = ( - SELECT o.total_image_size - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true - LIMIT 1 - ) - `) - - // drop organization tables and related constraints - await queryRunner.query(`DROP INDEX "organization_role_assignment_invitation_roleId_index"`) - await queryRunner.query(`DROP INDEX "organization_role_assignment_invitation_invitationId_index"`) - await queryRunner.query(`DROP TABLE "organization_role_assignment_invitation"`) - await queryRunner.query(`DROP INDEX "organization_role_assignment_roleId_index"`) - await queryRunner.query(`DROP INDEX "organization_role_assignment_organizationId_userId_index"`) - await queryRunner.query(`DROP TABLE "organization_role_assignment"`) - await queryRunner.query(`DROP TABLE "organization_role"`) - await queryRunner.query(`DROP TYPE "organization_role_permissions_enum"`) - await queryRunner.query(`DROP TABLE "organization_invitation"`) - await queryRunner.query(`DROP TYPE "organization_invitation_status_enum"`) - await queryRunner.query(`DROP TYPE "organization_invitation_role_enum"`) - await queryRunner.query(`DROP TABLE "organization_user"`) - await queryRunner.query(`DROP TYPE "organization_user_role_enum"`) - await queryRunner.query(`DROP TABLE "organization"`) - } -} diff --git a/apps/api/src/migrations/1741088883001-migration.ts b/apps/api/src/migrations/1741088883001-migration.ts deleted file mode 100644 index 214452085..000000000 --- a/apps/api/src/migrations/1741088883001-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1741088883001 implements MigrationInterface { - name = 'Migration1741088883001' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" ADD "email" character varying NOT NULL DEFAULT ''`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "email"`) - } -} diff --git a/apps/api/src/migrations/1741088883002-migration.ts b/apps/api/src/migrations/1741088883002-migration.ts deleted file mode 100644 index 38fba6775..000000000 --- a/apps/api/src/migrations/1741088883002-migration.ts +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { GlobalOrganizationRolesIds } from '../organization/constants/global-organization-roles.constant' -import { OrganizationResourcePermission } from '../organization/enums/organization-resource-permission.enum' - -export class Migration1741088883002 implements MigrationInterface { - name = 'Migration1741088883002' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.DEVELOPER}', - 'Developer', - 'Grants the ability to create sandboxes and keys in the organization', - ARRAY[ - '${OrganizationResourcePermission.WRITE_SANDBOXES}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.SANDBOXES_ADMIN}', - 'Sandboxes Admin', - 'Grants admin access to sandboxes in the organization', - ARRAY[ - '${OrganizationResourcePermission.WRITE_SANDBOXES}', - '${OrganizationResourcePermission.DELETE_SANDBOXES}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.SNAPSHOTS_ADMIN}', - 'Images Admin', - 'Grants admin access to images in the organization', - ARRAY[ - 'write:images', - 'delete:images' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.REGISTRIES_ADMIN}', - 'Registries Admin', - 'Grants admin access to registries in the organization', - ARRAY[ - '${OrganizationResourcePermission.WRITE_REGISTRIES}', - '${OrganizationResourcePermission.DELETE_REGISTRIES}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.SUPER_ADMIN}', - 'Super Admin', - 'Grants full access to all resources in the organization', - ARRAY[ - '${OrganizationResourcePermission.WRITE_REGISTRIES}', - '${OrganizationResourcePermission.DELETE_REGISTRIES}', - 'write:images', - 'delete:images', - '${OrganizationResourcePermission.WRITE_SANDBOXES}', - '${OrganizationResourcePermission.DELETE_SANDBOXES}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DELETE FROM "organization_role" WHERE "isGlobal" = TRUE`) - } -} diff --git a/apps/api/src/migrations/1741877019888-migration.ts b/apps/api/src/migrations/1741877019888-migration.ts deleted file mode 100644 index 3815094a5..000000000 --- a/apps/api/src/migrations/1741877019888-migration.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1741877019888 implements MigrationInterface { - name = 'Migration1741877019888' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "public"."warm_pool_target_enum" AS ENUM('eu', 'us', 'asia')`) - await queryRunner.query(`CREATE TYPE "public"."warm_pool_class_enum" AS ENUM('small', 'medium', 'large')`) - await queryRunner.query( - `CREATE TABLE "warm_pool" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "pool" integer NOT NULL, "image" character varying NOT NULL, "target" "public"."warm_pool_target_enum" NOT NULL DEFAULT 'eu', "cpu" integer NOT NULL, "mem" integer NOT NULL, "disk" integer NOT NULL, "gpu" integer NOT NULL, "gpuType" character varying NOT NULL, "class" "public"."warm_pool_class_enum" NOT NULL DEFAULT 'small', "osUser" character varying NOT NULL, "errorReason" character varying, "env" text NOT NULL DEFAULT '{}', "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_fb06a13baeb3ac0ced145345d90" PRIMARY KEY ("id"))`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "warm_pool"`) - await queryRunner.query(`DROP TYPE "public"."warm_pool_class_enum"`) - await queryRunner.query(`DROP TYPE "public"."warm_pool_target_enum"`) - } -} diff --git a/apps/api/src/migrations/1742215525714-migration.ts b/apps/api/src/migrations/1742215525714-migration.ts deleted file mode 100644 index 4c511ba07..000000000 --- a/apps/api/src/migrations/1742215525714-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1742215525714 implements MigrationInterface { - name = 'Migration1742215525714' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "image_quota" SET DEFAULT '0'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "image_quota" SET DEFAULT '5'`) - } -} diff --git a/apps/api/src/migrations/1742475055353-migration.ts b/apps/api/src/migrations/1742475055353-migration.ts deleted file mode 100644 index 55232c40d..000000000 --- a/apps/api/src/migrations/1742475055353-migration.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1742475055353 implements MigrationInterface { - name = 'Migration1742475055353' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TYPE "public"."user_role_enum" AS ENUM('admin', 'user')`) - await queryRunner.query(`ALTER TABLE "user" ADD "role" "public"."user_role_enum" NOT NULL DEFAULT 'user'`) - - await queryRunner.query(`UPDATE "user" SET "role" = 'admin' WHERE "id" = 'boxlite-admin'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "role"`) - await queryRunner.query(`DROP TYPE "public"."user_role_enum"`) - } -} diff --git a/apps/api/src/migrations/1742831092942-migration.ts b/apps/api/src/migrations/1742831092942-migration.ts deleted file mode 100644 index 4b4471037..000000000 --- a/apps/api/src/migrations/1742831092942-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1742831092942 implements MigrationInterface { - name = 'Migration1742831092942' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" ADD "pending" boolean NOT NULL DEFAULT false`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "pending"`) - } -} diff --git a/apps/api/src/migrations/1743593463168-migration.ts b/apps/api/src/migrations/1743593463168-migration.ts deleted file mode 100644 index 4735198a8..000000000 --- a/apps/api/src/migrations/1743593463168-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1743593463168 implements MigrationInterface { - name = 'Migration1743593463168' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization_invitation" ADD "invitedBy" character varying NOT NULL DEFAULT ''`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization_invitation" DROP COLUMN "invitedBy"`) - } -} diff --git a/apps/api/src/migrations/1743683015304-migration.ts b/apps/api/src/migrations/1743683015304-migration.ts deleted file mode 100644 index 694d88e8a..000000000 --- a/apps/api/src/migrations/1743683015304-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1743683015304 implements MigrationInterface { - name = 'Migration1743683015304' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "name"`) - await queryRunner.query(`ALTER TABLE "workspace" DROP CONSTRAINT "PK_ca86b6f9b3be5fe26d307d09b49"`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4()`) - await queryRunner.query(`ALTER TABLE "workspace" ADD CONSTRAINT "workspace_id_pk" PRIMARY KEY ("id")`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" DROP CONSTRAINT "workspace_id_pk"`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "id" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "workspace" ADD CONSTRAINT "PK_ca86b6f9b3be5fe26d307d09b49" PRIMARY KEY ("id")`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ADD "name" character varying NOT NULL DEFAULT ''`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "name" DROP DEFAULT`) - } -} diff --git a/apps/api/src/migrations/1744028841133-migration.ts b/apps/api/src/migrations/1744028841133-migration.ts deleted file mode 100644 index 442da6180..000000000 --- a/apps/api/src/migrations/1744028841133-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1744028841133 implements MigrationInterface { - name = 'Migration1744028841133' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace_usage_periods" DROP COLUMN "storage"`) - await queryRunner.query(`ALTER TABLE "workspace_usage_periods" ADD "organizationId" character varying NOT NULL`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace_usage_periods" DROP COLUMN "organizationId"`) - await queryRunner.query(`ALTER TABLE "workspace_usage_periods" ADD "storage" double precision NOT NULL`) - } -} diff --git a/apps/api/src/migrations/1744114341077-migration.ts b/apps/api/src/migrations/1744114341077-migration.ts deleted file mode 100644 index 4b4ce894f..000000000 --- a/apps/api/src/migrations/1744114341077-migration.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1744114341077 implements MigrationInterface { - name = 'Migration1744114341077' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" DROP CONSTRAINT "organization_role_assignment_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" DROP CONSTRAINT "organization_role_assignment_invitation_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "workspace" ADD "authToken" character varying NOT NULL DEFAULT MD5(random()::text)`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" DROP CONSTRAINT "organization_role_assignment_invitation_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" DROP CONSTRAINT "organization_role_assignment_roleId_fk"`, - ) - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "authToken"`) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - } -} diff --git a/apps/api/src/migrations/1744378115901-migration.ts b/apps/api/src/migrations/1744378115901-migration.ts deleted file mode 100644 index 8c07075df..000000000 --- a/apps/api/src/migrations/1744378115901-migration.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1744378115901 implements MigrationInterface { - name = 'Migration1744378115901' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "suspended" boolean NOT NULL DEFAULT false`) - await queryRunner.query(`ALTER TABLE "organization" ADD "suspensionReason" character varying`) - await queryRunner.query(`ALTER TABLE "organization" ADD "suspendedUntil" TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization" ADD "suspendedAt" TIMESTAMP`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "suspendedAt"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "suspendedUntil"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "suspensionReason"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "suspended"`) - } -} diff --git a/apps/api/src/migrations/1744808444807-migration.ts b/apps/api/src/migrations/1744808444807-migration.ts deleted file mode 100644 index 286bd0893..000000000 --- a/apps/api/src/migrations/1744808444807-migration.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1744808444807 implements MigrationInterface { - name = 'Migration1744808444807' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "volume" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "organizationId" uuid, "name" character varying NOT NULL, "state" character varying NOT NULL, "errorReason" character varying, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), "lastUsedAt" TIMESTAMP, CONSTRAINT "volume_organizationId_name_unique" UNIQUE ("organizationId", "name"), CONSTRAINT "volume_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT MD5(random()::text)`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT md5((random()))`) - await queryRunner.query(`DROP TABLE "volume"`) - } -} diff --git a/apps/api/src/migrations/1744868914148-migration.ts b/apps/api/src/migrations/1744868914148-migration.ts deleted file mode 100644 index bfa66d5a8..000000000 --- a/apps/api/src/migrations/1744868914148-migration.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { GlobalOrganizationRolesIds } from '../organization/constants/global-organization-roles.constant' -import { OrganizationResourcePermission } from '../organization/enums/organization-resource-permission.enum' - -export class Migration1744868914148 implements MigrationInterface { - name = 'Migration1744868914148' - - public async up(queryRunner: QueryRunner): Promise { - // update enums - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum" RENAME TO "api_key_permissions_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum"[] USING "permissions"::"text"::"public"."api_key_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum_old"`) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME TO "organization_role_permissions_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum_old"`) - - // add volumes admin role - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.VOLUMES_ADMIN}', - 'Volumes Admin', - 'Grants admin access to volumes in the organization', - ARRAY[ - '${OrganizationResourcePermission.READ_VOLUMES}', - '${OrganizationResourcePermission.WRITE_VOLUMES}', - '${OrganizationResourcePermission.DELETE_VOLUMES}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - } - - public async down(queryRunner: QueryRunner): Promise { - // remove volumes admin role - await queryRunner.query( - `DELETE FROM "organization_role" WHERE "id" = '${GlobalOrganizationRolesIds.VOLUMES_ADMIN}'`, - ) - - // revert enums - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum_old" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum_old"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum"`) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum_old" RENAME TO "organization_role_permissions_enum"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum_old" AS ENUM('write:registries', 'delete:registries', 'write:images', 'delete:images', 'write:sandboxes', 'delete:sandboxes')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum_old"[] USING "permissions"::"text"::"public"."api_key_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum"`) - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum_old" RENAME TO "api_key_permissions_enum"`) - } -} diff --git a/apps/api/src/migrations/1744971114480-migration.ts b/apps/api/src/migrations/1744971114480-migration.ts deleted file mode 100644 index b0f60351d..000000000 --- a/apps/api/src/migrations/1744971114480-migration.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1744971114480 implements MigrationInterface { - name = 'Migration1744971114480' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" ADD "volumes" jsonb NOT NULL DEFAULT '[]'`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT MD5(random()::text)`) - await queryRunner.query(`ALTER TABLE "volume" DROP COLUMN "state"`) - await queryRunner.query( - `CREATE TYPE "public"."volume_state_enum" AS ENUM('creating', 'ready', 'pending_create', 'pending_delete', 'deleting', 'deleted', 'error')`, - ) - await queryRunner.query( - `ALTER TABLE "volume" ADD "state" "public"."volume_state_enum" NOT NULL DEFAULT 'pending_create'`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "volume" DROP COLUMN "state"`) - await queryRunner.query(`DROP TYPE "public"."volume_state_enum"`) - await queryRunner.query(`ALTER TABLE "volume" ADD "state" character varying NOT NULL`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT md5((random()))`) - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "volumes"`) - } -} diff --git a/apps/api/src/migrations/1745393243334-migration.ts b/apps/api/src/migrations/1745393243334-migration.ts deleted file mode 100644 index bf3b3f59b..000000000 --- a/apps/api/src/migrations/1745393243334-migration.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1745393243334 implements MigrationInterface { - name = 'Migration1745393243334' - - public async up(queryRunner: QueryRunner): Promise { - // First, get all images with their current entrypoint values - const images = await queryRunner.query(`SELECT id, entrypoint FROM "image" WHERE entrypoint IS NOT NULL`) - - // Rename the column to avoid data loss - await queryRunner.query(`ALTER TABLE "image" RENAME COLUMN "entrypoint" TO "entrypoint_old"`) - - // Add the new jsonb column - await queryRunner.query(`ALTER TABLE "image" ADD "entrypoint" text[]`) - - // Update each image to convert the string entrypoint to a JSON array - for (const image of images) { - const entrypointValue = image.entrypoint - if (entrypointValue) { - // Convert the string to a JSON array with a single element - await queryRunner.query(`UPDATE "image" SET "entrypoint" = $1 WHERE id = $2`, [ - entrypointValue.split(' '), - image.id, - ]) - } - } - - // Drop the old column - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "entrypoint_old"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // First, get all images with their current entrypoint values - const images = await queryRunner.query(`SELECT id, entrypoint FROM "image" WHERE entrypoint IS NOT NULL`) - - // Rename the column to avoid data loss - await queryRunner.query(`ALTER TABLE "image" RENAME COLUMN "entrypoint" TO "entrypoint_old"`) - - // Add the new character varying column - await queryRunner.query(`ALTER TABLE "image" ADD "entrypoint" character varying`) - - // Update each image to convert the JSON array to a string - for (const image of images) { - const entrypointArray = image.entrypoint_old - if (entrypointArray && Array.isArray(entrypointArray) && entrypointArray.length > 0) { - // Take the first element of the array as the string value - await queryRunner.query(`UPDATE "image" SET "entrypoint" = $1 WHERE id = $2`, [entrypointArray[0], image.id]) - } - } - - // Drop the old column - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "entrypoint_old"`) - } -} diff --git a/apps/api/src/migrations/1745494761360-migration.ts b/apps/api/src/migrations/1745494761360-migration.ts deleted file mode 100644 index 68a83e932..000000000 --- a/apps/api/src/migrations/1745494761360-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1745494761360 implements MigrationInterface { - name = 'Migration1745494761360' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" ADD "emailVerified" boolean NOT NULL DEFAULT false`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "emailVerified"`) - } -} diff --git a/apps/api/src/migrations/1745574377029-migration.ts b/apps/api/src/migrations/1745574377029-migration.ts deleted file mode 100644 index 4f2f85fd5..000000000 --- a/apps/api/src/migrations/1745574377029-migration.ts +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1745574377029 implements MigrationInterface { - name = 'Migration1745574377029' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "build_info" ("imageRef" character varying NOT NULL, "dockerfileContent" text, "contextHashes" text, "lastUsedAt" TIMESTAMP NOT NULL DEFAULT now(), "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "build_info_imageRef_pk" PRIMARY KEY ("imageRef"))`, - ) - await queryRunner.renameColumn('image_node', 'internalImageName', 'imageRef') - await queryRunner.query(`ALTER TABLE "image_node" DROP COLUMN "image"`) - await queryRunner.query(`ALTER TABLE "image" ADD "buildInfoImageRef" character varying`) - await queryRunner.query(`ALTER TABLE "workspace" ADD "buildInfoImageRef" character varying`) - await queryRunner.query(`ALTER TYPE "public"."image_node_state_enum" RENAME TO "image_node_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."image_node_state_enum" AS ENUM('pulling_image', 'building_image', 'ready', 'error', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "image_node" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "image_node" ALTER COLUMN "state" TYPE "public"."image_node_state_enum" USING "state"::"text"::"public"."image_node_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "image_node" ALTER COLUMN "state" SET DEFAULT 'pulling_image'`) - await queryRunner.query(`DROP TYPE "public"."image_node_state_enum_old"`) - await queryRunner.query(`ALTER TYPE "public"."image_state_enum" RENAME TO "image_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."image_state_enum" AS ENUM('build_pending', 'building', 'pending', 'pulling_image', 'pending_validation', 'validating', 'active', 'error', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "image" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "image" ALTER COLUMN "state" TYPE "public"."image_state_enum" USING "state"::"text"::"public"."image_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "image" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."image_state_enum_old"`) - await queryRunner.query(`ALTER TYPE "public"."workspace_state_enum" RENAME TO "workspace_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."workspace_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'resizing', 'error', 'pending_build', 'building_image', 'unknown', 'pulling_image', 'archiving', 'archived')`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "workspace" ALTER COLUMN "state" TYPE "public"."workspace_state_enum" USING "state"::"text"::"public"."workspace_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "state" SET DEFAULT 'unknown'`) - await queryRunner.query(`DROP TYPE "public"."workspace_state_enum_old"`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "image" DROP NOT NULL`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT MD5(random()::text)`) - await queryRunner.query( - `ALTER TABLE "image" ADD CONSTRAINT "image_buildInfoImageRef_fk" FOREIGN KEY ("buildInfoImageRef") REFERENCES "build_info"("imageRef") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - await queryRunner.query( - `ALTER TABLE "workspace" ADD CONSTRAINT "workspace_buildInfoImageRef_fk" FOREIGN KEY ("buildInfoImageRef") REFERENCES "build_info"("imageRef") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" DROP CONSTRAINT "workspace_buildInfoImageRef_fk"`) - await queryRunner.query(`ALTER TABLE "image" DROP CONSTRAINT "image_buildInfoImageRef_fk"`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "authToken" SET DEFAULT md5((random()))`) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "image" SET NOT NULL`) - await queryRunner.query( - `CREATE TYPE "public"."workspace_state_enum_old" AS ENUM('archived', 'archiving', 'creating', 'destroyed', 'destroying', 'error', 'pulling_image', 'resizing', 'restoring', 'started', 'starting', 'stopped', 'stopping', 'unknown')`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "workspace" ALTER COLUMN "state" TYPE "public"."workspace_state_enum_old" USING "state"::"text"::"public"."workspace_state_enum_old"`, - ) - await queryRunner.query(`ALTER TABLE "workspace" ALTER COLUMN "state" SET DEFAULT 'unknown'`) - await queryRunner.query(`DROP TYPE "public"."workspace_state_enum"`) - await queryRunner.query(`ALTER TYPE "public"."workspace_state_enum_old" RENAME TO "workspace_state_enum"`) - await queryRunner.query( - `CREATE TYPE "public"."image_state_enum_old" AS ENUM('active', 'error', 'pending', 'pending_validation', 'pulling_image', 'removing', 'validating')`, - ) - await queryRunner.query(`ALTER TABLE "image" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "image" ALTER COLUMN "state" TYPE "public"."image_state_enum_old" USING "state"::"text"::"public"."image_state_enum_old"`, - ) - await queryRunner.query(`ALTER TABLE "image" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."image_state_enum"`) - await queryRunner.query(`ALTER TYPE "public"."image_state_enum_old" RENAME TO "image_state_enum"`) - await queryRunner.query( - `CREATE TYPE "public"."image_node_state_enum_old" AS ENUM('error', 'pulling_image', 'ready', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "image_node" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "image_node" ALTER COLUMN "state" TYPE "public"."image_node_state_enum_old" USING "state"::"text"::"public"."image_node_state_enum_old"`, - ) - await queryRunner.query(`ALTER TABLE "image_node" ALTER COLUMN "state" SET DEFAULT 'pulling_image'`) - await queryRunner.query(`DROP TYPE "public"."image_node_state_enum"`) - await queryRunner.query(`ALTER TYPE "public"."image_node_state_enum_old" RENAME TO "image_node_state_enum"`) - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "buildInfoImageRef"`) - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "buildInfoImageRef"`) - await queryRunner.renameColumn('image_node', 'imageRef', 'internalImageName') - await queryRunner.query(`ALTER TABLE "image_node" ADD "image" character varying NOT NULL`) - await queryRunner.query(`DROP TABLE "build_info"`) - } -} diff --git a/apps/api/src/migrations/1745840296260-migration.ts b/apps/api/src/migrations/1745840296260-migration.ts deleted file mode 100644 index 688c3eab8..000000000 --- a/apps/api/src/migrations/1745840296260-migration.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import * as crypto from 'crypto' - -export class Migration1745840296260 implements MigrationInterface { - name = 'Migration1745840296260' - - public async up(queryRunner: QueryRunner): Promise { - // Add the new columns - await queryRunner.query(`ALTER TABLE "api_key" ADD "keyHash" character varying NOT NULL DEFAULT ''`) - await queryRunner.query(`ALTER TABLE "api_key" ADD "keyPrefix" character varying NOT NULL DEFAULT ''`) - await queryRunner.query(`ALTER TABLE "api_key" ADD "keySuffix" character varying NOT NULL DEFAULT ''`) - - // Get all existing API keys - const existingKeys = await queryRunner.query(`SELECT "value" FROM "api_key"`) - - // Update each key with its hash, prefix, and suffix - for (const key of existingKeys) { - const value = key.value - const keyHash = crypto.createHash('sha256').update(value).digest('hex') - const keyPrefix = value.substring(0, 3) - const keySuffix = value.slice(-3) - - await queryRunner.query( - `UPDATE "api_key" - SET "keyHash" = $1, - "keyPrefix" = $2, - "keySuffix" = $3 - WHERE "value" = $4`, - [keyHash, keyPrefix, keySuffix, value], - ) - } - - // Drop value column and its unique constraint - await queryRunner.query(`ALTER TABLE "api_key" DROP CONSTRAINT "UQ_4b0873b633484d5de20b2d8f852"`) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "value"`) - - // Add unique constraint - await queryRunner.query(`ALTER TABLE "api_key" ADD CONSTRAINT "api_key_keyHash_unique" UNIQUE ("keyHash")`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Revert the schema changes - await queryRunner.query(`ALTER TABLE "api_key" DROP CONSTRAINT "api_key_keyHash_unique"`) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "keySuffix"`) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "keyPrefix"`) - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "keyHash"`) - await queryRunner.query(`ALTER TABLE "api_key" ADD "value" character varying NOT NULL DEFAULT ''`) - } -} diff --git a/apps/api/src/migrations/1745864972652-migration.ts b/apps/api/src/migrations/1745864972652-migration.ts deleted file mode 100644 index ea92927f1..000000000 --- a/apps/api/src/migrations/1745864972652-migration.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1745864972652 implements MigrationInterface { - name = 'Migration1745864972652' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "workspace" - ALTER COLUMN "env" DROP DEFAULT, - ALTER COLUMN "env" TYPE jsonb USING "env"::jsonb, - ALTER COLUMN "env" SET DEFAULT '{}'::jsonb, - ALTER COLUMN "env" SET NOT NULL; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - ALTER TABLE "workspace" - ALTER COLUMN "env" DROP DEFAULT, - ALTER COLUMN "env" TYPE text USING "env"::text, - ALTER COLUMN "env" SET DEFAULT '{}'::text, - ALTER COLUMN "env" SET NOT NULL; - `) - } -} diff --git a/apps/api/src/migrations/1746354231722-migration.ts b/apps/api/src/migrations/1746354231722-migration.ts deleted file mode 100644 index 1e142e5d6..000000000 --- a/apps/api/src/migrations/1746354231722-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1746354231722 implements MigrationInterface { - name = 'Migration1746354231722' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "image" ADD "buildNodeId" character varying`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "image" DROP COLUMN "buildNodeId"`) - } -} diff --git a/apps/api/src/migrations/1746604150910-migration.ts b/apps/api/src/migrations/1746604150910-migration.ts deleted file mode 100644 index 2e6425f7b..000000000 --- a/apps/api/src/migrations/1746604150910-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1746604150910 implements MigrationInterface { - name = 'Migration1746604150910' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "volume_quota" integer NOT NULL DEFAULT '10'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "volume_quota"`) - } -} diff --git a/apps/api/src/migrations/1747658203010-migration.ts b/apps/api/src/migrations/1747658203010-migration.ts deleted file mode 100644 index 5c54ed143..000000000 --- a/apps/api/src/migrations/1747658203010-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1747658203010 implements MigrationInterface { - name = 'Migration1747658203010' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_key" ADD "lastUsedAt" TIMESTAMP`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "lastUsedAt"`) - } -} diff --git a/apps/api/src/migrations/1748006546552-migration.ts b/apps/api/src/migrations/1748006546552-migration.ts deleted file mode 100644 index 94699b60e..000000000 --- a/apps/api/src/migrations/1748006546552-migration.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1748006546552 implements MigrationInterface { - name = 'Migration1748006546552' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "max_concurrent_workspaces"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "workspace_quota"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "total_image_size"`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "total_memory_quota" SET DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "total_disk_quota" SET DEFAULT '30'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_cpu_per_workspace" SET DEFAULT '4'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_memory_per_workspace" SET DEFAULT '8'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_image_size" SET DEFAULT '20'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "image_quota" SET DEFAULT '100'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "volume_quota" SET DEFAULT '100'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "volume_quota" SET DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "image_quota" SET DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_image_size" SET DEFAULT '2'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_memory_per_workspace" SET DEFAULT '4'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "max_cpu_per_workspace" SET DEFAULT '2'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "total_disk_quota" SET DEFAULT '100'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "total_memory_quota" SET DEFAULT '40'`) - await queryRunner.query(`ALTER TABLE "organization" ADD "total_image_size" integer NOT NULL DEFAULT '5'`) - await queryRunner.query(`ALTER TABLE "organization" ADD "workspace_quota" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "organization" ADD "max_concurrent_workspaces" integer NOT NULL DEFAULT '10'`) - } -} diff --git a/apps/api/src/migrations/1748866194353-migration.ts b/apps/api/src/migrations/1748866194353-migration.ts deleted file mode 100644 index 793989493..000000000 --- a/apps/api/src/migrations/1748866194353-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1748866194353 implements MigrationInterface { - name = 'Migration1748866194353' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" ADD "autoArchiveInterval" integer NOT NULL DEFAULT '10080'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "workspace" DROP COLUMN "autoArchiveInterval"`) - } -} diff --git a/apps/api/src/migrations/1749474791343-migration.ts b/apps/api/src/migrations/1749474791343-migration.ts deleted file mode 100644 index 4bd919693..000000000 --- a/apps/api/src/migrations/1749474791343-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1749474791343 implements MigrationInterface { - name = 'Migration1749474791343' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_key" ADD "expiresAt" TIMESTAMP`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "api_key" DROP COLUMN "expiresAt"`) - } -} diff --git a/apps/api/src/migrations/1749474791344-migration.ts b/apps/api/src/migrations/1749474791344-migration.ts deleted file mode 100644 index e7beb601e..000000000 --- a/apps/api/src/migrations/1749474791344-migration.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1749474791344 implements MigrationInterface { - name = 'Migration1749474791344' - - public async up(queryRunner: QueryRunner): Promise { - // Snapshot to backup rename - await queryRunner.renameColumn('workspace', 'snapshotRegistryId', 'backupRegistryId') - await queryRunner.renameColumn('workspace', 'snapshotImage', 'backupImage') - await queryRunner.renameColumn('workspace', 'lastSnapshotAt', 'lastBackupAt') - await queryRunner.renameColumn('workspace', 'snapshotState', 'backupState') - await queryRunner.renameColumn('workspace', 'existingSnapshotImages', 'existingBackupImages') - - // Node to runner rename - await queryRunner.renameColumn('image', 'buildNodeId', 'buildRunnerId') - await queryRunner.renameTable('image_node', 'image_runner') - await queryRunner.renameColumn('image_runner', 'nodeId', 'runnerId') - await queryRunner.renameTable('node', 'runner') - await queryRunner.renameColumn('workspace', 'nodeId', 'runnerId') - await queryRunner.renameColumn('workspace', 'prevNodeId', 'prevRunnerId') - - // Image to snapshot rename - await queryRunner.renameColumn('warm_pool', 'image', 'snapshot') - await queryRunner.renameColumn('organization', 'image_quota', 'snapshot_quota') - await queryRunner.renameColumn('organization', 'max_image_size', 'max_snapshot_size') - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME VALUE 'write:images' TO 'write:snapshots'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME VALUE 'delete:images' TO 'delete:snapshots'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."api_key_permissions_enum" RENAME VALUE 'write:images' TO 'write:snapshots'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."api_key_permissions_enum" RENAME VALUE 'delete:images' TO 'delete:snapshots'`, - ) - await queryRunner.renameTable('image_runner', 'snapshot_runner') - await queryRunner.renameColumn('snapshot_runner', 'imageRef', 'snapshotRef') - await queryRunner.query( - `ALTER TYPE "public"."snapshot_runner_state_enum" RENAME VALUE 'pulling_image' TO 'pulling_snapshot'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."snapshot_runner_state_enum" RENAME VALUE 'building_image' TO 'building_snapshot'`, - ) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "state" SET DEFAULT 'pulling_snapshot'`) - await queryRunner.renameColumn('build_info', 'imageRef', 'snapshotRef') - await queryRunner.renameTable('image', 'snapshot') - await queryRunner.renameColumn('snapshot', 'buildInfoImageRef', 'buildInfoSnapshotRef') - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME VALUE 'pulling_image' TO 'pulling'`) - await queryRunner.renameColumn('workspace', 'image', 'snapshot') - await queryRunner.renameColumn('workspace', 'buildInfoImageRef', 'buildInfoSnapshotRef') - await queryRunner.renameColumn('workspace', 'backupImage', 'backupSnapshot') - await queryRunner.renameColumn('workspace', 'existingBackupImages', 'existingBackupSnapshots') - await queryRunner.query( - `ALTER TYPE "public"."workspace_state_enum" RENAME VALUE 'pulling_image' TO 'pulling_snapshot'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."workspace_state_enum" RENAME VALUE 'building_image' TO 'building_snapshot'`, - ) - - // Workspace to sandbox rename - await queryRunner.renameTable('workspace', 'sandbox') - await queryRunner.renameTable('workspace_usage_periods', 'sandbox_usage_periods') - await queryRunner.renameColumn('sandbox_usage_periods', 'workspaceId', 'sandboxId') - await queryRunner.renameColumn('organization', 'max_cpu_per_workspace', 'max_cpu_per_sandbox') - await queryRunner.renameColumn('organization', 'max_memory_per_workspace', 'max_memory_per_sandbox') - await queryRunner.renameColumn('organization', 'max_disk_per_workspace', 'max_disk_per_sandbox') - - // Snapshot fields - await queryRunner.query(`ALTER TABLE "snapshot" ADD "imageName" character varying NOT NULL DEFAULT ''`) - await queryRunner.query(`ALTER TABLE "snapshot" ADD "cpu" integer NOT NULL DEFAULT '1'`) - await queryRunner.query(`ALTER TABLE "snapshot" ADD "gpu" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "snapshot" ADD "mem" integer NOT NULL DEFAULT '1'`) - await queryRunner.query(`ALTER TABLE "snapshot" ADD "disk" integer NOT NULL DEFAULT '3'`) - await queryRunner.query(`UPDATE "snapshot" SET "imageName" = "name"`) - - // Add hideFromUsers column - await queryRunner.query(`ALTER TABLE "snapshot" ADD "hideFromUsers" boolean NOT NULL DEFAULT false`) - - // Set hideFromUsers to true for general snapshots with names starting with "boxlite-ai/" - await queryRunner.query( - `UPDATE "snapshot" SET "hideFromUsers" = true WHERE "general" = true AND "name" LIKE 'boxlite-ai/%'`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - // Remove hideFromUsers column - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "hideFromUsers"`) - - // Snapshot fields - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "disk"`) - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "mem"`) - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "gpu"`) - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "cpu"`) - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "imageName"`) - - // Revert workspace to sandbox rename - await queryRunner.renameColumn('organization', 'max_disk_per_sandbox', 'max_disk_per_workspace') - await queryRunner.renameColumn('organization', 'max_memory_per_sandbox', 'max_memory_per_workspace') - await queryRunner.renameColumn('organization', 'max_cpu_per_sandbox', 'max_cpu_per_workspace') - await queryRunner.renameColumn('sandbox_usage_periods', 'sandboxId', 'workspaceId') - await queryRunner.renameTable('sandbox_usage_periods', 'workspace_usage_periods') - await queryRunner.renameTable('sandbox', 'workspace') - - // Revert image to snapshot rename - await queryRunner.query( - `ALTER TYPE "public"."workspace_state_enum" RENAME VALUE 'pulling_snapshot' TO 'pulling_image'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."workspace_state_enum" RENAME VALUE 'building_snapshot' TO 'building_image'`, - ) - await queryRunner.renameColumn('workspace', 'existingBackupSnapshots', 'existingBackupImages') - await queryRunner.renameColumn('workspace', 'backupSnapshot', 'backupImage') - await queryRunner.renameColumn('workspace', 'buildInfoSnapshotRef', 'buildInfoImageRef') - await queryRunner.renameColumn('workspace', 'snapshot', 'image') - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME VALUE 'pulling' TO 'pulling_image'`) - await queryRunner.renameColumn('snapshot', 'buildInfoSnapshotRef', 'buildInfoImageRef') - await queryRunner.renameTable('snapshot', 'image') - await queryRunner.renameColumn('build_info', 'snapshotRef', 'imageRef') - await queryRunner.query( - `ALTER TYPE "public"."snapshot_runner_state_enum" RENAME VALUE 'pulling_snapshot' TO 'pulling_image'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."snapshot_runner_state_enum" RENAME VALUE 'building_snapshot' TO 'building_image'`, - ) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "state" SET DEFAULT 'pulling_image'`) - await queryRunner.renameColumn('snapshot_runner', 'snapshotRef', 'imageRef') - await queryRunner.renameTable('snapshot_runner', 'image_runner') - await queryRunner.query( - `ALTER TYPE "public"."api_key_permissions_enum" RENAME VALUE 'write:snapshots' TO 'write:images'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."api_key_permissions_enum" RENAME VALUE 'delete:snapshots' TO 'delete:images'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME VALUE 'write:snapshots' TO 'write:images'`, - ) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME VALUE 'delete:snapshots' TO 'delete:images'`, - ) - await queryRunner.renameColumn('organization', 'max_snapshot_size', 'max_image_size') - await queryRunner.renameColumn('organization', 'snapshot_quota', 'image_quota') - await queryRunner.renameColumn('warm_pool', 'snapshot', 'image') - - // Revert node to runner rename - await queryRunner.renameColumn('workspace', 'prevRunnerId', 'prevNodeId') - await queryRunner.renameColumn('workspace', 'runnerId', 'nodeId') - await queryRunner.renameTable('runner', 'node') - await queryRunner.renameColumn('image_runner', 'runnerId', 'nodeId') - await queryRunner.renameTable('image_runner', 'image_node') - await queryRunner.renameColumn('image', 'buildRunnerId', 'buildNodeId') - - // Revert snapshot to backup rename - await queryRunner.renameColumn('workspace', 'existingBackupImages', 'existingSnapshotImages') - await queryRunner.renameColumn('workspace', 'backupState', 'snapshotState') - await queryRunner.renameColumn('workspace', 'lastBackupAt', 'lastSnapshotAt') - await queryRunner.renameColumn('workspace', 'backupImage', 'snapshotImage') - await queryRunner.renameColumn('workspace', 'backupRegistryId', 'snapshotRegistryId') - } -} diff --git a/apps/api/src/migrations/1749474791345-migration.ts b/apps/api/src/migrations/1749474791345-migration.ts deleted file mode 100644 index bb9e4a39f..000000000 --- a/apps/api/src/migrations/1749474791345-migration.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1749474791345 implements MigrationInterface { - name = 'Migration1749474791345' - - public async up(queryRunner: QueryRunner): Promise { - // For sandbox_state_enum - await queryRunner.query(`ALTER TYPE "public"."sandbox_state_enum" RENAME TO "sandbox_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."sandbox_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'error', 'build_failed', 'pending_build', 'building_snapshot', 'unknown', 'pulling_snapshot', 'archiving', 'archived')`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "state" TYPE "public"."sandbox_state_enum" USING "state"::"text"::"public"."sandbox_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" SET DEFAULT 'unknown'`) - await queryRunner.query(`DROP TYPE "public"."sandbox_state_enum_old"`) - - // For snapshot_state_enum - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('build_pending', 'building', 'pending', 'pulling', 'pending_validation', 'validating', 'active', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // For snapshot_state_enum - recreate without build_failed - await queryRunner.query(`UPDATE "snapshot" SET "state" = 'error' WHERE "state" = 'build_failed'`) - - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('build_pending', 'building', 'pending', 'pulling', 'pending_validation', 'validating', 'active', 'error', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - - // For sandbox_state_enum - recreate without build_failed - await queryRunner.query(`UPDATE "sandbox" SET "state" = 'error' WHERE "state" = 'build_failed'`) - - await queryRunner.query(`ALTER TYPE "public"."sandbox_state_enum" RENAME TO "sandbox_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."sandbox_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'error', 'pending_build', 'building_snapshot', 'unknown', 'pulling_snapshot', 'archiving', 'archived')`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "state" TYPE "public"."sandbox_state_enum" USING "state"::"text"::"public"."sandbox_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" SET DEFAULT 'unknown'`) - await queryRunner.query(`DROP TYPE "public"."sandbox_state_enum_old"`) - } -} diff --git a/apps/api/src/migrations/1750077343089-migration.ts b/apps/api/src/migrations/1750077343089-migration.ts deleted file mode 100644 index 55a604d21..000000000 --- a/apps/api/src/migrations/1750077343089-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1750077343089 implements MigrationInterface { - name = 'Migration1750077343089' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "daemonVersion" character varying`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "daemonVersion"`) - } -} diff --git a/apps/api/src/migrations/1750436374899-migration.ts b/apps/api/src/migrations/1750436374899-migration.ts deleted file mode 100644 index a3c5c0fa3..000000000 --- a/apps/api/src/migrations/1750436374899-migration.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1750436374899 implements MigrationInterface { - name = 'Migration1750436374899' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "audit_log" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "actorId" character varying NOT NULL, "actorEmail" character varying NOT NULL DEFAULT '', "organizationId" character varying, "action" character varying NOT NULL, "targetType" character varying, "targetId" character varying, "statusCode" integer, "errorMessage" character varying, "ipAddress" character varying, "userAgent" text, "source" character varying, "metadata" jsonb, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "audit_log_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query( - `CREATE INDEX "audit_log_organizationId_createdAt_index" ON "audit_log" ("organizationId", "createdAt") `, - ) - await queryRunner.query(`CREATE INDEX "audit_log_createdAt_index" ON "audit_log" ("createdAt") `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."audit_log_createdAt_index"`) - await queryRunner.query(`DROP INDEX "public"."audit_log_organizationId_createdAt_index"`) - await queryRunner.query(`DROP TABLE "audit_log"`) - } -} diff --git a/apps/api/src/migrations/1750668569562-migration.ts b/apps/api/src/migrations/1750668569562-migration.ts deleted file mode 100644 index 04c9ccd4b..000000000 --- a/apps/api/src/migrations/1750668569562-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1750668569562 implements MigrationInterface { - name = 'Migration1750668569562' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "autoDeleteInterval" integer NOT NULL DEFAULT '-1'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "autoDeleteInterval"`) - } -} diff --git a/apps/api/src/migrations/1750751712412-migration.ts b/apps/api/src/migrations/1750751712412-migration.ts deleted file mode 100644 index 6ff2f08aa..000000000 --- a/apps/api/src/migrations/1750751712412-migration.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1750751712412 implements MigrationInterface { - name = 'Migration1750751712412' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('build_pending', 'building', 'pending', 'pulling', 'pending_validation', 'validating', 'active', 'inactive', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum_old" AS ENUM('active', 'build_failed', 'build_pending', 'building', 'error', 'pending', 'pending_validation', 'pulling', 'removing', 'validating')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum_old" USING "state"::"text"::"public"."snapshot_state_enum_old"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum"`) - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum_old" RENAME TO "snapshot_state_enum"`) - } -} diff --git a/apps/api/src/migrations/1751456907334-migration.ts b/apps/api/src/migrations/1751456907334-migration.ts deleted file mode 100644 index 91a52d0c9..000000000 --- a/apps/api/src/migrations/1751456907334-migration.ts +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { GlobalOrganizationRolesIds } from '../organization/constants/global-organization-roles.constant' -import { OrganizationResourcePermission } from '../organization/enums/organization-resource-permission.enum' - -export class Migration1751456907334 implements MigrationInterface { - name = 'Migration1751456907334' - - public async up(queryRunner: QueryRunner): Promise { - // update enums - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum" RENAME TO "api_key_permissions_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:snapshots', 'delete:snapshots', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'read:audit_logs')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum"[] USING "permissions"::"text"::"public"."api_key_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum_old"`) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME TO "organization_role_permissions_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:snapshots', 'delete:snapshots', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'read:audit_logs')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum_old"`) - - // add auditor role - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.AUDITOR}', - 'Auditor', - 'Grants access to audit logs in the organization', - ARRAY[ - '${OrganizationResourcePermission.READ_AUDIT_LOGS}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - // update organization role foreign keys - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" DROP CONSTRAINT "organization_role_assignment_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" DROP CONSTRAINT "organization_role_assignment_invitation_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - // delete auditor role - await queryRunner.query(`DELETE FROM "organization_role" WHERE "id" = '${GlobalOrganizationRolesIds.AUDITOR}'`) - - // remove read:audit_logs permission from api keys and organization roles - await queryRunner.query( - `UPDATE "api_key" SET "permissions" = array_remove("permissions", '${OrganizationResourcePermission.READ_AUDIT_LOGS}')`, - ) - await queryRunner.query( - `UPDATE "organization_role" SET "permissions" = array_remove("permissions", '${OrganizationResourcePermission.READ_AUDIT_LOGS}')`, - ) - - // revert enums - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum_old" AS ENUM('delete:registries', 'delete:sandboxes', 'delete:snapshots', 'delete:volumes', 'read:volumes', 'write:registries', 'write:sandboxes', 'write:snapshots', 'write:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum_old"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum"`) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum_old" RENAME TO "organization_role_permissions_enum"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum_old" AS ENUM('delete:registries', 'delete:sandboxes', 'delete:snapshots', 'delete:volumes', 'read:volumes', 'write:registries', 'write:sandboxes', 'write:snapshots', 'write:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum_old"[] USING "permissions"::"text"::"public"."api_key_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum"`) - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum_old" RENAME TO "api_key_permissions_enum"`) - } -} diff --git a/apps/api/src/migrations/1752494676200-migration.ts b/apps/api/src/migrations/1752494676200-migration.ts deleted file mode 100644 index 22994cc2a..000000000 --- a/apps/api/src/migrations/1752494676200-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1752494676200 implements MigrationInterface { - name = 'Migration1752494676200' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "team"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "team" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, CONSTRAINT "PK_f57d8293406df4af348402e4b74" PRIMARY KEY ("id"))`, - ) - } -} diff --git a/apps/api/src/migrations/1752494676205-migration.ts b/apps/api/src/migrations/1752494676205-migration.ts deleted file mode 100644 index 28934e422..000000000 --- a/apps/api/src/migrations/1752494676205-migration.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1752494676205 implements MigrationInterface { - name = 'Migration1752494676205' - - public async up(queryRunner: QueryRunner): Promise { - // Convert runner.region from enum to varchar - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "region" TYPE varchar USING "region"::text`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "region" SET DEFAULT 'us'`) - - // Convert sandbox.region from enum to varchar - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" TYPE varchar USING "region"::text`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" SET DEFAULT 'us'`) - - // Convert warm_pool.target from enum to varchar - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" TYPE varchar USING "target"::text`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" SET DEFAULT 'us'`) - - // Drop the enum type if it exists - await queryRunner.query(`DROP TYPE IF EXISTS "public"."runner_region_enum"`) - await queryRunner.query(`DROP TYPE IF EXISTS "public"."sandbox_region_enum"`) - await queryRunner.query(`DROP TYPE IF EXISTS "public"."warm_pool_target_enum"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Recreate the enum type - await queryRunner.query(`CREATE TYPE "public"."warm_pool_target_enum" AS ENUM('eu', 'us', 'asia')`) - await queryRunner.query(`CREATE TYPE "public"."sandbox_region_enum" AS ENUM('eu', 'us', 'asia')`) - await queryRunner.query(`CREATE TYPE "public"."runner_region_enum" AS ENUM('eu', 'us', 'asia')`) - - // Convert back to enum for runner.region - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "region" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "runner" ALTER COLUMN "region" TYPE "public"."runner_region_enum" USING "region"::"public"."runner_region_enum"`, - ) - - // Convert back to enum for sandbox.region - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "region" TYPE "public"."sandbox_region_enum" USING "region"::"public"."sandbox_region_enum"`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" SET DEFAULT 'eu'`) - - // Convert back to enum for warm_pool.target - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "warm_pool" ALTER COLUMN "target" TYPE "public"."warm_pool_target_enum" USING "target"::"public"."warm_pool_target_enum"`, - ) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" SET DEFAULT 'eu'`) - } -} diff --git a/apps/api/src/migrations/1752848014862-migration.ts b/apps/api/src/migrations/1752848014862-migration.ts deleted file mode 100644 index a4d230683..000000000 --- a/apps/api/src/migrations/1752848014862-migration.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1752848014862 implements MigrationInterface { - name = 'Migration1752848014862' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "lastChecked" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox_usage_periods" ALTER COLUMN "startAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox_usage_periods" ALTER COLUMN "endAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "lastActivityAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "lastBackupAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "lastUsedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "volume" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "volume" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization_user" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization_user" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query( - `ALTER TABLE "organization_invitation" ALTER COLUMN "expiresAt" TYPE TIMESTAMP WITH TIME ZONE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_invitation" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_invitation" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`, - ) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "suspendedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "suspendedUntil" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization_role" ALTER COLUMN "createdAt" TYPE TIMESTAMP WITH TIME ZONE`) - await queryRunner.query(`ALTER TABLE "organization_role" ALTER COLUMN "updatedAt" TYPE TIMESTAMP WITH TIME ZONE`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization_role" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_role" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "suspendedUntil" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "suspendedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_invitation" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_invitation" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_invitation" ALTER COLUMN "expiresAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_user" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "organization_user" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "volume" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "volume" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "imageName" SET DEFAULT ''`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "build_info" ALTER COLUMN "lastUsedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "lastBackupAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "lastActivityAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "snapshot_runner" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox_usage_periods" ALTER COLUMN "endAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "sandbox_usage_periods" ALTER COLUMN "startAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "lastChecked" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "updatedAt" TYPE TIMESTAMP`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "createdAt" TYPE TIMESTAMP`) - } -} diff --git a/apps/api/src/migrations/1753099115783-migration.ts b/apps/api/src/migrations/1753099115783-migration.ts deleted file mode 100644 index 73d6c6f0b..000000000 --- a/apps/api/src/migrations/1753099115783-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753099115783 implements MigrationInterface { - name = 'Migration1753099115783' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "enabled"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "snapshot" ADD "enabled" boolean NOT NULL DEFAULT true`) - } -} diff --git a/apps/api/src/migrations/1753100751730-migration.ts b/apps/api/src/migrations/1753100751730-migration.ts deleted file mode 100644 index a56f51a1c..000000000 --- a/apps/api/src/migrations/1753100751730-migration.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753100751730 implements MigrationInterface { - name = 'Migration1753100751730' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('runner', 'memory', 'memoryGiB') - await queryRunner.renameColumn('runner', 'disk', 'diskGiB') - await queryRunner.query( - `ALTER TABLE "runner" ADD "currentCpuUsagePercentage" double precision NOT NULL DEFAULT '0'`, - ) - await queryRunner.query( - `ALTER TABLE "runner" ADD "currentMemoryUsagePercentage" double precision NOT NULL DEFAULT '0'`, - ) - await queryRunner.query( - `ALTER TABLE "runner" ADD "currentDiskUsagePercentage" double precision NOT NULL DEFAULT '0'`, - ) - await queryRunner.query(`ALTER TABLE "runner" ADD "currentAllocatedCpu" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "currentAllocatedMemoryGiB" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "currentAllocatedDiskGiB" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "currentSnapshotCount" integer NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "availabilityScore" integer NOT NULL DEFAULT '0'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "availabilityScore"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentSnapshotCount"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentAllocatedDiskGiB"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentAllocatedMemoryGiB"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentAllocatedCpu"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentDiskUsagePercentage"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentMemoryUsagePercentage"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentCpuUsagePercentage"`) - await queryRunner.renameColumn('runner', 'diskGiB', 'disk') - await queryRunner.renameColumn('runner', 'memoryGiB', 'memory') - } -} diff --git a/apps/api/src/migrations/1753100751731-migration.ts b/apps/api/src/migrations/1753100751731-migration.ts deleted file mode 100644 index c5e0dcc65..000000000 --- a/apps/api/src/migrations/1753100751731-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { GlobalOrganizationRolesIds } from '../organization/constants/global-organization-roles.constant' - -export class Migration1753100751731 implements MigrationInterface { - name = 'Migration1753100751731' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE "organization_role" - SET "name" = 'Snapshots Admin', "description" = 'Grants admin access to snapshots in the organization' - WHERE "id" = '${GlobalOrganizationRolesIds.SNAPSHOTS_ADMIN}' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - UPDATE "organization_role" - SET "name" = 'Images Admin', "description" = 'Grants admin access to images in the organization' - WHERE "id" = '${GlobalOrganizationRolesIds.SNAPSHOTS_ADMIN}' - `) - } -} diff --git a/apps/api/src/migrations/1753185133351-migration.ts b/apps/api/src/migrations/1753185133351-migration.ts deleted file mode 100644 index 2b1d470ec..000000000 --- a/apps/api/src/migrations/1753185133351-migration.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753185133351 implements MigrationInterface { - name = 'Migration1753185133351' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "version" character varying NOT NULL DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "proxyUrl" character varying NOT NULL DEFAULT ''`) - // Copy apiUrl to proxyUrl for all existing records - await queryRunner.query(`UPDATE "runner" SET "proxyUrl" = "apiUrl"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "version"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "proxyUrl"`) - } -} diff --git a/apps/api/src/migrations/1753274135567-migration.ts b/apps/api/src/migrations/1753274135567-migration.ts deleted file mode 100644 index 94058d2f1..000000000 --- a/apps/api/src/migrations/1753274135567-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753274135567 implements MigrationInterface { - name = 'Migration1753274135567' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "project" SET DEFAULT ''`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "project" DROP DEFAULT`) - } -} diff --git a/apps/api/src/migrations/1753430929609-migration.ts b/apps/api/src/migrations/1753430929609-migration.ts deleted file mode 100644 index abeeec971..000000000 --- a/apps/api/src/migrations/1753430929609-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753430929609 implements MigrationInterface { - name = 'Migration1753430929609' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now()`) - - // For existing users, set createdAt to match their personal organization's createdAt - await queryRunner.query(` - UPDATE "user" u - SET "createdAt" = o."createdAt" - FROM "organization" o - WHERE o."createdBy" = u.id - AND o.personal = true; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "createdAt"`) - } -} diff --git a/apps/api/src/migrations/1753717830378-migration.ts b/apps/api/src/migrations/1753717830378-migration.ts deleted file mode 100644 index 060af620c..000000000 --- a/apps/api/src/migrations/1753717830378-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1753717830378 implements MigrationInterface { - name = 'Migration1753717830378' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "networkBlockAll" boolean NOT NULL DEFAULT false`) - await queryRunner.query(`ALTER TABLE "sandbox" ADD "networkAllowList" character varying`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "networkAllowList"`) - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "networkBlockAll"`) - } -} diff --git a/apps/api/src/migrations/1754042247109-migration.ts b/apps/api/src/migrations/1754042247109-migration.ts deleted file mode 100644 index 9a13757ef..000000000 --- a/apps/api/src/migrations/1754042247109-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1754042247109 implements MigrationInterface { - name = 'Migration1754042247109' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization" ADD "suspensionCleanupGracePeriodHours" integer NOT NULL DEFAULT '24'`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "suspensionCleanupGracePeriodHours"`) - } -} diff --git a/apps/api/src/migrations/1755003696741-migration.ts b/apps/api/src/migrations/1755003696741-migration.ts deleted file mode 100644 index 9f3bfa6c4..000000000 --- a/apps/api/src/migrations/1755003696741-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1755003696741 implements MigrationInterface { - name = 'Migration1755003696741' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "backupErrorReason" text`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "backupErrorReason"`) - } -} diff --git a/apps/api/src/migrations/1755356869493-migration.ts b/apps/api/src/migrations/1755356869493-migration.ts deleted file mode 100644 index ce4ea9704..000000000 --- a/apps/api/src/migrations/1755356869493-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1755356869493 implements MigrationInterface { - name = 'Migration1755356869493' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "webhook_initialization" ("organizationId" character varying NOT NULL, "svixApplicationId" character varying, "lastError" text, "retryCount" integer NOT NULL DEFAULT '0', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "webhook_initialization_organizationId_pk" PRIMARY KEY ("organizationId"))`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "webhook_initialization"`) - } -} diff --git a/apps/api/src/migrations/1755464957487-migration.ts b/apps/api/src/migrations/1755464957487-migration.ts deleted file mode 100644 index ad4896e0b..000000000 --- a/apps/api/src/migrations/1755464957487-migration.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1755464957487 implements MigrationInterface { - name = 'Migration1755464957487' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "ssh_access" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "sandboxId" character varying NOT NULL, "token" text NOT NULL, "expiresAt" TIMESTAMP NOT NULL, "createdAt" TIMESTAMP NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "ssh_access_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query( - `ALTER TABLE "ssh_access" ADD CONSTRAINT "ssh_access_sandboxId_fk" FOREIGN KEY ("sandboxId") REFERENCES "sandbox"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - await queryRunner.query( - `ALTER TABLE "sandbox" ADD "sshPass" character varying(32) NOT NULL DEFAULT REPLACE(uuid_generate_v4()::text, '-', '')`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "sshPass"`) - await queryRunner.query(`ALTER TABLE "ssh_access" DROP CONSTRAINT "ssh_access_sandboxId_fk"`) - await queryRunner.query(`DROP TABLE "ssh_access"`) - } -} diff --git a/apps/api/src/migrations/1755521645207-migration.ts b/apps/api/src/migrations/1755521645207-migration.ts deleted file mode 100644 index e18e15c50..000000000 --- a/apps/api/src/migrations/1755521645207-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1755521645207 implements MigrationInterface { - name = 'Migration1755521645207' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TABLE "sandbox_usage_periods_archive" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "sandboxId" character varying NOT NULL, "organizationId" character varying NOT NULL, "startAt" TIMESTAMP WITH TIME ZONE NOT NULL, "endAt" TIMESTAMP WITH TIME ZONE NOT NULL, "cpu" double precision NOT NULL, "gpu" double precision NOT NULL, "mem" double precision NOT NULL, "disk" double precision NOT NULL, "region" character varying NOT NULL, CONSTRAINT "sandbox_usage_periods_archive_id_pk" PRIMARY KEY ("id"))`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE "sandbox_usage_periods_archive"`) - } -} diff --git a/apps/api/src/migrations/1755860619921-migration.ts b/apps/api/src/migrations/1755860619921-migration.ts deleted file mode 100644 index 911663f42..000000000 --- a/apps/api/src/migrations/1755860619921-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1755860619921 implements MigrationInterface { - name = 'Migration1755860619921' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization" ADD "sandboxLimitedNetworkEgress" boolean NOT NULL DEFAULT false`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "sandboxLimitedNetworkEgress"`) - } -} diff --git a/apps/api/src/migrations/1757513754037-migration.ts b/apps/api/src/migrations/1757513754037-migration.ts deleted file mode 100644 index f19dfa2b8..000000000 --- a/apps/api/src/migrations/1757513754037-migration.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1757513754037 implements MigrationInterface { - name = 'Migration1757513754037' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" ADD "region" character varying`) - - await queryRunner.query( - `ALTER TYPE "public"."docker_registry_registrytype_enum" RENAME TO "docker_registry_registrytype_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."docker_registry_registrytype_enum" AS ENUM('internal', 'organization', 'transient', 'backup')`, - ) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "docker_registry" ALTER COLUMN "registryType" TYPE "public"."docker_registry_registrytype_enum" USING "registryType"::"text"::"public"."docker_registry_registrytype_enum"`, - ) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" SET DEFAULT 'internal'`) - await queryRunner.query(`DROP TYPE "public"."docker_registry_registrytype_enum_old"`) - - // Create the base default registry by copying from the default internal one - await queryRunner.query(` - INSERT INTO public.docker_registry ( - name, url, username, password, "isDefault", project, "createdAt", "updatedAt", "registryType", "organizationId", "region" - ) - SELECT - 'Backup Registry' AS name, - url, - username, - password, - "isDefault", - project, - now() AS "createdAt", - now() AS "updatedAt", - 'backup' AS "registryType", - "organizationId", - "region" - FROM public.docker_registry - WHERE "registryType" = 'internal' - AND "isDefault" = true - ORDER BY "createdAt" ASC - LIMIT 1 - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DELETE FROM public.docker_registry - WHERE "registryType" = 'backup' - `) - - await queryRunner.query( - `CREATE TYPE "public"."docker_registry_registrytype_enum_old" AS ENUM('internal', 'organization', 'transient', 'backup')`, - ) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "docker_registry" ALTER COLUMN "registryType" TYPE "public"."docker_registry_registrytype_enum_old" USING "registryType"::"text"::"public"."docker_registry_registrytype_enum_old"`, - ) - await queryRunner.query(`ALTER TABLE "docker_registry" ALTER COLUMN "registryType" SET DEFAULT 'internal'`) - await queryRunner.query(`DROP TYPE "public"."docker_registry_registrytype_enum"`) - await queryRunner.query( - `ALTER TYPE "public"."docker_registry_registrytype_enum_old" RENAME TO "docker_registry_registrytype_enum"`, - ) - - await queryRunner.query(`ALTER TABLE "docker_registry" DROP COLUMN "region"`) - } -} diff --git a/apps/api/src/migrations/1757513754038-migration.ts b/apps/api/src/migrations/1757513754038-migration.ts deleted file mode 100644 index 6c0fc3ec8..000000000 --- a/apps/api/src/migrations/1757513754038-migration.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1757513754038 implements MigrationInterface { - name = 'Migration1757513754038' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" ADD "isFallback" boolean NOT NULL DEFAULT false`) - - // Update existing registries that have isDefault = true and region = null to be fallback registries - await queryRunner.query(` - UPDATE "docker_registry" - SET "isFallback" = true - WHERE "isDefault" = true AND "region" IS NULL AND "registryType" = 'backup' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "docker_registry" DROP COLUMN "isFallback"`) - } -} diff --git a/apps/api/src/migrations/1759241690773-migration.ts b/apps/api/src/migrations/1759241690773-migration.ts deleted file mode 100644 index 92604deef..000000000 --- a/apps/api/src/migrations/1759241690773-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1759241690773 implements MigrationInterface { - name = 'Migration1759241690773' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "used"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "capacity"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "capacity" integer NOT NULL DEFAULT '1000'`) - await queryRunner.query(`ALTER TABLE "runner" ADD "used" integer NOT NULL DEFAULT '0'`) - } -} diff --git a/apps/api/src/migrations/1759768058397-migration.ts b/apps/api/src/migrations/1759768058397-migration.ts deleted file mode 100644 index 0bd9239c3..000000000 --- a/apps/api/src/migrations/1759768058397-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1759768058397 implements MigrationInterface { - name = 'Migration1759768058397' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "name" character varying`) - await queryRunner.query(`UPDATE "sandbox" SET "name" = "id"`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "name" SET NOT NULL`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "name" SET DEFAULT 'sandbox-' || substring(gen_random_uuid()::text, 1, 10)`, - ) - await queryRunner.query( - `ALTER TABLE "sandbox" ADD CONSTRAINT "sandbox_organizationId_name_unique" UNIQUE ("organizationId", "name")`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP CONSTRAINT "sandbox_organizationId_name_unique"`) - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "name"`) - } -} diff --git a/apps/api/src/migrations/1761912147638-migration.ts b/apps/api/src/migrations/1761912147638-migration.ts deleted file mode 100644 index f83a07410..000000000 --- a/apps/api/src/migrations/1761912147638-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { configuration } from '../config/configuration' - -export class Migration1761912147638 implements MigrationInterface { - name = 'Migration1761912147638' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "defaultRegion" character varying NULL`) - await queryRunner.query(`UPDATE "organization" SET "defaultRegion" = '${configuration.defaultRegion.id}'`) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "defaultRegion" SET NOT NULL`) - - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" DROP DEFAULT`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" DROP DEFAULT`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "defaultRegion"`) - - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "region" SET DEFAULT 'us'`) - await queryRunner.query(`ALTER TABLE "warm_pool" ALTER COLUMN "target" SET DEFAULT 'us'`) - } -} diff --git a/apps/api/src/migrations/1761912147639-migration.ts b/apps/api/src/migrations/1761912147639-migration.ts deleted file mode 100644 index fd24cfb1e..000000000 --- a/apps/api/src/migrations/1761912147639-migration.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1761912147639 implements MigrationInterface { - name = 'Migration1761912147639' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.renameColumn('snapshot', 'internalName', 'ref') - await queryRunner.renameColumn('snapshot', 'buildRunnerId', 'initialRunnerId') - await queryRunner.query(`ALTER TABLE "snapshot" ADD "skipValidation" boolean NOT NULL DEFAULT false`) - - // Update snapshot states - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('pending', 'pulling', 'pending_validation', 'validating', 'active', 'inactive', 'building', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Revert snapshot states - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('build_pending', 'pending', 'pulling', 'pending_validation', 'validating', 'active', 'inactive', 'building', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "skipValidation"`) - await queryRunner.renameColumn('snapshot', 'initialRunnerId', 'buildRunnerId') - await queryRunner.renameColumn('snapshot', 'ref', 'internalName') - } -} diff --git a/apps/api/src/migrations/1763561822000-migration.ts b/apps/api/src/migrations/1763561822000-migration.ts deleted file mode 100644 index cb510f8ea..000000000 --- a/apps/api/src/migrations/1763561822000-migration.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1763561822000 implements MigrationInterface { - name = 'Migration1763561822000' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "authenticated_rate_limit" integer`) - await queryRunner.query(`ALTER TABLE "organization" ADD "sandbox_create_rate_limit" integer`) - await queryRunner.query(`ALTER TABLE "organization" ADD "sandbox_lifecycle_rate_limit" integer`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "sandbox_lifecycle_rate_limit"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "sandbox_create_rate_limit"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "authenticated_rate_limit"`) - } -} diff --git a/apps/api/src/migrations/1764073472179-migration.ts b/apps/api/src/migrations/1764073472179-migration.ts deleted file mode 100644 index b63338597..000000000 --- a/apps/api/src/migrations/1764073472179-migration.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { configuration } from '../config/configuration' - -export class Migration1764073472179 implements MigrationInterface { - name = 'Migration1764073472179' - - public async up(queryRunner: QueryRunner): Promise { - // Create region table - await queryRunner.query( - `CREATE TABLE "region" ("id" character varying NOT NULL, "name" character varying NOT NULL, "organizationId" uuid, "hidden" boolean NOT NULL DEFAULT false, "enforceQuotas" boolean NOT NULL DEFAULT true, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "region_id_pk" PRIMARY KEY ("id"))`, - ) - - // Add unique constraints for region name - await queryRunner.query( - `CREATE UNIQUE INDEX "region_organizationId_null_name_unique" ON "region" ("name") WHERE "organizationId" IS NULL`, - ) - await queryRunner.query( - `CREATE UNIQUE INDEX "region_organizationId_name_unique" ON "region" ("organizationId", "name") WHERE "organizationId" IS NOT NULL`, - ) - - // Expand organization table with defaultRegionId column (make it nullable) - await queryRunner.query(`ALTER TABLE "organization" ADD "defaultRegionId" character varying NULL`) - await queryRunner.query(`UPDATE "organization" SET "defaultRegionId" = "defaultRegion"`) - - // Add default value for required defaultRegion column before dropping it in the contract migration - await queryRunner.query( - `ALTER TABLE "organization" ALTER COLUMN "defaultRegion" SET DEFAULT '${configuration.defaultRegion.id}'`, - ) - - // Create region_quota table - await queryRunner.query( - `CREATE TABLE "region_quota" ("organizationId" uuid NOT NULL, "regionId" character varying NOT NULL, "total_cpu_quota" integer NOT NULL DEFAULT '10', "total_memory_quota" integer NOT NULL DEFAULT '10', "total_disk_quota" integer NOT NULL DEFAULT '30', "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "region_quota_organizationId_regionId_pk" PRIMARY KEY ("organizationId", "regionId"))`, - ) - await queryRunner.query( - `ALTER TABLE "region_quota" ADD CONSTRAINT "region_quota_organizationId_fk" FOREIGN KEY ("organizationId") REFERENCES "organization"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - - // For existing organizations, migrate their region-specific quotas to their default region - await queryRunner.query(` - INSERT INTO "region_quota" ("organizationId", "regionId", "total_cpu_quota", "total_memory_quota", "total_disk_quota") - SELECT - o."id" as "organizationId", - o."defaultRegionId" as "regionId", - o."total_cpu_quota", - o."total_memory_quota", - o."total_disk_quota" - FROM "organization" o - `) - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop region table - await queryRunner.query(`DROP TABLE "region"`) - - // Drop defaultRegionId column from organization table - await queryRunner.dropColumn('organization', 'defaultRegionId') - - // Drop region_quota table - await queryRunner.query(`DROP TABLE "region_quota"`) - } -} diff --git a/apps/api/src/migrations/1764073472180-migration.ts b/apps/api/src/migrations/1764073472180-migration.ts deleted file mode 100644 index 295c3c6d2..000000000 --- a/apps/api/src/migrations/1764073472180-migration.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { configuration } from '../config/configuration' - -export class Migration1764073472180 implements MigrationInterface { - name = 'Migration1764073472180' - - public async up(queryRunner: QueryRunner): Promise { - // Remove defaultRegion column from organization table - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "defaultRegion"`) - - // Remove region-specific quotas from organization table - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "total_cpu_quota"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "total_memory_quota"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "total_disk_quota"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Restore defaultRegion column to organization table - await queryRunner.query(`ALTER TABLE "organization" ADD "defaultRegion" character varying NULL`) - await queryRunner.query(`UPDATE "organization" SET "defaultRegion" = "defaultRegionId"`) - await queryRunner.query( - `ALTER TABLE "organization" ALTER COLUMN "defaultRegion" SET DEFAULT '${configuration.defaultRegion.id}'`, - ) - await queryRunner.query(`ALTER TABLE "organization" ALTER COLUMN "defaultRegion" SET NOT NULL`) - - // Restore region-specific quotas to organization table - await queryRunner.query(`ALTER TABLE "organization" ADD "total_disk_quota" integer NOT NULL DEFAULT '30'`) - await queryRunner.query(`ALTER TABLE "organization" ADD "total_memory_quota" integer NOT NULL DEFAULT '10'`) - await queryRunner.query(`ALTER TABLE "organization" ADD "total_cpu_quota" integer NOT NULL DEFAULT '10'`) - - // For each organization, restore region-specific quotas by taking the maximum values among all region quotas - await queryRunner.query(` - UPDATE "organization" o - SET - "total_cpu_quota" = COALESCE(q."total_cpu_quota", 10), - "total_memory_quota" = COALESCE(q."total_memory_quota", 10), - "total_disk_quota" = COALESCE(q."total_disk_quota", 30) - FROM ( - SELECT - "organizationId", - MAX("total_cpu_quota") as "total_cpu_quota", - MAX("total_memory_quota") as "total_memory_quota", - MAX("total_disk_quota") as "total_disk_quota" - FROM "region_quota" - GROUP BY "organizationId" - ) q - WHERE o."id" = q."organizationId" - `) - } -} diff --git a/apps/api/src/migrations/1764844895057-migration.ts b/apps/api/src/migrations/1764844895057-migration.ts deleted file mode 100644 index 1b84ef044..000000000 --- a/apps/api/src/migrations/1764844895057-migration.ts +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' -import { GlobalOrganizationRolesIds } from '../organization/constants/global-organization-roles.constant' -import { OrganizationResourcePermission } from '../organization/enums/organization-resource-permission.enum' - -export class Migration1764844895057 implements MigrationInterface { - name = 'Migration1764844895057' - - public async up(queryRunner: QueryRunner): Promise { - // add region type field with its type and constraints - await queryRunner.query(`CREATE TYPE "public"."region_regiontype_enum" AS ENUM('shared', 'dedicated', 'custom')`) - await queryRunner.query(`ALTER TABLE "region" ADD "regionType" "public"."region_regiontype_enum"`) - await queryRunner.query( - `ALTER TABLE "region" ADD CONSTRAINT "region_not_custom" CHECK ("organizationId" IS NOT NULL OR "regionType" != 'custom')`, - ) - await queryRunner.query( - `ALTER TABLE "region" ADD CONSTRAINT "region_not_shared" CHECK ("organizationId" IS NULL OR "regionType" != 'shared')`, - ) - await queryRunner.query(`UPDATE "region" SET "regionType" = 'custom' WHERE "organizationId" IS NOT NULL`) - await queryRunner.query(`UPDATE "region" SET "regionType" = 'shared' WHERE "organizationId" IS NULL`) - await queryRunner.query(`ALTER TABLE "region" ALTER COLUMN "regionType" SET NOT NULL`) - - // update api key permission enum - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum" RENAME TO "api_key_permissions_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:snapshots', 'delete:snapshots', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'write:regions', 'delete:regions', 'read:runners', 'write:runners', 'delete:runners', 'read:audit_logs')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum"[] USING "permissions"::"text"::"public"."api_key_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum_old"`) - - // update organization role permission enum - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum" RENAME TO "organization_role_permissions_enum_old"`, - ) - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum" AS ENUM('write:registries', 'delete:registries', 'write:snapshots', 'delete:snapshots', 'write:sandboxes', 'delete:sandboxes', 'read:volumes', 'write:volumes', 'delete:volumes', 'write:regions', 'delete:regions', 'read:runners', 'write:runners', 'delete:runners', 'read:audit_logs')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum_old"`) - - // add infrastructure admin role - await queryRunner.query(` - INSERT INTO "organization_role" - ("id", "name", "description", "permissions", "isGlobal") - VALUES - ( - '${GlobalOrganizationRolesIds.INFRASTRUCTURE_ADMIN}', - 'Infrastructure Admin', - 'Grants admin access to infrastructure in the organization', - ARRAY[ - '${OrganizationResourcePermission.WRITE_REGIONS}', - '${OrganizationResourcePermission.DELETE_REGIONS}', - '${OrganizationResourcePermission.READ_RUNNERS}', - '${OrganizationResourcePermission.WRITE_RUNNERS}', - '${OrganizationResourcePermission.DELETE_RUNNERS}' - ]::organization_role_permissions_enum[], - TRUE - ) - `) - - // add runner name field - await queryRunner.query(`ALTER TABLE "runner" ADD "name" character varying`) - await queryRunner.query(`UPDATE "runner" SET "name" = "id"`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "name" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ADD CONSTRAINT "runner_region_name_unique" UNIQUE ("region", "name")`) - - // create new index for runner - await queryRunner.query( - `CREATE INDEX "runner_state_unschedulable_region_index" ON "runner" ("state", "unschedulable", "region") `, - ) - - // add region proxy and ssh gateway fields - await queryRunner.query(`ALTER TABLE "region" ADD "proxyUrl" character varying`) - await queryRunner.query(`ALTER TABLE "region" ADD "toolboxProxyUrl" character varying`) - await queryRunner.query(`ALTER TABLE "region" ADD "proxyApiKeyHash" character varying`) - await queryRunner.query(`ALTER TABLE "region" ADD "sshGatewayUrl" character varying`) - await queryRunner.query(`ALTER TABLE "region" ADD "sshGatewayApiKeyHash" character varying`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "proxyUrl" DROP DEFAULT`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "region" DROP DEFAULT`) - } - - public async down(queryRunner: QueryRunner): Promise { - // remove region proxy and ssh gateway fields - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "sshGatewayApiKeyHash"`) - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "sshGatewayUrl"`) - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "proxyApiKeyHash"`) - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "toolboxProxyUrl"`) - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "proxyUrl"`) - - // drop region type field - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "regionType"`) - await queryRunner.query(`DROP TYPE "public"."region_regiontype_enum"`) - - // remove infrastructure admin role - await queryRunner.query( - `DELETE FROM "organization_role" WHERE "id" = '${GlobalOrganizationRolesIds.INFRASTRUCTURE_ADMIN}'`, - ) - - // revert api key permission enum - await queryRunner.query( - `CREATE TYPE "public"."api_key_permissions_enum_old" AS ENUM('delete:registries', 'delete:sandboxes', 'delete:snapshots', 'delete:volumes', 'read:audit_logs', 'read:volumes', 'write:registries', 'write:sandboxes', 'write:snapshots', 'write:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "api_key" ALTER COLUMN "permissions" TYPE "public"."api_key_permissions_enum_old"[] USING "permissions"::"text"::"public"."api_key_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."api_key_permissions_enum"`) - await queryRunner.query(`ALTER TYPE "public"."api_key_permissions_enum_old" RENAME TO "api_key_permissions_enum"`) - - // revert organization role permission enum - await queryRunner.query( - `CREATE TYPE "public"."organization_role_permissions_enum_old" AS ENUM('delete:registries', 'delete:sandboxes', 'delete:snapshots', 'delete:volumes', 'read:audit_logs', 'read:volumes', 'write:registries', 'write:sandboxes', 'write:snapshots', 'write:volumes')`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role" ALTER COLUMN "permissions" TYPE "public"."organization_role_permissions_enum_old"[] USING "permissions"::"text"::"public"."organization_role_permissions_enum_old"[]`, - ) - await queryRunner.query(`DROP TYPE "public"."organization_role_permissions_enum"`) - await queryRunner.query( - `ALTER TYPE "public"."organization_role_permissions_enum_old" RENAME TO "organization_role_permissions_enum"`, - ) - - // drop runner name field - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "name"`) - - // drop new index for runner - await queryRunner.query(`DROP INDEX "public"."runner_state_unschedulable_region_index"`) - } -} diff --git a/apps/api/src/migrations/1764844895058-migration.ts b/apps/api/src/migrations/1764844895058-migration.ts deleted file mode 100644 index dc9f0f844..000000000 --- a/apps/api/src/migrations/1764844895058-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1764844895058 implements MigrationInterface { - name = 'Migration1764844895058' - - public async up(queryRunner: QueryRunner): Promise { - // drop region hidden field - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "hidden"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // revert drop region hidden field - await queryRunner.query(`ALTER TABLE "region" ADD "hidden" boolean NOT NULL DEFAULT false`) - } -} diff --git a/apps/api/src/migrations/1765282546000-migration.ts b/apps/api/src/migrations/1765282546000-migration.ts deleted file mode 100644 index 3ffd25ea9..000000000 --- a/apps/api/src/migrations/1765282546000-migration.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1765282546000 implements MigrationInterface { - name = 'Migration1765282546000' - - public async up(queryRunner: QueryRunner): Promise { - // Remove skipValidation column - await queryRunner.query(`ALTER TABLE "snapshot" DROP COLUMN "skipValidation"`) - - // Update any snapshots in VALIDATING or PENDING_VALIDATION state to PENDING - await queryRunner.query(`UPDATE "snapshot" SET "state" = 'pending' WHERE "state" = 'validating'`) - await queryRunner.query(`UPDATE "snapshot" SET "state" = 'pending' WHERE "state" = 'pending_validation'`) - - // Update snapshot_state_enum to remove VALIDATING and PENDING_VALIDATION - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('pending', 'pulling', 'active', 'inactive', 'building', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Revert snapshot_state_enum to include VALIDATING and PENDING_VALIDATION - await queryRunner.query(`ALTER TYPE "public"."snapshot_state_enum" RENAME TO "snapshot_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."snapshot_state_enum" AS ENUM('pending', 'pulling', 'pending_validation', 'validating', 'active', 'inactive', 'building', 'error', 'build_failed', 'removing')`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "snapshot" ALTER COLUMN "state" TYPE "public"."snapshot_state_enum" USING "state"::"text"::"public"."snapshot_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "snapshot" ALTER COLUMN "state" SET DEFAULT 'pending'`) - await queryRunner.query(`DROP TYPE "public"."snapshot_state_enum_old"`) - - // Re-add skipValidation column - await queryRunner.query(`ALTER TABLE "snapshot" ADD "skipValidation" boolean NOT NULL DEFAULT false`) - } -} diff --git a/apps/api/src/migrations/1765366773736-migration.ts b/apps/api/src/migrations/1765366773736-migration.ts deleted file mode 100644 index f9f91a4c2..000000000 --- a/apps/api/src/migrations/1765366773736-migration.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1765366773736 implements MigrationInterface { - name = 'Migration1765366773736' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" ADD "recoverable" boolean NOT NULL DEFAULT false`) - - // Update existing sandboxes with recoverable error reasons to set recoverable = true - await queryRunner.query(` - UPDATE "sandbox" - SET "recoverable" = true - WHERE "state" = 'error' - AND ( - LOWER("errorReason") LIKE '%no space left on device%' - OR LOWER("errorReason") LIKE '%storage limit%' - OR LOWER("errorReason") LIKE '%disk quota exceeded%' - ) - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "recoverable"`) - } -} diff --git a/apps/api/src/migrations/1765400000000-migration.ts b/apps/api/src/migrations/1765400000000-migration.ts deleted file mode 100644 index 651dd53b6..000000000 --- a/apps/api/src/migrations/1765400000000-migration.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1765400000000 implements MigrationInterface { - name = 'Migration1765400000000' - - public async up(queryRunner: QueryRunner): Promise { - // Normalize Docker Hub URLs to 'docker.io' for consistency - // The runner will convert to 'index.docker.io/v1/' for builds where needed - await queryRunner.query(` - UPDATE "docker_registry" - SET "url" = 'docker.io' - WHERE LOWER("url") LIKE '%docker.io%' - `) - } - - public async down(queryRunner: QueryRunner): Promise { - // Cannot reliably reverse this migration as we don't know the original URLs - // This is a one-way normalization - } -} diff --git a/apps/api/src/migrations/1765806205881-migration.ts b/apps/api/src/migrations/1765806205881-migration.ts deleted file mode 100644 index f2f37aadb..000000000 --- a/apps/api/src/migrations/1765806205881-migration.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1765806205881 implements MigrationInterface { - name = 'Migration1765806205881' - - public async up(queryRunner: QueryRunner): Promise { - // Create snapshot_region table - await queryRunner.query(` - CREATE TABLE "snapshot_region" ( - "snapshotId" uuid NOT NULL, - "regionId" character varying NOT NULL, - "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), - "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), - CONSTRAINT "PK_snapshot_region" PRIMARY KEY ("snapshotId", "regionId") - ) - `) - - // Add foreign key constraints - await queryRunner.query(` - ALTER TABLE "snapshot_region" - ADD CONSTRAINT "FK_snapshot_region_snapshot" - FOREIGN KEY ("snapshotId") REFERENCES "snapshot"("id") ON DELETE CASCADE ON UPDATE NO ACTION - `) - - await queryRunner.query(` - ALTER TABLE "snapshot_region" - ADD CONSTRAINT "FK_snapshot_region_region" - FOREIGN KEY ("regionId") REFERENCES "region"("id") ON DELETE CASCADE ON UPDATE NO ACTION - `) - - // Migrate existing snapshots: add snapshot_region entries based on organization's default region - await queryRunner.query(` - INSERT INTO "snapshot_region" ("snapshotId", "regionId") - SELECT s.id, o."defaultRegionId" - FROM "snapshot" s - INNER JOIN "organization" o ON s."organizationId" = o.id - WHERE o."defaultRegionId" IS NOT NULL - `) - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop foreign key constraints - await queryRunner.query(`ALTER TABLE "snapshot_region" DROP CONSTRAINT "FK_snapshot_region_region"`) - await queryRunner.query(`ALTER TABLE "snapshot_region" DROP CONSTRAINT "FK_snapshot_region_snapshot"`) - - // Drop snapshot_region table - await queryRunner.query(`DROP TABLE "snapshot_region"`) - } -} diff --git a/apps/api/src/migrations/1766415256696-migration.ts b/apps/api/src/migrations/1766415256696-migration.ts deleted file mode 100644 index 2cb32a26b..000000000 --- a/apps/api/src/migrations/1766415256696-migration.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1766415256696 implements MigrationInterface { - name = 'Migration1766415256696' - - public async up(queryRunner: QueryRunner): Promise { - // region snapshot manager field - await queryRunner.query(`ALTER TABLE "region" ADD "snapshotManagerUrl" character varying`) - - // docker registry indexes - await queryRunner.query( - `CREATE INDEX "docker_registry_registryType_isDefault_index" ON "docker_registry" ("registryType", "isDefault") `, - ) - await queryRunner.query( - `CREATE INDEX "docker_registry_region_registryType_index" ON "docker_registry" ("region", "registryType") `, - ) - await queryRunner.query( - `CREATE INDEX "docker_registry_organizationId_registryType_index" ON "docker_registry" ("organizationId", "registryType") `, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - // drop region snapshot manager field - await queryRunner.query(`ALTER TABLE "region" DROP COLUMN "snapshotManagerUrl"`) - - // drop docker registry indexes - await queryRunner.query(`DROP INDEX "public"."docker_registry_organizationId_registryType_index"`) - await queryRunner.query(`DROP INDEX "public"."docker_registry_region_registryType_index"`) - await queryRunner.query(`DROP INDEX "public"."docker_registry_registryType_isDefault_index"`) - } -} diff --git a/apps/api/src/migrations/1767830400000-migration.ts b/apps/api/src/migrations/1767830400000-migration.ts deleted file mode 100644 index 77849c825..000000000 --- a/apps/api/src/migrations/1767830400000-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1767830400000 implements MigrationInterface { - name = 'Migration1767830400000' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "currentStartedSandboxes" integer NOT NULL DEFAULT 0`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentStartedSandboxes"`) - } -} diff --git a/apps/api/src/migrations/1768306129179-migration.ts b/apps/api/src/migrations/1768306129179-migration.ts deleted file mode 100644 index 45d74bbd4..000000000 --- a/apps/api/src/migrations/1768306129179-migration.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1768306129179 implements MigrationInterface { - name = 'Migration1768306129179' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE TYPE "public"."job_status_enum" AS ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED')`, - ) - await queryRunner.query(`CREATE TYPE "public"."job_resourcetype_enum" AS ENUM('SANDBOX', 'SNAPSHOT', 'BACKUP')`) - await queryRunner.query( - `CREATE TABLE "job" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "version" integer NOT NULL, "type" character varying NOT NULL, "status" "public"."job_status_enum" NOT NULL DEFAULT 'PENDING', "runnerId" character varying NOT NULL, "resourceType" "public"."job_resourcetype_enum" NOT NULL, "resourceId" character varying NOT NULL, "payload" character varying, "resultMetadata" character varying, "traceContext" jsonb, "errorMessage" text, "startedAt" TIMESTAMP WITH TIME ZONE, "completedAt" TIMESTAMP WITH TIME ZONE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "job_id_pk" PRIMARY KEY ("id"))`, - ) - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL`, - ) - await queryRunner.query(`CREATE INDEX "job_resourceType_resourceId_index" ON "job" ("resourceType", "resourceId") `) - await queryRunner.query(`CREATE INDEX "job_status_createdAt_index" ON "job" ("status", "createdAt") `) - await queryRunner.query(`CREATE INDEX "job_runnerId_status_index" ON "job" ("runnerId", "status") `) - await queryRunner.query(`ALTER TABLE "runner" RENAME COLUMN "version" TO "apiVersion"`) - await queryRunner.query(`ALTER TABLE "runner" ADD "appVersion" character varying DEFAULT 'v0.0.0-dev'`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "domain" DROP NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" DROP CONSTRAINT "UQ_330d74ac3d0e349b4c73c62ad6d"`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "apiUrl" DROP NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "proxyUrl" DROP NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "cpu" TYPE double precision`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "memoryGiB" TYPE double precision`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "diskGiB" TYPE double precision`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "gpu" DROP NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "gpuType" DROP NOT NULL`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "gpuType" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "gpu" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "diskGiB" TYPE integer`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "memoryGiB" TYPE integer`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "cpu" TYPE integer`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "proxyUrl" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "apiUrl" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" ADD CONSTRAINT "UQ_330d74ac3d0e349b4c73c62ad6d" UNIQUE ("domain")`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "domain" SET NOT NULL`) - await queryRunner.query(`ALTER TABLE "runner" RENAME COLUMN "apiVersion" TO "version"`) - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "appVersion"`) - await queryRunner.query(`DROP INDEX "public"."job_runnerId_status_index"`) - await queryRunner.query(`DROP INDEX "public"."job_status_createdAt_index"`) - await queryRunner.query(`DROP INDEX "public"."job_resourceType_resourceId_index"`) - await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_JOB"`) - await queryRunner.query(`DROP TABLE "job"`) - await queryRunner.query(`DROP TYPE "public"."job_resourcetype_enum"`) - await queryRunner.query(`DROP TYPE "public"."job_status_enum"`) - } -} diff --git a/apps/api/src/migrations/1768461678804-migration.ts b/apps/api/src/migrations/1768461678804-migration.ts deleted file mode 100644 index b1266ad50..000000000 --- a/apps/api/src/migrations/1768461678804-migration.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1768461678804 implements MigrationInterface { - name = 'Migration1768461678804' - - // TODO: Add migrationsTransactionMode: 'each', to data-source.ts - // TypeORM currently does not support non-transactional reverts - // Needed because CREATE/DROP INDEX CONCURRENTLY cannot run inside a transaction - // public readonly transaction = false - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - CREATE INDEX IF NOT EXISTS "idx_sandbox_volumes_gin" - ON "sandbox" - USING GIN ("volumes" jsonb_path_ops) - WHERE "desiredState" <> 'destroyed'; - `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(` - DROP INDEX IF EXISTS "idx_sandbox_volumes_gin"; - `) - } -} diff --git a/apps/api/src/migrations/1768475454675-migration.ts b/apps/api/src/migrations/1768475454675-migration.ts deleted file mode 100644 index fb06955f1..000000000 --- a/apps/api/src/migrations/1768475454675-migration.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1768475454675 implements MigrationInterface { - name = 'Migration1768475454675' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `CREATE INDEX "idx_region_custom" ON "region" ("organizationId") WHERE "regionType" = 'custom'`, - ) - await queryRunner.query( - `CREATE UNIQUE INDEX "region_sshGatewayApiKeyHash_unique" ON "region" ("sshGatewayApiKeyHash") WHERE "sshGatewayApiKeyHash" IS NOT NULL`, - ) - await queryRunner.query( - `CREATE UNIQUE INDEX "region_proxyApiKeyHash_unique" ON "region" ("proxyApiKeyHash") WHERE "proxyApiKeyHash" IS NOT NULL`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."region_proxyApiKeyHash_unique"`) - await queryRunner.query(`DROP INDEX "public"."region_sshGatewayApiKeyHash_unique"`) - await queryRunner.query(`DROP INDEX "public"."idx_region_custom"`) - } -} diff --git a/apps/api/src/migrations/1768485728153-migration.ts b/apps/api/src/migrations/1768485728153-migration.ts deleted file mode 100644 index b17226cae..000000000 --- a/apps/api/src/migrations/1768485728153-migration.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1768485728153 implements MigrationInterface { - name = 'Migration1768485728153' - - public async up(queryRunner: QueryRunner): Promise { - // Note: not using CONCURRENTLY + skipping transactions because of reverting issue: https://github.com/typeorm/typeorm/issues/9981 - - await queryRunner.query(`CREATE INDEX "api_key_org_user_idx" ON "api_key" ("organizationId", "userId") `) - await queryRunner.query( - `CREATE INDEX "warm_pool_find_idx" ON "warm_pool" ("snapshot", "target", "class", "cpu", "mem", "disk", "gpu", "osUser", "env") `, - ) - await queryRunner.query(`CREATE INDEX "snapshot_runner_state_idx" ON "snapshot_runner" ("state") `) - await queryRunner.query(`CREATE INDEX "snapshot_runner_runnerid_idx" ON "snapshot_runner" ("runnerId") `) - await queryRunner.query( - `CREATE INDEX "snapshot_runner_runnerid_snapshotref_idx" ON "snapshot_runner" ("runnerId", "snapshotRef") `, - ) - await queryRunner.query(`CREATE INDEX "snapshot_runner_snapshotref_idx" ON "snapshot_runner" ("snapshotRef") `) - await queryRunner.query(`CREATE INDEX "sandbox_pending_idx" ON "sandbox" ("id") WHERE "pending" = true`) - await queryRunner.query( - `CREATE INDEX "sandbox_active_only_idx" ON "sandbox" ("id") WHERE "state" <> ALL (ARRAY['destroyed'::sandbox_state_enum, 'archived'::sandbox_state_enum])`, - ) - await queryRunner.query( - `CREATE INDEX "sandbox_runner_state_desired_idx" ON "sandbox" ("runnerId", "state", "desiredState") WHERE "pending" = false`, - ) - await queryRunner.query(`CREATE INDEX "sandbox_backupstate_idx" ON "sandbox" ("backupState") `) - await queryRunner.query(`CREATE INDEX "sandbox_resources_idx" ON "sandbox" ("cpu", "mem", "disk", "gpu") `) - await queryRunner.query(`CREATE INDEX "sandbox_region_idx" ON "sandbox" ("region") `) - await queryRunner.query(`CREATE INDEX "sandbox_organizationid_idx" ON "sandbox" ("organizationId") `) - await queryRunner.query(`CREATE INDEX "sandbox_runner_state_idx" ON "sandbox" ("runnerId", "state") `) - await queryRunner.query(`CREATE INDEX "sandbox_runnerid_idx" ON "sandbox" ("runnerId") `) - await queryRunner.query(`CREATE INDEX "sandbox_snapshot_idx" ON "sandbox" ("snapshot") `) - await queryRunner.query(`CREATE INDEX "sandbox_desiredstate_idx" ON "sandbox" ("desiredState") `) - await queryRunner.query(`CREATE INDEX "sandbox_state_idx" ON "sandbox" ("state") `) - await queryRunner.query(`CREATE INDEX "snapshot_state_idx" ON "snapshot" ("state") `) - await queryRunner.query(`CREATE INDEX "snapshot_name_idx" ON "snapshot" ("name") `) - await queryRunner.query( - `CREATE INDEX "sandbox_labels_gin_full_idx" ON "sandbox" USING gin ("labels" jsonb_path_ops)`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."sandbox_labels_gin_full_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_name_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_state_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_state_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_desiredstate_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_snapshot_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_runnerid_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_runner_state_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_organizationid_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_region_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_resources_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_backupstate_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_runner_state_desired_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_active_only_idx"`) - await queryRunner.query(`DROP INDEX "public"."sandbox_pending_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_runner_snapshotref_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_runner_runnerid_snapshotref_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_runner_runnerid_idx"`) - await queryRunner.query(`DROP INDEX "public"."snapshot_runner_state_idx"`) - await queryRunner.query(`DROP INDEX "public"."warm_pool_find_idx"`) - await queryRunner.query(`DROP INDEX "public"."api_key_org_user_idx"`) - } -} diff --git a/apps/api/src/migrations/1768583941244-migration.ts b/apps/api/src/migrations/1768583941244-migration.ts deleted file mode 100644 index 37f7e7aac..000000000 --- a/apps/api/src/migrations/1768583941244-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1768583941244 implements MigrationInterface { - name = 'Migration1768583941244' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "currentCpuLoadAverage" double precision NOT NULL DEFAULT '0'`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "currentCpuLoadAverage"`) - } -} diff --git a/apps/api/src/migrations/1769516172576-migration.ts b/apps/api/src/migrations/1769516172576-migration.ts deleted file mode 100644 index d6814c57c..000000000 --- a/apps/api/src/migrations/1769516172576-migration.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1769516172576 implements MigrationInterface { - name = 'Migration1769516172576' - - public async up(queryRunner: QueryRunner): Promise { - // For sandbox_state_enum - add 'resizing' value - await queryRunner.query(`ALTER TYPE "public"."sandbox_state_enum" ADD VALUE IF NOT EXISTS 'resizing'`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop any index with explicit desiredState enum type cast in WHERE clause (required for enum swap) - - // For sandbox_state_enum - remove 'resizing' value - await queryRunner.query(`UPDATE "sandbox" SET "state" = 'stopped' WHERE "state" = 'resizing'`) - await queryRunner.query(`ALTER TYPE "public"."sandbox_state_enum" RENAME TO "sandbox_state_enum_old"`) - await queryRunner.query( - `CREATE TYPE "public"."sandbox_state_enum" AS ENUM('creating', 'restoring', 'destroyed', 'destroying', 'started', 'stopped', 'starting', 'stopping', 'error', 'build_failed', 'pending_build', 'building_snapshot', 'unknown', 'pulling_snapshot', 'archiving', 'archived')`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "state" TYPE "public"."sandbox_state_enum" USING "state"::"text"::"public"."sandbox_state_enum"`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "state" SET DEFAULT 'unknown'`) - await queryRunner.query(`DROP TYPE "public"."sandbox_state_enum_old"`) - - // Recreate the indices that were dropped - } -} diff --git a/apps/api/src/migrations/1769516172577-migration.ts b/apps/api/src/migrations/1769516172577-migration.ts deleted file mode 100644 index a3502011e..000000000 --- a/apps/api/src/migrations/1769516172577-migration.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1769516172577 implements MigrationInterface { - name = 'Migration1769516172577' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "draining" boolean NOT NULL DEFAULT false`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "draining"`) - } -} diff --git a/apps/api/src/migrations/1770043707083-migration.ts b/apps/api/src/migrations/1770043707083-migration.ts deleted file mode 100644 index c55327d6e..000000000 --- a/apps/api/src/migrations/1770043707083-migration.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770043707083 implements MigrationInterface { - name = 'Migration1770043707083' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "experimentalConfig" jsonb`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "experimentalConfig"`) - } -} diff --git a/apps/api/src/migrations/1770212429837-migration.ts b/apps/api/src/migrations/1770212429837-migration.ts deleted file mode 100644 index 2af396339..000000000 --- a/apps/api/src/migrations/1770212429837-migration.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770212429837 implements MigrationInterface { - name = 'Migration1770212429837' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" ADD "authenticated_rate_limit_ttl_seconds" integer`) - await queryRunner.query(`ALTER TABLE "organization" ADD "sandbox_create_rate_limit_ttl_seconds" integer`) - await queryRunner.query(`ALTER TABLE "organization" ADD "sandbox_lifecycle_rate_limit_ttl_seconds" integer`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "sandbox_lifecycle_rate_limit_ttl_seconds"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "sandbox_create_rate_limit_ttl_seconds"`) - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "authenticated_rate_limit_ttl_seconds"`) - } -} diff --git a/apps/api/src/migrations/1770823569571-migration.ts b/apps/api/src/migrations/1770823569571-migration.ts deleted file mode 100644 index 57c69d2d6..000000000 --- a/apps/api/src/migrations/1770823569571-migration.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770823569571 implements MigrationInterface { - name = 'Migration1770823569571' - - public async up(queryRunner: QueryRunner): Promise { - // Note: not using CONCURRENTLY + skipping transactions because of reverting issue: https://github.com/typeorm/typeorm/issues/9981 - await queryRunner.query(`CREATE INDEX "idx_sandbox_authtoken" ON "sandbox" ("authToken") `) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."idx_sandbox_authtoken"`) - } -} diff --git a/apps/api/src/migrations/1770880371265-migration.ts b/apps/api/src/migrations/1770880371265-migration.ts deleted file mode 100644 index 708710f09..000000000 --- a/apps/api/src/migrations/1770880371265-migration.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770880371265 implements MigrationInterface { - name = 'Migration1770880371265' - - public async up(queryRunner: QueryRunner): Promise { - // Note: not using CONCURRENTLY + skipping transactions because of reverting issue: https://github.com/typeorm/typeorm/issues/9981 - await queryRunner.query( - `CREATE INDEX "idx_sandbox_usage_periods_sandbox_end" ON "sandbox_usage_periods" ("sandboxId", "endAt") `, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."idx_sandbox_usage_periods_sandbox_end"`) - } -} diff --git a/apps/api/src/migrations/README.md b/apps/api/src/migrations/README.md index a96f808ed..b79ae3ab9 100644 --- a/apps/api/src/migrations/README.md +++ b/apps/api/src/migrations/README.md @@ -2,6 +2,12 @@ This project uses the **Expand and Contract** pattern for database migrations to support zero-downtime deployments. +## Pre-launch Baseline + +Because BoxLite has not launched yet, the historical migration chain has been squashed into +the root baseline `1741087887225-migration.ts`. Fresh databases should run this single baseline first. +Future schema changes should use the expand-and-contract workflow below. + ## Overview The expand and contract pattern splits database changes into two phases: @@ -16,7 +22,8 @@ This allows the database and API to be updated independently while maintaining c - `pre-deploy/` - Migrations that run **before** the API is deployed - `post-deploy/` - Migrations that run **after** the API is deployed -Note: Root folder migrations (not in pre-deploy or post-deploy) are legacy migrations created before the expand-and-contract pattern was introduced. These run during `migration:run:init` only. +Note: the root folder is reserved for the pre-launch baseline only. Do not add new root migrations; +use `pre-deploy/` or `post-deploy/` for new changes. ## Developer Workflow @@ -131,10 +138,10 @@ public async up(queryRunner: QueryRunner): Promise { The trigger intercepts every INSERT and UPDATE on the table and automatically copies the value between columns: -| API Version | Writes to | Trigger copies to | Result | -|-------------|-----------|-------------------|--------| -| Old API | `name` | `display_name` | Both columns have the value | -| New API | `display_name` | `name` | Both columns have the value | +| API Version | Writes to | Trigger copies to | Result | +| ----------- | -------------- | ----------------- | --------------------------- | +| Old API | `name` | `display_name` | Both columns have the value | +| New API | `display_name` | `name` | Both columns have the value | **Deployment timeline:** diff --git a/apps/api/src/migrations/post-deploy/1770880371266-migration.ts b/apps/api/src/migrations/post-deploy/1770880371266-migration.ts deleted file mode 100644 index f3c89095e..000000000 --- a/apps/api/src/migrations/post-deploy/1770880371266-migration.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770880371266 implements MigrationInterface { - name = 'Migration1770880371266' - - public async up(queryRunner: QueryRunner): Promise { - // Note: not using CONCURRENTLY + skipping transactions because of reverting issue: https://github.com/typeorm/typeorm/issues/9981 - await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_JOB"`) - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL AND "type" != 'CREATE_BACKUP'`, - ) - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_BACKUP_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL AND "type" = 'CREATE_BACKUP'`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_BACKUP_JOB"`) - await queryRunner.query(`DROP INDEX "public"."IDX_UNIQUE_INCOMPLETE_JOB"`) - await queryRunner.query( - `CREATE UNIQUE INDEX "IDX_UNIQUE_INCOMPLETE_JOB" ON "job" ("resourceType", "resourceId", "runnerId") WHERE "completedAt" IS NULL`, - ) - } -} diff --git a/apps/api/src/migrations/post-deploy/1774438866002-migration.ts b/apps/api/src/migrations/post-deploy/1774438866002-migration.ts deleted file mode 100644 index b8e72d70d..000000000 --- a/apps/api/src/migrations/post-deploy/1774438866002-migration.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1774438866002 implements MigrationInterface { - name = 'Migration1774438866002' - - public async up(queryRunner: QueryRunner): Promise { - /** - * Drop DB-level default for sandbox name. Now set exclusively in the entity constructor. - */ - await queryRunner.query(`ALTER TABLE "sandbox" DROP CONSTRAINT "sandbox_organizationId_name_unique"`) - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "name" DROP DEFAULT`) - await queryRunner.query( - `ALTER TABLE "sandbox" ADD CONSTRAINT "sandbox_organizationId_name_unique" UNIQUE ("organizationId", "name")`, - ) - - /** - * Drop DB-level default for sandbox authToken. Now set exclusively via class field initializer. - */ - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "authToken" DROP DEFAULT`) - - /** - * The initial migration incorrectly added the column, it was never actually added to the entity definition. - */ - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "sshPass"`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "sandbox" ADD "sshPass" character varying(32) NOT NULL DEFAULT REPLACE(uuid_generate_v4()::text, '-', '')`, - ) - - await queryRunner.query(`ALTER TABLE "sandbox" DROP CONSTRAINT "sandbox_organizationId_name_unique"`) - await queryRunner.query( - `ALTER TABLE "sandbox" ALTER COLUMN "name" SET DEFAULT 'sandbox-' || substring(gen_random_uuid()::text, 1, 10)`, - ) - await queryRunner.query( - `ALTER TABLE "sandbox" ADD CONSTRAINT "sandbox_organizationId_name_unique" UNIQUE ("organizationId", "name")`, - ) - - await queryRunner.query(`ALTER TABLE "sandbox" ALTER COLUMN "authToken" SET DEFAULT MD5(random()::text)`) - } -} diff --git a/apps/api/src/migrations/post-deploy/1774454800508-migration.ts b/apps/api/src/migrations/post-deploy/1774454800508-migration.ts deleted file mode 100644 index 3d71fe11e..000000000 --- a/apps/api/src/migrations/post-deploy/1774454800508-migration.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1774454800508 implements MigrationInterface { - name = 'Migration1774454800508' - - public async up(queryRunner: QueryRunner): Promise { - // Drop dual-write triggers and function (no longer needed after deployment) - await queryRunner.query(`DROP TRIGGER IF EXISTS sandbox_activity_sync_to_old ON "sandbox_last_activity"`) - await queryRunner.query(`DROP TRIGGER IF EXISTS sandbox_activity_sync_to_new ON "sandbox"`) - await queryRunner.query(`DROP FUNCTION IF EXISTS sync_sandbox_last_activity()`) - - // Drop the old column - await queryRunner.query(`ALTER TABLE "sandbox" DROP COLUMN "lastActivityAt"`) - } - - public async down(queryRunner: QueryRunner): Promise { - // Restore the old column - await queryRunner.query(`ALTER TABLE "sandbox" ADD "lastActivityAt" TIMESTAMP WITH TIME ZONE`) - - // Backfill data from sandbox_last_activity to restored column - await queryRunner.query(` - UPDATE "sandbox" s - SET "lastActivityAt" = sla."lastActivityAt" - FROM "sandbox_last_activity" sla - WHERE s.id = sla."sandboxId" - `) - - // Recreate sync function for dual-write - await queryRunner.query(` - CREATE OR REPLACE FUNCTION sync_sandbox_last_activity() - RETURNS TRIGGER AS $$ - BEGIN - IF TG_TABLE_NAME = 'sandbox' THEN - INSERT INTO sandbox_last_activity ("sandboxId", "lastActivityAt") - VALUES (NEW.id, NEW."lastActivityAt") - ON CONFLICT ("sandboxId") DO UPDATE SET "lastActivityAt" = EXCLUDED."lastActivityAt" - WHERE sandbox_last_activity."lastActivityAt" IS DISTINCT FROM EXCLUDED."lastActivityAt"; - ELSIF TG_TABLE_NAME = 'sandbox_last_activity' THEN - UPDATE sandbox SET "lastActivityAt" = NEW."lastActivityAt" - WHERE id = NEW."sandboxId" AND "lastActivityAt" IS DISTINCT FROM NEW."lastActivityAt"; - END IF; - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - `) - - // Recreate trigger on sandbox table (old -> new) - await queryRunner.query(` - CREATE TRIGGER sandbox_activity_sync_to_new - AFTER INSERT OR UPDATE OF "lastActivityAt" ON "sandbox" - FOR EACH ROW EXECUTE FUNCTION sync_sandbox_last_activity(); - `) - - // Recreate trigger on sandbox_last_activity table (new -> old) - await queryRunner.query(` - CREATE TRIGGER sandbox_activity_sync_to_old - AFTER INSERT OR UPDATE OF "lastActivityAt" ON "sandbox_last_activity" - FOR EACH ROW EXECUTE FUNCTION sync_sandbox_last_activity(); - `) - - // Re-sync any rows that changed during the window between backfill and trigger creation - await queryRunner.query(` - UPDATE "sandbox" s - SET "lastActivityAt" = sla."lastActivityAt" - FROM "sandbox_last_activity" sla - WHERE s.id = sla."sandboxId" - AND s."lastActivityAt" IS DISTINCT FROM sla."lastActivityAt" - `) - } -} diff --git a/apps/api/src/migrations/pre-deploy/1770900000000-migration.ts b/apps/api/src/migrations/pre-deploy/1770900000000-migration.ts deleted file mode 100644 index fada64c76..000000000 --- a/apps/api/src/migrations/pre-deploy/1770900000000-migration.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1770900000000 implements MigrationInterface { - name = 'Migration1770900000000' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization" ADD "snapshot_deactivation_timeout_minutes" integer NOT NULL DEFAULT 20160`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "organization" DROP COLUMN "snapshot_deactivation_timeout_minutes"`) - } -} diff --git a/apps/api/src/migrations/pre-deploy/1773744656413-migration.ts b/apps/api/src/migrations/pre-deploy/1773744656413-migration.ts deleted file mode 100644 index 0fb8f835e..000000000 --- a/apps/api/src/migrations/pre-deploy/1773744656413-migration.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1773744656413 implements MigrationInterface { - name = 'Migration1773744656413' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "currentAllocatedCpu" TYPE double precision`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "currentAllocatedMemoryGiB" TYPE double precision`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "currentAllocatedDiskGiB" TYPE double precision`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "runner" ALTER COLUMN "currentAllocatedDiskGiB" TYPE integer USING ROUND("currentAllocatedDiskGiB")::integer`, - ) - await queryRunner.query( - `ALTER TABLE "runner" ALTER COLUMN "currentAllocatedMemoryGiB" TYPE integer USING ROUND("currentAllocatedMemoryGiB")::integer`, - ) - await queryRunner.query( - `ALTER TABLE "runner" ALTER COLUMN "currentAllocatedCpu" TYPE integer USING ROUND("currentAllocatedCpu")::integer`, - ) - } -} diff --git a/apps/api/src/migrations/pre-deploy/1773916204375-migration.ts b/apps/api/src/migrations/pre-deploy/1773916204375-migration.ts deleted file mode 100644 index ce1561051..000000000 --- a/apps/api/src/migrations/pre-deploy/1773916204375-migration.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1773916204375 implements MigrationInterface { - name = 'Migration1773916204375' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" ADD "serviceHealth" jsonb`) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "runner" DROP COLUMN "serviceHealth"`) - } -} diff --git a/apps/api/src/migrations/pre-deploy/1774438866001-migration.ts b/apps/api/src/migrations/pre-deploy/1774438866001-migration.ts deleted file mode 100644 index 144460e34..000000000 --- a/apps/api/src/migrations/pre-deploy/1774438866001-migration.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -/** - * Reconciliation migration that resolves drift between the database schema and the current entity definitions in the codebase. - */ -export class Migration1774438866001 implements MigrationInterface { - name = 'Migration1774438866001' - - public async up(queryRunner: QueryRunner): Promise { - /** - * Constraint renames due to conflict with custom naming strategy. - */ - await queryRunner.query( - `ALTER TABLE "snapshot_region" RENAME CONSTRAINT "FK_snapshot_region_snapshot" TO "snapshot_region_snapshotId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot_region" RENAME CONSTRAINT "FK_snapshot_region_region" TO "snapshot_region_regionId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot" RENAME CONSTRAINT "public.snapshot_buildInfoImageRef_fk" TO "snapshot_buildInfoSnapshotRef_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "sandbox" RENAME CONSTRAINT "public.sandbox_buildInfoSnapshotRef_fk" TO "sandbox_buildInfoSnapshotRef_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot" RENAME CONSTRAINT "image_organizationId_name_unique" TO "snapshot_organizationId_name_unique"`, - ) - await queryRunner.query(`ALTER TABLE "sandbox" RENAME CONSTRAINT "public.sandbox_id_pk" TO "sandbox_id_pk"`) - - /** Add missing column defaults for runner that the entity defines but the original migration omitted. */ - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "cpu" SET DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "memoryGiB" SET DEFAULT '0'`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "diskGiB" SET DEFAULT '0'`) - - /** - * Recreate roleId FKs on junction tables with NO ACTION instead of CASCADE. - * TypeORM's onDelete: CASCADE on ManyToMany only applies to the owning side FK, - * the inverse side (roleId) defaults to NO ACTION. The original migrations incorrectly - * created both FKs with CASCADE. This aligns the database with TypeORM's expected schema. - */ - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" DROP CONSTRAINT "organization_role_assignment_invitation_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" DROP CONSTRAINT "organization_role_assignment_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" DROP CONSTRAINT "organization_role_assignment_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" DROP CONSTRAINT "organization_role_assignment_invitation_roleId_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment" ADD CONSTRAINT "organization_role_assignment_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - await queryRunner.query( - `ALTER TABLE "organization_role_assignment_invitation" ADD CONSTRAINT "organization_role_assignment_invitation_roleId_fk" FOREIGN KEY ("roleId") REFERENCES "organization_role"("id") ON DELETE CASCADE ON UPDATE CASCADE`, - ) - - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "diskGiB" DROP DEFAULT`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "memoryGiB" DROP DEFAULT`) - await queryRunner.query(`ALTER TABLE "runner" ALTER COLUMN "cpu" DROP DEFAULT`) - - await queryRunner.query(`ALTER TABLE "sandbox" RENAME CONSTRAINT "sandbox_id_pk" TO "public.sandbox_id_pk"`) - - await queryRunner.query( - `ALTER TABLE "snapshot" RENAME CONSTRAINT "snapshot_organizationId_name_unique" TO "image_organizationId_name_unique"`, - ) - - await queryRunner.query( - `ALTER TABLE "sandbox" RENAME CONSTRAINT "sandbox_buildInfoSnapshotRef_fk" TO "public.sandbox_buildInfoSnapshotRef_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot" RENAME CONSTRAINT "snapshot_buildInfoSnapshotRef_fk" TO "public.snapshot_buildInfoImageRef_fk"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot_region" RENAME CONSTRAINT "snapshot_region_regionId_fk" TO "FK_snapshot_region_region"`, - ) - await queryRunner.query( - `ALTER TABLE "snapshot_region" RENAME CONSTRAINT "snapshot_region_snapshotId_fk" TO "FK_snapshot_region_snapshot"`, - ) - } -} diff --git a/apps/api/src/migrations/pre-deploy/1774454790153-migration.ts b/apps/api/src/migrations/pre-deploy/1774454790153-migration.ts deleted file mode 100644 index 6b32c41a3..000000000 --- a/apps/api/src/migrations/pre-deploy/1774454790153-migration.ts +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { MigrationInterface, QueryRunner } from 'typeorm' - -export class Migration1774454790153 implements MigrationInterface { - name = 'Migration1774454790153' - - public async up(queryRunner: QueryRunner): Promise { - // Create new table - await queryRunner.query( - `CREATE TABLE "sandbox_last_activity" ("sandboxId" character varying NOT NULL, "lastActivityAt" TIMESTAMP WITH TIME ZONE, CONSTRAINT "sandbox_last_activity_sandboxId_pk" PRIMARY KEY ("sandboxId"))`, - ) - await queryRunner.query( - `ALTER TABLE "sandbox_last_activity" ADD CONSTRAINT "sandbox_last_activity_sandboxId_fk" FOREIGN KEY ("sandboxId") REFERENCES "sandbox"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, - ) - - // Copy existing data from sandbox.lastActivityAt to new table - await queryRunner.query(` - INSERT INTO "sandbox_last_activity" ("sandboxId", "lastActivityAt") - SELECT "id", COALESCE("lastActivityAt", "createdAt", NOW()) - FROM "sandbox" - WHERE "state" != 'destroyed' - `) - - // Create sync function for dual-write - await queryRunner.query(` - CREATE OR REPLACE FUNCTION sync_sandbox_last_activity() - RETURNS TRIGGER AS $$ - BEGIN - IF TG_TABLE_NAME = 'sandbox' THEN - INSERT INTO sandbox_last_activity ("sandboxId", "lastActivityAt") - VALUES (NEW.id, NEW."lastActivityAt") - ON CONFLICT ("sandboxId") DO UPDATE SET "lastActivityAt" = EXCLUDED."lastActivityAt" - WHERE sandbox_last_activity."lastActivityAt" IS DISTINCT FROM EXCLUDED."lastActivityAt"; - ELSIF TG_TABLE_NAME = 'sandbox_last_activity' THEN - UPDATE sandbox SET "lastActivityAt" = NEW."lastActivityAt" - WHERE id = NEW."sandboxId" AND "lastActivityAt" IS DISTINCT FROM NEW."lastActivityAt"; - END IF; - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - `) - - // Create trigger on sandbox table (old -> new) - await queryRunner.query(` - CREATE TRIGGER sandbox_activity_sync_to_new - AFTER INSERT OR UPDATE OF "lastActivityAt" ON "sandbox" - FOR EACH ROW EXECUTE FUNCTION sync_sandbox_last_activity(); - `) - - // Create trigger on sandbox_last_activity table (new -> old) - await queryRunner.query(` - CREATE TRIGGER sandbox_activity_sync_to_old - AFTER INSERT OR UPDATE OF "lastActivityAt" ON "sandbox_last_activity" - FOR EACH ROW EXECUTE FUNCTION sync_sandbox_last_activity(); - `) - - // Re-sync any rows that were created or changed during the window between initial copy and trigger creation - await queryRunner.query(` - INSERT INTO "sandbox_last_activity" ("sandboxId", "lastActivityAt") - SELECT "id", COALESCE("lastActivityAt", "createdAt", NOW()) - FROM "sandbox" - WHERE "state" != 'destroyed' - ON CONFLICT ("sandboxId") DO UPDATE - SET "lastActivityAt" = EXCLUDED."lastActivityAt" - WHERE "sandbox_last_activity"."lastActivityAt" IS DISTINCT FROM EXCLUDED."lastActivityAt" - `) - } - - public async down(queryRunner: QueryRunner): Promise { - // Drop dual-write triggers and function - await queryRunner.query(`DROP TRIGGER IF EXISTS sandbox_activity_sync_to_old ON "sandbox_last_activity"`) - await queryRunner.query(`DROP TRIGGER IF EXISTS sandbox_activity_sync_to_new ON "sandbox"`) - await queryRunner.query(`DROP FUNCTION IF EXISTS sync_sandbox_last_activity()`) - - // Drop table - await queryRunner.query(`ALTER TABLE "sandbox_last_activity" DROP CONSTRAINT "sandbox_last_activity_sandboxId_fk"`) - await queryRunner.query(`DROP TABLE "sandbox_last_activity"`) - } -} diff --git a/apps/api/src/notification/emitters/notification-redis.emitter.ts b/apps/api/src/notification/emitters/notification-redis.emitter.ts index 40128cf00..d691cce5a 100644 --- a/apps/api/src/notification/emitters/notification-redis.emitter.ts +++ b/apps/api/src/notification/emitters/notification-redis.emitter.ts @@ -8,19 +8,16 @@ import { Emitter } from '@socket.io/redis-emitter' import { InjectRedis } from '@nestjs-modules/ioredis' import Redis from 'ioredis' import { NotificationEmitter } from '../gateways/notification-emitter.abstract' -import { SandboxDto } from '../../sandbox/dto/sandbox.dto' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SandboxDesiredState } from '../../sandbox/enums/sandbox-desired-state.enum' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SnapshotDto } from '../../sandbox/dto/snapshot.dto' -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' -import { SnapshotEvents } from '../../sandbox/constants/snapshot-events' -import { VolumeDto } from '../../sandbox/dto/volume.dto' -import { VolumeState } from '../../sandbox/enums/volume-state.enum' -import { VolumeEvents } from '../../sandbox/constants/volume-events' -import { RunnerDto } from '../../sandbox/dto/runner.dto' -import { RunnerState } from '../../sandbox/enums/runner-state.enum' -import { RunnerEvents } from '../../sandbox/constants/runner-events' +import { BoxDto } from '../../box/dto/box.dto' +import { BoxState } from '../../box/enums/box-state.enum' +import { BoxDesiredState } from '../../box/enums/box-desired-state.enum' +import { BoxEvents } from '../../box/constants/box-events.constants' +import { VolumeDto } from '../../box/dto/volume.dto' +import { VolumeState } from '../../box/enums/volume-state.enum' +import { VolumeEvents } from '../../box/constants/volume-events' +import { RunnerDto } from '../../box/dto/runner.dto' +import { RunnerState } from '../../box/enums/runner-state.enum' +import { RunnerEvents } from '../../box/constants/runner-events' @Injectable() export class NotificationRedisEmitter extends NotificationEmitter implements OnModuleInit { @@ -36,36 +33,16 @@ export class NotificationRedisEmitter extends NotificationEmitter implements OnM this.logger.debug('Socket.io Redis emitter initialized (publish-only)') } - emitSandboxCreated(sandbox: SandboxDto) { - this.emitter.to(sandbox.organizationId).emit(SandboxEvents.CREATED, sandbox) + emitBoxCreated(box: BoxDto) { + this.emitter.to(box.organizationId).emit(BoxEvents.CREATED, box) } - emitSandboxStateUpdated(sandbox: SandboxDto, oldState: SandboxState, newState: SandboxState) { - this.emitter.to(sandbox.organizationId).emit(SandboxEvents.STATE_UPDATED, { sandbox, oldState, newState }) + emitBoxStateUpdated(box: BoxDto, oldState: BoxState, newState: BoxState) { + this.emitter.to(box.organizationId).emit(BoxEvents.STATE_UPDATED, { box, oldState, newState }) } - emitSandboxDesiredStateUpdated( - sandbox: SandboxDto, - oldDesiredState: SandboxDesiredState, - newDesiredState: SandboxDesiredState, - ) { - this.emitter - .to(sandbox.organizationId) - .emit(SandboxEvents.DESIRED_STATE_UPDATED, { sandbox, oldDesiredState, newDesiredState }) - } - - emitSnapshotCreated(snapshot: SnapshotDto) { - this.emitter.to(snapshot.organizationId).emit(SnapshotEvents.CREATED, snapshot) - } - - emitSnapshotStateUpdated(snapshot: SnapshotDto, oldState: SnapshotState, newState: SnapshotState) { - this.emitter - .to(snapshot.organizationId) - .emit(SnapshotEvents.STATE_UPDATED, { snapshot: snapshot, oldState, newState }) - } - - emitSnapshotRemoved(snapshot: SnapshotDto) { - this.emitter.to(snapshot.organizationId).emit(SnapshotEvents.REMOVED, snapshot) + emitBoxDesiredStateUpdated(box: BoxDto, oldDesiredState: BoxDesiredState, newDesiredState: BoxDesiredState) { + this.emitter.to(box.organizationId).emit(BoxEvents.DESIRED_STATE_UPDATED, { box, oldDesiredState, newDesiredState }) } emitVolumeCreated(volume: VolumeDto) { diff --git a/apps/api/src/notification/gateways/notification-emitter.abstract.ts b/apps/api/src/notification/gateways/notification-emitter.abstract.ts index 5efe3d5d4..763e9a87e 100644 --- a/apps/api/src/notification/gateways/notification-emitter.abstract.ts +++ b/apps/api/src/notification/gateways/notification-emitter.abstract.ts @@ -4,27 +4,22 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { SandboxDto } from '../../sandbox/dto/sandbox.dto' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SandboxDesiredState } from '../../sandbox/enums/sandbox-desired-state.enum' -import { SnapshotDto } from '../../sandbox/dto/snapshot.dto' -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' -import { VolumeDto } from '../../sandbox/dto/volume.dto' -import { VolumeState } from '../../sandbox/enums/volume-state.enum' -import { RunnerDto } from '../../sandbox/dto/runner.dto' -import { RunnerState } from '../../sandbox/enums/runner-state.enum' +import { BoxDto } from '../../box/dto/box.dto' +import { BoxState } from '../../box/enums/box-state.enum' +import { BoxDesiredState } from '../../box/enums/box-desired-state.enum' +import { VolumeDto } from '../../box/dto/volume.dto' +import { VolumeState } from '../../box/enums/volume-state.enum' +import { RunnerDto } from '../../box/dto/runner.dto' +import { RunnerState } from '../../box/enums/runner-state.enum' export abstract class NotificationEmitter { - abstract emitSandboxCreated(sandbox: SandboxDto): void - abstract emitSandboxStateUpdated(sandbox: SandboxDto, oldState: SandboxState, newState: SandboxState): void - abstract emitSandboxDesiredStateUpdated( - sandbox: SandboxDto, - oldDesiredState: SandboxDesiredState, - newDesiredState: SandboxDesiredState, + abstract emitBoxCreated(box: BoxDto): void + abstract emitBoxStateUpdated(box: BoxDto, oldState: BoxState, newState: BoxState): void + abstract emitBoxDesiredStateUpdated( + box: BoxDto, + oldDesiredState: BoxDesiredState, + newDesiredState: BoxDesiredState, ): void - abstract emitSnapshotCreated(snapshot: SnapshotDto): void - abstract emitSnapshotStateUpdated(snapshot: SnapshotDto, oldState: SnapshotState, newState: SnapshotState): void - abstract emitSnapshotRemoved(snapshot: SnapshotDto): void abstract emitVolumeCreated(volume: VolumeDto): void abstract emitVolumeStateUpdated(volume: VolumeDto, oldState: VolumeState, newState: VolumeState): void abstract emitVolumeLastUsedAtUpdated(volume: VolumeDto): void diff --git a/apps/api/src/notification/gateways/notification.gateway.ts b/apps/api/src/notification/gateways/notification.gateway.ts index c9b30bfb3..fa9afc934 100644 --- a/apps/api/src/notification/gateways/notification.gateway.ts +++ b/apps/api/src/notification/gateways/notification.gateway.ts @@ -8,24 +8,21 @@ import { Logger, OnModuleInit, UnauthorizedException } from '@nestjs/common' import { WebSocketGateway, WebSocketServer, OnGatewayInit } from '@nestjs/websockets' import { Server, Socket } from 'socket.io' import { createAdapter } from '@socket.io/redis-adapter' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SandboxDto } from '../../sandbox/dto/sandbox.dto' -import { SnapshotDto } from '../../sandbox/dto/snapshot.dto' -import { SnapshotEvents } from '../../sandbox/constants/snapshot-events' -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' +import { BoxEvents } from '../../box/constants/box-events.constants' +import { BoxState } from '../../box/enums/box-state.enum' +import { BoxDto } from '../../box/dto/box.dto' import { InjectRedis } from '@nestjs-modules/ioredis' import Redis from 'ioredis' import { JwtStrategy } from '../../auth/jwt.strategy' import { ApiKeyStrategy } from '../../auth/api-key.strategy' import { isAuthContext } from '../../common/interfaces/auth-context.interface' -import { VolumeEvents } from '../../sandbox/constants/volume-events' -import { VolumeDto } from '../../sandbox/dto/volume.dto' -import { VolumeState } from '../../sandbox/enums/volume-state.enum' -import { SandboxDesiredState } from '../../sandbox/enums/sandbox-desired-state.enum' -import { RunnerDto } from '../../sandbox/dto/runner.dto' -import { RunnerState } from '../../sandbox/enums/runner-state.enum' -import { RunnerEvents } from '../../sandbox/constants/runner-events' +import { VolumeEvents } from '../../box/constants/volume-events' +import { VolumeDto } from '../../box/dto/volume.dto' +import { VolumeState } from '../../box/enums/volume-state.enum' +import { BoxDesiredState } from '../../box/enums/box-desired-state.enum' +import { RunnerDto } from '../../box/dto/runner.dto' +import { RunnerState } from '../../box/enums/runner-state.enum' +import { RunnerEvents } from '../../box/constants/runner-events' import { NotificationEmitter } from './notification-emitter.abstract' @WebSocketGateway({ @@ -103,36 +100,16 @@ export class NotificationGateway extends NotificationEmitter implements OnGatewa }) } - emitSandboxCreated(sandbox: SandboxDto) { - this.server.to(sandbox.organizationId).emit(SandboxEvents.CREATED, sandbox) + emitBoxCreated(box: BoxDto) { + this.server.to(box.organizationId).emit(BoxEvents.CREATED, box) } - emitSandboxStateUpdated(sandbox: SandboxDto, oldState: SandboxState, newState: SandboxState) { - this.server.to(sandbox.organizationId).emit(SandboxEvents.STATE_UPDATED, { sandbox, oldState, newState }) + emitBoxStateUpdated(box: BoxDto, oldState: BoxState, newState: BoxState) { + this.server.to(box.organizationId).emit(BoxEvents.STATE_UPDATED, { box, oldState, newState }) } - emitSandboxDesiredStateUpdated( - sandbox: SandboxDto, - oldDesiredState: SandboxDesiredState, - newDesiredState: SandboxDesiredState, - ) { - this.server - .to(sandbox.organizationId) - .emit(SandboxEvents.DESIRED_STATE_UPDATED, { sandbox, oldDesiredState, newDesiredState }) - } - - emitSnapshotCreated(snapshot: SnapshotDto) { - this.server.to(snapshot.organizationId).emit(SnapshotEvents.CREATED, snapshot) - } - - emitSnapshotStateUpdated(snapshot: SnapshotDto, oldState: SnapshotState, newState: SnapshotState) { - this.server - .to(snapshot.organizationId) - .emit(SnapshotEvents.STATE_UPDATED, { snapshot: snapshot, oldState, newState }) - } - - emitSnapshotRemoved(snapshot: SnapshotDto) { - this.server.to(snapshot.organizationId).emit(SnapshotEvents.REMOVED, snapshot) + emitBoxDesiredStateUpdated(box: BoxDto, oldDesiredState: BoxDesiredState, newDesiredState: BoxDesiredState) { + this.server.to(box.organizationId).emit(BoxEvents.DESIRED_STATE_UPDATED, { box, oldDesiredState, newDesiredState }) } emitVolumeCreated(volume: VolumeDto) { diff --git a/apps/api/src/notification/notification.module.ts b/apps/api/src/notification/notification.module.ts index 3f23ee53b..03d9730e2 100644 --- a/apps/api/src/notification/notification.module.ts +++ b/apps/api/src/notification/notification.module.ts @@ -10,7 +10,7 @@ import { NotificationGateway } from './gateways/notification.gateway' import { NotificationRedisEmitter } from './emitters/notification-redis.emitter' import { NotificationEmitter } from './gateways/notification-emitter.abstract' import { OrganizationModule } from '../organization/organization.module' -import { SandboxModule } from '../sandbox/sandbox.module' +import { BoxModule } from '../box/box.module' import { RedisModule } from '@nestjs-modules/ioredis' import { AuthModule } from '../auth/auth.module' import { RegionModule } from '../region/region.module' @@ -19,7 +19,7 @@ import { isApiEnabled } from '../common/utils/app-mode' const gatewayEnabled = isApiEnabled() && process.env.NOTIFICATION_GATEWAY_DISABLED !== 'true' @Module({ - imports: [OrganizationModule, SandboxModule, RedisModule, AuthModule, RegionModule], + imports: [OrganizationModule, BoxModule, RedisModule, AuthModule, RegionModule], providers: [ NotificationService, ...(gatewayEnabled diff --git a/apps/api/src/notification/services/notification.service.ts b/apps/api/src/notification/services/notification.service.ts index 3fcf2971f..9297e05a8 100644 --- a/apps/api/src/notification/services/notification.service.ts +++ b/apps/api/src/notification/services/notification.service.ts @@ -7,76 +7,53 @@ import { Injectable } from '@nestjs/common' import { OnEvent } from '@nestjs/event-emitter' import { NotificationEmitter } from '../gateways/notification-emitter.abstract' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SandboxCreatedEvent } from '../../sandbox/events/sandbox-create.event' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SnapshotCreatedEvent } from '../../sandbox/events/snapshot-created.event' -import { SnapshotEvents } from '../../sandbox/constants/snapshot-events' -import { SnapshotDto } from '../../sandbox/dto/snapshot.dto' -import { SnapshotStateUpdatedEvent } from '../../sandbox/events/snapshot-state-updated.event' -import { SnapshotRemovedEvent } from '../../sandbox/events/snapshot-removed.event' -import { VolumeEvents } from '../../sandbox/constants/volume-events' -import { VolumeCreatedEvent } from '../../sandbox/events/volume-created.event' -import { VolumeDto } from '../../sandbox/dto/volume.dto' -import { VolumeStateUpdatedEvent } from '../../sandbox/events/volume-state-updated.event' -import { VolumeLastUsedAtUpdatedEvent } from '../../sandbox/events/volume-last-used-at-updated.event' -import { SandboxDesiredStateUpdatedEvent } from '../../sandbox/events/sandbox-desired-state-updated.event' -import { RunnerEvents } from '../../sandbox/constants/runner-events' -import { RunnerDto } from '../../sandbox/dto/runner.dto' -import { RunnerCreatedEvent } from '../../sandbox/events/runner-created.event' -import { RunnerStateUpdatedEvent } from '../../sandbox/events/runner-state-updated.event' -import { RunnerUnschedulableUpdatedEvent } from '../../sandbox/events/runner-unschedulable-updated.event' +import { BoxEvents } from '../../box/constants/box-events.constants' +import { BoxCreatedEvent } from '../../box/events/box-create.event' +import { BoxStateUpdatedEvent } from '../../box/events/box-state-updated.event' +import { VolumeEvents } from '../../box/constants/volume-events' +import { VolumeCreatedEvent } from '../../box/events/volume-created.event' +import { VolumeDto } from '../../box/dto/volume.dto' +import { VolumeStateUpdatedEvent } from '../../box/events/volume-state-updated.event' +import { VolumeLastUsedAtUpdatedEvent } from '../../box/events/volume-last-used-at-updated.event' +import { BoxDesiredStateUpdatedEvent } from '../../box/events/box-desired-state-updated.event' +import { RunnerEvents } from '../../box/constants/runner-events' +import { RunnerDto } from '../../box/dto/runner.dto' +import { RunnerCreatedEvent } from '../../box/events/runner-created.event' +import { RunnerStateUpdatedEvent } from '../../box/events/runner-state-updated.event' +import { RunnerUnschedulableUpdatedEvent } from '../../box/events/runner-unschedulable-updated.event' import { RegionService } from '../../region/services/region.service' -import { SandboxService } from '../../sandbox/services/sandbox.service' +import { BoxService } from '../../box/services/box.service' import { InjectRedis } from '@nestjs-modules/ioredis' import { Redis } from 'ioredis' -import { SANDBOX_EVENT_CHANNEL } from '../../common/constants/constants' +import { BOX_EVENT_CHANNEL } from '../../common/constants/constants' @Injectable() export class NotificationService { constructor( private readonly notificationEmitter: NotificationEmitter, private readonly regionService: RegionService, - private readonly sandboxService: SandboxService, + private readonly boxService: BoxService, @InjectRedis() private readonly redis: Redis, ) {} - @OnEvent(SandboxEvents.CREATED) - async handleSandboxCreated(event: SandboxCreatedEvent) { - const dto = await this.sandboxService.toSandboxDto(event.sandbox) - this.notificationEmitter.emitSandboxCreated(dto) + @OnEvent(BoxEvents.CREATED) + async handleBoxCreated(event: BoxCreatedEvent) { + const dto = await this.boxService.toBoxDto(event.box) + this.notificationEmitter.emitBoxCreated(dto) } - @OnEvent(SandboxEvents.STATE_UPDATED) - async handleSandboxStateUpdated(event: SandboxStateUpdatedEvent) { - const dto = await this.sandboxService.toSandboxDto(event.sandbox) - this.notificationEmitter.emitSandboxStateUpdated(dto, event.oldState, event.newState) - this.redis.publish(SANDBOX_EVENT_CHANNEL, JSON.stringify(event)) + @OnEvent(BoxEvents.STATE_UPDATED) + async handleBoxStateUpdated(event: BoxStateUpdatedEvent) { + const dto = await this.boxService.toBoxDto(event.box) + this.notificationEmitter.emitBoxStateUpdated(dto, event.oldState, event.newState) + this.redis.publish(BOX_EVENT_CHANNEL, JSON.stringify(event)) } - @OnEvent(SandboxEvents.DESIRED_STATE_UPDATED) - async handleSandboxDesiredStateUpdated(event: SandboxDesiredStateUpdatedEvent) { - const dto = await this.sandboxService.toSandboxDto(event.sandbox) - this.notificationEmitter.emitSandboxDesiredStateUpdated(dto, event.oldDesiredState, event.newDesiredState) - this.redis.publish(SANDBOX_EVENT_CHANNEL, JSON.stringify(event)) - } - - @OnEvent(SnapshotEvents.CREATED) - async handleSnapshotCreated(event: SnapshotCreatedEvent) { - const dto = SnapshotDto.fromSnapshot(event.snapshot) - this.notificationEmitter.emitSnapshotCreated(dto) - } - - @OnEvent(SnapshotEvents.STATE_UPDATED) - async handleSnapshotStateUpdated(event: SnapshotStateUpdatedEvent) { - const dto = SnapshotDto.fromSnapshot(event.snapshot) - this.notificationEmitter.emitSnapshotStateUpdated(dto, event.oldState, event.newState) - } - - @OnEvent(SnapshotEvents.REMOVED) - async handleSnapshotRemoved(event: SnapshotRemovedEvent) { - const dto = SnapshotDto.fromSnapshot(event.snapshot) - this.notificationEmitter.emitSnapshotRemoved(dto) + @OnEvent(BoxEvents.DESIRED_STATE_UPDATED) + async handleBoxDesiredStateUpdated(event: BoxDesiredStateUpdatedEvent) { + const dto = await this.boxService.toBoxDto(event.box) + this.notificationEmitter.emitBoxDesiredStateUpdated(dto, event.oldDesiredState, event.newDesiredState) + this.redis.publish(BOX_EVENT_CHANNEL, JSON.stringify(event)) } @OnEvent(VolumeEvents.CREATED) diff --git a/apps/api/src/object-storage/controllers/object-storage.controller.ts b/apps/api/src/object-storage/controllers/object-storage.controller.ts index af6a4a1ba..734b7fda1 100644 --- a/apps/api/src/object-storage/controllers/object-storage.controller.ts +++ b/apps/api/src/object-storage/controllers/object-storage.controller.ts @@ -9,7 +9,7 @@ import { ApiOAuth2, ApiTags, ApiOperation, ApiResponse, ApiHeader, ApiBearerAuth import { CombinedAuthGuard } from '../../auth/combined-auth.guard' import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' import { ObjectStorageService } from '../services/object-storage.service' -import { StorageAccessDto } from '../../sandbox/dto/storage-access-dto' +import { StorageAccessDto } from '../../box/dto/storage-access-dto' import { CustomHeaders } from '../../common/constants/header.constants' import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' import { AuthContext } from '../../common/decorators/auth-context.decorator' diff --git a/apps/api/src/object-storage/services/object-storage.service.spec.ts b/apps/api/src/object-storage/services/object-storage.service.spec.ts new file mode 100644 index 000000000..c494b500b --- /dev/null +++ b/apps/api/src/object-storage/services/object-storage.service.spec.ts @@ -0,0 +1,121 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BadRequestException } from '@nestjs/common' +import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts' +import { ObjectStorageService } from './object-storage.service' + +const mockSend = jest.fn() + +jest.mock('@aws-sdk/client-sts', () => ({ + STSClient: jest.fn().mockImplementation(() => ({ send: mockSend })), + AssumeRoleCommand: jest.fn().mockImplementation((input) => ({ input })), +})) + +describe('ObjectStorageService', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + function buildService(values: Record) { + const configService = { + get: jest.fn((key: string) => values[key]), + getOrThrow: jest.fn((key: string) => { + const value = values[key] + if (value === undefined) { + throw new Error(`Missing config: ${key}`) + } + return value + }), + } + return new ObjectStorageService(configService as any) + } + + const awsConfig = { + 's3.endpoint': 'https://s3.ap-southeast-1.amazonaws.com', + 's3.stsEndpoint': 'https://sts.ap-southeast-1.amazonaws.com', + 's3.defaultBucket': 'boxlite-dev-storage', + 's3.region': 'ap-southeast-1', + 's3.accountId': '123456789012', + 's3.roleName': 'boxlite-dev-s3-access', + } + + it('vends AWS credentials via the SDK default chain when no static keys are set', async () => { + mockSend.mockResolvedValue({ + Credentials: { + AccessKeyId: 'ASIA-test', + SecretAccessKey: 'secret-test', + SessionToken: 'token-test', + }, + }) + const service = buildService(awsConfig) + + const access = await service.getPushAccess('org-1') + + // No `credentials` key at all → SDK default chain (ECS task role). + expect(STSClient).toHaveBeenCalledWith({ + region: 'ap-southeast-1', + endpoint: 'https://sts.ap-southeast-1.amazonaws.com', + maxAttempts: 3, + }) + expect(AssumeRoleCommand).toHaveBeenCalledWith( + expect.objectContaining({ + RoleArn: 'arn:aws:iam::123456789012:role/boxlite-dev-s3-access', + DurationSeconds: 3600, + }), + ) + const sessionPolicy = JSON.parse((AssumeRoleCommand as unknown as jest.Mock).mock.calls[0][0].Policy) + expect(sessionPolicy.Statement[0]).toMatchObject({ + Action: ['s3:PutObject', 's3:GetObject'], + Resource: ['arn:aws:s3:::boxlite-dev-storage/org-1/*'], + }) + expect(access).toMatchObject({ + accessKey: 'ASIA-test', + secret: 'secret-test', + sessionToken: 'token-test', + organizationId: 'org-1', + bucket: 'boxlite-dev-storage', + }) + }) + + it('still signs with static keys when they are configured', async () => { + mockSend.mockResolvedValue({ + Credentials: { AccessKeyId: 'a', SecretAccessKey: 'b', SessionToken: 'c' }, + }) + const service = buildService({ + ...awsConfig, + 's3.accessKey': 'static-id', + 's3.secretKey': 'static-secret', + }) + + await service.getPushAccess('org-1') + + expect(STSClient).toHaveBeenCalledWith( + expect.objectContaining({ + credentials: { accessKeyId: 'static-id', secretAccessKey: 'static-secret' }, + }), + ) + }) + + it('rejects a lone static key instead of silently using the default chain', async () => { + const service = buildService({ ...awsConfig, 's3.accessKey': 'static-id' }) + + await expect(service.getPushAccess('org-1')).rejects.toThrow(/S3_ACCESS_KEY and S3_SECRET_KEY must be set together/) + expect(STSClient).not.toHaveBeenCalled() + }) + + it('rejects the MinIO path without static keys instead of sending an unsigned request', async () => { + const service = buildService({ + ...awsConfig, + 's3.endpoint': 'http://minio:9000', + 's3.stsEndpoint': 'http://minio:9000/minio/v1/assume-role', + }) + + await expect(service.getPushAccess('org-1')).rejects.toThrow(BadRequestException) + await expect(service.getPushAccess('org-1')).rejects.toThrow(/S3_ACCESS_KEY and S3_SECRET_KEY/) + expect(AssumeRoleCommand).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/object-storage/services/object-storage.service.ts b/apps/api/src/object-storage/services/object-storage.service.ts index a56d8a4bb..450f3b836 100644 --- a/apps/api/src/object-storage/services/object-storage.service.ts +++ b/apps/api/src/object-storage/services/object-storage.service.ts @@ -6,7 +6,7 @@ import { BadRequestException, Injectable, Logger, ServiceUnavailableException } from '@nestjs/common' import { TypedConfigService } from '../../config/typed-config.service' -import { StorageAccessDto } from '../../sandbox/dto/storage-access-dto' +import { StorageAccessDto } from '../../box/dto/storage-access-dto' import axios from 'axios' import * as aws4 from 'aws4' import * as xml2js from 'xml2js' @@ -15,8 +15,11 @@ import { STSClient, AssumeRoleCommand } from '@aws-sdk/client-sts' interface S3Config { endpoint: string stsEndpoint: string - accessKey: string - secretKey: string + // Static keys are only required for S3-compatible deployments (MinIO). + // On AWS they stay unset and the SDK default chain supplies the ECS + // task-role credentials. + accessKey?: string + secretKey?: string bucket: string region: string accountId?: string @@ -38,11 +41,19 @@ export class ObjectStorageService { try { const bucket = this.configService.getOrThrow('s3.defaultBucket') + const accessKey = this.configService.get('s3.accessKey') + const secretKey = this.configService.get('s3.secretKey') + // Both-or-neither (mirrors observability-s3.reader.ts): a lone key is a + // typo'd pair, and silently falling back to the SDK default chain would + // mask the misconfig. + if ((accessKey && !secretKey) || (!accessKey && secretKey)) { + throw new BadRequestException('S3_ACCESS_KEY and S3_SECRET_KEY must be set together') + } const s3Config: S3Config = { endpoint: this.configService.getOrThrow('s3.endpoint'), stsEndpoint: this.configService.getOrThrow('s3.stsEndpoint'), - accessKey: this.configService.getOrThrow('s3.accessKey'), - secretKey: this.configService.getOrThrow('s3.secretKey'), + accessKey, + secretKey, bucket, region: this.configService.getOrThrow('s3.region'), accountId: this.configService.getOrThrow('s3.accountId'), @@ -80,6 +91,12 @@ export class ObjectStorageService { } private async getMinioCredentials(config: S3Config): Promise { + if (!config.accessKey || !config.secretKey) { + throw new BadRequestException( + 'MinIO STS requires S3_ACCESS_KEY and S3_SECRET_KEY to sign the assume-role request', + ) + } + const body = new URLSearchParams({ Action: 'AssumeRole', Version: '2011-06-15', @@ -131,10 +148,9 @@ export class ObjectStorageService { const stsClient = new STSClient({ region: config.region, endpoint: config.stsEndpoint, - credentials: { - accessKeyId: config.accessKey, - secretAccessKey: config.secretKey, - }, + ...(config.accessKey && config.secretKey + ? { credentials: { accessKeyId: config.accessKey, secretAccessKey: config.secretKey } } + : {}), maxAttempts: 3, }) diff --git a/apps/api/src/openapi-webhooks.ts b/apps/api/src/openapi-webhooks.ts index e71bd8dcf..69439425d 100644 --- a/apps/api/src/openapi-webhooks.ts +++ b/apps/api/src/openapi-webhooks.ts @@ -7,11 +7,8 @@ import { OpenAPIObject, getSchemaPath } from '@nestjs/swagger' import { WebhookEvent } from './webhook/constants/webhook-events.constants' import { - SandboxCreatedWebhookDto, - SandboxStateUpdatedWebhookDto, - SnapshotCreatedWebhookDto, - SnapshotStateUpdatedWebhookDto, - SnapshotRemovedWebhookDto, + BoxCreatedWebhookDto, + BoxStateUpdatedWebhookDto, VolumeCreatedWebhookDto, VolumeStateUpdatedWebhookDto, } from './webhook/dto/webhook-event-payloads.dto' @@ -42,13 +39,13 @@ export function addWebhookDocumentation(document: OpenAPIObject): OpenAPIObjectW return { ...document, webhooks: { - [WebhookEvent.SANDBOX_CREATED]: { + [WebhookEvent.BOX_CREATED]: { post: { requestBody: { - description: 'Sandbox created event', + description: 'Box created event', content: { 'application/json': { - schema: { $ref: getSchemaPath(SandboxCreatedWebhookDto) }, + schema: { $ref: getSchemaPath(BoxCreatedWebhookDto) }, }, }, }, @@ -59,64 +56,13 @@ export function addWebhookDocumentation(document: OpenAPIObject): OpenAPIObjectW }, }, }, - [WebhookEvent.SANDBOX_STATE_UPDATED]: { + [WebhookEvent.BOX_STATE_UPDATED]: { post: { requestBody: { - description: 'Sandbox state updated event', + description: 'Box state updated event', content: { 'application/json': { - schema: { $ref: getSchemaPath(SandboxStateUpdatedWebhookDto) }, - }, - }, - }, - responses: { - '200': { - description: 'Webhook received successfully', - }, - }, - }, - }, - [WebhookEvent.SNAPSHOT_CREATED]: { - post: { - requestBody: { - description: 'Snapshot created event', - content: { - 'application/json': { - schema: { $ref: getSchemaPath(SnapshotCreatedWebhookDto) }, - }, - }, - }, - responses: { - '200': { - description: 'Webhook received successfully', - }, - }, - }, - }, - [WebhookEvent.SNAPSHOT_STATE_UPDATED]: { - post: { - requestBody: { - description: 'Snapshot state updated event', - content: { - 'application/json': { - schema: { $ref: getSchemaPath(SnapshotStateUpdatedWebhookDto) }, - }, - }, - }, - responses: { - '200': { - description: 'Webhook received successfully', - }, - }, - }, - }, - [WebhookEvent.SNAPSHOT_REMOVED]: { - post: { - requestBody: { - description: 'Snapshot removed event', - content: { - 'application/json': { - schema: { $ref: getSchemaPath(SnapshotRemovedWebhookDto) }, + schema: { $ref: getSchemaPath(BoxStateUpdatedWebhookDto) }, }, }, }, @@ -127,6 +73,7 @@ export function addWebhookDocumentation(document: OpenAPIObject): OpenAPIObjectW }, }, }, + // TODO(image-rewrite): TEMPLATE_* webhook docs removed with box_template. [WebhookEvent.VOLUME_CREATED]: { post: { requestBody: { diff --git a/apps/api/src/organization/constants/global-organization-roles.constant.ts b/apps/api/src/organization/constants/global-organization-roles.constant.ts deleted file mode 100644 index e076242d2..000000000 --- a/apps/api/src/organization/constants/global-organization-roles.constant.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const GlobalOrganizationRolesIds = { - DEVELOPER: '00000000-0000-0000-0000-000000000001', - SANDBOXES_ADMIN: '00000000-0000-0000-0000-000000000002', - SNAPSHOTS_ADMIN: '00000000-0000-0000-0000-000000000003', - REGISTRIES_ADMIN: '00000000-0000-0000-0000-000000000004', - SUPER_ADMIN: '00000000-0000-0000-0000-000000000005', - VOLUMES_ADMIN: '00000000-0000-0000-0000-000000000006', - AUDITOR: '00000000-0000-0000-0000-000000000007', - INFRASTRUCTURE_ADMIN: '00000000-0000-0000-0000-000000000008', -} as const diff --git a/apps/api/src/organization/constants/organization-events.constant.ts b/apps/api/src/organization/constants/organization-events.constant.ts index 1b74badaf..129c6eb09 100644 --- a/apps/api/src/organization/constants/organization-events.constant.ts +++ b/apps/api/src/organization/constants/organization-events.constant.ts @@ -10,7 +10,6 @@ export const OrganizationEvents = { INVITATION_DECLINED: 'invitation.declined', INVITATION_CANCELLED: 'invitation.cancelled', CREATED: 'organization.created', - SUSPENDED_SANDBOX_STOPPED: 'organization.suspended-sandbox-stopped', - SUSPENDED_SNAPSHOT_DEACTIVATED: 'organization.suspended-snapshot-deactivated', + SUSPENDED_BOX_STOPPED: 'organization.suspended-box-stopped', PERMISSIONS_UNASSIGNED: 'permissions.unassigned', } as const diff --git a/apps/api/src/organization/constants/sandbox-states-consuming-compute.constant.ts b/apps/api/src/organization/constants/sandbox-states-consuming-compute.constant.ts deleted file mode 100644 index b3bcc7d44..000000000 --- a/apps/api/src/organization/constants/sandbox-states-consuming-compute.constant.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' - -export const SANDBOX_STATES_CONSUMING_COMPUTE: SandboxState[] = [ - SandboxState.CREATING, - SandboxState.RESTORING, - SandboxState.STARTED, - SandboxState.STARTING, - SandboxState.STOPPING, - SandboxState.PENDING_BUILD, - SandboxState.BUILDING_SNAPSHOT, - SandboxState.UNKNOWN, - SandboxState.PULLING_SNAPSHOT, -] diff --git a/apps/api/src/organization/constants/sandbox-states-consuming-disk.constant.ts b/apps/api/src/organization/constants/sandbox-states-consuming-disk.constant.ts deleted file mode 100644 index 1bd5327c6..000000000 --- a/apps/api/src/organization/constants/sandbox-states-consuming-disk.constant.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SANDBOX_STATES_CONSUMING_COMPUTE } from './sandbox-states-consuming-compute.constant' - -export const SANDBOX_STATES_CONSUMING_DISK: SandboxState[] = [ - ...SANDBOX_STATES_CONSUMING_COMPUTE, - SandboxState.STOPPED, - SandboxState.ARCHIVING, - SandboxState.RESIZING, -] diff --git a/apps/api/src/organization/constants/snapshot-states-consuming-resources.constant.ts b/apps/api/src/organization/constants/snapshot-states-consuming-resources.constant.ts deleted file mode 100644 index 6675c13d0..000000000 --- a/apps/api/src/organization/constants/snapshot-states-consuming-resources.constant.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' - -export const SNAPSHOT_STATES_CONSUMING_RESOURCES: SnapshotState[] = [ - SnapshotState.BUILDING, - SnapshotState.PENDING, - SnapshotState.PULLING, - SnapshotState.ACTIVE, -] diff --git a/apps/api/src/organization/constants/volume-states-consuming-resources.constant.ts b/apps/api/src/organization/constants/volume-states-consuming-resources.constant.ts deleted file mode 100644 index 87c9a77b5..000000000 --- a/apps/api/src/organization/constants/volume-states-consuming-resources.constant.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { VolumeState } from '../../sandbox/enums/volume-state.enum' - -export const VOLUME_STATES_CONSUMING_RESOURCES: VolumeState[] = [ - VolumeState.CREATING, - VolumeState.READY, - VolumeState.PENDING_CREATE, - VolumeState.PENDING_DELETE, - VolumeState.DELETING, -] diff --git a/apps/api/src/organization/controllers/organization-region.controller.ts b/apps/api/src/organization/controllers/organization-region.controller.ts index a2db7c032..759d3c00a 100644 --- a/apps/api/src/organization/controllers/organization-region.controller.ts +++ b/apps/api/src/organization/controllers/organization-region.controller.ts @@ -39,7 +39,6 @@ import { FeatureFlags } from '../../common/constants/feature-flags' import { CustomHeaders } from '../../common/constants/header.constants' import { AuthContext } from '../../common/decorators/auth-context.decorator' import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { SnapshotManagerCredentialsDto } from '../../region/dto/snapshot-manager-credentials.dto' import { UpdateRegionDto } from '../../region/dto/update-region.dto' @ApiTags('organizations') @@ -247,33 +246,4 @@ export class OrganizationRegionController { const apiKey = await this.regionService.regenerateSshGatewayApiKey(id) return new RegenerateApiKeyResponseDto(apiKey) } - - @Post(':id/regenerate-snapshot-manager-credentials') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: 'Regenerate snapshot manager credentials for a region', - operationId: 'regenerateSnapshotManagerCredentials', - }) - @ApiResponse({ - status: 200, - description: 'The snapshot manager credentials have been successfully regenerated.', - type: SnapshotManagerCredentialsDto, - }) - @ApiParam({ - name: 'id', - description: 'Region ID', - type: String, - }) - @Audit({ - action: AuditAction.REGENERATE_SNAPSHOT_MANAGER_CREDENTIALS, - targetType: AuditTarget.REGION, - targetIdFromRequest: (req) => req.params.id, - }) - @UseGuards(RegionAccessGuard) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_REGIONS]) - @RequireFlagsEnabled({ flags: [{ flagKey: FeatureFlags.ORGANIZATION_INFRASTRUCTURE, defaultValue: false }] }) - async regenerateSnapshotManagerCredentials(@Param('id') id: string): Promise { - return await this.regionService.regenerateSnapshotManagerCredentials(id) - } } diff --git a/apps/api/src/organization/controllers/organization.controller.ts b/apps/api/src/organization/controllers/organization.controller.ts index 289b59aa0..758847ebb 100644 --- a/apps/api/src/organization/controllers/organization.controller.ts +++ b/apps/api/src/organization/controllers/organization.controller.ts @@ -24,8 +24,6 @@ import { RequiredOrganizationMemberRole } from '../decorators/required-organizat import { CreateOrganizationDto } from '../dto/create-organization.dto' import { OrganizationDto } from '../dto/organization.dto' import { OrganizationInvitationDto } from '../dto/organization-invitation.dto' -import { OrganizationUsageOverviewDto } from '../dto/organization-usage-overview.dto' -import { UpdateOrganizationQuotaDto } from '../dto/update-organization-quota.dto' import { OrganizationMemberRole } from '../enums/organization-member-role.enum' import { OrganizationActionGuard } from '../guards/organization-action.guard' import { OrganizationService } from '../services/organization.service' @@ -43,13 +41,11 @@ import { Audit, TypedRequest } from '../../audit/decorators/audit.decorator' import { AuditAction } from '../../audit/enums/audit-action.enum' import { AuditTarget } from '../../audit/enums/audit-target.enum' import { EmailUtils } from '../../common/utils/email.util' -import { OrganizationUsageService } from '../services/organization-usage.service' -import { OrganizationSandboxDefaultLimitedNetworkEgressDto } from '../dto/organization-sandbox-default-limited-network-egress.dto' +import { OrganizationBoxDefaultLimitedNetworkEgressDto } from '../dto/organization-box-default-limited-network-egress.dto' import { TypedConfigService } from '../../config/typed-config.service' import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' -import { UpdateOrganizationRegionQuotaDto } from '../dto/update-organization-region-quota.dto' import { UpdateOrganizationDefaultRegionDto } from '../dto/update-organization-default-region.dto' -import { RegionQuotaDto } from '../dto/region-quota.dto' +import { UpdateOrganizationNameDto } from '../dto/update-organization-name.dto' import { RequireFlagsEnabled } from '@openfeature/nestjs-sdk' import { OrGuard } from '../../auth/or.guard' import { OtelCollectorGuard } from '../../auth/otel-collector.guard' @@ -66,7 +62,6 @@ export class OrganizationController { private readonly organizationService: OrganizationService, private readonly organizationUserService: OrganizationUserService, private readonly organizationInvitationService: OrganizationInvitationService, - private readonly organizationUsageService: OrganizationUsageService, private readonly userService: UserService, private readonly configService: TypedConfigService, ) {} @@ -211,6 +206,45 @@ export class OrganizationController { return OrganizationDto.fromOrganization(organization) } + @Patch('/:organizationId/name') + @ApiOperation({ + summary: 'Update organization name', + operationId: 'updateOrganizationName', + }) + @ApiResponse({ + status: 200, + description: 'Organization name updated successfully', + type: OrganizationDto, + }) + @ApiParam({ + name: 'organizationId', + description: 'Organization ID', + type: 'string', + }) + @ApiBody({ + type: UpdateOrganizationNameDto, + required: true, + }) + @UseGuards(AuthGuard('jwt'), AuthenticatedRateLimitGuard, OrganizationActionGuard) + @RequiredOrganizationMemberRole(OrganizationMemberRole.OWNER) + @Audit({ + action: AuditAction.UPDATE, + targetType: AuditTarget.ORGANIZATION, + targetIdFromRequest: (req) => String(req.params.organizationId), + requestMetadata: { + body: (req: TypedRequest) => ({ + name: req.body?.name, + }), + }, + }) + async updateName( + @Param('organizationId') organizationId: string, + @Body() updateOrganizationNameDto: UpdateOrganizationNameDto, + ): Promise { + const organization = await this.organizationService.updateName(organizationId, updateOrganizationNameDto.name) + return OrganizationDto.fromOrganization(organization) + } + @Patch('/:organizationId/default-region') @HttpCode(204) @ApiOperation({ @@ -261,8 +295,10 @@ export class OrganizationController { }) @UseGuards(AuthGuard('jwt')) async findAll(@AuthContext() authContext: IAuthContext): Promise { - const organizations = await this.organizationService.findByUser(authContext.userId) - return organizations.map(OrganizationDto.fromOrganization) + const organizations = await this.organizationService.findByUserWithDefaultFlag(authContext.userId) + return organizations.map(({ organization, isDefaultForAuthenticatedUser }) => + OrganizationDto.fromOrganization(organization, isDefaultForAuthenticatedUser), + ) } @Get('/:organizationId') @@ -315,110 +351,6 @@ export class OrganizationController { return this.organizationService.delete(organizationId) } - @Get('/:organizationId/usage') - @ApiOperation({ - summary: 'Get organization current usage overview', - operationId: 'getOrganizationUsageOverview', - }) - @ApiResponse({ - status: 200, - description: 'Current usage overview', - type: OrganizationUsageOverviewDto, - }) - @ApiParam({ - name: 'organizationId', - description: 'Organization ID', - type: 'string', - }) - @UseGuards(AuthGuard('jwt'), OrganizationActionGuard) - async getUsageOverview(@Param('organizationId') organizationId: string): Promise { - return this.organizationUsageService.getUsageOverview(organizationId) - } - - @Patch('/:organizationId/quota') - @HttpCode(204) - @ApiOperation({ - summary: 'Update organization quota', - operationId: 'updateOrganizationQuota', - }) - @ApiResponse({ - status: 204, - description: 'Organization quota updated successfully', - }) - @ApiParam({ - name: 'organizationId', - description: 'Organization ID', - type: 'string', - }) - @RequiredSystemRole(SystemRole.ADMIN) - @UseGuards(CombinedAuthGuard, AuthenticatedRateLimitGuard, SystemActionGuard) - @Audit({ - action: AuditAction.UPDATE_QUOTA, - targetType: AuditTarget.ORGANIZATION, - targetIdFromRequest: (req) => req.params.organizationId, - requestMetadata: { - body: (req: TypedRequest) => ({ - maxCpuPerSandbox: req.body?.maxCpuPerSandbox, - maxMemoryPerSandbox: req.body?.maxMemoryPerSandbox, - maxDiskPerSandbox: req.body?.maxDiskPerSandbox, - snapshotQuota: req.body?.snapshotQuota, - maxSnapshotSize: req.body?.maxSnapshotSize, - volumeQuota: req.body?.volumeQuota, - }), - }, - }) - async updateOrganizationQuota( - @Param('organizationId') organizationId: string, - @Body() updateDto: UpdateOrganizationQuotaDto, - ): Promise { - await this.organizationService.updateQuota(organizationId, updateDto) - } - - @Patch('/:organizationId/quota/:regionId') - @HttpCode(204) - @ApiOperation({ - summary: 'Update organization region quota', - operationId: 'updateOrganizationRegionQuota', - }) - @ApiResponse({ - status: 204, - description: 'Region quota updated successfully', - }) - @ApiParam({ - name: 'organizationId', - description: 'Organization ID', - type: 'string', - }) - @ApiParam({ - name: 'regionId', - description: 'ID of the region where the updated quota will be applied', - type: 'string', - }) - @RequiredSystemRole(SystemRole.ADMIN) - @UseGuards(CombinedAuthGuard, AuthenticatedRateLimitGuard, SystemActionGuard) - @Audit({ - action: AuditAction.UPDATE_REGION_QUOTA, - targetType: AuditTarget.ORGANIZATION, - targetIdFromRequest: (req) => req.params.organizationId, - requestMetadata: { - params: (req) => ({ - regionId: req.params.regionId, - }), - body: (req: TypedRequest) => ({ - totalCpuQuota: req.body?.totalCpuQuota, - totalMemoryQuota: req.body?.totalMemoryQuota, - totalDiskQuota: req.body?.totalDiskQuota, - }), - }, - }) - async updateOrganizationRegionQuota( - @Param('organizationId') organizationId: string, - @Param('regionId') regionId: string, - @Body() updateDto: UpdateOrganizationRegionQuotaDto, - ): Promise { - await this.organizationService.updateRegionQuota(organizationId, regionId, updateDto) - } - @Post('/:organizationId/leave') @ApiOperation({ summary: 'Leave organization', @@ -512,10 +444,10 @@ export class OrganizationController { return this.organizationService.unsuspend(organizationId) } - @Get('/by-sandbox-id/:sandboxId') + @Get('/by-box-id/:boxId') @ApiOperation({ - summary: 'Get organization by sandbox ID', - operationId: 'getOrganizationBySandboxId', + summary: 'Get organization by box ID', + operationId: 'getOrganizationByBoxId', }) @ApiResponse({ status: 200, @@ -523,51 +455,25 @@ export class OrganizationController { type: OrganizationDto, }) @ApiParam({ - name: 'sandboxId', - description: 'Sandbox ID', + name: 'boxId', + description: 'Box ID', type: 'string', }) @RequiredApiRole([SystemRole.ADMIN, 'proxy']) @UseGuards(CombinedAuthGuard, AuthenticatedRateLimitGuard, SystemActionGuard) - async getBySandboxId(@Param('sandboxId') sandboxId: string): Promise { - const organization = await this.organizationService.findBySandboxId(sandboxId) + async getByBoxId(@Param('boxId') boxId: string): Promise { + const organization = await this.organizationService.findByBoxId(boxId) if (!organization) { - throw new NotFoundException(`Organization with sandbox ID ${sandboxId} not found`) + throw new NotFoundException(`Organization with box ID ${boxId} not found`) } return OrganizationDto.fromOrganization(organization) } - @Get('/region-quota/by-sandbox-id/:sandboxId') - @ApiOperation({ - summary: 'Get region quota by sandbox ID', - operationId: 'getRegionQuotaBySandboxId', - }) - @ApiResponse({ - status: 200, - description: 'Region quota', - type: RegionQuotaDto, - }) - @ApiParam({ - name: 'sandboxId', - description: 'Sandbox ID', - type: 'string', - }) - @RequiredApiRole([SystemRole.ADMIN, 'proxy']) - @UseGuards(CombinedAuthGuard, AuthenticatedRateLimitGuard, SystemActionGuard) - async getRegionQuotaBySandboxId(@Param('sandboxId') sandboxId: string): Promise { - const regionQuota = await this.organizationService.getRegionQuotaBySandboxId(sandboxId) - if (!regionQuota) { - throw new NotFoundException(`Region quota for sandbox with ID ${sandboxId} not found`) - } - - return regionQuota - } - - @Get('/otel-config/by-sandbox-auth-token/:authToken') + @Get('/otel-config/by-box-auth-token/:authToken') @ApiOperation({ - summary: 'Get organization OTEL config by sandbox auth token', - operationId: 'getOrganizationOtelConfigBySandboxAuthToken', + summary: 'Get organization OTEL config by box auth token', + operationId: 'getOrganizationOtelConfigByBoxAuthToken', }) @ApiResponse({ status: 200, @@ -576,28 +482,28 @@ export class OrganizationController { }) @ApiParam({ name: 'authToken', - description: 'Sandbox Auth Token', + description: 'Box Auth Token', type: 'string', }) @RequiredApiRole([SystemRole.ADMIN, 'otel-collector']) @UseGuards(CombinedAuthGuard, OrGuard([SystemActionGuard, OtelCollectorGuard])) - async getOtelConfigBySandboxAuthToken(@Param('authToken') authToken: string): Promise { - const otelConfigDto = await this.organizationService.getOtelConfigBySandboxAuthToken(authToken) + async getOtelConfigByBoxAuthToken(@Param('authToken') authToken: string): Promise { + const otelConfigDto = await this.organizationService.getOtelConfigByBoxAuthToken(authToken) if (!otelConfigDto) { - throw new NotFoundException(`Organization OTEL config with sandbox auth token ${authToken} not found`) + throw new NotFoundException(`Organization OTEL config with box auth token ${authToken} not found`) } return otelConfigDto } - @Post('/:organizationId/sandbox-default-limited-network-egress') + @Post('/:organizationId/box-default-limited-network-egress') @ApiOperation({ - summary: 'Update sandbox default limited network egress', - operationId: 'updateSandboxDefaultLimitedNetworkEgress', + summary: 'Update box default limited network egress', + operationId: 'updateBoxDefaultLimitedNetworkEgress', }) @ApiResponse({ status: 204, - description: 'Sandbox default limited network egress updated successfully', + description: 'Box default limited network egress updated successfully', }) @ApiParam({ name: 'organizationId', @@ -607,22 +513,22 @@ export class OrganizationController { @RequiredSystemRole(SystemRole.ADMIN) @UseGuards(CombinedAuthGuard, SystemActionGuard) @Audit({ - action: AuditAction.UPDATE_SANDBOX_DEFAULT_LIMITED_NETWORK_EGRESS, + action: AuditAction.UPDATE_BOX_DEFAULT_LIMITED_NETWORK_EGRESS, targetType: AuditTarget.ORGANIZATION, targetIdFromRequest: (req) => req.params.organizationId, requestMetadata: { - body: (req: TypedRequest) => ({ - sandboxDefaultLimitedNetworkEgress: req.body?.sandboxDefaultLimitedNetworkEgress, + body: (req: TypedRequest) => ({ + boxDefaultLimitedNetworkEgress: req.body?.boxDefaultLimitedNetworkEgress, }), }, }) - async updateSandboxDefaultLimitedNetworkEgress( + async updateBoxDefaultLimitedNetworkEgress( @Param('organizationId') organizationId: string, - @Body() body: OrganizationSandboxDefaultLimitedNetworkEgressDto, + @Body() body: OrganizationBoxDefaultLimitedNetworkEgressDto, ): Promise { - return this.organizationService.updateSandboxDefaultLimitedNetworkEgress( + return this.organizationService.updateBoxDefaultLimitedNetworkEgress( organizationId, - body.sandboxDefaultLimitedNetworkEgress, + body.boxDefaultLimitedNetworkEgress, ) } diff --git a/apps/api/src/organization/dto/create-organization-invitation.dto.ts b/apps/api/src/organization/dto/create-organization-invitation.dto.ts index 3376fcaf7..91f77c365 100644 --- a/apps/api/src/organization/dto/create-organization-invitation.dto.ts +++ b/apps/api/src/organization/dto/create-organization-invitation.dto.ts @@ -7,7 +7,6 @@ import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' import { Type } from 'class-transformer' import { IsArray, IsDate, IsEmail, IsEnum, IsOptional, IsString } from 'class-validator' -import { GlobalOrganizationRolesIds } from '../constants/global-organization-roles.constant' import { OrganizationMemberRole } from '../enums/organization-member-role.enum' @ApiSchema({ name: 'CreateOrganizationInvitation' }) @@ -32,11 +31,11 @@ export class CreateOrganizationInvitationDto { @ApiProperty({ description: 'Array of assigned role IDs for the invitee', type: [String], - default: [GlobalOrganizationRolesIds.DEVELOPER], + default: [], }) @IsArray() @IsString({ each: true }) - assignedRoleIds: string[] + assignedRoleIds: string[] = [] @ApiPropertyOptional({ description: 'Expiration date of the invitation', diff --git a/apps/api/src/organization/dto/create-organization-quota.dto.ts b/apps/api/src/organization/dto/create-organization-quota.dto.ts deleted file mode 100644 index 1789c991b..000000000 --- a/apps/api/src/organization/dto/create-organization-quota.dto.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { IsNumber, IsOptional } from 'class-validator' - -@ApiSchema({ name: 'CreateOrganizationQuota' }) -export class CreateOrganizationQuotaDto { - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - totalCpuQuota?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - totalMemoryQuota?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - totalDiskQuota?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - maxCpuPerSandbox?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - maxMemoryPerSandbox?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - maxDiskPerSandbox?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - snapshotQuota?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - maxSnapshotSize?: number - - @ApiPropertyOptional() - @IsNumber() - @IsOptional() - volumeQuota?: number -} diff --git a/apps/api/src/organization/dto/organization-box-default-limited-network-egress.dto.ts b/apps/api/src/organization/dto/organization-box-default-limited-network-egress.dto.ts new file mode 100644 index 000000000..a3528ee24 --- /dev/null +++ b/apps/api/src/organization/dto/organization-box-default-limited-network-egress.dto.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiSchema } from '@nestjs/swagger' + +@ApiSchema({ name: 'OrganizationBoxDefaultLimitedNetworkEgress' }) +export class OrganizationBoxDefaultLimitedNetworkEgressDto { + @ApiProperty({ + description: 'Box default limited network egress', + }) + boxDefaultLimitedNetworkEgress: boolean +} diff --git a/apps/api/src/organization/dto/organization-sandbox-default-limited-network-egress.dto.ts b/apps/api/src/organization/dto/organization-sandbox-default-limited-network-egress.dto.ts deleted file mode 100644 index 61be6d185..000000000 --- a/apps/api/src/organization/dto/organization-sandbox-default-limited-network-egress.dto.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'OrganizationSandboxDefaultLimitedNetworkEgress' }) -export class OrganizationSandboxDefaultLimitedNetworkEgressDto { - @ApiProperty({ - description: 'Sandbox default limited network egress', - }) - sandboxDefaultLimitedNetworkEgress: boolean -} diff --git a/apps/api/src/organization/dto/organization-usage-overview.dto.ts b/apps/api/src/organization/dto/organization-usage-overview.dto.ts deleted file mode 100644 index bfddac470..000000000 --- a/apps/api/src/organization/dto/organization-usage-overview.dto.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'RegionUsageOverview' }) -export class RegionUsageOverviewDto { - @ApiProperty() - regionId: string - - @ApiProperty() - totalCpuQuota: number - @ApiProperty() - currentCpuUsage: number - - @ApiProperty() - totalMemoryQuota: number - @ApiProperty() - currentMemoryUsage: number - - @ApiProperty() - totalDiskQuota: number - @ApiProperty() - currentDiskUsage: number -} - -@ApiSchema({ name: 'OrganizationUsageOverview' }) -export class OrganizationUsageOverviewDto { - @ApiProperty({ - type: [RegionUsageOverviewDto], - }) - regionUsage: RegionUsageOverviewDto[] - - // Snapshot usage - @ApiProperty() - totalSnapshotQuota: number - @ApiProperty() - currentSnapshotUsage: number - - // Volume usage - @ApiProperty() - totalVolumeQuota: number - @ApiProperty() - currentVolumeUsage: number -} diff --git a/apps/api/src/organization/dto/organization-user.dto.ts b/apps/api/src/organization/dto/organization-user.dto.ts index 5660b2003..bce101511 100644 --- a/apps/api/src/organization/dto/organization-user.dto.ts +++ b/apps/api/src/organization/dto/organization-user.dto.ts @@ -38,6 +38,11 @@ export class OrganizationUserDto { }) role: OrganizationMemberRole + @ApiProperty({ + description: 'Whether this organization membership is the user default organization', + }) + isDefaultForUser: boolean + @ApiProperty({ description: 'Roles assigned to the user', type: [OrganizationRoleDto], diff --git a/apps/api/src/organization/dto/organization.dto.ts b/apps/api/src/organization/dto/organization.dto.ts index ec3f3e7de..dd9850054 100644 --- a/apps/api/src/organization/dto/organization.dto.ts +++ b/apps/api/src/organization/dto/organization.dto.ts @@ -25,7 +25,14 @@ export class OrganizationDto { createdBy: string @ApiProperty({ - description: 'Personal organization flag', + description: 'Whether this organization is the authenticated user default organization', + }) + isDefaultForAuthenticatedUser: boolean + + @ApiProperty({ + description: + 'Deprecated alias for isDefaultForAuthenticatedUser. Kept for backward compatibility with older REST clients.', + deprecated: true, }) personal: boolean @@ -65,30 +72,30 @@ export class OrganizationDto { suspensionCleanupGracePeriodHours?: number @ApiProperty({ - description: 'Max CPU per sandbox', + description: 'Max CPU per box', }) - maxCpuPerSandbox: number + maxCpuPerBox: number @ApiProperty({ - description: 'Max memory per sandbox', + description: 'Max memory per box', }) - maxMemoryPerSandbox: number + maxMemoryPerBox: number @ApiProperty({ - description: 'Max disk per sandbox', + description: 'Max disk per box', }) - maxDiskPerSandbox: number + maxDiskPerBox: number @ApiProperty({ - description: 'Time in minutes before an unused snapshot is deactivated', + description: 'Time in minutes before an unused template is deactivated', default: 20160, }) - snapshotDeactivationTimeoutMinutes: number + templateDeactivationTimeoutMinutes: number @ApiProperty({ - description: 'Sandbox default network block all', + description: 'Box default network block all', }) - sandboxLimitedNetworkEgress: boolean + boxLimitedNetworkEgress: boolean @ApiPropertyOptional({ description: 'Default region ID', @@ -103,16 +110,16 @@ export class OrganizationDto { authenticatedRateLimit: number | null @ApiProperty({ - description: 'Sandbox create rate limit per minute', + description: 'Box create rate limit per minute', nullable: true, }) - sandboxCreateRateLimit: number | null + boxCreateRateLimit: number | null @ApiProperty({ - description: 'Sandbox lifecycle rate limit per minute', + description: 'Box lifecycle rate limit per minute', nullable: true, }) - sandboxLifecycleRateLimit: number | null + boxLifecycleRateLimit: number | null @ApiProperty({ description: 'Experimental configuration', @@ -126,18 +133,18 @@ export class OrganizationDto { authenticatedRateLimitTtlSeconds: number | null @ApiProperty({ - description: 'Sandbox create rate limit TTL in seconds', + description: 'Box create rate limit TTL in seconds', nullable: true, }) - sandboxCreateRateLimitTtlSeconds: number | null + boxCreateRateLimitTtlSeconds: number | null @ApiProperty({ - description: 'Sandbox lifecycle rate limit TTL in seconds', + description: 'Box lifecycle rate limit TTL in seconds', nullable: true, }) - sandboxLifecycleRateLimitTtlSeconds: number | null + boxLifecycleRateLimitTtlSeconds: number | null - static fromOrganization(organization: Organization): OrganizationDto { + static fromOrganization(organization: Organization, isDefaultForAuthenticatedUser = false): OrganizationDto { const experimentalConfig = organization._experimentalConfig if (experimentalConfig && experimentalConfig.otel && experimentalConfig.otel.headers) { experimentalConfig.otel.headers = Object.entries(experimentalConfig.otel.headers).reduce( @@ -153,7 +160,8 @@ export class OrganizationDto { id: organization.id, name: organization.name, createdBy: organization.createdBy, - personal: organization.personal, + isDefaultForAuthenticatedUser, + personal: isDefaultForAuthenticatedUser, createdAt: organization.createdAt, updatedAt: organization.updatedAt, suspended: organization.suspended, @@ -161,19 +169,19 @@ export class OrganizationDto { suspendedAt: organization.suspendedAt, suspendedUntil: organization.suspendedUntil, suspensionCleanupGracePeriodHours: organization.suspensionCleanupGracePeriodHours, - maxCpuPerSandbox: organization.maxCpuPerSandbox, - maxMemoryPerSandbox: organization.maxMemoryPerSandbox, - maxDiskPerSandbox: organization.maxDiskPerSandbox, - snapshotDeactivationTimeoutMinutes: organization.snapshotDeactivationTimeoutMinutes, - sandboxLimitedNetworkEgress: organization.sandboxLimitedNetworkEgress, + maxCpuPerBox: organization.maxCpuPerBox, + maxMemoryPerBox: organization.maxMemoryPerBox, + maxDiskPerBox: organization.maxDiskPerBox, + templateDeactivationTimeoutMinutes: organization.templateDeactivationTimeoutMinutes, + boxLimitedNetworkEgress: organization.boxLimitedNetworkEgress, defaultRegionId: organization.defaultRegionId, authenticatedRateLimit: organization.authenticatedRateLimit, - sandboxCreateRateLimit: organization.sandboxCreateRateLimit, - sandboxLifecycleRateLimit: organization.sandboxLifecycleRateLimit, + boxCreateRateLimit: organization.boxCreateRateLimit, + boxLifecycleRateLimit: organization.boxLifecycleRateLimit, experimentalConfig, authenticatedRateLimitTtlSeconds: organization.authenticatedRateLimitTtlSeconds, - sandboxCreateRateLimitTtlSeconds: organization.sandboxCreateRateLimitTtlSeconds, - sandboxLifecycleRateLimitTtlSeconds: organization.sandboxLifecycleRateLimitTtlSeconds, + boxCreateRateLimitTtlSeconds: organization.boxCreateRateLimitTtlSeconds, + boxLifecycleRateLimitTtlSeconds: organization.boxLifecycleRateLimitTtlSeconds, } return dto diff --git a/apps/api/src/organization/dto/region-quota.dto.ts b/apps/api/src/organization/dto/region-quota.dto.ts deleted file mode 100644 index da8218fac..000000000 --- a/apps/api/src/organization/dto/region-quota.dto.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { RegionQuota } from '../entities/region-quota.entity' - -@ApiSchema({ name: 'RegionQuota' }) -export class RegionQuotaDto { - @ApiProperty() - organizationId: string - - @ApiProperty() - regionId: string - - @ApiProperty() - totalCpuQuota: number - - @ApiProperty() - totalMemoryQuota: number - - @ApiProperty() - totalDiskQuota: number - - constructor(regionQuota: RegionQuota) { - this.organizationId = regionQuota.organizationId - this.regionId = regionQuota.regionId - this.totalCpuQuota = regionQuota.totalCpuQuota - this.totalMemoryQuota = regionQuota.totalMemoryQuota - this.totalDiskQuota = regionQuota.totalDiskQuota - } -} diff --git a/apps/api/src/organization/dto/sandbox-usage-overview-internal.dto.ts b/apps/api/src/organization/dto/sandbox-usage-overview-internal.dto.ts deleted file mode 100644 index 9be55bdba..000000000 --- a/apps/api/src/organization/dto/sandbox-usage-overview-internal.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export type SandboxUsageOverviewInternalDto = { - currentCpuUsage: number - currentMemoryUsage: number - currentDiskUsage: number -} - -export type PendingSandboxUsageOverviewInternalDto = { - pendingCpuUsage: number | null - pendingMemoryUsage: number | null - pendingDiskUsage: number | null -} - -export type SandboxUsageOverviewWithPendingInternalDto = SandboxUsageOverviewInternalDto & - PendingSandboxUsageOverviewInternalDto diff --git a/apps/api/src/organization/dto/snapshot-usage-overview-internal.dto.ts b/apps/api/src/organization/dto/snapshot-usage-overview-internal.dto.ts deleted file mode 100644 index 7eeac7eca..000000000 --- a/apps/api/src/organization/dto/snapshot-usage-overview-internal.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export type SnapshotUsageOverviewInternalDto = { - currentSnapshotUsage: number -} - -export type PendingSnapshotUsageOverviewInternalDto = { - pendingSnapshotUsage: number | null -} - -export type SnapshotUsageOverviewWithPendingInternalDto = SnapshotUsageOverviewInternalDto & - PendingSnapshotUsageOverviewInternalDto diff --git a/apps/api/src/organization/dto/update-organization-member-access.dto.ts b/apps/api/src/organization/dto/update-organization-member-access.dto.ts index a6340538c..5029a3c6f 100644 --- a/apps/api/src/organization/dto/update-organization-member-access.dto.ts +++ b/apps/api/src/organization/dto/update-organization-member-access.dto.ts @@ -6,7 +6,6 @@ import { ApiProperty, ApiSchema } from '@nestjs/swagger' import { IsArray, IsEnum, IsString } from 'class-validator' -import { GlobalOrganizationRolesIds } from '../constants/global-organization-roles.constant' import { OrganizationMemberRole } from '../enums/organization-member-role.enum' @ApiSchema({ name: 'UpdateOrganizationMemberAccess' }) @@ -22,9 +21,9 @@ export class UpdateOrganizationMemberAccessDto { @ApiProperty({ description: 'Array of assigned role IDs', type: [String], - default: [GlobalOrganizationRolesIds.DEVELOPER], + default: [], }) @IsArray() @IsString({ each: true }) - assignedRoleIds: string[] + assignedRoleIds: string[] = [] } diff --git a/apps/api/src/organization/dto/update-organization-name.dto.ts b/apps/api/src/organization/dto/update-organization-name.dto.ts new file mode 100644 index 000000000..d3029d2e7 --- /dev/null +++ b/apps/api/src/organization/dto/update-organization-name.dto.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ApiProperty, ApiSchema } from '@nestjs/swagger' +import { IsNotEmpty, IsString, MaxLength } from 'class-validator' + +@ApiSchema({ name: 'UpdateOrganizationName' }) +export class UpdateOrganizationNameDto { + @ApiProperty({ + description: 'The public name of the organization', + example: 'Default Organization', + required: true, + }) + @IsString() + @IsNotEmpty() + @MaxLength(100) + name: string +} diff --git a/apps/api/src/organization/dto/update-organization-quota.dto.ts b/apps/api/src/organization/dto/update-organization-quota.dto.ts deleted file mode 100644 index ed8c64b6d..000000000 --- a/apps/api/src/organization/dto/update-organization-quota.dto.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'UpdateOrganizationQuota' }) -export class UpdateOrganizationQuotaDto { - @ApiProperty({ nullable: true }) - maxCpuPerSandbox?: number - - @ApiProperty({ nullable: true }) - maxMemoryPerSandbox?: number - - @ApiProperty({ nullable: true }) - maxDiskPerSandbox?: number - - @ApiProperty({ nullable: true }) - snapshotQuota?: number - - @ApiProperty({ nullable: true }) - maxSnapshotSize?: number - - @ApiProperty({ nullable: true }) - volumeQuota?: number - - @ApiProperty({ nullable: true }) - authenticatedRateLimit?: number - - @ApiProperty({ nullable: true }) - sandboxCreateRateLimit?: number - - @ApiProperty({ nullable: true }) - sandboxLifecycleRateLimit?: number - - @ApiProperty({ nullable: true }) - authenticatedRateLimitTtlSeconds?: number - - @ApiProperty({ nullable: true }) - sandboxCreateRateLimitTtlSeconds?: number - - @ApiProperty({ nullable: true }) - sandboxLifecycleRateLimitTtlSeconds?: number - - @ApiProperty({ nullable: true, description: 'Time in minutes before an unused snapshot is deactivated' }) - snapshotDeactivationTimeoutMinutes?: number -} diff --git a/apps/api/src/organization/dto/update-organization-region-quota.dto.ts b/apps/api/src/organization/dto/update-organization-region-quota.dto.ts deleted file mode 100644 index b8bff537f..000000000 --- a/apps/api/src/organization/dto/update-organization-region-quota.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'UpdateOrganizationRegionQuota' }) -export class UpdateOrganizationRegionQuotaDto { - @ApiProperty({ nullable: true }) - totalCpuQuota?: number - - @ApiProperty({ nullable: true }) - totalMemoryQuota?: number - - @ApiProperty({ nullable: true }) - totalDiskQuota?: number -} diff --git a/apps/api/src/organization/dto/volume-usage-overview-internal.dto.ts b/apps/api/src/organization/dto/volume-usage-overview-internal.dto.ts deleted file mode 100644 index b14cd646c..000000000 --- a/apps/api/src/organization/dto/volume-usage-overview-internal.dto.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export type VolumeUsageOverviewInternalDto = { - currentVolumeUsage: number -} - -export type PendingVolumeUsageOverviewInternalDto = { - pendingVolumeUsage: number | null -} - -export type VolumeUsageOverviewWithPendingInternalDto = VolumeUsageOverviewInternalDto & - PendingVolumeUsageOverviewInternalDto diff --git a/apps/api/src/organization/entities/organization-user.entity.ts b/apps/api/src/organization/entities/organization-user.entity.ts index cba4b4903..630bef4ce 100644 --- a/apps/api/src/organization/entities/organization-user.entity.ts +++ b/apps/api/src/organization/entities/organization-user.entity.ts @@ -4,11 +4,22 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { Column, CreateDateColumn, Entity, JoinColumn, JoinTable, ManyToMany, ManyToOne, PrimaryColumn } from 'typeorm' +import { + Column, + CreateDateColumn, + Entity, + Index, + JoinColumn, + JoinTable, + ManyToMany, + ManyToOne, + PrimaryColumn, +} from 'typeorm' import { Organization } from './organization.entity' import { OrganizationRole } from './organization-role.entity' import { OrganizationMemberRole } from '../enums/organization-member-role.enum' +@Index('organization_user_default_user_unique', ['userId'], { unique: true, where: '"isDefaultForUser" = true' }) @Entity() export class OrganizationUser { @PrimaryColumn() @@ -24,6 +35,11 @@ export class OrganizationUser { }) role: OrganizationMemberRole + @Column({ + default: false, + }) + isDefaultForUser: boolean + @ManyToOne(() => Organization, (organization) => organization.users, { onDelete: 'CASCADE', }) diff --git a/apps/api/src/organization/entities/organization.entity.ts b/apps/api/src/organization/entities/organization.entity.ts index a0f076c35..eb65c7e66 100644 --- a/apps/api/src/organization/entities/organization.entity.ts +++ b/apps/api/src/organization/entities/organization.entity.ts @@ -8,7 +8,6 @@ import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn, Up import { OrganizationUser } from './organization-user.entity' import { OrganizationRole } from './organization-role.entity' import { OrganizationInvitation } from './organization-invitation.entity' -import { RegionQuota } from './region-quota.entity' @Entity() export class Organization { @@ -21,11 +20,6 @@ export class Organization { @Column() createdBy: string - @Column({ - default: false, - }) - personal: boolean - @Column({ default: true, }) @@ -37,44 +31,23 @@ export class Organization { @Column({ type: 'int', default: 4, - name: 'max_cpu_per_sandbox', + name: 'max_cpu_per_box', }) - maxCpuPerSandbox: number + maxCpuPerBox: number @Column({ type: 'int', default: 8, - name: 'max_memory_per_sandbox', + name: 'max_memory_per_box', }) - maxMemoryPerSandbox: number + maxMemoryPerBox: number @Column({ type: 'int', default: 10, - name: 'max_disk_per_sandbox', - }) - maxDiskPerSandbox: number - - @Column({ - type: 'int', - default: 20, - name: 'max_snapshot_size', - }) - maxSnapshotSize: number - - @Column({ - type: 'int', - default: 100, - name: 'snapshot_quota', - }) - snapshotQuota: number - - @Column({ - type: 'int', - default: 100, - name: 'volume_quota', + name: 'max_disk_per_box', }) - volumeQuota: number + maxDiskPerBox: number @Column({ type: 'int', @@ -86,16 +59,16 @@ export class Organization { @Column({ type: 'int', nullable: true, - name: 'sandbox_create_rate_limit', + name: 'box_create_rate_limit', }) - sandboxCreateRateLimit: number | null + boxCreateRateLimit: number | null @Column({ type: 'int', nullable: true, - name: 'sandbox_lifecycle_rate_limit', + name: 'box_lifecycle_rate_limit', }) - sandboxLifecycleRateLimit: number | null + boxLifecycleRateLimit: number | null @Column({ type: 'int', @@ -107,22 +80,16 @@ export class Organization { @Column({ type: 'int', nullable: true, - name: 'sandbox_create_rate_limit_ttl_seconds', + name: 'box_create_rate_limit_ttl_seconds', }) - sandboxCreateRateLimitTtlSeconds: number | null + boxCreateRateLimitTtlSeconds: number | null @Column({ type: 'int', nullable: true, - name: 'sandbox_lifecycle_rate_limit_ttl_seconds', - }) - sandboxLifecycleRateLimitTtlSeconds: number | null - - @OneToMany(() => RegionQuota, (quota) => quota.organization, { - cascade: true, - onDelete: 'CASCADE', + name: 'box_lifecycle_rate_limit_ttl_seconds', }) - regionQuotas: RegionQuota[] + boxLifecycleRateLimitTtlSeconds: number | null @OneToMany(() => OrganizationRole, (organizationRole) => organizationRole.organization, { cascade: true, @@ -173,14 +140,14 @@ export class Organization { @Column({ type: 'int', default: 20160, - name: 'snapshot_deactivation_timeout_minutes', + name: 'template_deactivation_timeout_minutes', }) - snapshotDeactivationTimeoutMinutes: number + templateDeactivationTimeoutMinutes: number @Column({ default: false, }) - sandboxLimitedNetworkEgress: boolean + boxLimitedNetworkEgress: boolean @CreateDateColumn({ type: 'timestamp with time zone', @@ -200,11 +167,11 @@ export class Organization { // configuration for experimental features _experimentalConfig: Record | null - get sandboxMetadata(): Record { + get boxMetadata(): Record { return { organizationId: this.id, organizationName: this.name, - limitNetworkEgress: String(this.sandboxLimitedNetworkEgress), + limitNetworkEgress: String(this.boxLimitedNetworkEgress), } } diff --git a/apps/api/src/organization/entities/region-quota.entity.ts b/apps/api/src/organization/entities/region-quota.entity.ts deleted file mode 100644 index 0ae56c867..000000000 --- a/apps/api/src/organization/entities/region-quota.entity.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm' -import { Organization } from './organization.entity' - -@Entity() -export class RegionQuota { - @PrimaryColumn() - organizationId: string - - @PrimaryColumn() - regionId: string - - @ManyToOne(() => Organization, (organization) => organization.regionQuotas, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'organizationId' }) - organization: Organization - - @Column({ - type: 'int', - default: 10, - name: 'total_cpu_quota', - }) - totalCpuQuota: number - - @Column({ - type: 'int', - default: 10, - name: 'total_memory_quota', - }) - totalMemoryQuota: number - - @Column({ - type: 'int', - default: 30, - name: 'total_disk_quota', - }) - totalDiskQuota: number - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date - - constructor( - organizationId: string, - regionId: string, - totalCpuQuota: number, - totalMemoryQuota: number, - totalDiskQuota: number, - ) { - this.organizationId = organizationId - this.regionId = regionId - this.totalCpuQuota = totalCpuQuota - this.totalMemoryQuota = totalMemoryQuota - this.totalDiskQuota = totalDiskQuota - } -} diff --git a/apps/api/src/organization/enums/organization-resource-permission.enum.ts b/apps/api/src/organization/enums/organization-resource-permission.enum.ts index 963606a8c..f854e604b 100644 --- a/apps/api/src/organization/enums/organization-resource-permission.enum.ts +++ b/apps/api/src/organization/enums/organization-resource-permission.enum.ts @@ -12,13 +12,13 @@ export enum OrganizationResourcePermission { WRITE_REGISTRIES = 'write:registries', DELETE_REGISTRIES = 'delete:registries', - // snapshots - WRITE_SNAPSHOTS = 'write:snapshots', - DELETE_SNAPSHOTS = 'delete:snapshots', + // templates + WRITE_TEMPLATES = 'write:templates', + DELETE_TEMPLATES = 'delete:templates', - // sandboxes - WRITE_SANDBOXES = 'write:sandboxes', - DELETE_SANDBOXES = 'delete:sandboxes', + // boxes + WRITE_BOXES = 'write:boxes', + DELETE_BOXES = 'delete:boxes', // volumes READ_VOLUMES = 'read:volumes', diff --git a/apps/api/src/organization/events/organization-suspended-box-stopped.event.ts b/apps/api/src/organization/events/organization-suspended-box-stopped.event.ts new file mode 100644 index 000000000..3d5384e60 --- /dev/null +++ b/apps/api/src/organization/events/organization-suspended-box-stopped.event.ts @@ -0,0 +1,9 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export class OrganizationSuspendedBoxStoppedEvent { + constructor(public readonly boxId: string) {} +} diff --git a/apps/api/src/organization/events/organization-suspended-sandbox-stopped.event.ts b/apps/api/src/organization/events/organization-suspended-sandbox-stopped.event.ts deleted file mode 100644 index cc649e921..000000000 --- a/apps/api/src/organization/events/organization-suspended-sandbox-stopped.event.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export class OrganizationSuspendedSandboxStoppedEvent { - constructor(public readonly sandboxId: string) {} -} diff --git a/apps/api/src/organization/events/organization-suspended-snapshot-deactivated.event.ts b/apps/api/src/organization/events/organization-suspended-snapshot-deactivated.event.ts deleted file mode 100644 index c8e9c0a8a..000000000 --- a/apps/api/src/organization/events/organization-suspended-snapshot-deactivated.event.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export class OrganizationSuspendedSnapshotDeactivatedEvent { - constructor(public readonly snapshotId: string) {} -} diff --git a/apps/api/src/organization/guards/organization-access.guard.spec.ts b/apps/api/src/organization/guards/organization-access.guard.spec.ts new file mode 100644 index 000000000..be99a579a --- /dev/null +++ b/apps/api/src/organization/guards/organization-access.guard.spec.ts @@ -0,0 +1,90 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +import { ExecutionContext } from '@nestjs/common' +import { SystemRole } from '../../user/enums/system-role.enum' +import { OrganizationAccessGuard } from './organization-access.guard' + +function httpContext(request: Record): ExecutionContext { + return { + switchToHttp: () => ({ + getRequest: () => request, + }), + } as ExecutionContext +} + +function createGuard() { + const organization = { id: 'org-123' } + const organizationUser = { organizationId: 'org-123', userId: 'user-1' } + const redis = { + get: jest.fn().mockResolvedValue(null), + set: jest.fn().mockResolvedValue('OK'), + } + const organizationService = { + findOne: jest.fn().mockResolvedValue(organization), + } + const organizationUserService = { + findOne: jest.fn().mockResolvedValue(organizationUser), + } + + const guard = new OrganizationAccessGuard(organizationService as any, organizationUserService as any) + ;(guard as any).redis = redis + + return { + guard, + mocks: { organizationService, organizationUserService, redis }, + } +} + +describe('OrganizationAccessGuard', () => { + it('resolves the legacy REST default prefix to the authenticated API-key organization', async () => { + const { guard, mocks } = createGuard() + const request = { + params: { prefix: 'default' }, + user: { + userId: 'user-1', + email: 'user@example.com', + role: SystemRole.USER, + organizationId: 'org-123', + apiKey: { organizationId: 'org-123' }, + }, + } + + await expect(guard.canActivate(httpContext(request))).resolves.toBe(true) + + expect(mocks.organizationService.findOne).toHaveBeenCalledWith('org-123') + expect(mocks.organizationUserService.findOne).toHaveBeenCalledWith('org-123', 'user-1') + expect(request.user).toMatchObject({ + organizationId: 'org-123', + organization: { id: 'org-123' }, + organizationUser: { organizationId: 'org-123', userId: 'user-1' }, + }) + }) + + it('still rejects explicit organization prefixes that do not match the API key', async () => { + const { guard, mocks } = createGuard() + const request = { + params: { prefix: 'other-org' }, + user: { + userId: 'user-1', + email: 'user@example.com', + role: SystemRole.USER, + organizationId: 'org-123', + apiKey: { organizationId: 'org-123' }, + }, + } + + await expect(guard.canActivate(httpContext(request))).resolves.toBe(false) + + expect(mocks.organizationService.findOne).not.toHaveBeenCalled() + expect(mocks.organizationUserService.findOne).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/organization/guards/organization-access.guard.ts b/apps/api/src/organization/guards/organization-access.guard.ts index 931e0f643..0b3ba360f 100644 --- a/apps/api/src/organization/guards/organization-access.guard.ts +++ b/apps/api/src/organization/guards/organization-access.guard.ts @@ -35,7 +35,10 @@ export class OrganizationAccessGuard implements CanActivate { } // note: semantic parameter names must be used (avoid :id) - const organizationIdParam = request.params.organizationId || request.params.orgId + const organizationIdParam = this.resolveOrganizationIdParam( + request.params.organizationId || request.params.orgId || request.params.prefix, + authContext, + ) if ( authContext.role !== 'ssh-gateway' && @@ -64,6 +67,10 @@ export class OrganizationAccessGuard implements CanActivate { } const organizationId = organizationIdParam || authContext.organizationId + if (!organizationId) { + this.logger.warn('Organization ID missing from the request context.') + return false + } const organization = await this.getCachedOrganization(organizationId) @@ -98,6 +105,17 @@ export class OrganizationAccessGuard implements CanActivate { return true } + private resolveOrganizationIdParam(organizationIdParam: string | undefined, authContext: AuthContext) { + // REST SDKs released before path_prefix support hardcoded `/v1/default/...`. + // API keys are already org-scoped, so preserve that legacy route shape by + // resolving `default` to the authenticated org instead of rejecting it. + if (organizationIdParam === 'default') { + return authContext.organizationId + } + + return organizationIdParam + } + private async getCachedOrganization(organizationId: string): Promise { try { const cachedOrganization = await this.redis.get(`organization:${organizationId}`) diff --git a/apps/api/src/organization/guards/organization-resource-action.guard.spec.ts b/apps/api/src/organization/guards/organization-resource-action.guard.spec.ts new file mode 100644 index 000000000..bc6621c96 --- /dev/null +++ b/apps/api/src/organization/guards/organization-resource-action.guard.spec.ts @@ -0,0 +1,141 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'mock-uuid'), + validate: jest.fn(() => true), +})) + +import { ExecutionContext } from '@nestjs/common' +import { GUARDS_METADATA } from '@nestjs/common/constants' +import { Reflector } from '@nestjs/core' +import { OrGuard } from '../../auth/or.guard' +import { RunnerAuthGuard } from '../../auth/runner-auth.guard' +import { OrganizationResourceActionGuard } from './organization-resource-action.guard' + +class BoxAccessGuard { + canActivate() { + return true + } +} + +function httpContext(request: Record, handler = function handler() {}): ExecutionContext { + return { + getClass: () => class Controller {}, + getHandler: () => handler, + switchToHttp: () => ({ + getRequest: () => request, + }), + } as unknown as ExecutionContext +} + +function createGuard() { + const reflector = new Reflector() + const organizationService = { findOne: jest.fn() } + const organizationUserService = { findOne: jest.fn() } + const guard = new OrganizationResourceActionGuard( + organizationService as never, + organizationUserService as never, + reflector, + ) + ;(guard as any).redis = { + get: jest.fn().mockResolvedValue(null), + set: jest.fn().mockResolvedValue('OK'), + } + + return { + guard, + organizationService, + organizationUserService, + } +} + +describe('OrganizationResourceActionGuard', () => { + it('lets runner-authenticated runner routes bypass organization context resolution', async () => { + const { guard, organizationService, organizationUserService } = createGuard() + const handler = function runnerHandler() {} + Reflect.defineMetadata(GUARDS_METADATA, [RunnerAuthGuard], handler) + + const request = { + params: {}, + user: { + role: 'runner', + runnerId: 'runner-1', + runner: { id: 'runner-1' }, + }, + } + + await expect(guard.canActivate(httpContext(request, handler))).resolves.toBe(true) + + expect(organizationService.findOne).not.toHaveBeenCalled() + expect(organizationUserService.findOne).not.toHaveBeenCalled() + }) + + it('lets runner-authenticated box resource routes defer access checks to BoxAccessGuard', async () => { + const { guard, organizationService, organizationUserService } = createGuard() + const handler = function boxHandler() {} + Reflect.defineMetadata(GUARDS_METADATA, [BoxAccessGuard], handler) + + const request = { + params: { boxId: 'box-1' }, + user: { + role: 'runner', + runnerId: 'runner-1', + runner: { id: 'runner-1' }, + }, + } + + await expect(guard.canActivate(httpContext(request, handler))).resolves.toBe(true) + + expect(organizationService.findOne).not.toHaveBeenCalled() + expect(organizationUserService.findOne).not.toHaveBeenCalled() + }) + + it('unwraps OrGuard metadata so runner activity updates reach BoxAccessGuard', async () => { + const { guard, organizationService, organizationUserService } = createGuard() + const handler = function runnerActivityHandler() {} + Reflect.defineMetadata(GUARDS_METADATA, [OrGuard([BoxAccessGuard])], handler) + + const request = { + params: { boxId: 'box-1' }, + user: { + role: 'runner', + runnerId: 'runner-1', + runner: { id: 'runner-1' }, + }, + } + + await expect(guard.canActivate(httpContext(request, handler))).resolves.toBe(true) + + expect(organizationService.findOne).not.toHaveBeenCalled() + expect(organizationUserService.findOne).not.toHaveBeenCalled() + }) + + it('does not bypass organization context for runner users on non-runner routes', async () => { + const { guard, organizationService } = createGuard() + const request = { + params: {}, + user: { + role: 'runner', + runnerId: 'runner-1', + runner: { id: 'runner-1' }, + }, + } + + await expect(guard.canActivate(httpContext(request))).resolves.toBe(false) + + expect(organizationService.findOne).not.toHaveBeenCalled() + }) + + it('falls through to the organization guard when authentication did not populate a user', async () => { + const { guard, organizationService, organizationUserService } = createGuard() + + await expect(guard.canActivate(httpContext({ params: {} }))).resolves.toBe(false) + + expect(organizationService.findOne).not.toHaveBeenCalled() + expect(organizationUserService.findOne).not.toHaveBeenCalled() + }) +}) diff --git a/apps/api/src/organization/guards/organization-resource-action.guard.ts b/apps/api/src/organization/guards/organization-resource-action.guard.ts index 66af9f63b..63dd8433e 100644 --- a/apps/api/src/organization/guards/organization-resource-action.guard.ts +++ b/apps/api/src/organization/guards/organization-resource-action.guard.ts @@ -4,7 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { Injectable, ExecutionContext, Logger } from '@nestjs/common' +import { CanActivate, Injectable, ExecutionContext, Logger, Type } from '@nestjs/common' +import { GUARDS_METADATA } from '@nestjs/common/constants' import { Reflector } from '@nestjs/core' import { OrganizationAccessGuard } from './organization-access.guard' import { RequiredOrganizationResourcePermissions } from '../decorators/required-organization-resource-permissions.decorator' @@ -13,6 +14,11 @@ import { OrganizationService } from '../services/organization.service' import { OrganizationUserService } from '../services/organization-user.service' import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' import { SystemRole } from '../../user/enums/system-role.enum' +import { RunnerAuthGuard } from '../../auth/runner-auth.guard' +import { isRunnerContext } from '../../common/interfaces/runner-context.interface' +import { OR_GUARD_INNER_GUARDS } from '../../auth/or.guard' + +const RUNNER_COMPATIBLE_RESOURCE_GUARD_NAMES = new Set(['RunnerAuthGuard', 'BoxAccessGuard']) @Injectable() export class OrganizationResourceActionGuard extends OrganizationAccessGuard { @@ -26,11 +32,18 @@ export class OrganizationResourceActionGuard extends OrganizationAccessGuard { super(organizationService, organizationUserService) } async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest() + if (isRunnerContext(request.user) && this.handlerAllowsRunnerResourceAccess(context)) { + return true + } + const canActivate = await super.canActivate(context) - const request = context.switchToHttp().getRequest() // TODO: initialize authContext safely const authContext: OrganizationAuthContext = request.user + if (!authContext) { + return false + } if (authContext.role === SystemRole.ADMIN) { return true @@ -62,4 +75,27 @@ export class OrganizationResourceActionGuard extends OrganizationAccessGuard { return requiredPermissions.every((permission) => assignedPermissions.has(permission)) } + + private handlerAllowsRunnerResourceAccess(context: ExecutionContext): boolean { + const guards = + this.reflector.getAllAndMerge>(GUARDS_METADATA, [context.getHandler(), context.getClass()]) ?? [] + return guards.some((guard) => this.guardAllowsRunnerResourceAccess(guard)) + } + + private guardAllowsRunnerResourceAccess(guard: unknown): boolean { + if (guard === RunnerAuthGuard) { + return true + } + + const innerGuards = (guard as { [OR_GUARD_INNER_GUARDS]?: Type[] })?.[OR_GUARD_INNER_GUARDS] + if (innerGuards?.some((innerGuard) => this.guardAllowsRunnerResourceAccess(innerGuard))) { + return true + } + + return RUNNER_COMPATIBLE_RESOURCE_GUARD_NAMES.has(this.guardName(guard)) + } + + private guardName(guard: unknown): string { + return typeof guard === 'function' ? guard.name : '' + } } diff --git a/apps/api/src/organization/helpers/organization-usage.helper.ts b/apps/api/src/organization/helpers/organization-usage.helper.ts deleted file mode 100644 index cb84202d7..000000000 --- a/apps/api/src/organization/helpers/organization-usage.helper.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export type OrganizationUsageQuotaType = 'cpu' | 'memory' | 'disk' | 'snapshot_count' | 'volume_count' -export type OrganizationUsageResourceType = 'sandbox' | 'snapshot' | 'volume' - -const QUOTA_TO_RESOURCE_MAP: Record = { - cpu: 'sandbox', - memory: 'sandbox', - disk: 'sandbox', - snapshot_count: 'snapshot', - volume_count: 'volume', -} as const - -export function getResourceTypeFromQuota(quotaType: OrganizationUsageQuotaType): OrganizationUsageResourceType { - return QUOTA_TO_RESOURCE_MAP[quotaType] -} diff --git a/apps/api/src/organization/organization.module.ts b/apps/api/src/organization/organization.module.ts index 8effa975c..bf668250b 100644 --- a/apps/api/src/organization/organization.module.ts +++ b/apps/api/src/organization/organization.module.ts @@ -19,17 +19,13 @@ import { OrganizationRoleService } from './services/organization-role.service' import { OrganizationUserService } from './services/organization-user.service' import { OrganizationInvitationService } from './services/organization-invitation.service' import { UserModule } from '../user/user.module' -import { Sandbox } from '../sandbox/entities/sandbox.entity' -import { Snapshot } from '../sandbox/entities/snapshot.entity' -import { Volume } from '../sandbox/entities/volume.entity' -import { RedisLockProvider } from '../sandbox/common/redis-lock.provider' -import { SnapshotRunner } from '../sandbox/entities/snapshot-runner.entity' -import { OrganizationUsageService } from './services/organization-usage.service' +import { Box } from '../box/entities/box.entity' +import { Volume } from '../box/entities/volume.entity' +import { RedisLockProvider } from '../box/common/redis-lock.provider' import { DataSource } from 'typeorm' import { EventEmitter2 } from '@nestjs/event-emitter' -import { SandboxRepository } from '../sandbox/repositories/sandbox.repository' -import { SandboxLookupCacheInvalidationService } from '../sandbox/services/sandbox-lookup-cache-invalidation.service' -import { RegionQuota } from './entities/region-quota.entity' +import { BoxRepository } from '../box/repositories/box.repository' +import { BoxLookupCacheInvalidationService } from '../box/services/box-lookup-cache-invalidation.service' import { RegionModule } from '../region/region.module' import { OrganizationRegionController } from './controllers/organization-region.controller' import { Region } from '../region/entities/region.entity' @@ -44,11 +40,8 @@ import { EncryptionModule } from '../encryption/encryption.module' OrganizationRole, OrganizationUser, OrganizationInvitation, - Sandbox, - Snapshot, + Box, Volume, - SnapshotRunner, - RegionQuota, Region, ]), EncryptionModule, @@ -65,25 +58,18 @@ import { EncryptionModule } from '../encryption/encryption.module' OrganizationRoleService, OrganizationUserService, OrganizationInvitationService, - OrganizationUsageService, RedisLockProvider, - SandboxLookupCacheInvalidationService, + BoxLookupCacheInvalidationService, { - provide: SandboxRepository, - inject: [DataSource, EventEmitter2, SandboxLookupCacheInvalidationService], + provide: BoxRepository, + inject: [DataSource, EventEmitter2, BoxLookupCacheInvalidationService], useFactory: ( dataSource: DataSource, eventEmitter: EventEmitter2, - sandboxLookupCacheInvalidationService: SandboxLookupCacheInvalidationService, - ) => new SandboxRepository(dataSource, eventEmitter, sandboxLookupCacheInvalidationService), + boxLookupCacheInvalidationService: BoxLookupCacheInvalidationService, + ) => new BoxRepository(dataSource, eventEmitter, boxLookupCacheInvalidationService), }, ], - exports: [ - OrganizationService, - OrganizationRoleService, - OrganizationUserService, - OrganizationInvitationService, - OrganizationUsageService, - ], + exports: [OrganizationService, OrganizationRoleService, OrganizationUserService, OrganizationInvitationService], }) export class OrganizationModule {} diff --git a/apps/api/src/organization/services/default-organization-membership.spec.ts b/apps/api/src/organization/services/default-organization-membership.spec.ts new file mode 100644 index 000000000..61327a89a --- /dev/null +++ b/apps/api/src/organization/services/default-organization-membership.spec.ts @@ -0,0 +1,240 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { ForbiddenException } from '@nestjs/common' +import { OrganizationInvitationService } from './organization-invitation.service' +import { OrganizationService } from './organization.service' +import { OrganizationUserService } from './organization-user.service' +import { OrganizationMemberRole } from '../enums/organization-member-role.enum' +import { OrganizationInvitationAcceptedEvent } from '../events/organization-invitation-accepted.event' +import { SystemRole } from '../../user/enums/system-role.enum' +import { UserCreatedEvent } from '../../user/events/user-created.event' +import { UserDeletedEvent } from '../../user/events/user-deleted.event' +import { OrganizationDto } from '../dto/organization.dto' + +const legacyOrganizationPersonalFlag = ['pers', 'onal'].join('') + +function createEntityManager() { + const saved: unknown[] = [] + const entityManager = { + count: jest.fn().mockResolvedValue(0), + findOne: jest.fn().mockResolvedValue(null), + remove: jest.fn().mockResolvedValue(undefined), + save: jest.fn(async (entity) => { + saved.push(entity) + return entity + }), + transaction: jest.fn(async (callback) => callback(entityManager)), + } + + return { entityManager, saved } +} + +function createOrganizationService() { + const configService = { + get: jest.fn(() => undefined), + getOrThrow: jest.fn((key: string) => { + if (key === 'organizationBoxDefaultLimitedNetworkEgress') return false + throw new Error(`Unexpected config key: ${key}`) + }), + } + + // Argument positions must match OrganizationService's constructor signature + // (organization.service.ts): orgRepo, boxRepo, eventEmitter, configService, + // redisLockProvider, regionRepo, regionService, encryptionService. + return new OrganizationService( + { manager: {} } as never, + {} as never, + { emitAsync: jest.fn() } as never, + configService as never, + {} as never, + {} as never, + {} as never, + {} as never, + ) +} + +describe('default organization membership semantics', () => { + it('keeps the deprecated Organization.personal response field as an alias for the authenticated user default flag', () => { + const dto = OrganizationDto.fromOrganization( + { + id: 'org-1', + name: 'Default Organization', + createdBy: 'user-1', + createdAt: new Date('2026-06-08T00:00:00.000Z'), + updatedAt: new Date('2026-06-08T00:00:00.000Z'), + suspended: false, + maxCpuPerBox: 4, + maxMemoryPerBox: 8, + maxDiskPerBox: 10, + templateDeactivationTimeoutMinutes: 20160, + boxLimitedNetworkEgress: false, + authenticatedRateLimit: null, + boxCreateRateLimit: null, + boxLifecycleRateLimit: null, + authenticatedRateLimitTtlSeconds: null, + boxCreateRateLimitTtlSeconds: null, + boxLifecycleRateLimitTtlSeconds: null, + } as never, + true, + ) + + expect(dto.isDefaultForAuthenticatedUser).toBe(true) + expect(dto.personal).toBe(true) + }) + + it('creates signup default organizations as normal organizations with a default owner membership', async () => { + const { entityManager, saved } = createEntityManager() + const service = createOrganizationService() + + const organization = await service.handleUserCreatedEvent( + new UserCreatedEvent( + entityManager as never, + { + id: 'user-1', + emailVerified: true, + role: SystemRole.USER, + } as never, + ), + ) + + expect(organization.name).toBe('Default Organization') + expect(organization).not.toHaveProperty('personal') + expect(organization.users).toHaveLength(1) + expect(organization.users[0]).toMatchObject({ + userId: 'user-1', + role: OrganizationMemberRole.OWNER, + isDefaultForUser: true, + }) + expect(saved).toContain(organization) + }) + + it('allows invitations to default organizations because they are regular organizations', async () => { + const organizationInvitationRepository = { + findOne: jest.fn().mockResolvedValue(null), + save: jest.fn(async (invitation) => invitation), + } + const service = new OrganizationInvitationService( + organizationInvitationRepository as never, + { + findOne: jest.fn().mockResolvedValue({ + id: 'org-1', + name: 'Default Organization', + [legacyOrganizationPersonalFlag]: true, + }), + } as never, + { + findOne: jest.fn().mockResolvedValue(null), + } as never, + { + findByIds: jest.fn().mockResolvedValue([]), + } as never, + { + findOneByEmail: jest.fn().mockResolvedValue(null), + } as never, + { + emit: jest.fn(), + } as never, + {} as never, + {} as never, + ) + + await expect( + service.create( + 'org-1', + { + email: 'member@example.com', + role: OrganizationMemberRole.MEMBER, + assignedRoleIds: [], + }, + 'user-1', + ), + ).resolves.toMatchObject({ + organizationId: 'org-1', + email: 'member@example.com', + }) + }) + + it('creates accepted invitation memberships as non-default memberships', async () => { + const { entityManager } = createEntityManager() + const service = new OrganizationUserService({} as never, {} as never, {} as never, {} as never, {} as never) + + await expect( + service.handleOrganizationInvitationAcceptedEvent( + new OrganizationInvitationAcceptedEvent( + entityManager as never, + 'org-1', + 'user-2', + OrganizationMemberRole.MEMBER, + [], + ), + ), + ).resolves.toMatchObject({ + organizationId: 'org-1', + userId: 'user-2', + role: OrganizationMemberRole.MEMBER, + isDefaultForUser: false, + }) + }) + + it('protects removing a user from their default organization unless forced', async () => { + const organizationUserRepository = { + findOne: jest.fn().mockResolvedValue({ + organizationId: 'org-1', + userId: 'user-1', + role: OrganizationMemberRole.MEMBER, + isDefaultForUser: true, + }), + manager: { + remove: jest.fn(), + }, + } + const service = new OrganizationUserService( + organizationUserRepository as never, + {} as never, + {} as never, + {} as never, + {} as never, + ) + + await expect(service.delete('org-1', 'user-1')).rejects.toBeInstanceOf(ForbiddenException) + expect(organizationUserRepository.manager.remove).not.toHaveBeenCalled() + }) + + it('does not delete a default organization that still has other members when its creator is deleted', async () => { + const organization = { id: 'org-1' } + const deletedUserMembership = { + organizationId: 'org-1', + userId: 'user-1', + role: OrganizationMemberRole.OWNER, + isDefaultForUser: true, + } + const fallbackOwner = { + organizationId: 'org-1', + userId: 'user-2', + role: OrganizationMemberRole.MEMBER, + isDefaultForUser: false, + } + const entityManager = { + count: jest.fn().mockResolvedValueOnce(2).mockResolvedValueOnce(0), + findOne: jest + .fn() + .mockResolvedValueOnce({ organization }) + .mockResolvedValueOnce(deletedUserMembership) + .mockResolvedValueOnce(fallbackOwner), + remove: jest.fn().mockResolvedValue(undefined), + save: jest.fn(async (entity) => entity), + } + const service = createOrganizationService() + + await service.handleUserDeletedEvent(new UserDeletedEvent(entityManager as never, 'user-1')) + + expect(fallbackOwner.role).toBe(OrganizationMemberRole.OWNER) + expect(entityManager.save).toHaveBeenCalledWith(fallbackOwner) + expect(entityManager.remove).toHaveBeenCalledWith(deletedUserMembership) + expect(entityManager.remove).not.toHaveBeenCalledWith(organization) + }) +}) diff --git a/apps/api/src/organization/services/organization-invitation.service.ts b/apps/api/src/organization/services/organization-invitation.service.ts index c9ef8444b..cfde4a045 100644 --- a/apps/api/src/organization/services/organization-invitation.service.ts +++ b/apps/api/src/organization/services/organization-invitation.service.ts @@ -51,10 +51,6 @@ export class OrganizationInvitationService { if (!organization) { throw new NotFoundException(`Organization with ID ${organizationId} not found`) } - if (organization.personal) { - throw new ForbiddenException('Cannot invite users to personal organization') - } - const normalizedEmail = EmailUtils.normalize(createOrganizationInvitationDto.email) const existingUser = await this.userService.findOneByEmail(normalizedEmail, true) diff --git a/apps/api/src/organization/services/organization-usage.service.ts b/apps/api/src/organization/services/organization-usage.service.ts deleted file mode 100644 index 5afef3100..000000000 --- a/apps/api/src/organization/services/organization-usage.service.ts +++ /dev/null @@ -1,1400 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common' -import { OnEvent } from '@nestjs/event-emitter' -import { InjectRepository } from '@nestjs/typeorm' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { In, Repository } from 'typeorm' -import { SANDBOX_STATES_CONSUMING_COMPUTE } from '../constants/sandbox-states-consuming-compute.constant' -import { SANDBOX_STATES_CONSUMING_DISK } from '../constants/sandbox-states-consuming-disk.constant' -import { SNAPSHOT_STATES_CONSUMING_RESOURCES } from '../constants/snapshot-states-consuming-resources.constant' -import { VOLUME_STATES_CONSUMING_RESOURCES } from '../constants/volume-states-consuming-resources.constant' -import { OrganizationUsageOverviewDto, RegionUsageOverviewDto } from '../dto/organization-usage-overview.dto' -import { - PendingSandboxUsageOverviewInternalDto, - SandboxUsageOverviewInternalDto, - SandboxUsageOverviewWithPendingInternalDto, -} from '../dto/sandbox-usage-overview-internal.dto' -import { - PendingSnapshotUsageOverviewInternalDto, - SnapshotUsageOverviewInternalDto, - SnapshotUsageOverviewWithPendingInternalDto, -} from '../dto/snapshot-usage-overview-internal.dto' -import { - PendingVolumeUsageOverviewInternalDto, - VolumeUsageOverviewInternalDto, - VolumeUsageOverviewWithPendingInternalDto, -} from '../dto/volume-usage-overview-internal.dto' -import { Organization } from '../entities/organization.entity' -import { OrganizationUsageQuotaType, OrganizationUsageResourceType } from '../helpers/organization-usage.helper' -import { RedisLockProvider } from '../../sandbox/common/redis-lock.provider' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SnapshotEvents } from '../../sandbox/constants/snapshot-events' -import { VolumeEvents } from '../../sandbox/constants/volume-events' -import { Snapshot } from '../../sandbox/entities/snapshot.entity' -import { Volume } from '../../sandbox/entities/volume.entity' -import { SandboxCreatedEvent } from '../../sandbox/events/sandbox-create.event' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SnapshotCreatedEvent } from '../../sandbox/events/snapshot-created.event' -import { SnapshotStateUpdatedEvent } from '../../sandbox/events/snapshot-state-updated.event' -import { VolumeCreatedEvent } from '../../sandbox/events/volume-created.event' -import { VolumeStateUpdatedEvent } from '../../sandbox/events/volume-state-updated.event' -import { SandboxDesiredState } from '../../sandbox/enums/sandbox-desired-state.enum' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { OrganizationService } from './organization.service' -import { SandboxRepository } from '../../sandbox/repositories/sandbox.repository' - -@Injectable() -export class OrganizationUsageService { - private readonly logger = new Logger(OrganizationUsageService.name) - - /** - * Time-to-live for cached quota usage values - */ - private readonly CACHE_TTL_SECONDS = 60 - - /** - * Cache is considered stale if it was last populated from db more than `CACHE_MAX_AGE_MS` ago - */ - private readonly CACHE_MAX_AGE_MS = 60 * 60 * 1000 - - constructor( - @InjectRedis() private readonly redis: Redis, - @InjectRepository(Organization) - private readonly organizationRepository: Repository, - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(Volume) - private readonly volumeRepository: Repository, - private readonly redisLockProvider: RedisLockProvider, - private readonly organizationService: OrganizationService, - ) {} - - /** - * Get the current usage overview for all organization quotas. - * - * @param organizationId - * @param organization - Provide the organization entity to avoid fetching it from the database (optional) - */ - async getUsageOverview(organizationId: string, organization?: Organization): Promise { - if (organization && organization.id !== organizationId) { - throw new BadRequestException('Organization ID mismatch') - } - - if (!organization) { - organization = await this.organizationRepository.findOne({ where: { id: organizationId } }) - } - - if (!organization) { - throw new NotFoundException(`Organization with ID ${organizationId} not found`) - } - - const regionQuotas = await this.organizationService.getRegionQuotas(organizationId) - - const regionUsage: RegionUsageOverviewDto[] = await Promise.all( - regionQuotas.map(async (rq) => { - const sandboxUsage = await this.getSandboxUsageOverview(organizationId, rq.regionId) - - const usage: RegionUsageOverviewDto = { - regionId: rq.regionId, - totalCpuQuota: rq.totalCpuQuota, - totalMemoryQuota: rq.totalMemoryQuota, - totalDiskQuota: rq.totalDiskQuota, - currentCpuUsage: sandboxUsage.currentCpuUsage, - currentMemoryUsage: sandboxUsage.currentMemoryUsage, - currentDiskUsage: sandboxUsage.currentDiskUsage, - } - - return usage - }), - ) - - const snapshotUsage = await this.getSnapshotUsageOverview(organizationId) - const volumeUsage = await this.getVolumeUsageOverview(organizationId) - - return { - regionUsage, - totalSnapshotQuota: organization.snapshotQuota, - totalVolumeQuota: organization.volumeQuota, - currentSnapshotUsage: snapshotUsage.currentSnapshotUsage, - currentVolumeUsage: volumeUsage.currentVolumeUsage, - } - } - - /** - * Get the current and pending usage overview for sandbox-related organization quotas in a specific region. - * - * @param organizationId - * @param regionId - * @param excludeSandboxId - If provided, the usage overview will exclude the current usage of the sandbox with the given ID - */ - async getSandboxUsageOverview( - organizationId: string, - regionId: string, - excludeSandboxId?: string, - ): Promise { - let cachedUsageOverview = await this.getCachedSandboxUsageOverview(organizationId, regionId) - - // cache hit - if (cachedUsageOverview) { - if (excludeSandboxId) { - return await this.excludeSandboxFromUsageOverview(cachedUsageOverview, excludeSandboxId) - } - - return cachedUsageOverview - } - - // cache miss, wait for lock - const lockKey = `org:${organizationId}:fetch-sandbox-usage-from-db` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - // check if cache was updated while waiting for lock - cachedUsageOverview = await this.getCachedSandboxUsageOverview(organizationId, regionId) - - // cache hit - if (cachedUsageOverview) { - if (excludeSandboxId) { - return await this.excludeSandboxFromUsageOverview(cachedUsageOverview, excludeSandboxId) - } - - return cachedUsageOverview - } - - // cache miss, fetch from db - const usageOverview = await this.fetchSandboxUsageFromDb(organizationId, regionId) - - // get pending usage separately since it's not stored in DB - const pendingUsageOverview = await this.getCachedPendingSandboxUsageOverview(organizationId, regionId) - - const combinedUsageOverview: SandboxUsageOverviewWithPendingInternalDto = { - ...usageOverview, - ...pendingUsageOverview, - } - - if (excludeSandboxId) { - return await this.excludeSandboxFromUsageOverview(combinedUsageOverview, excludeSandboxId) - } - - return combinedUsageOverview - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - /** - * Get the current and pending usage overview for snapshot-related organization quotas. - * - * @param organizationId - */ - async getSnapshotUsageOverview(organizationId: string): Promise { - let cachedUsageOverview = await this.getCachedSnapshotUsageOverview(organizationId) - - // cache hit - if (cachedUsageOverview) { - return cachedUsageOverview - } - - // cache miss, wait for lock - const lockKey = `org:${organizationId}:fetch-snapshot-usage-from-db` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - // check if cache was updated while waiting for lock - cachedUsageOverview = await this.getCachedSnapshotUsageOverview(organizationId) - - // cache hit - if (cachedUsageOverview) { - return cachedUsageOverview - } - - // cache miss, fetch from db - const usageOverview = await this.fetchSnapshotUsageFromDb(organizationId) - - // get pending usage separately since it's not stored in DB - const pendingUsageOverview = await this.getCachedPendingSnapshotUsageOverview(organizationId) - - return { - ...usageOverview, - ...pendingUsageOverview, - } - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - /** - * Get the current and pending usage overview for volume-related organization quotas. - * - * @param organizationId - */ - async getVolumeUsageOverview(organizationId: string): Promise { - let cachedUsageOverview = await this.getCachedVolumeUsageOverview(organizationId) - - // cache hit - if (cachedUsageOverview) { - return cachedUsageOverview - } - - // cache miss, wait for lock - const lockKey = `org:${organizationId}:fetch-volume-usage-from-db` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - // check if cache was updated while waiting for lock - cachedUsageOverview = await this.getCachedVolumeUsageOverview(organizationId) - - // cache hit - if (cachedUsageOverview) { - return cachedUsageOverview - } - - // cache miss, fetch from db - const usageOverview = await this.fetchVolumeUsageFromDb(organizationId) - - // get pending usage separately since it's not stored in DB - const pendingUsageOverview = await this.getCachedPendingVolumeUsageOverview(organizationId) - - return { - ...usageOverview, - ...pendingUsageOverview, - } - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - /** - * Exclude the current usage of a specific sandbox from the usage overview. - * - * @param usageOverview - * @param excludeSandboxId - */ - private async excludeSandboxFromUsageOverview( - usageOverview: T, - excludeSandboxId: string, - ): Promise { - const excludedSandbox = await this.sandboxRepository.findOne({ - where: { id: excludeSandboxId }, - }) - - if (!excludedSandbox) { - return usageOverview - } - - let cpuToSubtract = 0 - let memToSubtract = 0 - let diskToSubtract = 0 - - if (SANDBOX_STATES_CONSUMING_COMPUTE.includes(excludedSandbox.state)) { - cpuToSubtract = excludedSandbox.cpu - memToSubtract = excludedSandbox.mem - } - - if (SANDBOX_STATES_CONSUMING_DISK.includes(excludedSandbox.state)) { - diskToSubtract = excludedSandbox.disk - } - - return { - ...usageOverview, - currentCpuUsage: Math.max(0, usageOverview.currentCpuUsage - cpuToSubtract), - currentMemoryUsage: Math.max(0, usageOverview.currentMemoryUsage - memToSubtract), - currentDiskUsage: Math.max(0, usageOverview.currentDiskUsage - diskToSubtract), - } - } - - /** - * Get the cached current and pending usage overview for sandbox-related organization quotas in a specific region. - * - * @param organizationId - * @param regionId - */ - private async getCachedSandboxUsageOverview( - organizationId: string, - regionId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]), - redis.call("GET", KEYS[2]), - redis.call("GET", KEYS[3]), - redis.call("GET", KEYS[4]), - redis.call("GET", KEYS[5]), - redis.call("GET", KEYS[6]) - } - ` - - const result = (await this.redis.eval( - script, - 6, - this.getCurrentQuotaUsageCacheKey(organizationId, 'cpu', regionId), - this.getCurrentQuotaUsageCacheKey(organizationId, 'memory', regionId), - this.getCurrentQuotaUsageCacheKey(organizationId, 'disk', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'cpu', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'memory', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'disk', regionId), - )) as (string | null)[] - - const [cpuUsage, memoryUsage, diskUsage, pendingCpuUsage, pendingMemoryUsage, pendingDiskUsage] = result - - // Cache miss - if (cpuUsage === null || memoryUsage === null || diskUsage === null) { - return null - } - - // Check cache staleness for current usage - const isStale = await this.isCacheStale(organizationId, 'sandbox', regionId) - - if (isStale) { - return null - } - - // Validate current usage values are non-negative numbers - const parsedCpuUsage = this.parseNonNegativeCachedValue(cpuUsage) - const parsedMemoryUsage = this.parseNonNegativeCachedValue(memoryUsage) - const parsedDiskUsage = this.parseNonNegativeCachedValue(diskUsage) - - if (parsedCpuUsage === null || parsedMemoryUsage === null || parsedDiskUsage === null) { - return null - } - - // Parse pending usage values (null is acceptable) - const parsedPendingCpuUsage = this.parseNonNegativeCachedValue(pendingCpuUsage) - const parsedPendingMemoryUsage = this.parseNonNegativeCachedValue(pendingMemoryUsage) - const parsedPendingDiskUsage = this.parseNonNegativeCachedValue(pendingDiskUsage) - - return { - currentCpuUsage: parsedCpuUsage, - currentMemoryUsage: parsedMemoryUsage, - currentDiskUsage: parsedDiskUsage, - pendingCpuUsage: parsedPendingCpuUsage, - pendingMemoryUsage: parsedPendingMemoryUsage, - pendingDiskUsage: parsedPendingDiskUsage, - } - } - - /** - * Get the cached pending usage overview for sandbox-related organization quotas in a specific region. - * - * @param organizationId - * @param regionId - */ - private async getCachedPendingSandboxUsageOverview( - organizationId: string, - regionId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]), - redis.call("GET", KEYS[2]), - redis.call("GET", KEYS[3]) - } - ` - const result = (await this.redis.eval( - script, - 3, - this.getPendingQuotaUsageCacheKey(organizationId, 'cpu', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'memory', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'disk', regionId), - )) as (string | null)[] - - const [pendingCpuUsage, pendingMemoryUsage, pendingDiskUsage] = result - - const parsedPendingCpuUsage = this.parseNonNegativeCachedValue(pendingCpuUsage) - const parsedPendingMemoryUsage = this.parseNonNegativeCachedValue(pendingMemoryUsage) - const parsedPendingDiskUsage = this.parseNonNegativeCachedValue(pendingDiskUsage) - - return { - pendingCpuUsage: parsedPendingCpuUsage, - pendingMemoryUsage: parsedPendingMemoryUsage, - pendingDiskUsage: parsedPendingDiskUsage, - } - } - - /** - * Get the cached overview for current and pending usage for snapshot-related organization quotas. - * - * @param organizationId - */ - private async getCachedSnapshotUsageOverview( - organizationId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]), - redis.call("GET", KEYS[2]) - } - ` - const result = (await this.redis.eval( - script, - 2, - this.getCurrentQuotaUsageCacheKey(organizationId, 'snapshot_count'), - this.getPendingQuotaUsageCacheKey(organizationId, 'snapshot_count'), - )) as (string | null)[] - - const [currentSnapshotUsage, pendingSnapshotUsage] = result - - // Cache miss - if (currentSnapshotUsage === null) { - return null - } - - // Check cache staleness for current usage - const isStale = await this.isCacheStale(organizationId, 'snapshot') - - if (isStale) { - return null - } - - // Validate current usage values are non-negative numbers - const parsedCurrentSnapshotUsage = this.parseNonNegativeCachedValue(currentSnapshotUsage) - - if (parsedCurrentSnapshotUsage === null) { - return null - } - - // Parse pending usage values (null is acceptable) - const parsedPendingSnapshotUsage = this.parseNonNegativeCachedValue(pendingSnapshotUsage) - - return { - currentSnapshotUsage: parsedCurrentSnapshotUsage, - pendingSnapshotUsage: parsedPendingSnapshotUsage, - } - } - - /** - * Get the cached pending usage overview for snapshot-related organization quotas. - * - * @param organizationId - */ - private async getCachedPendingSnapshotUsageOverview( - organizationId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]) - } - ` - const result = (await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'snapshot_count'), - )) as (string | null)[] - - const [pendingSnapshotUsage] = result - - // Parse pending usage values (null is acceptable) - const parsedPendingSnapshotUsage = this.parseNonNegativeCachedValue(pendingSnapshotUsage) - - return { - pendingSnapshotUsage: parsedPendingSnapshotUsage, - } - } - - /** - * Get the cached overview for current and pending usage for volume-related organization quotas. - * - * @param organizationId - */ - private async getCachedVolumeUsageOverview( - organizationId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]), - redis.call("GET", KEYS[2]) - } - ` - - const result = (await this.redis.eval( - script, - 2, - this.getCurrentQuotaUsageCacheKey(organizationId, 'volume_count'), - this.getPendingQuotaUsageCacheKey(organizationId, 'volume_count'), - )) as (string | null)[] - - const [currentVolumeUsage, pendingVolumeUsage] = result - - if (currentVolumeUsage === null) { - return null - } - - // Check cache staleness for current usage - const isStale = await this.isCacheStale(organizationId, 'volume') - - if (isStale) { - return null - } - - // Validate current usage values are non-negative numbers - const parsedCurrentVolumeUsage = this.parseNonNegativeCachedValue(currentVolumeUsage) - - if (parsedCurrentVolumeUsage === null) { - return null - } - - // Parse pending usage values (null is acceptable) - const parsedPendingVolumeUsage = this.parseNonNegativeCachedValue(pendingVolumeUsage) - - return { - currentVolumeUsage: parsedCurrentVolumeUsage, - pendingVolumeUsage: parsedPendingVolumeUsage, - } - } - - /** - * Get the cached pending usage overview for volume-related organization quotas. - * - * @param organizationId - */ - private async getCachedPendingVolumeUsageOverview( - organizationId: string, - ): Promise { - const script = ` - return { - redis.call("GET", KEYS[1]) - } - ` - - const result = (await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'volume_count'), - )) as (string | null)[] - - const [pendingVolumeUsage] = result - - // Parse pending usage values (null is acceptable) - const parsedPendingVolumeUsage = this.parseNonNegativeCachedValue(pendingVolumeUsage) - - return { - pendingVolumeUsage: parsedPendingVolumeUsage, - } - } - - /** - * Attempts to parse a given value to a non-negative number. - * - * @param value - The value to parse. - * @returns The parsed non-negative number or `null` if the given value is null or not a non-negative number. - */ - private parseNonNegativeCachedValue(value: string | null): number | null { - if (value === null) { - return null - } - - const parsedValue = Number(value) - - if (isNaN(parsedValue) || parsedValue < 0) { - return null - } - - return parsedValue - } - - /** - * Fetch the current usage overview for sandbox-related organization quotas in a specific region from the database and cache the results. - * - * @param organizationId - * @param regionId - */ - async fetchSandboxUsageFromDb(organizationId: string, regionId: string): Promise { - // fetch from db - // For CPU/memory, we need to also count RESIZING sandboxes that were hot resizing (desiredState = 'started') - // since they are still running and consuming compute resources - const sandboxUsageMetrics: { - used_cpu: number - used_mem: number - used_disk: number - } = await this.sandboxRepository - .createQueryBuilder('sandbox') - .select([ - `SUM(CASE WHEN sandbox.state IN (:...statesConsumingCompute) OR (sandbox.state = :resizingState AND sandbox."desiredState" = :startedDesiredState) THEN sandbox.cpu ELSE 0 END) as used_cpu`, - `SUM(CASE WHEN sandbox.state IN (:...statesConsumingCompute) OR (sandbox.state = :resizingState AND sandbox."desiredState" = :startedDesiredState) THEN sandbox.mem ELSE 0 END) as used_mem`, - 'SUM(CASE WHEN sandbox.state IN (:...statesConsumingDisk) THEN sandbox.disk ELSE 0 END) as used_disk', - ]) - .where('sandbox.organizationId = :organizationId', { organizationId }) - .andWhere('sandbox.region = :regionId', { regionId }) - .setParameter('statesConsumingCompute', SANDBOX_STATES_CONSUMING_COMPUTE) - .setParameter('statesConsumingDisk', SANDBOX_STATES_CONSUMING_DISK) - .setParameter('resizingState', SandboxState.RESIZING) - .setParameter('startedDesiredState', SandboxDesiredState.STARTED) - .getRawOne() - - const cpuUsage = Number(sandboxUsageMetrics.used_cpu) || 0 - const memoryUsage = Number(sandboxUsageMetrics.used_mem) || 0 - const diskUsage = Number(sandboxUsageMetrics.used_disk) || 0 - - // cache the results - const cpuCacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, 'cpu', regionId) - const memoryCacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, 'memory', regionId) - const diskCacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, 'disk', regionId) - - await this.redis - .multi() - .setex(cpuCacheKey, this.CACHE_TTL_SECONDS, cpuUsage) - .setex(memoryCacheKey, this.CACHE_TTL_SECONDS, memoryUsage) - .setex(diskCacheKey, this.CACHE_TTL_SECONDS, diskUsage) - .exec() - - await this.resetCacheStaleness(organizationId, 'sandbox', regionId) - - return { - currentCpuUsage: cpuUsage, - currentMemoryUsage: memoryUsage, - currentDiskUsage: diskUsage, - } - } - - /** - * Fetch the current usage overview for snapshot-related organization quotas from the database and cache the results. - * - * @param organizationId - */ - private async fetchSnapshotUsageFromDb(organizationId: string): Promise { - // fetch from db - const snapshotUsage = await this.snapshotRepository.count({ - where: { - organizationId, - state: In(SNAPSHOT_STATES_CONSUMING_RESOURCES), - }, - }) - - // cache the result - const cacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, 'snapshot_count') - await this.redis.setex(cacheKey, this.CACHE_TTL_SECONDS, snapshotUsage) - - await this.resetCacheStaleness(organizationId, 'snapshot') - - return { - currentSnapshotUsage: snapshotUsage, - } - } - - /** - * Fetch the current usage overview for volume-related organization quotas from the database and cache the results. - * - * @param organizationId - */ - private async fetchVolumeUsageFromDb(organizationId: string): Promise { - // fetch from db - const volumeUsage = await this.volumeRepository.count({ - where: { - organizationId, - state: In(VOLUME_STATES_CONSUMING_RESOURCES), - }, - }) - - // cache the result - const cacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, 'volume_count') - await this.redis.setex(cacheKey, this.CACHE_TTL_SECONDS, volumeUsage) - - await this.resetCacheStaleness(organizationId, 'volume') - - return { - currentVolumeUsage: volumeUsage, - } - } - - /** - * Get the cache key for the current usage of a given organization quota. - */ - private getCurrentQuotaUsageCacheKey( - organizationId: string, - quotaType: 'cpu' | 'memory' | 'disk', - regionId: string, - ): string - private getCurrentQuotaUsageCacheKey(organizationId: string, quotaType: 'snapshot_count' | 'volume_count'): string - private getCurrentQuotaUsageCacheKey( - organizationId: string, - quotaType: OrganizationUsageQuotaType, - regionId?: string, - ): string { - return `org:${organizationId}${regionId ? `:region:${regionId}` : ''}:quota:${quotaType}:usage` - } - - /** - * Get the cache key for the pending usage of a given organization quota. - */ - private getPendingQuotaUsageCacheKey( - organizationId: string, - quotaType: 'cpu' | 'memory' | 'disk', - regionId: string, - ): string - private getPendingQuotaUsageCacheKey(organizationId: string, quotaType: 'snapshot_count' | 'volume_count'): string - private getPendingQuotaUsageCacheKey( - organizationId: string, - quotaType: OrganizationUsageQuotaType, - regionId?: string, - ): string { - return `org:${organizationId}${regionId ? `:region:${regionId}` : ''}:quota:${quotaType}:pending` - } - - /** - * Updates the current usage of a given organization quota in the cache. If cache is not present, this method is a no-op. - * - * If the corresponding quota type has pending usage in the cache and the delta is positive, the pending usage is decremented accordingly. - */ - private async updateCurrentQuotaUsage( - organizationId: string, - quotaType: 'cpu' | 'memory' | 'disk', - delta: number, - regionId: string, - ): Promise - private async updateCurrentQuotaUsage( - organizationId: string, - quotaType: 'snapshot_count' | 'volume_count', - delta: number, - ): Promise - private async updateCurrentQuotaUsage( - organizationId: string, - quotaType: OrganizationUsageQuotaType, - delta: number, - regionId?: string, - ): Promise { - const script = ` - local cacheKey = KEYS[1] - local pendingCacheKey = KEYS[2] - local delta = tonumber(ARGV[1]) - local ttl = tonumber(ARGV[2]) - - if redis.call("EXISTS", cacheKey) == 1 then - redis.call("INCRBY", cacheKey, delta) - redis.call("EXPIRE", cacheKey, ttl) - end - - local pending = tonumber(redis.call("GET", pendingCacheKey)) - if pending and pending > 0 and delta > 0 then - redis.call("DECRBY", pendingCacheKey, delta) - end - ` - - let currentQuotaUsageCacheKey = '' - let pendingQuotaUsageCacheKey = '' - - switch (quotaType) { - case 'cpu': - case 'memory': - case 'disk': - currentQuotaUsageCacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, quotaType, regionId) - pendingQuotaUsageCacheKey = this.getPendingQuotaUsageCacheKey(organizationId, quotaType, regionId) - break - case 'snapshot_count': - case 'volume_count': - currentQuotaUsageCacheKey = this.getCurrentQuotaUsageCacheKey(organizationId, quotaType) - pendingQuotaUsageCacheKey = this.getPendingQuotaUsageCacheKey(organizationId, quotaType) - break - } - - await this.redis.eval( - script, - 2, - currentQuotaUsageCacheKey, - pendingQuotaUsageCacheKey, - delta.toString(), - this.CACHE_TTL_SECONDS.toString(), - ) - } - - /** - * Increments the pending usage for sandbox-related organization quotas in a specific region. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * As a safeguard, an expiration time is set on the pending usage cache to prevent lockout for new operations. - * - * @param organizationId - * @param regionId - * @param cpu - The amount of CPU to increment. - * @param memory - The amount of memory to increment. - * @param disk - The amount of disk to increment. - * @param excludeSandboxId - If provided, pending usage will be incremented only for quotas that are not consumed by the sandbox in its current state. - * @returns an object with the boolean values indicating if the pending usage was incremented for each quota type - */ - async incrementPendingSandboxUsage( - organizationId: string, - regionId: string, - cpu: number, - memory: number, - disk: number, - excludeSandboxId?: string, - ): Promise<{ - cpuIncremented: boolean - memoryIncremented: boolean - diskIncremented: boolean - }> { - // determine for which quota types we should increment the pending usage - let shouldIncrementCpu = true - let shouldIncrementMemory = true - let shouldIncrementDisk = true - - if (excludeSandboxId) { - const excludedSandbox = await this.sandboxRepository.findOne({ - where: { id: excludeSandboxId }, - }) - - if (excludedSandbox) { - if (SANDBOX_STATES_CONSUMING_COMPUTE.includes(excludedSandbox.state)) { - shouldIncrementCpu = false - shouldIncrementMemory = false - } - - if (SANDBOX_STATES_CONSUMING_DISK.includes(excludedSandbox.state)) { - shouldIncrementDisk = false - } - } - } - - // increment the pending usage for necessary quota types - const script = ` - local cpuKey = KEYS[1] - local memoryKey = KEYS[2] - local diskKey = KEYS[3] - - local shouldIncrementCpu = ARGV[1] == "true" - local shouldIncrementMemory = ARGV[2] == "true" - local shouldIncrementDisk = ARGV[3] == "true" - - local cpuIncrement = tonumber(ARGV[4]) - local memoryIncrement = tonumber(ARGV[5]) - local diskIncrement = tonumber(ARGV[6]) - - local ttl = tonumber(ARGV[7]) - - if shouldIncrementCpu then - redis.call("INCRBY", cpuKey, cpuIncrement) - redis.call("EXPIRE", cpuKey, ttl) - end - - if shouldIncrementMemory then - redis.call("INCRBY", memoryKey, memoryIncrement) - redis.call("EXPIRE", memoryKey, ttl) - end - - if shouldIncrementDisk then - redis.call("INCRBY", diskKey, diskIncrement) - redis.call("EXPIRE", diskKey, ttl) - end - ` - - await this.redis.eval( - script, - 3, - this.getPendingQuotaUsageCacheKey(organizationId, 'cpu', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'memory', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'disk', regionId), - shouldIncrementCpu.toString(), - shouldIncrementMemory.toString(), - shouldIncrementDisk.toString(), - cpu.toString(), - memory.toString(), - disk.toString(), - this.CACHE_TTL_SECONDS.toString(), - ) - - return { - cpuIncremented: shouldIncrementCpu, - memoryIncremented: shouldIncrementMemory, - diskIncremented: shouldIncrementDisk, - } - } - - /** - * Decrements the pending usage for sandbox-related organization quotas in a specific region. - * - * Use this method to roll back pending usage after incrementing it for an action that was subsequently rejected. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * @param organizationId - * @param regionId - * @param cpu - If provided, the amount of CPU to decrement. - * @param memory - If provided, the amount of memory to decrement. - * @param disk - If provided, the amount of disk to decrement. - */ - async decrementPendingSandboxUsage( - organizationId: string, - regionId: string, - cpu?: number, - memory?: number, - disk?: number, - ): Promise { - // decrement the pending usage for necessary quota types - const script = ` - local cpuKey = KEYS[1] - local memoryKey = KEYS[2] - local diskKey = KEYS[3] - - local cpuDecrement = tonumber(ARGV[1]) - local memoryDecrement = tonumber(ARGV[2]) - local diskDecrement = tonumber(ARGV[3]) - - if cpuDecrement then - redis.call("DECRBY", cpuKey, cpuDecrement) - end - - if memoryDecrement then - redis.call("DECRBY", memoryKey, memoryDecrement) - end - - if diskDecrement then - redis.call("DECRBY", diskKey, diskDecrement) - end - ` - - await this.redis.eval( - script, - 3, - this.getPendingQuotaUsageCacheKey(organizationId, 'cpu', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'memory', regionId), - this.getPendingQuotaUsageCacheKey(organizationId, 'disk', regionId), - cpu?.toString() ?? '0', - memory?.toString() ?? '0', - disk?.toString() ?? '0', - ) - } - - /** - * Increments the pending usage for snapshot-related organization quotas. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * As a safeguard, an expiration time is set on the pending usage cache to prevent lockout for new operations. - * - * @param organizationId - * @param snapshotCount - The count of snapshots to increment. - */ - async incrementPendingSnapshotUsage(organizationId: string, snapshotCount: number): Promise { - const script = ` - local snapshotCountKey = KEYS[1] - - local snapshotCountIncrement = tonumber(ARGV[1]) - local ttl = tonumber(ARGV[2]) - - redis.call("INCRBY", snapshotCountKey, snapshotCountIncrement) - redis.call("EXPIRE", snapshotCountKey, ttl) - ` - - await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'snapshot_count'), - snapshotCount.toString(), - this.CACHE_TTL_SECONDS.toString(), - ) - } - - /** - * Decrements the pending usage for snapshot-related organization quotas. - * - * Use this method to roll back pending usage after incrementing it for an action that was subsequently rejected. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * @param organizationId - * @param snapshotCount - If provided, the count of snapshots to decrement. - */ - async decrementPendingSnapshotUsage(organizationId: string, snapshotCount?: number): Promise { - // decrement the pending usage for necessary quota types - const script = ` - local snapshotCountKey = KEYS[1] - - local snapshotCountDecrement = tonumber(ARGV[1]) - - if snapshotCountDecrement then - redis.call("DECRBY", snapshotCountKey, snapshotCountDecrement) - end - ` - - await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'snapshot_count'), - snapshotCount?.toString() ?? '0', - ) - } - - /** - * Increments the pending usage for volume-related organization quotas. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * As a safeguard, an expiration time is set on the pending usage cache to prevent lockout for new operations. - * - * @param organizationId - * @param volumeCount - The count of volumes to increment. - */ - async incrementPendingVolumeUsage(organizationId: string, volumeCount: number): Promise { - const script = ` - local volumeCountKey = KEYS[1] - - local volumeCountIncrement = tonumber(ARGV[1]) - local ttl = tonumber(ARGV[2]) - - redis.call("INCRBY", volumeCountKey, volumeCountIncrement) - redis.call("EXPIRE", volumeCountKey, ttl) - ` - - await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'volume_count'), - volumeCount.toString(), - this.CACHE_TTL_SECONDS.toString(), - ) - } - - /** - * Decrements the pending usage for volume-related organization quotas. - * - * Use this method to roll back pending usage after incrementing it for an action that was subsequently rejected. - * - * Pending usage is used to protect against race conditions to prevent quota abuse. - * - * If a user action will result in increased quota usage, we will first increment the pending usage. - * - * When the user action is complete, this pending usage will be transfered to the actual usage. - * - * @param organizationId - * @param volumeCount - If provided, the count of volumes to decrement. - */ - async decrementPendingVolumeUsage(organizationId: string, volumeCount?: number): Promise { - // decrement the pending usage for necessary quota types - const script = ` - local volumeCountKey = KEYS[1] - - local volumeCountDecrement = tonumber(ARGV[1]) - - if volumeCountDecrement then - redis.call("DECRBY", volumeCountKey, volumeCountDecrement) - end - ` - - await this.redis.eval( - script, - 1, - this.getPendingQuotaUsageCacheKey(organizationId, 'volume_count'), - volumeCount?.toString() ?? '0', - ) - } - - /** - * Apply usage change after resize completes successfully. - * Updates current usage and clears pending by the deltas. - * Supports both positive (increase) and negative (decrease) deltas. - * @param organizationId - * @param regionId - * @param cpuDelta - * @param memDelta - * @param diskDelta - */ - async applyResizeUsageChange( - organizationId: string, - regionId: string, - cpuDelta: number, - memDelta: number, - diskDelta: number, - ): Promise { - if (cpuDelta !== 0) { - await this.updateCurrentQuotaUsage(organizationId, 'cpu', cpuDelta, regionId) - } - if (memDelta !== 0) { - await this.updateCurrentQuotaUsage(organizationId, 'memory', memDelta, regionId) - } - if (diskDelta !== 0) { - await this.updateCurrentQuotaUsage(organizationId, 'disk', diskDelta, regionId) - } - } - - /** - * Get the cache key for the timestamp of the last time the cached usage of organization quotas for a given resource type was populated from the database. - * - * @param organizationId - * @param resourceType - * @param regionId - */ - private getCacheStalenessKey( - organizationId: string, - resourceType: OrganizationUsageResourceType, - regionId?: string, - ): string { - return `org:${organizationId}${regionId ? `:region:${regionId}` : ''}:resource:${resourceType}:usage:fetched_at` - } - - /** - * Reset the timestamp of the last time the cached usage of organization quotas for a given resource type was populated from the database. - */ - private resetCacheStaleness(organizationId: string, resourceType: 'sandbox', regionId: string): Promise - private resetCacheStaleness(organizationId: string, resourceType: 'snapshot' | 'volume'): Promise - private async resetCacheStaleness( - organizationId: string, - resourceType: OrganizationUsageResourceType, - regionId?: string, - ): Promise { - const cacheKey = this.getCacheStalenessKey(organizationId, resourceType, regionId) - await this.redis.set(cacheKey, Date.now()) - } - - /** - * Check if the cached usage of organization quotas for a given resource type was last populated from the database more than CACHE_MAX_AGE_MS ago. - * - * @returns `true` if the cached usage is stale, `false` otherwise - */ - private async isCacheStale(organizationId: string, resourceType: 'sandbox', regionId: string): Promise - private async isCacheStale(organizationId: string, resourceType: 'snapshot' | 'volume'): Promise - private async isCacheStale( - organizationId: string, - resourceType: OrganizationUsageResourceType, - regionId?: string, - ): Promise { - const cacheKey = this.getCacheStalenessKey(organizationId, resourceType, regionId) - const cachedData = await this.redis.get(cacheKey) - - if (!cachedData) { - return true - } - - const lastFetchedAtTimestamp = Number(cachedData) - if (isNaN(lastFetchedAtTimestamp)) { - return true - } - - return Date.now() - lastFetchedAtTimestamp > this.CACHE_MAX_AGE_MS - } - - @OnEvent(SandboxEvents.CREATED) - async handleSandboxCreated(event: SandboxCreatedEvent) { - const lockKey = `sandbox:${event.sandbox.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'cpu', event.sandbox.cpu, event.sandbox.region) - await this.updateCurrentQuotaUsage( - event.sandbox.organizationId, - 'memory', - event.sandbox.mem, - event.sandbox.region, - ) - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'disk', event.sandbox.disk, event.sandbox.region) - } catch (error) { - this.logger.warn( - `Error updating cached sandbox quota usage for organization ${event.sandbox.organizationId} in region ${event.sandbox.region}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(SandboxEvents.STATE_UPDATED) - async handleSandboxStateUpdated(event: SandboxStateUpdatedEvent) { - if (event.oldState === SandboxState.RESIZING || event.newState === SandboxState.RESIZING) { - return - } - - const lockKey = `sandbox:${event.sandbox.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - // Special case for warm pool sandboxes (otherwise the quota usage deltas would be 0 due to the "unchanged" state) - if (event.oldState === event.newState && event.newState === SandboxState.STARTED) { - try { - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'cpu', event.sandbox.cpu, event.sandbox.region) - await this.updateCurrentQuotaUsage( - event.sandbox.organizationId, - 'memory', - event.sandbox.mem, - event.sandbox.region, - ) - await this.updateCurrentQuotaUsage( - event.sandbox.organizationId, - 'disk', - event.sandbox.disk, - event.sandbox.region, - ) - return - } catch (error) { - this.logger.warn( - `Error updating cached sandbox quota usage for organization ${event.sandbox.organizationId} in region ${event.sandbox.region}`, - error, - ) - return - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - try { - const cpuDelta = this.calculateQuotaUsageDelta( - event.sandbox.cpu, - event.oldState, - event.newState, - SANDBOX_STATES_CONSUMING_COMPUTE, - ) - - const memDelta = this.calculateQuotaUsageDelta( - event.sandbox.mem, - event.oldState, - event.newState, - SANDBOX_STATES_CONSUMING_COMPUTE, - ) - - const diskDelta = this.calculateQuotaUsageDelta( - event.sandbox.disk, - event.oldState, - event.newState, - SANDBOX_STATES_CONSUMING_DISK, - ) - - if (cpuDelta !== 0) { - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'cpu', cpuDelta, event.sandbox.region) - } - - if (memDelta !== 0) { - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'memory', memDelta, event.sandbox.region) - } - - if (diskDelta !== 0) { - await this.updateCurrentQuotaUsage(event.sandbox.organizationId, 'disk', diskDelta, event.sandbox.region) - } - } catch (error) { - this.logger.warn( - `Error updating cached sandbox quota usage for organization ${event.sandbox.organizationId} in region ${event.sandbox.region}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(SnapshotEvents.CREATED) - async handleSnapshotCreated(event: SnapshotCreatedEvent) { - const lockKey = `snapshot:${event.snapshot.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - await this.updateCurrentQuotaUsage(event.snapshot.organizationId, 'snapshot_count', 1) - } catch (error) { - this.logger.warn( - `Error updating cached snapshot quota usage for organization ${event.snapshot.organizationId}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(SnapshotEvents.STATE_UPDATED) - async handleSnapshotStateUpdated(event: SnapshotStateUpdatedEvent) { - const lockKey = `snapshot:${event.snapshot.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - const countDelta = this.calculateQuotaUsageDelta( - 1, - event.oldState, - event.newState, - SNAPSHOT_STATES_CONSUMING_RESOURCES, - ) - - if (countDelta !== 0) { - await this.updateCurrentQuotaUsage(event.snapshot.organizationId, 'snapshot_count', countDelta) - } - } catch (error) { - this.logger.warn( - `Error updating cached snapshot quota usage for organization ${event.snapshot.organizationId}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(VolumeEvents.CREATED) - async handleVolumeCreated(event: VolumeCreatedEvent) { - const lockKey = `volume:${event.volume.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - await this.updateCurrentQuotaUsage(event.volume.organizationId, 'volume_count', 1) - } catch (error) { - this.logger.warn( - `Error updating cached volume quota usage for organization ${event.volume.organizationId}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(VolumeEvents.STATE_UPDATED) - async handleVolumeStateUpdated(event: VolumeStateUpdatedEvent) { - const lockKey = `volume:${event.volume.id}:quota-usage-update` - await this.redisLockProvider.waitForLock(lockKey, 60) - - try { - const countDelta = this.calculateQuotaUsageDelta( - 1, - event.oldState, - event.newState, - VOLUME_STATES_CONSUMING_RESOURCES, - ) - - if (countDelta !== 0) { - await this.updateCurrentQuotaUsage(event.volume.organizationId, 'volume_count', countDelta) - } - } catch (error) { - this.logger.warn( - `Error updating cached volume quota usage for organization ${event.volume.organizationId}`, - error, - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - private calculateQuotaUsageDelta( - resourceAmount: number, - oldState: TState, - newState: TState, - statesConsumingResource: TState[], - ): number { - const wasConsumingResource = statesConsumingResource.includes(oldState) - const isConsumingResource = statesConsumingResource.includes(newState) - - if (!wasConsumingResource && isConsumingResource) { - return resourceAmount - } - - if (wasConsumingResource && !isConsumingResource) { - return -resourceAmount - } - - return 0 - } -} diff --git a/apps/api/src/organization/services/organization-user.service.ts b/apps/api/src/organization/services/organization-user.service.ts index 70d00d433..71d8a043d 100644 --- a/apps/api/src/organization/services/organization-user.service.ts +++ b/apps/api/src/organization/services/organization-user.service.ts @@ -158,6 +158,10 @@ export class OrganizationUserService { force = false, ): Promise { if (!force) { + if (organizationUser.isDefaultForUser) { + throw new ForbiddenException('Cannot remove a user from their default organization') + } + if (organizationUser.role === OrganizationMemberRole.OWNER) { const ownersCount = await entityManager.count(OrganizationUser, { where: { @@ -189,6 +193,7 @@ export class OrganizationUserService { organizationUser.userId = userId organizationUser.role = role organizationUser.assignedRoles = assignedRoles + organizationUser.isDefaultForUser = false return entityManager.save(organizationUser) } @@ -214,9 +219,7 @@ export class OrganizationUserService { const memberships = await payload.entityManager.find(OrganizationUser, { where: { userId: payload.userId, - organization: { - personal: false, - }, + isDefaultForUser: false, }, relations: { organization: true, @@ -225,7 +228,7 @@ export class OrganizationUserService { /* // TODO - // user deletion will fail if the user is the only owner of some non-personal organization + // user deletion will fail if the user is the only owner of some organization // potential improvements: // - auto-delete the organization if there are no other members // - auto-promote a new owner if there are other members diff --git a/apps/api/src/organization/services/organization.service.ts b/apps/api/src/organization/services/organization.service.ts index fc40e94ca..6f4a6f9da 100644 --- a/apps/api/src/organization/services/organization.service.ts +++ b/apps/api/src/organization/services/organization.service.ts @@ -12,11 +12,11 @@ import { OnModuleInit, OnApplicationShutdown, ConflictException, + BadRequestException, } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' import { EntityManager, In, Not, Repository } from 'typeorm' import { CreateOrganizationInternalDto } from '../dto/create-organization.internal.dto' -import { UpdateOrganizationQuotaDto } from '../dto/update-organization-quota.dto' import { Organization } from '../entities/organization.entity' import { OrganizationUser } from '../entities/organization-user.entity' import { OrganizationMemberRole } from '../enums/organization-member-role.enum' @@ -24,64 +24,51 @@ import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' import { UserEvents } from '../../user/constants/user-events.constant' import { UserCreatedEvent } from '../../user/events/user-created.event' import { UserDeletedEvent } from '../../user/events/user-deleted.event' -import { Snapshot } from '../../sandbox/entities/snapshot.entity' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' +import { BoxState } from '../../box/enums/box-state.enum' import { EventEmitter2 } from '@nestjs/event-emitter' import { OrganizationEvents } from '../constants/organization-events.constant' -import { CreateOrganizationQuotaDto } from '../dto/create-organization-quota.dto' import { UserEmailVerifiedEvent } from '../../user/events/user-email-verified.event' import { Cron, CronExpression } from '@nestjs/schedule' -import { RedisLockProvider } from '../../sandbox/common/redis-lock.provider' -import { OrganizationSuspendedSandboxStoppedEvent } from '../events/organization-suspended-sandbox-stopped.event' -import { SandboxDesiredState } from '../../sandbox/enums/sandbox-desired-state.enum' +import { RedisLockProvider } from '../../box/common/redis-lock.provider' +import { OrganizationSuspendedBoxStoppedEvent } from '../events/organization-suspended-box-stopped.event' +import { BoxDesiredState } from '../../box/enums/box-desired-state.enum' import { SystemRole } from '../../user/enums/system-role.enum' -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' -import { OrganizationSuspendedSnapshotDeactivatedEvent } from '../events/organization-suspended-snapshot-deactivated.event' import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' import { setTimeout } from 'timers/promises' import { TypedConfigService } from '../../config/typed-config.service' import { LogExecution } from '../../common/decorators/log-execution.decorator' import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { RegionQuota } from '../entities/region-quota.entity' -import { UpdateOrganizationRegionQuotaDto } from '../dto/update-organization-region-quota.dto' import { RegionService } from '../../region/services/region.service' import { Region } from '../../region/entities/region.entity' -import { RegionQuotaDto } from '../dto/region-quota.dto' import { RegionType } from '../../region/enums/region-type.enum' import { RegionDto } from '../../region/dto/region.dto' import { EncryptionService } from '../../encryption/encryption.service' import { OtelConfigDto } from '../dto/otel-config.dto' -import { sandboxLookupCacheKeyByAuthToken } from '../../sandbox/utils/sandbox-lookup-cache.util' -import { SandboxRepository } from '../../sandbox/repositories/sandbox.repository' +import { boxLookupCacheKeyByAuthToken } from '../../box/utils/box-lookup-cache.util' +import { BoxRepository } from '../../box/repositories/box.repository' @Injectable() export class OrganizationService implements OnModuleInit, TrackableJobExecutions, OnApplicationShutdown { + private static readonly DEFAULT_ORGANIZATION_NAME = 'Default Organization' + activeJobs = new Set() private readonly logger = new Logger(OrganizationService.name) - private defaultOrganizationQuota: CreateOrganizationQuotaDto - private defaultSandboxLimitedNetworkEgress: boolean + private defaultBoxLimitedNetworkEgress: boolean constructor( @InjectRepository(Organization) private readonly organizationRepository: Repository, - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, + private readonly boxRepository: BoxRepository, private readonly eventEmitter: EventEmitter2, private readonly configService: TypedConfigService, private readonly redisLockProvider: RedisLockProvider, - @InjectRepository(RegionQuota) - private readonly regionQuotaRepository: Repository, @InjectRepository(Region) private readonly regionRepository: Repository, private readonly regionService: RegionService, private readonly encryptionService: EncryptionService, ) { - this.defaultOrganizationQuota = this.configService.getOrThrow('defaultOrganizationQuota') - this.defaultSandboxLimitedNetworkEgress = this.configService.getOrThrow( - 'organizationSandboxDefaultLimitedNetworkEgress', - ) + this.defaultBoxLimitedNetworkEgress = this.configService.getOrThrow('organizationBoxDefaultLimitedNetworkEgress') } async onApplicationShutdown() { @@ -93,13 +80,13 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions } async onModuleInit(): Promise { - await this.stopSuspendedOrganizationSandboxes() + await this.stopSuspendedOrganizationBoxes() } async create( createOrganizationDto: CreateOrganizationInternalDto, createdBy: string, - personal = false, + defaultForCreator = false, creatorEmailVerified = false, ): Promise { return this.createWithEntityManager( @@ -107,7 +94,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions createOrganizationDto, createdBy, creatorEmailVerified, - personal, + defaultForCreator, ) } @@ -127,36 +114,65 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions }) } - async findBySandboxId(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, + async findByIds(organizationIds: string[]): Promise { + if (organizationIds.length === 0) { + return [] + } + + return this.organizationRepository.find({ + where: { id: In(organizationIds) }, + }) + } + + async findByBoxId(boxId: string): Promise { + const box = await this.boxRepository.findOne({ + where: { id: boxId }, }) - if (!sandbox) { + if (!box) { return null } - return this.organizationRepository.findOne({ where: { id: sandbox.organizationId } }) + return this.organizationRepository.findOne({ where: { id: box.organizationId } }) } - async findBySandboxAuthToken(authToken: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ + async findByBoxAuthToken(authToken: string): Promise { + const box = await this.boxRepository.findOne({ where: { authToken }, cache: { - id: sandboxLookupCacheKeyByAuthToken({ authToken }), + id: boxLookupCacheKeyByAuthToken({ authToken }), milliseconds: 10_000, }, }) - if (!sandbox) { + if (!box) { return null } - return this.organizationRepository.findOne({ where: { id: sandbox.organizationId } }) + return this.organizationRepository.findOne({ where: { id: box.organizationId } }) + } + + async findDefaultForUser(userId: string): Promise { + return this.findDefaultForUserWithEntityManager(this.organizationRepository.manager, userId) } - async findPersonal(userId: string): Promise { - return this.findPersonalWithEntityManager(this.organizationRepository.manager, userId) + async findByUserWithDefaultFlag( + userId: string, + ): Promise<{ organization: Organization; isDefaultForAuthenticatedUser: boolean }[]> { + const memberships = await this.organizationRepository.manager.find(OrganizationUser, { + where: { userId }, + relations: { + organization: true, + }, + order: { + createdAt: 'ASC', + }, + }) + + return memberships.map((membership) => ({ + organization: membership.organization, + isDefaultForAuthenticatedUser: membership.isDefaultForUser, + })) } async delete(organizationId: string): Promise { @@ -169,72 +185,20 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions return this.removeWithEntityManager(this.organizationRepository.manager, organization) } - async updateQuota(organizationId: string, updateDto: UpdateOrganizationQuotaDto): Promise { + async updateName(organizationId: string, name: string): Promise { const organization = await this.organizationRepository.findOne({ where: { id: organizationId } }) if (!organization) { throw new NotFoundException(`Organization with ID ${organizationId} not found`) } - organization.maxCpuPerSandbox = updateDto.maxCpuPerSandbox ?? organization.maxCpuPerSandbox - organization.maxMemoryPerSandbox = updateDto.maxMemoryPerSandbox ?? organization.maxMemoryPerSandbox - organization.maxDiskPerSandbox = updateDto.maxDiskPerSandbox ?? organization.maxDiskPerSandbox - organization.maxSnapshotSize = updateDto.maxSnapshotSize ?? organization.maxSnapshotSize - organization.volumeQuota = updateDto.volumeQuota ?? organization.volumeQuota - organization.snapshotQuota = updateDto.snapshotQuota ?? organization.snapshotQuota - organization.authenticatedRateLimit = updateDto.authenticatedRateLimit ?? organization.authenticatedRateLimit - organization.sandboxCreateRateLimit = updateDto.sandboxCreateRateLimit ?? organization.sandboxCreateRateLimit - organization.sandboxLifecycleRateLimit = - updateDto.sandboxLifecycleRateLimit ?? organization.sandboxLifecycleRateLimit - organization.authenticatedRateLimitTtlSeconds = - updateDto.authenticatedRateLimitTtlSeconds ?? organization.authenticatedRateLimitTtlSeconds - organization.sandboxCreateRateLimitTtlSeconds = - updateDto.sandboxCreateRateLimitTtlSeconds ?? organization.sandboxCreateRateLimitTtlSeconds - organization.sandboxLifecycleRateLimitTtlSeconds = - updateDto.sandboxLifecycleRateLimitTtlSeconds ?? organization.sandboxLifecycleRateLimitTtlSeconds - organization.snapshotDeactivationTimeoutMinutes = - updateDto.snapshotDeactivationTimeoutMinutes ?? organization.snapshotDeactivationTimeoutMinutes - - await this.organizationRepository.save(organization) - } - - async updateRegionQuota( - organizationId: string, - regionId: string, - updateDto: UpdateOrganizationRegionQuotaDto, - ): Promise { - const regionQuota = await this.regionQuotaRepository.findOne({ where: { organizationId, regionId } }) - if (!regionQuota) { - throw new NotFoundException('Region not found') + const trimmedName = name.trim() + if (!trimmedName) { + throw new BadRequestException('Organization name is required') } - regionQuota.totalCpuQuota = updateDto.totalCpuQuota ?? regionQuota.totalCpuQuota - regionQuota.totalMemoryQuota = updateDto.totalMemoryQuota ?? regionQuota.totalMemoryQuota - regionQuota.totalDiskQuota = updateDto.totalDiskQuota ?? regionQuota.totalDiskQuota - - await this.regionQuotaRepository.save(regionQuota) - } - - async getRegionQuotas(organizationId: string): Promise { - const regionQuotas = await this.regionQuotaRepository.find({ where: { organizationId } }) - return regionQuotas.map((regionQuota) => new RegionQuotaDto(regionQuota)) - } - - async getRegionQuota(organizationId: string, regionId: string): Promise { - const regionQuota = await this.regionQuotaRepository.findOne({ where: { organizationId, regionId } }) - if (!regionQuota) { - return null - } - return new RegionQuotaDto(regionQuota) - } + organization.name = trimmedName - async getRegionQuotaBySandboxId(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, - }) - if (!sandbox) { - return null - } - return this.getRegionQuota(sandbox.organizationId, sandbox.region) + return this.organizationRepository.save(organization) } /** @@ -313,15 +277,15 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions await this.organizationRepository.save(organization) } - async updateSandboxDefaultLimitedNetworkEgress( + async updateBoxDefaultLimitedNetworkEgress( organizationId: string, - sandboxDefaultLimitedNetworkEgress: boolean, + boxDefaultLimitedNetworkEgress: boolean, ): Promise { const organization = await this.organizationRepository.findOne({ where: { id: organizationId } }) if (!organization) { throw new NotFoundException(`Organization with ID ${organizationId} not found`) } - organization.sandboxLimitedNetworkEgress = sandboxDefaultLimitedNetworkEgress + organization.boxLimitedNetworkEgress = boxDefaultLimitedNetworkEgress await this.organizationRepository.save(organization) } @@ -342,24 +306,9 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions throw new ConflictException('Organization already has a default region set') } - const defaultRegion = await this.validateOrganizationDefaultRegion(defaultRegionId) + await this.validateOrganizationDefaultRegion(defaultRegionId) organization.defaultRegionId = defaultRegionId - if (defaultRegion.enforceQuotas) { - const regionQuota = new RegionQuota( - organization.id, - defaultRegionId, - this.defaultOrganizationQuota.totalCpuQuota, - this.defaultOrganizationQuota.totalMemoryQuota, - this.defaultOrganizationQuota.totalDiskQuota, - ) - if (organization.regionQuotas) { - organization.regionQuotas = [...organization.regionQuotas, regionQuota] - } else { - organization.regionQuotas = [regionQuota] - } - } - await this.organizationRepository.save(organization) } @@ -394,8 +343,8 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions await this.organizationRepository.save(organization) } - async getOtelConfigBySandboxAuthToken(sandboxAuthToken: string): Promise { - const organization = await this.findBySandboxAuthToken(sandboxAuthToken) + async getOtelConfigByBoxAuthToken(boxAuthToken: string): Promise { + const organization = await this.findByBoxAuthToken(boxAuthToken) if (!organization) { return null } @@ -459,16 +408,18 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions createOrganizationDto: CreateOrganizationInternalDto, createdBy: string, creatorEmailVerified: boolean, - personal = false, - quota: CreateOrganizationQuotaDto = this.defaultOrganizationQuota, - sandboxLimitedNetworkEgress: boolean = this.defaultSandboxLimitedNetworkEgress, + defaultForCreator = false, + boxLimitedNetworkEgress: boolean = this.defaultBoxLimitedNetworkEgress, ): Promise { - if (personal) { - const count = await entityManager.count(Organization, { - where: { createdBy, personal: true }, + if (defaultForCreator) { + const count = await entityManager.count(OrganizationUser, { + where: { + userId: createdBy, + isDefaultForUser: true, + }, }) if (count > 0) { - throw new ForbiddenException('Personal organization already exists') + throw new ForbiddenException('Default organization already exists for user') } } @@ -484,46 +435,28 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions organization.name = createOrganizationDto.name organization.createdBy = createdBy - organization.personal = personal - - organization.maxCpuPerSandbox = quota.maxCpuPerSandbox - organization.maxMemoryPerSandbox = quota.maxMemoryPerSandbox - organization.maxDiskPerSandbox = quota.maxDiskPerSandbox - organization.snapshotQuota = quota.snapshotQuota - organization.maxSnapshotSize = quota.maxSnapshotSize - organization.volumeQuota = quota.volumeQuota if (!creatorEmailVerified && !this.configService.get('skipUserEmailVerification')) { organization.suspended = true organization.suspendedAt = new Date() organization.suspensionReason = 'Please verify your email address' - } else if (this.configService.get('billingApiUrl') && !personal) { + } else if (this.configService.get('billingApiUrl') && !defaultForCreator) { organization.suspended = true organization.suspendedAt = new Date() organization.suspensionReason = 'Payment method required' } - organization.sandboxLimitedNetworkEgress = sandboxLimitedNetworkEgress + organization.boxLimitedNetworkEgress = boxLimitedNetworkEgress const owner = new OrganizationUser() owner.userId = createdBy owner.role = OrganizationMemberRole.OWNER + owner.isDefaultForUser = defaultForCreator organization.users = [owner] if (createOrganizationDto.defaultRegionId) { - const defaultRegion = await this.validateOrganizationDefaultRegion(createOrganizationDto.defaultRegionId) - - if (defaultRegion.enforceQuotas) { - const regionQuota = new RegionQuota( - organization.id, - createOrganizationDto.defaultRegionId, - quota.totalCpuQuota, - quota.totalMemoryQuota, - quota.totalDiskQuota, - ) - organization.regionQuotas = [regionQuota] - } + await this.validateOrganizationDefaultRegion(createOrganizationDto.defaultRegionId) } await entityManager.transaction(async (em) => { @@ -540,15 +473,22 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions force = false, ): Promise { if (!force) { - if (organization.personal) { - throw new ForbiddenException('Cannot delete personal organization') + const defaultMembershipsCount = await entityManager.count(OrganizationUser, { + where: { + organizationId: organization.id, + isDefaultForUser: true, + }, + }) + + if (defaultMembershipsCount > 0) { + throw new ForbiddenException("Cannot delete an organization while it is a user's default organization") } } await entityManager.remove(organization) } - private async unsuspendPersonalWithEntityManager(entityManager: EntityManager, userId: string): Promise { - const organization = await this.findPersonalWithEntityManager(entityManager, userId) + private async unsuspendDefaultForUserWithEntityManager(entityManager: EntityManager, userId: string): Promise { + const organization = await this.findDefaultForUserWithEntityManager(entityManager, userId) organization.suspended = false organization.suspendedAt = null @@ -557,16 +497,25 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions await entityManager.save(organization) } - private async findPersonalWithEntityManager(entityManager: EntityManager, userId: string): Promise { - const organization = await entityManager.findOne(Organization, { - where: { createdBy: userId, personal: true }, + private async findDefaultForUserWithEntityManager( + entityManager: EntityManager, + userId: string, + ): Promise { + const membership = await entityManager.findOne(OrganizationUser, { + where: { + userId, + isDefaultForUser: true, + }, + relations: { + organization: true, + }, }) - if (!organization) { - throw new NotFoundException(`Personal organization for user ${userId} not found`) + if (!membership?.organization) { + throw new NotFoundException(`Default organization for user ${userId} not found`) } - return organization + return membership.organization } /** @@ -581,13 +530,13 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions return region } - @Cron(CronExpression.EVERY_MINUTE, { name: 'stop-suspended-organization-sandboxes' }) + @Cron(CronExpression.EVERY_MINUTE, { name: 'stop-suspended-organization-boxes' }) @TrackJobExecution() - @LogExecution('stop-suspended-organization-sandboxes') + @LogExecution('stop-suspended-organization-boxes') @WithInstrumentation() - async stopSuspendedOrganizationSandboxes(): Promise { + async stopSuspendedOrganizationBoxes(): Promise { // lock the sync to only run one instance at a time - const lockKey = 'stop-suspended-organization-sandboxes' + const lockKey = 'stop-suspended-organization-boxes' if (!(await this.redisLockProvider.lock(lockKey, 60))) { return } @@ -599,11 +548,11 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions .andWhere(`"suspendedAt" < NOW() - INTERVAL '1 hour' * "suspensionCleanupGracePeriodHours"`) .andWhere(`"suspendedAt" > NOW() - INTERVAL '7 day'`) .andWhereExists( - this.sandboxRepository - .createQueryBuilder('sandbox') + this.boxRepository + .createQueryBuilder('box') .select('1') .where( - `"sandbox"."organizationId" = "organization"."id" AND "sandbox"."desiredState" = '${SandboxDesiredState.STARTED}' and "sandbox"."state" NOT IN ('${SandboxState.ERROR}', '${SandboxState.BUILD_FAILED}')`, + `"box"."organizationId" = "organization"."id" AND "box"."desiredState" = '${BoxDesiredState.STARTED}' and "box"."state" NOT IN ('${BoxState.ERROR}')`, ), ) .take(100) @@ -617,80 +566,26 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions return } - const sandboxes = await this.sandboxRepository.find({ + const boxes = await this.boxRepository.find({ where: { organizationId: In(suspendedOrganizationIds), - desiredState: SandboxDesiredState.STARTED, - state: Not(In([SandboxState.ERROR, SandboxState.BUILD_FAILED])), + desiredState: BoxDesiredState.STARTED, + state: Not(In([BoxState.ERROR])), }, }) - sandboxes.map((sandbox) => + boxes.map((box) => this.eventEmitter.emitAsync( - OrganizationEvents.SUSPENDED_SANDBOX_STOPPED, - new OrganizationSuspendedSandboxStoppedEvent(sandbox.id), + OrganizationEvents.SUSPENDED_BOX_STOPPED, + new OrganizationSuspendedBoxStoppedEvent(box.id), ), ) await this.redisLockProvider.unlock(lockKey) } - @Cron(CronExpression.EVERY_MINUTE, { name: 'deactivate-suspended-organization-snapshots' }) - @TrackJobExecution() - @LogExecution('deactivate-suspended-organization-snapshots') - @WithInstrumentation() - async deactivateSuspendedOrganizationSnapshots(): Promise { - // lock the sync to only run one instance at a time - const lockKey = 'deactivate-suspended-organization-snapshots' - if (!(await this.redisLockProvider.lock(lockKey, 60))) { - return - } - - const queryResult = await this.organizationRepository - .createQueryBuilder('organization') - .select('id') - .where('suspended = true') - .andWhere(`"suspendedAt" < NOW() - INTERVAL '1 hour' * "suspensionCleanupGracePeriodHours"`) - .andWhere(`"suspendedAt" > NOW() - INTERVAL '7 day'`) - .andWhereExists( - this.snapshotRepository - .createQueryBuilder('snapshot') - .select('1') - .where('snapshot.organizationId = organization.id') - .andWhere(`snapshot.state = '${SnapshotState.ACTIVE}'`) - .andWhere(`snapshot.general = false`), - ) - .take(100) - .getRawMany() - - const suspendedOrganizationIds = queryResult.map((result) => result.id) - - // Skip if no suspended organizations found to avoid empty IN clause - if (suspendedOrganizationIds.length === 0) { - await this.redisLockProvider.unlock(lockKey) - return - } - - const snapshotQueryResult = await this.snapshotRepository - .createQueryBuilder('snapshot') - .select('id') - .where('snapshot.organizationId IN (:...suspendedOrgIds)', { suspendedOrgIds: suspendedOrganizationIds }) - .andWhere(`snapshot.state = '${SnapshotState.ACTIVE}'`) - .andWhere(`snapshot.general = false`) - .take(100) - .getRawMany() - - const snapshotIds = snapshotQueryResult.map((result) => result.id) - - snapshotIds.map((id) => - this.eventEmitter.emitAsync( - OrganizationEvents.SUSPENDED_SNAPSHOT_DEACTIVATED, - new OrganizationSuspendedSnapshotDeactivatedEvent(id), - ), - ) - - await this.redisLockProvider.unlock(lockKey) - } + // TODO(image-rewrite): deactivateSuspendedOrganizationTemplates cron removed with box_template; + // rebuild suspended-org template cleanup once the image/template model lands. @OnAsyncEvent({ event: UserEvents.CREATED, @@ -700,13 +595,12 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions return this.createWithEntityManager( payload.entityManager, { - name: 'Personal', - defaultRegionId: payload.personalOrganizationDefaultRegionId, + name: OrganizationService.DEFAULT_ORGANIZATION_NAME, + defaultRegionId: payload.defaultOrganizationDefaultRegionId, }, payload.user.id, payload.user.role === SystemRole.ADMIN ? true : payload.user.emailVerified, true, - payload.personalOrganizationQuota, payload.user.role === SystemRole.ADMIN ? false : undefined, ) } @@ -716,7 +610,7 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions }) @TrackJobExecution() async handleUserEmailVerifiedEvent(payload: UserEmailVerifiedEvent): Promise { - await this.unsuspendPersonalWithEntityManager(payload.entityManager, payload.userId) + await this.unsuspendDefaultForUserWithEntityManager(payload.entityManager, payload.userId) } @OnAsyncEvent({ @@ -724,9 +618,57 @@ export class OrganizationService implements OnModuleInit, TrackableJobExecutions }) @TrackJobExecution() async handleUserDeletedEvent(payload: UserDeletedEvent): Promise { - const organization = await this.findPersonalWithEntityManager(payload.entityManager, payload.userId) + const organization = await this.findDefaultForUserWithEntityManager(payload.entityManager, payload.userId) + const membersCount = await payload.entityManager.count(OrganizationUser, { + where: { + organizationId: organization.id, + }, + }) + + if (membersCount <= 1) { + await this.removeWithEntityManager(payload.entityManager, organization, true) + return + } + + const deletedUserMembership = await payload.entityManager.findOne(OrganizationUser, { + where: { + organizationId: organization.id, + userId: payload.userId, + }, + }) + + if (!deletedUserMembership) { + return + } + + if (deletedUserMembership.role === OrganizationMemberRole.OWNER) { + const otherOwnersCount = await payload.entityManager.count(OrganizationUser, { + where: { + organizationId: organization.id, + role: OrganizationMemberRole.OWNER, + userId: Not(payload.userId), + }, + }) + + if (otherOwnersCount === 0) { + const fallbackOwner = await payload.entityManager.findOne(OrganizationUser, { + where: { + organizationId: organization.id, + userId: Not(payload.userId), + }, + order: { + createdAt: 'ASC', + }, + }) + + if (fallbackOwner) { + fallbackOwner.role = OrganizationMemberRole.OWNER + await payload.entityManager.save(fallbackOwner) + } + } + } - await this.removeWithEntityManager(payload.entityManager, organization, true) + await payload.entityManager.remove(deletedUserMembership) } assertOrganizationIsNotSuspended(organization: Organization): void { diff --git a/apps/api/src/region/constants/region-events.constant.ts b/apps/api/src/region/constants/region-events.constant.ts index c7bdc44dd..80c5c2ef0 100644 --- a/apps/api/src/region/constants/region-events.constant.ts +++ b/apps/api/src/region/constants/region-events.constant.ts @@ -7,6 +7,4 @@ export const RegionEvents = { CREATED: 'region.created', DELETED: 'region.deleted', - SNAPSHOT_MANAGER_CREDENTIALS_REGENERATED: 'region.snapshot-manager-credentials-regenerated', - SNAPSHOT_MANAGER_UPDATED: 'region.snapshot-manager-updated', } as const diff --git a/apps/api/src/region/dto/create-region-internal.dto.ts b/apps/api/src/region/dto/create-region-internal.dto.ts index 738556fb9..a0ef4e489 100644 --- a/apps/api/src/region/dto/create-region-internal.dto.ts +++ b/apps/api/src/region/dto/create-region-internal.dto.ts @@ -13,5 +13,4 @@ export class CreateRegionInternalDto { regionType: RegionType proxyUrl?: string | null sshGatewayUrl?: string | null - snapshotManagerUrl?: string | null } diff --git a/apps/api/src/region/dto/create-region.dto.ts b/apps/api/src/region/dto/create-region.dto.ts index 84fe35fe9..64e0297ff 100644 --- a/apps/api/src/region/dto/create-region.dto.ts +++ b/apps/api/src/region/dto/create-region.dto.ts @@ -32,14 +32,6 @@ export class CreateRegionDto { required: false, }) sshGatewayUrl?: string - - @ApiProperty({ - description: 'Snapshot Manager URL for the region', - example: 'https://snapshot-manager.example.com', - nullable: true, - required: false, - }) - snapshotManagerUrl?: string } @ApiSchema({ name: 'CreateRegionResponse' }) @@ -68,32 +60,9 @@ export class CreateRegionResponseDto { }) sshGatewayApiKey?: string - @ApiProperty({ - description: 'Snapshot Manager username for the region', - example: 'boxlite', - nullable: true, - required: false, - }) - snapshotManagerUsername?: string - - @ApiProperty({ - description: 'Snapshot Manager password for the region', - nullable: true, - required: false, - }) - snapshotManagerPassword?: string - - constructor(params: { - id: string - proxyApiKey?: string - sshGatewayApiKey?: string - snapshotManagerUsername?: string - snapshotManagerPassword?: string - }) { + constructor(params: { id: string; proxyApiKey?: string; sshGatewayApiKey?: string }) { this.id = params.id this.proxyApiKey = params.proxyApiKey this.sshGatewayApiKey = params.sshGatewayApiKey - this.snapshotManagerUsername = params.snapshotManagerUsername - this.snapshotManagerPassword = params.snapshotManagerPassword } } diff --git a/apps/api/src/region/dto/region.dto.ts b/apps/api/src/region/dto/region.dto.ts index 81990329a..755c2d7a5 100644 --- a/apps/api/src/region/dto/region.dto.ts +++ b/apps/api/src/region/dto/region.dto.ts @@ -68,14 +68,6 @@ export class RegionDto { }) sshGatewayUrl?: string | null - @ApiProperty({ - description: 'Snapshot Manager URL for the region', - example: 'http://snapshot-manager.example.com', - nullable: true, - required: false, - }) - snapshotManagerUrl?: string | null - static fromRegion(region: Region): RegionDto { return { id: region.id, @@ -86,7 +78,6 @@ export class RegionDto { updatedAt: region.updatedAt?.toISOString(), proxyUrl: region.proxyUrl, sshGatewayUrl: region.sshGatewayUrl, - snapshotManagerUrl: region.snapshotManagerUrl, } } } diff --git a/apps/api/src/region/dto/snapshot-manager-credentials.dto.ts b/apps/api/src/region/dto/snapshot-manager-credentials.dto.ts deleted file mode 100644 index 6d68a5806..000000000 --- a/apps/api/src/region/dto/snapshot-manager-credentials.dto.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiSchema, ApiProperty } from '@nestjs/swagger' -import { IsString, IsNotEmpty } from 'class-validator' - -@ApiSchema({ name: 'SnapshotManagerCredentials' }) -export class SnapshotManagerCredentialsDto { - @ApiProperty({ - description: 'Snapshot Manager username for the region', - example: 'boxlite', - }) - @IsString() - @IsNotEmpty() - username: string - - @ApiProperty({ - description: 'Snapshot Manager password for the region', - }) - @IsString() - @IsNotEmpty() - password: string - - constructor(params: { username: string; password: string }) { - this.username = params.username - this.password = params.password - } -} diff --git a/apps/api/src/region/dto/update-region.dto.ts b/apps/api/src/region/dto/update-region.dto.ts index 8ccdcade1..6ccae03c7 100644 --- a/apps/api/src/region/dto/update-region.dto.ts +++ b/apps/api/src/region/dto/update-region.dto.ts @@ -23,12 +23,4 @@ export class UpdateRegionDto { required: false, }) sshGatewayUrl?: string - - @ApiProperty({ - description: 'Snapshot Manager URL for the region', - example: 'https://snapshot-manager.example.com', - nullable: true, - required: false, - }) - snapshotManagerUrl?: string } diff --git a/apps/api/src/region/entities/region.entity.ts b/apps/api/src/region/entities/region.entity.ts index 0b6bb2c1e..c4c723396 100644 --- a/apps/api/src/region/entities/region.entity.ts +++ b/apps/api/src/region/entities/region.entity.ts @@ -90,9 +90,6 @@ export class Region { @Column({ nullable: true }) sshGatewayApiKeyHash: string | null - @Column({ nullable: true }) - snapshotManagerUrl: string | null - constructor(params: { name: string enforceQuotas: boolean @@ -104,7 +101,6 @@ export class Region { sshGatewayUrl?: string | null proxyApiKeyHash?: string | null sshGatewayApiKeyHash?: string | null - snapshotManagerUrl?: string | null }) { this.name = params.name this.enforceQuotas = params.enforceQuotas @@ -124,7 +120,6 @@ export class Region { this.sshGatewayUrl = params.sshGatewayUrl ?? null this.proxyApiKeyHash = params.proxyApiKeyHash ?? null this.sshGatewayApiKeyHash = params.sshGatewayApiKeyHash ?? null - this.snapshotManagerUrl = params.snapshotManagerUrl ?? null } @BeforeInsert() diff --git a/apps/api/src/region/events/region-created.event.ts b/apps/api/src/region/events/region-created.event.ts index 867ca439e..72775ec51 100644 --- a/apps/api/src/region/events/region-created.event.ts +++ b/apps/api/src/region/events/region-created.event.ts @@ -12,7 +12,5 @@ export class RegionCreatedEvent { public readonly entityManager: EntityManager, public readonly region: Region, public readonly organizationId: string | null, - public readonly snapshotManagerUsername?: string, - public readonly snapshotManagerPassword?: string, ) {} } diff --git a/apps/api/src/region/events/region-snapshot-manager-creds.event.ts b/apps/api/src/region/events/region-snapshot-manager-creds.event.ts deleted file mode 100644 index 06e8839d9..000000000 --- a/apps/api/src/region/events/region-snapshot-manager-creds.event.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { EntityManager } from 'typeorm' -import { Region } from '../entities/region.entity' - -export class RegionSnapshotManagerCredsRegeneratedEvent { - constructor( - public readonly regionId: string, - public readonly snapshotManagerUrl: string, - public readonly username: string, - public readonly password: string, - public readonly entityManager?: EntityManager, - ) {} -} - -export class RegionSnapshotManagerUpdatedEvent { - constructor( - public readonly region: Region, - public readonly organizationId: string, - public readonly snapshotManagerUrl: string | null, - public readonly prevSnapshotManagerUrl: string | null, - public readonly newUsername?: string, - public readonly newPassword?: string, - public readonly entityManager?: EntityManager, - ) {} -} diff --git a/apps/api/src/region/region.module.ts b/apps/api/src/region/region.module.ts index 0672814fb..c0e86490d 100644 --- a/apps/api/src/region/region.module.ts +++ b/apps/api/src/region/region.module.ts @@ -8,12 +8,11 @@ import { Module } from '@nestjs/common' import { TypeOrmModule } from '@nestjs/typeorm' import { Region } from './entities/region.entity' import { RegionService } from './services/region.service' -import { Runner } from '../sandbox/entities/runner.entity' +import { Runner } from '../box/entities/runner.entity' import { RegionController } from './controllers/region.controller' -import { Snapshot } from '../sandbox/entities/snapshot.entity' @Module({ - imports: [TypeOrmModule.forFeature([Region, Runner, Snapshot])], + imports: [TypeOrmModule.forFeature([Region, Runner])], controllers: [RegionController], providers: [RegionService], exports: [RegionService], diff --git a/apps/api/src/region/services/region.service.ts b/apps/api/src/region/services/region.service.ts index 59ca732af..d1269e4c6 100644 --- a/apps/api/src/region/services/region.service.ts +++ b/apps/api/src/region/services/region.service.ts @@ -13,29 +13,24 @@ import { HttpStatus, } from '@nestjs/common' import { InjectRepository } from '@nestjs/typeorm' -import { DataSource, In, IsNull, Like, Repository } from 'typeorm' +import { DataSource, In, IsNull, Repository } from 'typeorm' import { REGION_NAME_REGEX } from '../constants/region-name-regex.constant' import { CreateRegionInternalDto } from '../dto/create-region-internal.dto' import { Region } from '../entities/region.entity' -import { Runner } from '../../sandbox/entities/runner.entity' +import { Runner } from '../../box/entities/runner.entity' import { RegionType } from '../enums/region-type.enum' import { CreateRegionResponseDto } from '../dto/create-region.dto' -import { generateApiKeyHash, generateApiKeyValue, generateRandomString } from '../../common/utils/api-key' +import { generateApiKeyHash, generateApiKeyValue } from '../../common/utils/api-key' +import { TypedConfigService } from '../../config/typed-config.service' import { RegionDto } from '../dto/region.dto' import { EventEmitter2 } from '@nestjs/event-emitter' import { RegionEvents } from '../constants/region-events.constant' import { RegionCreatedEvent } from '../events/region-created.event' import { RegionDeletedEvent } from '../events/region-deleted.event' -import { SnapshotManagerCredentialsDto } from '../dto/snapshot-manager-credentials.dto' -import { - RegionSnapshotManagerCredsRegeneratedEvent, - RegionSnapshotManagerUpdatedEvent, -} from '../events/region-snapshot-manager-creds.event' import { UpdateRegionDto } from '../dto/update-region.dto' -import { Snapshot } from '../../sandbox/entities/snapshot.entity' import { InjectRedis } from '@nestjs-modules/ioredis' import { Redis } from 'ioredis' -import { toolboxProxyUrlCacheKey } from '../../sandbox/utils/sandbox-lookup-cache.util' +import { toolboxProxyUrlCacheKey } from '../../box/utils/box-lookup-cache.util' @Injectable() export class RegionService { @@ -48,9 +43,8 @@ export class RegionService { private readonly runnerRepository: Repository, private readonly dataSource: DataSource, private readonly eventEmitter: EventEmitter2, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, @InjectRedis() private readonly redis: Redis, + private readonly configService: TypedConfigService, ) {} /** @@ -78,11 +72,12 @@ export class RegionService { } try { - const proxyApiKey = createRegionDto.proxyUrl ? generateApiKeyValue() : undefined - const sshGatewayApiKey = createRegionDto.sshGatewayUrl ? generateApiKeyValue() : undefined - - const snapshotManagerUsername = createRegionDto.snapshotManagerUrl ? 'boxlite' : undefined - const snapshotManagerPassword = createRegionDto.snapshotManagerUrl ? generateRandomString(16) : undefined + const proxyApiKey = createRegionDto.proxyUrl + ? generateApiKeyValue(this.configService.getOrThrow('apiKey.prefix'), 'svc') + : undefined + const sshGatewayApiKey = createRegionDto.sshGatewayUrl + ? generateApiKeyValue(this.configService.getOrThrow('apiKey.prefix'), 'svc') + : undefined const region = new Region({ name: createRegionDto.name, @@ -94,23 +89,17 @@ export class RegionService { sshGatewayUrl: createRegionDto.sshGatewayUrl, proxyApiKeyHash: proxyApiKey ? generateApiKeyHash(proxyApiKey) : null, sshGatewayApiKeyHash: sshGatewayApiKey ? generateApiKeyHash(sshGatewayApiKey) : null, - snapshotManagerUrl: createRegionDto.snapshotManagerUrl, }) await this.dataSource.transaction(async (em) => { await em.save(region) - await this.eventEmitter.emitAsync( - RegionEvents.CREATED, - new RegionCreatedEvent(em, region, organizationId, snapshotManagerUsername, snapshotManagerPassword), - ) + await this.eventEmitter.emitAsync(RegionEvents.CREATED, new RegionCreatedEvent(em, region, organizationId)) }) return new CreateRegionResponseDto({ id: region.id, proxyApiKey, sshGatewayApiKey, - snapshotManagerUsername, - snapshotManagerPassword, }) } catch (error) { if (error.code === '23505') { @@ -290,47 +279,6 @@ export class RegionService { region.sshGatewayUrl = updateRegion.sshGatewayUrl ?? null } - if (updateRegion.snapshotManagerUrl !== undefined) { - if (region.snapshotManagerUrl) { - // If snapshots already exist, prevent changing the snapshot manager URL - const exists = await this.snapshotRepository.exists({ - where: { - ref: Like(`${region.snapshotManagerUrl.replace(/^https?:\/\//, '')}%`), - }, - }) - if (exists) { - throw new BadRequestException( - 'Cannot change snapshot manager URL for region with existing snapshots. Please delete existing snapshots first.', - ) - } - } - - const prevSnapshotManagerUrl = region.snapshotManagerUrl - region.snapshotManagerUrl = updateRegion.snapshotManagerUrl ?? null - - let newUsername: string | undefined = undefined - let newPassword: string | undefined = undefined - - // If the region did not have a snapshot manager, create new credentials - if (!prevSnapshotManagerUrl) { - newUsername = 'boxlite' - newPassword = generateRandomString(16) - } - - await this.eventEmitter.emitAsync( - RegionEvents.SNAPSHOT_MANAGER_UPDATED, - new RegionSnapshotManagerUpdatedEvent( - region, - region.organizationId, - region.snapshotManagerUrl, - prevSnapshotManagerUrl, - newUsername, - newPassword, - em, - ), - ) - } - await em.save(region) }) @@ -358,7 +306,7 @@ export class RegionService { throw new BadRequestException('Region does not have a proxy URL configured') } - const newApiKey = generateApiKeyValue() + const newApiKey = generateApiKeyValue(this.configService.getOrThrow('apiKey.prefix'), 'svc') region.proxyApiKeyHash = generateApiKeyHash(newApiKey) await this.regionRepository.save(region) @@ -383,42 +331,11 @@ export class RegionService { throw new BadRequestException('Region does not have an SSH gateway URL configured') } - const newApiKey = generateApiKeyValue() + const newApiKey = generateApiKeyValue(this.configService.getOrThrow('apiKey.prefix'), 'svc') region.sshGatewayApiKeyHash = generateApiKeyHash(newApiKey) await this.regionRepository.save(region) return newApiKey } - - /** - * @param regionId - The ID of the region. - * @throws {NotFoundException} If the region is not found. - * @throws {BadRequestException} If the region does not have a snapshot manager URL configured. - * @returns The newly generated snapshot manager credentials. - */ - async regenerateSnapshotManagerCredentials(regionId: string): Promise { - const region = await this.findOne(regionId) - - if (!region) { - throw new NotFoundException('Region not found') - } - - if (!region.snapshotManagerUrl) { - throw new BadRequestException('Region does not have a snapshot manager URL configured') - } - - const newUsername = 'boxlite' - const newPassword = generateRandomString(16) - - await this.eventEmitter.emitAsync( - RegionEvents.SNAPSHOT_MANAGER_CREDENTIALS_REGENERATED, - new RegionSnapshotManagerCredsRegeneratedEvent(regionId, region.snapshotManagerUrl, newUsername, newPassword), - ) - - return new SnapshotManagerCredentialsDto({ - username: newUsername, - password: newPassword, - }) - } } diff --git a/apps/api/src/sandbox-telemetry/controllers/sandbox-telemetry.controller.ts b/apps/api/src/sandbox-telemetry/controllers/sandbox-telemetry.controller.ts deleted file mode 100644 index 4238ea288..000000000 --- a/apps/api/src/sandbox-telemetry/controllers/sandbox-telemetry.controller.ts +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Controller, Get, Param, Query, UseGuards } from '@nestjs/common' -import { ApiOAuth2, ApiResponse, ApiOperation, ApiParam, ApiTags, ApiHeader, ApiBearerAuth } from '@nestjs/swagger' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' -import { SandboxAccessGuard } from '../../sandbox/guards/sandbox-access.guard' -import { CustomHeaders } from '../../common/constants/header.constants' -import { SandboxTelemetryService } from '../services/sandbox-telemetry.service' -import { LogsQueryParamsDto, TelemetryQueryParamsDto, MetricsQueryParamsDto } from '../dto/telemetry-query-params.dto' -import { PaginatedLogsDto } from '../dto/paginated-logs.dto' -import { PaginatedTracesDto } from '../dto/paginated-traces.dto' -import { TraceSpanDto } from '../dto/trace-span.dto' -import { MetricsResponseDto } from '../dto/metrics-response.dto' -import { RequireFlagsEnabled } from '@openfeature/nestjs-sdk' -import { AnalyticsApiDisabledGuard } from '../guards/analytics-api-disabled.guard' - -@ApiTags('sandbox') -@Controller('sandbox') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard, AnalyticsApiDisabledGuard) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class SandboxTelemetryController { - constructor(private readonly sandboxTelemetryService: SandboxTelemetryService) {} - - @Get(':sandboxId/telemetry/logs') - @ApiOperation({ - summary: 'Get sandbox logs', - operationId: 'getSandboxLogs', - description: 'Retrieve OTEL logs for a sandbox within a time range', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Paginated list of log entries', - type: PaginatedLogsDto, - }) - @UseGuards(SandboxAccessGuard) - @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) - async getSandboxLogs( - @Param('sandboxId') sandboxId: string, - @Query() queryParams: LogsQueryParamsDto, - ): Promise { - return this.sandboxTelemetryService.getLogs( - sandboxId, - queryParams.from, - queryParams.to, - queryParams.page ?? 1, - queryParams.limit ?? 100, - queryParams.severities, - queryParams.search, - ) - } - - @Get(':sandboxId/telemetry/traces') - @ApiOperation({ - summary: 'Get sandbox traces', - operationId: 'getSandboxTraces', - description: 'Retrieve OTEL traces for a sandbox within a time range', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Paginated list of trace summaries', - type: PaginatedTracesDto, - }) - @UseGuards(SandboxAccessGuard) - @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) - async getSandboxTraces( - @Param('sandboxId') sandboxId: string, - @Query() queryParams: TelemetryQueryParamsDto, - ): Promise { - return this.sandboxTelemetryService.getTraces( - sandboxId, - queryParams.from, - queryParams.to, - queryParams.page ?? 1, - queryParams.limit ?? 100, - ) - } - - @Get(':sandboxId/telemetry/traces/:traceId') - @ApiOperation({ - summary: 'Get trace spans', - operationId: 'getSandboxTraceSpans', - description: 'Retrieve all spans for a specific trace', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'traceId', - description: 'ID of the trace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'List of spans in the trace', - type: [TraceSpanDto], - }) - @UseGuards(SandboxAccessGuard) - @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) - async getSandboxTraceSpans( - @Param('sandboxId') sandboxId: string, - @Param('traceId') traceId: string, - ): Promise { - return this.sandboxTelemetryService.getTraceSpans(sandboxId, traceId) - } - - @Get(':sandboxId/telemetry/metrics') - @ApiOperation({ - summary: 'Get sandbox metrics', - operationId: 'getSandboxMetrics', - description: 'Retrieve OTEL metrics for a sandbox within a time range', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Metrics time series data', - type: MetricsResponseDto, - }) - @UseGuards(SandboxAccessGuard) - @RequireFlagsEnabled({ flags: [{ flagKey: 'organization_experiments', defaultValue: true }] }) - async getSandboxMetrics( - @Param('sandboxId') sandboxId: string, - @Query() queryParams: MetricsQueryParamsDto, - ): Promise { - return this.sandboxTelemetryService.getMetrics(sandboxId, queryParams.from, queryParams.to, queryParams.metricNames) - } -} diff --git a/apps/api/src/sandbox-telemetry/index.ts b/apps/api/src/sandbox-telemetry/index.ts deleted file mode 100644 index 1670fae21..000000000 --- a/apps/api/src/sandbox-telemetry/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -export * from './sandbox-telemetry.module' -export * from './services/sandbox-telemetry.service' -export * from './dto' diff --git a/apps/api/src/sandbox-telemetry/sandbox-telemetry.module.ts b/apps/api/src/sandbox-telemetry/sandbox-telemetry.module.ts deleted file mode 100644 index 4369af680..000000000 --- a/apps/api/src/sandbox-telemetry/sandbox-telemetry.module.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Module } from '@nestjs/common' -import { SandboxTelemetryController } from './controllers/sandbox-telemetry.controller' -import { SandboxTelemetryService } from './services/sandbox-telemetry.service' -import { SandboxModule } from '../sandbox/sandbox.module' -import { OrganizationModule } from '../organization/organization.module' - -@Module({ - imports: [SandboxModule, OrganizationModule], - controllers: [SandboxTelemetryController], - providers: [SandboxTelemetryService], - exports: [SandboxTelemetryService], -}) -export class SandboxTelemetryModule {} diff --git a/apps/api/src/sandbox-telemetry/services/sandbox-telemetry.service.ts b/apps/api/src/sandbox-telemetry/services/sandbox-telemetry.service.ts deleted file mode 100644 index f7fb4ebd2..000000000 --- a/apps/api/src/sandbox-telemetry/services/sandbox-telemetry.service.ts +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { ClickHouseService } from '../../clickhouse/clickhouse.service' -import { LogEntryDto } from '../dto/log-entry.dto' -import { PaginatedLogsDto } from '../dto/paginated-logs.dto' -import { TraceSummaryDto } from '../dto/trace-summary.dto' -import { TraceSpanDto } from '../dto/trace-span.dto' -import { PaginatedTracesDto } from '../dto/paginated-traces.dto' -import { MetricsResponseDto, MetricSeriesDto, MetricDataPointDto } from '../dto/metrics-response.dto' - -interface ClickHouseLogRow { - Timestamp: string - Body: string - SeverityText: string - SeverityNumber: number - ServiceName: string - ResourceAttributes: Record - LogAttributes: Record - TraceId: string - SpanId: string -} - -interface ClickHouseTraceAggregateRow { - TraceId: string - startTime: string - endTime: string - spanCount: number - rootSpanName: string - totalDuration: number - statusCode: string -} - -interface ClickHouseSpanRow { - TraceId: string - SpanId: string - ParentSpanId: string - SpanName: string - Timestamp: string - Duration: number - SpanAttributes: Record - StatusCode: string - StatusMessage: string -} - -interface ClickHouseMetricRow { - timestamp: string - MetricName: string - value: number -} - -interface ClickHouseCountRow { - count: number -} - -@Injectable() -export class SandboxTelemetryService { - private readonly logger = new Logger(SandboxTelemetryService.name) - - constructor(private readonly clickhouseService: ClickHouseService) {} - - private getServiceName(sandboxId: string): string { - return `sandbox-${sandboxId}` - } - - isConfigured(): boolean { - return this.clickhouseService.isConfigured() - } - - async getLogs( - sandboxId: string, - from: string, - to: string, - page: number, - limit: number, - severities?: string[], - search?: string, - ): Promise { - const serviceName = this.getServiceName(sandboxId) - const offset = (page - 1) * limit - - // Build WHERE clause for optional filters - let whereClause = `ServiceName = {serviceName:String} - AND Timestamp >= {from:DateTime64} - AND Timestamp <= {to:DateTime64}` - - if (severities && severities.length > 0) { - whereClause += ` AND SeverityText IN ({severities:Array(String)})` - } - - if (search) { - whereClause += ` AND Body ILIKE {search:String}` - } - - const params: Record = { - serviceName, - from: new Date(from), - to: new Date(to), - limit, - offset, - } - - if (severities && severities.length > 0) { - params.severities = severities - } - - if (search) { - params.search = `%${search}%` - } - - // Get total count - const countQuery = ` - SELECT count() as count - FROM otel_logs - WHERE ${whereClause} - ` - const countResult = await this.clickhouseService.query(countQuery, params) - const total = countResult[0]?.count || 0 - - // Get paginated logs - const logsQuery = ` - SELECT Timestamp, Body, SeverityText, SeverityNumber, ServiceName, - ResourceAttributes, LogAttributes, TraceId, SpanId - FROM otel_logs - WHERE ${whereClause} - ORDER BY Timestamp DESC - LIMIT {limit:UInt32} OFFSET {offset:UInt32} - ` - const rows = await this.clickhouseService.query(logsQuery, params) - - const items: LogEntryDto[] = rows.map((row) => ({ - timestamp: row.Timestamp, - body: row.Body, - severityText: row.SeverityText, - severityNumber: row.SeverityNumber, - serviceName: row.ServiceName, - resourceAttributes: row.ResourceAttributes || {}, - logAttributes: row.LogAttributes || {}, - traceId: row.TraceId || undefined, - spanId: row.SpanId || undefined, - })) - - return { - items, - total, - page, - totalPages: Math.ceil(total / limit), - } - } - - async getTraces( - sandboxId: string, - from: string, - to: string, - page: number, - limit: number, - ): Promise { - const serviceName = this.getServiceName(sandboxId) - const offset = (page - 1) * limit - - const params = { - serviceName, - from: new Date(from), - to: new Date(to), - limit, - offset, - } - - // Get total count of unique traces - const countQuery = ` - SELECT count(DISTINCT TraceId) as count - FROM otel_traces - WHERE ServiceName = {serviceName:String} - AND Timestamp >= {from:DateTime64} - AND Timestamp <= {to:DateTime64} - ` - const countResult = await this.clickhouseService.query(countQuery, params) - const total = countResult[0]?.count || 0 - - // Get aggregated trace data - const tracesQuery = ` - SELECT - TraceId, - min(Timestamp) as startTime, - max(Timestamp) as endTime, - count() as spanCount, - argMinIf(SpanName, Timestamp, ParentSpanId = '') as rootSpanName, - max(Duration) as totalDuration, - any(StatusCode) as statusCode - FROM otel_traces - WHERE ServiceName = {serviceName:String} - AND Timestamp >= {from:DateTime64} - AND Timestamp <= {to:DateTime64} - GROUP BY TraceId - ORDER BY startTime DESC - LIMIT {limit:UInt32} OFFSET {offset:UInt32} - ` - const rows = await this.clickhouseService.query(tracesQuery, params) - - const items: TraceSummaryDto[] = rows.map((row) => ({ - traceId: row.TraceId, - rootSpanName: row.rootSpanName, - startTime: row.startTime, - endTime: row.endTime, - durationMs: row.totalDuration / 1_000_000, // Convert nanoseconds to milliseconds - spanCount: row.spanCount, - statusCode: row.statusCode || undefined, - })) - - return { - items, - total, - page, - totalPages: Math.ceil(total / limit), - } - } - - async getTraceSpans(sandboxId: string, traceId: string): Promise { - const serviceName = this.getServiceName(sandboxId) - - const query = ` - SELECT TraceId, SpanId, ParentSpanId, SpanName, Timestamp, Duration, - SpanAttributes, StatusCode, StatusMessage - FROM otel_traces - WHERE TraceId = {traceId:String} - AND ServiceName = {serviceName:String} - ORDER BY Timestamp ASC - ` - - const rows = await this.clickhouseService.query(query, { traceId, serviceName }) - - return rows.map((row) => ({ - traceId: row.TraceId, - spanId: row.SpanId, - parentSpanId: row.ParentSpanId || undefined, - spanName: row.SpanName, - timestamp: row.Timestamp, - durationNs: row.Duration, - spanAttributes: row.SpanAttributes || {}, - statusCode: row.StatusCode || undefined, - statusMessage: row.StatusMessage || undefined, - })) - } - - async getMetrics(sandboxId: string, from: string, to: string, metricNames?: string[]): Promise { - const serviceName = this.getServiceName(sandboxId) - - let whereClause = `ServiceName = {serviceName:String} - AND TimeUnix >= {from:DateTime64} - AND TimeUnix <= {to:DateTime64}` - - const params: Record = { - serviceName, - from: new Date(from), - to: new Date(to), - } - - if (metricNames && metricNames.length > 0) { - whereClause += ` AND MetricName IN ({metricNames:Array(String)})` - params.metricNames = metricNames - } - - // Query gauge metrics with 1-minute intervals - const gaugeQuery = ` - SELECT - toStartOfInterval(TimeUnix, INTERVAL 1 MINUTE) as timestamp, - MetricName, - avg(Value) as value - FROM otel_metrics_gauge - WHERE ${whereClause} - GROUP BY timestamp, MetricName - ORDER BY timestamp ASC - ` - - const rows = await this.clickhouseService.query(gaugeQuery, params) - - // Group by metric name - const seriesMap = new Map() - for (const row of rows) { - if (!seriesMap.has(row.MetricName)) { - seriesMap.set(row.MetricName, []) - } - seriesMap.get(row.MetricName)!.push({ - timestamp: row.timestamp, - value: row.value, - }) - } - - const series: MetricSeriesDto[] = Array.from(seriesMap.entries()).map(([metricName, dataPoints]) => ({ - metricName, - dataPoints, - })) - - return { series } - } -} diff --git a/apps/api/src/sandbox/constants/sandbox-events.constants.ts b/apps/api/src/sandbox/constants/sandbox-events.constants.ts deleted file mode 100644 index 4a0ea58de..000000000 --- a/apps/api/src/sandbox/constants/sandbox-events.constants.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const SandboxEvents = { - ARCHIVED: 'sandbox.archived', - STATE_UPDATED: 'sandbox.state.updated', - DESIRED_STATE_UPDATED: 'sandbox.desired-state.updated', - CREATED: 'sandbox.created', - STARTED: 'sandbox.started', - STOPPED: 'sandbox.stopped', - DESTROYED: 'sandbox.destroyed', - PUBLIC_STATUS_UPDATED: 'sandbox.public-status.updated', - ORGANIZATION_UPDATED: 'sandbox.organization.updated', - BACKUP_CREATED: 'sandbox.backup.created', -} as const diff --git a/apps/api/src/sandbox/constants/sandbox.constants.ts b/apps/api/src/sandbox/constants/sandbox.constants.ts deleted file mode 100644 index ef3383bda..000000000 --- a/apps/api/src/sandbox/constants/sandbox.constants.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION = '00000000-0000-0000-0000-000000000000' diff --git a/apps/api/src/sandbox/constants/snapshot-events.ts b/apps/api/src/sandbox/constants/snapshot-events.ts deleted file mode 100644 index e9c75397f..000000000 --- a/apps/api/src/sandbox/constants/snapshot-events.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const SnapshotEvents = { - CREATED: 'snapshot.created', - ACTIVATED: 'snapshot.activated', - STATE_UPDATED: 'snapshot.state.updated', - REMOVED: 'snapshot.removed', -} as const diff --git a/apps/api/src/sandbox/controllers/preview.controller.ts b/apps/api/src/sandbox/controllers/preview.controller.ts deleted file mode 100644 index e269977bf..000000000 --- a/apps/api/src/sandbox/controllers/preview.controller.ts +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import Redis from 'ioredis' -import { Controller, Get, Param, Logger, NotFoundException, UseGuards, Req } from '@nestjs/common' -import { SandboxService } from '../services/sandbox.service' -import { ApiResponse, ApiOperation, ApiParam, ApiTags, ApiOAuth2, ApiBearerAuth } from '@nestjs/swagger' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { OrganizationUserService } from '../../organization/services/organization-user.service' - -@ApiTags('preview') -@Controller('preview') -export class PreviewController { - private readonly logger = new Logger(PreviewController.name) - - constructor( - @InjectRedis() private readonly redis: Redis, - private readonly sandboxService: SandboxService, - private readonly organizationUserService: OrganizationUserService, - ) {} - - @Get(':sandboxId/public') - @ApiOperation({ - summary: 'Check if sandbox is public', - operationId: 'isSandboxPublic', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Public status of the sandbox', - type: Boolean, - }) - async isSandboxPublic(@Param('sandboxId') sandboxId: string): Promise { - const cached = await this.redis.get(`preview:public:${sandboxId}`) - if (cached) { - if (cached === '1') { - return true - } - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - try { - const isPublic = await this.sandboxService.isSandboxPublic(sandboxId) - // for private sandboxes, throw 404 as well - // to prevent using the method to check if a sandbox exists - if (!isPublic) { - // cache the result for 3 seconds to avoid unnecessary requests to the database - await this.redis.setex(`preview:public:${sandboxId}`, 3, '0') - - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - // cache the result for 3 seconds to avoid unnecessary requests to the database - await this.redis.setex(`preview:public:${sandboxId}`, 3, '1') - return true - } catch (ex) { - if (ex instanceof NotFoundException) { - // cache the not found sandbox as well - // as it is the same case as for the private sandboxes - await this.redis.setex(`preview:public:${sandboxId}`, 3, '0') - throw ex - } - throw ex - } - } - - @Get(':sandboxId/validate/:authToken') - @ApiOperation({ - summary: 'Check if sandbox auth token is valid', - operationId: 'isValidAuthToken', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'authToken', - description: 'Auth token of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox auth token validation status', - type: Boolean, - }) - async isValidAuthToken( - @Param('sandboxId') sandboxId: string, - @Param('authToken') authToken: string, - ): Promise { - const cached = await this.redis.get(`preview:token:${sandboxId}:${authToken}`) - if (cached) { - if (cached === '1') { - return true - } - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - const sandbox = await this.sandboxService.findOne(sandboxId) - if (!sandbox) { - await this.redis.setex(`preview:token:${sandboxId}:${authToken}`, 3, '0') - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - if (sandbox.authToken === authToken) { - await this.redis.setex(`preview:token:${sandboxId}:${authToken}`, 3, '1') - return true - } - await this.redis.setex(`preview:token:${sandboxId}:${authToken}`, 3, '0') - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - @Get(':sandboxId/access') - @ApiOperation({ - summary: 'Check if user has access to the sandbox', - operationId: 'hasSandboxAccess', - }) - @ApiResponse({ - status: 200, - description: 'User access status to the sandbox', - type: Boolean, - }) - @UseGuards(CombinedAuthGuard) - @ApiOAuth2(['openid', 'profile', 'email']) - @ApiBearerAuth() - async hasSandboxAccess(@Req() req: Request, @Param('sandboxId') sandboxId: string): Promise { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const userId = req.user?.userId - - const cached = await this.redis.get(`preview:access:${sandboxId}:${userId}`) - if (cached) { - if (cached === '1') { - return true - } - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - const sandbox = await this.sandboxService.findOne(sandboxId) - const hasAccess = await this.organizationUserService.exists(sandbox.organizationId, userId) - if (!hasAccess) { - await this.redis.setex(`preview:access:${sandboxId}:${userId}`, 3, '0') - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - // if user has access, keep it in cache longer - await this.redis.setex(`preview:access:${sandboxId}:${userId}`, 30, '1') - return true - } - - @Get(':signedPreviewToken/:port/sandbox-id') - @ApiOperation({ - summary: 'Get sandbox ID from signed preview URL token', - operationId: 'getSandboxIdFromSignedPreviewUrlToken', - }) - @ApiParam({ - name: 'signedPreviewToken', - description: 'Signed preview URL token', - type: 'string', - }) - @ApiParam({ - name: 'port', - description: 'Port number to get sandbox ID from signed preview URL token', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox ID from signed preview URL token', - type: String, - }) - async getSandboxIdFromSignedPreviewUrlToken( - @Param('signedPreviewToken') signedPreviewToken: string, - @Param('port') port: number, - ): Promise { - return this.sandboxService.getSandboxIdFromSignedPreviewUrlToken(signedPreviewToken, port) - } -} diff --git a/apps/api/src/sandbox/controllers/sandbox.controller.ts b/apps/api/src/sandbox/controllers/sandbox.controller.ts deleted file mode 100644 index f6d7cc8d7..000000000 --- a/apps/api/src/sandbox/controllers/sandbox.controller.ts +++ /dev/null @@ -1,1315 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Controller, - Get, - Post, - Delete, - Body, - Param, - Query, - Logger, - UseGuards, - HttpCode, - UseInterceptors, - Put, - NotFoundException, - Res, - Request, - RawBodyRequest, - Next, - ParseBoolPipe, -} from '@nestjs/common' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { SandboxService } from '../services/sandbox.service' -import { CreateSandboxDto } from '../dto/create-sandbox.dto' -import { - ApiOAuth2, - ApiResponse, - ApiQuery, - ApiOperation, - ApiParam, - ApiTags, - ApiHeader, - ApiBearerAuth, -} from '@nestjs/swagger' -import { SandboxDto, SandboxLabelsDto } from '../dto/sandbox.dto' -import { ResizeSandboxDto } from '../dto/resize-sandbox.dto' -import { UpdateSandboxStateDto } from '../dto/update-sandbox-state.dto' -import { PaginatedSandboxesDto } from '../dto/paginated-sandboxes.dto' -import { RunnerService } from '../services/runner.service' -import { RunnerAuthGuard } from '../../auth/runner-auth.guard' -import { RunnerContextDecorator } from '../../common/decorators/runner-context.decorator' -import { RunnerContext } from '../../common/interfaces/runner-context.interface' -import { SandboxState } from '../enums/sandbox-state.enum' -import { Sandbox } from '../entities/sandbox.entity' -import { ContentTypeInterceptor } from '../../common/interceptors/content-type.interceptors' -import { SandboxAccessGuard } from '../guards/sandbox-access.guard' -import { CustomHeaders } from '../../common/constants/header.constants' -import { AuthContext } from '../../common/decorators/auth-context.decorator' -import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' -import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { PortPreviewUrlDto, SignedPortPreviewUrlDto } from '../dto/port-preview-url.dto' -import { IncomingMessage, ServerResponse } from 'http' -import { NextFunction } from 'http-proxy-middleware/dist/types' -import { LogProxy } from '../proxy/log-proxy' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { SandboxStateUpdatedEvent } from '../events/sandbox-state-updated.event' -import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../../audit/decorators/audit.decorator' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -// import { UpdateSandboxNetworkSettingsDto } from '../dto/update-sandbox-network-settings.dto' -import { SshAccessDto, SshAccessValidationDto } from '../dto/ssh-access.dto' -import { ListSandboxesQueryDto } from '../dto/list-sandboxes-query.dto' -import { ProxyGuard } from '../guards/proxy.guard' -import { OrGuard } from '../../auth/or.guard' -import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' -import { SkipThrottle } from '@nestjs/throttler' -import { ThrottlerScope } from '../../common/decorators/throttler-scope.decorator' -import { SshGatewayGuard } from '../guards/ssh-gateway.guard' -import { ToolboxProxyUrlDto } from '../dto/toolbox-proxy-url.dto' -import { UrlDto } from '../../common/dto/url.dto' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { SANDBOX_EVENT_CHANNEL } from '../../common/constants/constants' -import { RequireFlagsEnabled } from '@openfeature/nestjs-sdk' -import { FeatureFlags } from '../../common/constants/feature-flags' -import { RegionSandboxAccessGuard } from '../guards/region-sandbox-access.guard' - -@ApiTags('sandbox') -@Controller('sandbox') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class SandboxController { - private readonly logger = new Logger(SandboxController.name) - private readonly sandboxCallbacks: Map void> = new Map() - private readonly redisSubscriber: Redis - constructor( - private readonly runnerService: RunnerService, - private readonly sandboxService: SandboxService, - @InjectRedis() private readonly redis: Redis, - ) { - this.redisSubscriber = this.redis.duplicate() - this.redisSubscriber.subscribe(SANDBOX_EVENT_CHANNEL) - this.redisSubscriber.on('message', (channel, message) => { - if (channel !== SANDBOX_EVENT_CHANNEL) { - return - } - - try { - const event = JSON.parse(message) as SandboxStateUpdatedEvent - this.handleSandboxStateUpdated(event) - } catch (error) { - this.logger.error('Failed to parse sandbox state updated event:', error) - return - } - }) - } - - @Get() - @ApiOperation({ - summary: 'List all sandboxes', - operationId: 'listSandboxes', - }) - @ApiResponse({ - status: 200, - description: 'List of all sandboxes', - type: [SandboxDto], - }) - @ApiQuery({ - name: 'verbose', - required: false, - type: Boolean, - description: 'Include verbose output', - }) - @ApiQuery({ - name: 'labels', - type: String, - required: false, - example: '{"label1": "value1", "label2": "value2"}', - description: 'JSON encoded labels to filter by', - }) - @ApiQuery({ - name: 'includeErroredDeleted', - required: false, - type: Boolean, - description: 'Include errored and deleted sandboxes', - }) - async listSandboxes( - @AuthContext() authContext: OrganizationAuthContext, - @Query('verbose') verbose?: boolean, - @Query('labels') labelsQuery?: string, - @Query('includeErroredDeleted') includeErroredDeleted?: boolean, - ): Promise { - const labels = labelsQuery ? JSON.parse(labelsQuery) : undefined - const sandboxes = await this.sandboxService.findAllDeprecated( - authContext.organizationId, - labels, - includeErroredDeleted, - ) - - return this.sandboxService.toSandboxDtos(sandboxes) - } - - @Get('paginated') - @ApiOperation({ - summary: 'List all sandboxes paginated', - operationId: 'listSandboxesPaginated', - }) - @ApiResponse({ - status: 200, - description: 'Paginated list of all sandboxes', - type: PaginatedSandboxesDto, - }) - async listSandboxesPaginated( - @AuthContext() authContext: OrganizationAuthContext, - @Query() queryParams: ListSandboxesQueryDto, - ): Promise { - const { - page, - limit, - id, - name, - labels, - includeErroredDeleted: includeErroredDestroyed, - states, - snapshots, - regions, - minCpu, - maxCpu, - minMemoryGiB, - maxMemoryGiB, - minDiskGiB, - maxDiskGiB, - lastEventAfter, - lastEventBefore, - sort: sortField, - order: sortDirection, - } = queryParams - - const result = await this.sandboxService.findAll( - authContext.organizationId, - page, - limit, - { - id, - name, - labels: labels ? JSON.parse(labels) : undefined, - includeErroredDestroyed, - states, - snapshots, - regionIds: regions, - minCpu, - maxCpu, - minMemoryGiB, - maxMemoryGiB, - minDiskGiB, - maxDiskGiB, - lastEventAfter, - lastEventBefore, - }, - { - field: sortField, - direction: sortDirection, - }, - ) - - return { - items: await this.sandboxService.toSandboxDtos(result.items), - total: result.total, - page: result.page, - totalPages: result.totalPages, - } - } - - @Post() - @HttpCode(200) // for BoxLite Api compatibility - @UseInterceptors(ContentTypeInterceptor) - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-create') - @ApiOperation({ - summary: 'Create a new sandbox', - operationId: 'createSandbox', - }) - @ApiResponse({ - status: 200, - description: 'The sandbox has been successfully created.', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @Audit({ - action: AuditAction.CREATE, - targetType: AuditTarget.SANDBOX, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - name: req.body?.name, - snapshot: req.body?.snapshot, - user: req.body?.user, - env: req.body?.env - ? Object.fromEntries(Object.keys(req.body?.env).map((key) => [key, MASKED_AUDIT_VALUE])) - : undefined, - labels: req.body?.labels, - public: req.body?.public, - class: req.body?.class, - target: req.body?.target, - cpu: req.body?.cpu, - gpu: req.body?.gpu, - memory: req.body?.memory, - disk: req.body?.disk, - autoStopInterval: req.body?.autoStopInterval, - autoArchiveInterval: req.body?.autoArchiveInterval, - autoDeleteInterval: req.body?.autoDeleteInterval, - volumes: req.body?.volumes, - buildInfo: req.body?.buildInfo, - networkBlockAll: req.body?.networkBlockAll, - networkAllowList: req.body?.networkAllowList, - }), - }, - }) - async createSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Body() createSandboxDto: CreateSandboxDto, - ): Promise { - const organization = authContext.organization - let sandbox: SandboxDto - - if (createSandboxDto.buildInfo) { - if (createSandboxDto.snapshot) { - throw new BadRequestError('Cannot specify a snapshot when using a build info entry') - } - sandbox = await this.sandboxService.createFromBuildInfo(createSandboxDto, organization) - } else { - if (createSandboxDto.cpu || createSandboxDto.gpu || createSandboxDto.memory || createSandboxDto.disk) { - throw new BadRequestError('Cannot specify Sandbox resources when using a snapshot') - } - sandbox = await this.sandboxService.createFromSnapshot(createSandboxDto, organization) - if (sandbox.state === SandboxState.STARTED) { - return sandbox - } - - await this.waitForSandboxStarted(sandbox, 30) - } - - return sandbox - } - - @Get('for-runner') - @UseGuards(RunnerAuthGuard) - @ApiOperation({ - summary: 'Get sandboxes for the authenticated runner', - operationId: 'getSandboxesForRunner', - }) - @ApiQuery({ - name: 'states', - required: false, - type: String, - description: 'Comma-separated list of sandbox states to filter by', - }) - @ApiQuery({ - name: 'skipReconcilingSandboxes', - required: false, - type: Boolean, - description: 'Skip sandboxes where state differs from desired state', - }) - @ApiResponse({ - status: 200, - description: 'List of sandboxes for the authenticated runner', - type: [SandboxDto], - }) - async getSandboxesForRunner( - @RunnerContextDecorator() runnerContext: RunnerContext, - @Query('states') states?: string, - @Query('skipReconcilingSandboxes') skipReconcilingSandboxes?: string, - ): Promise { - const stateArray = states - ? states.split(',').map((s) => { - if (!Object.values(SandboxState).includes(s as SandboxState)) { - throw new BadRequestError(`Invalid sandbox state: ${s}`) - } - return s as SandboxState - }) - : undefined - - const skip = skipReconcilingSandboxes === 'true' - const sandboxes = await this.sandboxService.findByRunnerId(runnerContext.runnerId, stateArray, skip) - - return this.sandboxService.toSandboxDtos(sandboxes) - } - - @Get(':sandboxIdOrName') - @ApiOperation({ - summary: 'Get sandbox details', - operationId: 'getSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiQuery({ - name: 'verbose', - required: false, - type: Boolean, - description: 'Include verbose output', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox details', - type: SandboxDto, - }) - @UseGuards(SandboxAccessGuard) - async getSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - @Query('verbose') verbose?: boolean, - ): Promise { - const sandbox = await this.sandboxService.findOneByIdOrName(sandboxIdOrName, authContext.organizationId) - - return this.sandboxService.toSandboxDto(sandbox) - } - - @Delete(':sandboxIdOrName') - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Delete sandbox', - operationId: 'deleteSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox has been deleted', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.DELETE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.DELETE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async deleteSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const sandbox = await this.sandboxService.destroy(sandboxIdOrName, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxIdOrName/recover') - @HttpCode(200) - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Recover sandbox from error state', - operationId: 'recoverSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Recovery initiated', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.RECOVER, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async recoverSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const recoveredSandbox = await this.sandboxService.recover(sandboxIdOrName, authContext.organization) - let sandboxDto = await this.sandboxService.toSandboxDto(recoveredSandbox) - - if (sandboxDto.state !== SandboxState.STARTED) { - sandboxDto = await this.waitForSandboxStarted(sandboxDto, 30) - } - - return sandboxDto - } - - @Post(':sandboxIdOrName/start') - @HttpCode(200) - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Start sandbox', - operationId: 'startSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox has been started or is being restored from archived state', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.START, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async startSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const sbx = await this.sandboxService.start(sandboxIdOrName, authContext.organization) - let sandbox = await this.sandboxService.toSandboxDto(sbx) - - if (![SandboxState.ARCHIVED, SandboxState.RESTORING, SandboxState.STARTED].includes(sandbox.state)) { - sandbox = await this.waitForSandboxStarted(sandbox, 30) - } - - return sandbox - } - - @Post(':sandboxIdOrName/stop') - @HttpCode(200) // for BoxLite Api compatibility - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Stop sandbox', - operationId: 'stopSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiQuery({ - name: 'force', - required: false, - type: 'boolean', - description: 'Force stop the sandbox using SIGKILL instead of SIGTERM', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox has been stopped', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.STOP, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - query: (req) => ({ - force: req.query?.force === 'true', - }), - }, - }) - async stopSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Query('force', new ParseBoolPipe({ optional: true })) force?: boolean, - ): Promise { - const sandbox = await this.sandboxService.stop(sandboxIdOrName, authContext.organizationId, force) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxIdOrName/resize') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Resize sandbox resources', - operationId: 'resizeSandbox', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox has been resized', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @RequireFlagsEnabled({ flags: [{ flagKey: FeatureFlags.SANDBOX_RESIZE, defaultValue: false }] }) - @Audit({ - action: AuditAction.RESIZE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - cpu: req.body?.cpu, - memory: req.body?.memory, - disk: req.body?.disk, - }), - }, - }) - async resizeSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Body() resizeSandboxDto: ResizeSandboxDto, - ): Promise { - const sandbox = await this.sandboxService.resize(sandboxIdOrName, resizeSandboxDto, authContext.organization) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Put(':sandboxIdOrName/labels') - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: 'Replace sandbox labels', - operationId: 'replaceLabels', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Labels have been successfully replaced', - type: SandboxLabelsDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.REPLACE_LABELS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - labels: req.body?.labels, - }), - }, - }) - async replaceLabels( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Body() labelsDto: SandboxLabelsDto, - ): Promise { - const sandbox = await this.sandboxService.replaceLabels( - sandboxIdOrName, - labelsDto.labels, - authContext.organizationId, - ) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Put(':sandboxId/state') - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: 'Update sandbox state', - operationId: 'updateSandboxState', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox state has been successfully updated', - }) - @UseGuards(RunnerAuthGuard) - @UseGuards(SandboxAccessGuard) - async updateSandboxState( - @Param('sandboxId') sandboxId: string, - @Body() updateStateDto: UpdateSandboxStateDto, - ): Promise { - await this.sandboxService.updateState( - sandboxId, - updateStateDto.state, - updateStateDto.recoverable, - updateStateDto.errorReason, - ) - } - - @Post(':sandboxIdOrName/backup') - @ApiOperation({ - summary: 'Create sandbox backup', - operationId: 'createBackup', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox backup has been initiated', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.CREATE_BACKUP, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async createBackup( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const sandbox = await this.sandboxService.createBackup(sandboxIdOrName, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxIdOrName/public/:isPublic') - @ApiOperation({ - summary: 'Update public status', - operationId: 'updatePublicStatus', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'isPublic', - description: 'Public status to set', - type: 'boolean', - }) - @ApiResponse({ - status: 200, - description: 'Public status has been successfully updated', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.UPDATE_PUBLIC_STATUS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - params: (req) => ({ - isPublic: req.params.isPublic, - }), - }, - }) - async updatePublicStatus( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('isPublic') isPublic: boolean, - ): Promise { - const sandbox = await this.sandboxService.updatePublicStatus(sandboxIdOrName, isPublic, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxId/last-activity') - @ApiOperation({ - summary: 'Update sandbox last activity', - operationId: 'updateLastActivity', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 201, - description: 'Last activity has been updated', - }) - @UseGuards(OrGuard([SandboxAccessGuard, ProxyGuard, SshGatewayGuard, RegionSandboxAccessGuard])) - async updateLastActivity(@Param('sandboxId') sandboxId: string): Promise { - await this.sandboxService.updateLastActivityAt(sandboxId, new Date()) - } - - @Post(':sandboxIdOrName/autostop/:interval') - @ApiOperation({ - summary: 'Set sandbox auto-stop interval', - operationId: 'setAutostopInterval', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'interval', - description: 'Auto-stop interval in minutes (0 to disable)', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Auto-stop interval has been set', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.SET_AUTO_STOP_INTERVAL, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - params: (req) => ({ - interval: req.params.interval, - }), - }, - }) - async setAutostopInterval( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('interval') interval: number, - ): Promise { - const sandbox = await this.sandboxService.setAutostopInterval(sandboxIdOrName, interval, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxIdOrName/autoarchive/:interval') - @ApiOperation({ - summary: 'Set sandbox auto-archive interval', - operationId: 'setAutoArchiveInterval', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'interval', - description: 'Auto-archive interval in minutes (0 means the maximum interval will be used)', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Auto-archive interval has been set', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.SET_AUTO_ARCHIVE_INTERVAL, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - params: (req) => ({ - interval: req.params.interval, - }), - }, - }) - async setAutoArchiveInterval( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('interval') interval: number, - ): Promise { - const sandbox = await this.sandboxService.setAutoArchiveInterval( - sandboxIdOrName, - interval, - authContext.organizationId, - ) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Post(':sandboxIdOrName/autodelete/:interval') - @ApiOperation({ - summary: 'Set sandbox auto-delete interval', - operationId: 'setAutoDeleteInterval', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'interval', - description: - 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Auto-delete interval has been set', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.SET_AUTO_DELETE_INTERVAL, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - params: (req) => ({ - interval: req.params.interval, - }), - }, - }) - async setAutoDeleteInterval( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('interval') interval: number, - ): Promise { - const sandbox = await this.sandboxService.setAutoDeleteInterval( - sandboxIdOrName, - interval, - authContext.organizationId, - ) - return this.sandboxService.toSandboxDto(sandbox) - } - - // TODO: Network settings endpoint will not be enabled for now - // @Post(':sandboxIdOrName/network-settings') - // @ApiOperation({ - // summary: 'Update sandbox network settings', - // operationId: 'updateNetworkSettings', - // }) - // @ApiParam({ - // name: 'sandboxIdOrName', - // description: 'ID or name of the sandbox', - // type: 'string', - // }) - // @ApiResponse({ - // status: 200, - // description: 'Network settings have been updated', - // type: SandboxDto, - // }) - // @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - // @UseGuards(SandboxAccessGuard) - // @Audit({ - // action: AuditAction.UPDATE_NETWORK_SETTINGS, - // targetType: AuditTarget.SANDBOX, - // targetIdFromRequest: (req) => req.params.sandboxIdOrName, - // targetIdFromResult: (result: SandboxDto) => result?.id, - // requestMetadata: { - // body: (req: TypedRequest) => ({ - // networkBlockAll: req.body?.networkBlockAll, - // networkAllowList: req.body?.networkAllowList, - // }), - // }, - // }) - // async updateNetworkSettings( - // @AuthContext() authContext: OrganizationAuthContext, - // @Param('sandboxIdOrName') sandboxIdOrName: string, - // @Body() networkSettings: UpdateSandboxNetworkSettingsDto, - // ): Promise { - // const sandbox = await this.sandboxService.updateNetworkSettings( - // sandboxIdOrName, - // networkSettings.networkBlockAll, - // networkSettings.networkAllowList, - // authContext.organizationId, - // ) - // return SandboxDto.fromSandbox(sandbox, '') - // } - - @Post(':sandboxIdOrName/archive') - @HttpCode(200) - @SkipThrottle({ authenticated: true }) - @ThrottlerScope('sandbox-lifecycle') - @ApiOperation({ - summary: 'Archive sandbox', - operationId: 'archiveSandbox', - }) - @ApiResponse({ - status: 200, - description: 'Sandbox has been archived', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.ARCHIVE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - }) - async archiveSandbox( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const sandbox = await this.sandboxService.archive(sandboxIdOrName, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Get(':sandboxIdOrName/ports/:port/preview-url') - @ApiOperation({ - summary: 'Get preview URL for a sandbox port', - operationId: 'getPortPreviewUrl', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'port', - description: 'Port number to get preview URL for', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Preview URL for the specified port', - type: PortPreviewUrlDto, - }) - @UseGuards(SandboxAccessGuard) - async getPortPreviewUrl( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('port') port: number, - ): Promise { - return this.sandboxService.getPortPreviewUrl(sandboxIdOrName, authContext.organizationId, port) - } - - @Get(':sandboxIdOrName/ports/:port/signed-preview-url') - @ApiOperation({ - summary: 'Get signed preview URL for a sandbox port', - operationId: 'getSignedPortPreviewUrl', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'port', - description: 'Port number to get signed preview URL for', - type: 'integer', - }) - @ApiQuery({ - name: 'expiresInSeconds', - required: false, - type: 'integer', - description: 'Expiration time in seconds (default: 60 seconds)', - }) - @ApiResponse({ - status: 200, - description: 'Signed preview URL for the specified port', - type: SignedPortPreviewUrlDto, - }) - @UseGuards(SandboxAccessGuard) - async getSignedPortPreviewUrl( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('port') port: number, - @Query('expiresInSeconds') expiresInSeconds?: number, - ): Promise { - return this.sandboxService.getSignedPortPreviewUrl( - sandboxIdOrName, - authContext.organizationId, - port, - expiresInSeconds, - ) - } - - @Post(':sandboxIdOrName/ports/:port/signed-preview-url/:token/expire') - @ApiOperation({ - summary: 'Expire signed preview URL for a sandbox port', - operationId: 'expireSignedPortPreviewUrl', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiParam({ - name: 'port', - description: 'Port number to expire signed preview URL for', - type: 'integer', - }) - @ApiParam({ - name: 'token', - description: 'Token to expire signed preview URL for', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Signed preview URL has been expired', - }) - @UseGuards(SandboxAccessGuard) - async expireSignedPortPreviewUrl( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Param('port') port: number, - @Param('token') token: string, - ): Promise { - await this.sandboxService.expireSignedPreviewUrlToken(sandboxIdOrName, authContext.organizationId, token, port) - } - - @Get(':sandboxIdOrName/build-logs') - @ApiOperation({ - summary: 'Get build logs', - operationId: 'getBuildLogs', - deprecated: true, - description: 'This endpoint is deprecated. Use `getBuildLogsUrl` instead.', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Build logs stream', - }) - @ApiQuery({ - name: 'follow', - required: false, - type: Boolean, - description: 'Whether to follow the logs stream', - }) - @UseGuards(SandboxAccessGuard) - async getBuildLogs( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Query('follow', new ParseBoolPipe({ optional: true })) follow?: boolean, - ): Promise { - const sandbox = await this.sandboxService.findOneByIdOrName(sandboxIdOrName, authContext.organizationId) - if (!sandbox.runnerId) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} has no runner assigned`) - } - - if (!sandbox.buildInfo) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} has no build info`) - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - if (!runner.apiUrl) { - throw new NotFoundException(`Runner for sandbox ${sandboxIdOrName} has no API URL`) - } - - const logProxy = new LogProxy( - runner.apiUrl, - sandbox.buildInfo.snapshotRef.split(':')[0], - runner.apiKey, - follow === true, - req, - res, - next, - ) - return logProxy.create() - } - - @Get(':sandboxIdOrName/build-logs-url') - @ApiOperation({ - summary: 'Get build logs URL', - operationId: 'getBuildLogsUrl', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Build logs URL', - type: UrlDto, - }) - @UseGuards(SandboxAccessGuard) - async getBuildLogsUrl( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - ): Promise { - const buildLogsUrl = await this.sandboxService.getBuildLogsUrl(sandboxIdOrName, authContext.organizationId) - - return new UrlDto(buildLogsUrl) - } - - @Post(':sandboxIdOrName/ssh-access') - @HttpCode(200) - @ApiOperation({ - summary: 'Create SSH access for sandbox', - operationId: 'createSshAccess', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiQuery({ - name: 'expiresInMinutes', - required: false, - type: Number, - description: 'Expiration time in minutes (default: 60)', - }) - @ApiResponse({ - status: 200, - description: 'SSH access has been created', - type: SshAccessDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.CREATE_SSH_ACCESS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SshAccessDto) => result?.sandboxId, - requestMetadata: { - query: (req) => ({ - expiresInMinutes: req.query.expiresInMinutes, - }), - }, - }) - async createSshAccess( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Query('expiresInMinutes') expiresInMinutes?: number, - ): Promise { - return await this.sandboxService.createSshAccess(sandboxIdOrName, expiresInMinutes, authContext.organizationId) - } - - @Delete(':sandboxIdOrName/ssh-access') - @HttpCode(200) - @ApiOperation({ - summary: 'Revoke SSH access for sandbox', - operationId: 'revokeSshAccess', - }) - @ApiParam({ - name: 'sandboxIdOrName', - description: 'ID or name of the sandbox', - type: 'string', - }) - @ApiQuery({ - name: 'token', - required: false, - type: String, - description: 'SSH access token to revoke. If not provided, all SSH access for the sandbox will be revoked.', - }) - @ApiResponse({ - status: 200, - description: 'SSH access has been revoked', - type: SandboxDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(SandboxAccessGuard) - @Audit({ - action: AuditAction.REVOKE_SSH_ACCESS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxIdOrName, - targetIdFromResult: (result: SandboxDto) => result?.id, - requestMetadata: { - query: (req) => ({ - token: req.query.token, - }), - }, - }) - async revokeSshAccess( - @AuthContext() authContext: OrganizationAuthContext, - @Param('sandboxIdOrName') sandboxIdOrName: string, - @Query('token') token?: string, - ): Promise { - const sandbox = await this.sandboxService.revokeSshAccess(sandboxIdOrName, token, authContext.organizationId) - return this.sandboxService.toSandboxDto(sandbox) - } - - @Get('ssh-access/validate') - @ApiOperation({ - summary: 'Validate SSH access for sandbox', - operationId: 'validateSshAccess', - }) - @ApiQuery({ - name: 'token', - required: true, - type: String, - description: 'SSH access token to validate', - }) - @ApiResponse({ - status: 200, - description: 'SSH access validation result', - type: SshAccessValidationDto, - }) - async validateSshAccess(@Query('token') token: string): Promise { - const result = await this.sandboxService.validateSshAccess(token) - return SshAccessValidationDto.fromValidationResult(result.valid, result.sandboxId) - } - - @Get(':sandboxId/toolbox-proxy-url') - @ApiOperation({ - summary: 'Get toolbox proxy URL for a sandbox', - operationId: 'getToolboxProxyUrl', - }) - @ApiParam({ - name: 'sandboxId', - description: 'ID of the sandbox', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Toolbox proxy URL for the specified sandbox', - type: ToolboxProxyUrlDto, - }) - @UseGuards(SandboxAccessGuard) - async getToolboxProxyUrl(@Param('sandboxId') sandboxId: string): Promise { - const url = await this.sandboxService.getToolboxProxyUrl(sandboxId) - return new ToolboxProxyUrlDto(url) - } - - // wait up to `timeoutSeconds` for the sandbox to start; if it doesn’t, return current sandbox - private async waitForSandboxStarted(sandbox: SandboxDto, timeoutSeconds: number): Promise { - let latestSandbox: Sandbox - const waitForStarted = new Promise((resolve, reject) => { - // eslint-disable-next-line - let timeout: NodeJS.Timeout - const handleStateUpdated = (event: SandboxStateUpdatedEvent) => { - if (event.sandbox.id !== sandbox.id) { - return - } - latestSandbox = event.sandbox - if (event.sandbox.state === SandboxState.STARTED) { - this.sandboxCallbacks.delete(sandbox.id) - clearTimeout(timeout) - resolve(this.sandboxService.toSandboxDto(event.sandbox)) - } - if (event.sandbox.state === SandboxState.ERROR || event.sandbox.state === SandboxState.BUILD_FAILED) { - this.sandboxCallbacks.delete(sandbox.id) - clearTimeout(timeout) - reject(new BadRequestError(`Sandbox failed to start: ${event.sandbox.errorReason}`)) - } - } - - this.sandboxCallbacks.set(sandbox.id, handleStateUpdated) - - timeout = setTimeout(() => { - this.sandboxCallbacks.delete(sandbox.id) - if (latestSandbox) { - resolve(this.sandboxService.toSandboxDto(latestSandbox)) - } else { - resolve(sandbox) - } - }, timeoutSeconds * 1000) - }) - - return waitForStarted - } - - private handleSandboxStateUpdated(event: SandboxStateUpdatedEvent) { - const callback = this.sandboxCallbacks.get(event.sandbox.id) - if (callback) { - callback(event) - } - } -} diff --git a/apps/api/src/sandbox/controllers/snapshot.controller.ts b/apps/api/src/sandbox/controllers/snapshot.controller.ts deleted file mode 100644 index 28a0903eb..000000000 --- a/apps/api/src/sandbox/controllers/snapshot.controller.ts +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Body, - Controller, - Delete, - Get, - Param, - Patch, - Post, - Query, - UseGuards, - HttpCode, - ForbiddenException, - Logger, - NotFoundException, - Res, - Request, - RawBodyRequest, - Next, - ParseBoolPipe, -} from '@nestjs/common' -import { IncomingMessage, ServerResponse } from 'http' -import { NextFunction } from 'express' -import { SnapshotService } from '../services/snapshot.service' -import { RunnerService } from '../services/runner.service' -import { - ApiOAuth2, - ApiTags, - ApiOperation, - ApiResponse, - ApiParam, - ApiQuery, - ApiHeader, - ApiBearerAuth, -} from '@nestjs/swagger' -import { CreateSnapshotDto } from '../dto/create-snapshot.dto' -import { SnapshotDto } from '../dto/snapshot.dto' -import { PaginatedSnapshotsDto } from '../dto/paginated-snapshots.dto' -import { SnapshotAccessGuard } from '../guards/snapshot-access.guard' -import { SnapshotReadAccessGuard } from '../guards/snapshot-read-access.guard' -import { CustomHeaders } from '../../common/constants/header.constants' -import { AuthContext } from '../../common/decorators/auth-context.decorator' -import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' -import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { SystemActionGuard } from '../../auth/system-action.guard' -import { RequiredSystemRole } from '../../common/decorators/required-role.decorator' -import { SystemRole } from '../../user/enums/system-role.enum' -import { SetSnapshotGeneralStatusDto } from '../dto/update-snapshot.dto' -import { LogProxy } from '../proxy/log-proxy' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { Snapshot } from '../entities/snapshot.entity' -import { Audit, TypedRequest } from '../../audit/decorators/audit.decorator' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -import { ListSnapshotsQueryDto } from '../dto/list-snapshots-query.dto' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' -import { UrlDto } from '../../common/dto/url.dto' - -@ApiTags('snapshots') -@Controller('snapshots') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@UseGuards(CombinedAuthGuard, SystemActionGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class SnapshotController { - private readonly logger = new Logger(SnapshotController.name) - - constructor( - private readonly snapshotService: SnapshotService, - private readonly runnerService: RunnerService, - ) {} - - @Post() - @HttpCode(200) - @ApiOperation({ - summary: 'Create a new snapshot', - operationId: 'createSnapshot', - }) - @ApiResponse({ - status: 200, - description: 'The snapshot has been successfully created.', - type: SnapshotDto, - }) - @ApiResponse({ - status: 400, - description: 'Bad request - Snapshots with tag ":latest" are not allowed', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SNAPSHOTS]) - @Audit({ - action: AuditAction.CREATE, - targetType: AuditTarget.SNAPSHOT, - targetIdFromResult: (result: SnapshotDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - name: req.body?.name, - imageName: req.body?.imageName, - entrypoint: req.body?.entrypoint, - general: req.body?.general, - cpu: req.body?.cpu, - memory: req.body?.memory, - disk: req.body?.disk, - gpu: req.body?.gpu, - buildInfo: req.body?.buildInfo, - }), - }, - }) - async createSnapshot( - @AuthContext() authContext: OrganizationAuthContext, - @Body() createSnapshotDto: CreateSnapshotDto, - ): Promise { - if (createSnapshotDto.general && authContext.role !== SystemRole.ADMIN) { - throw new ForbiddenException('Insufficient permissions for creating general snapshots') - } - - if (createSnapshotDto.buildInfo) { - if (createSnapshotDto.imageName) { - throw new BadRequestError('Cannot specify an image name when using a build info entry') - } - if (createSnapshotDto.entrypoint) { - throw new BadRequestError('Cannot specify an entrypoint when using a build info entry') - } - } else { - if (!createSnapshotDto.imageName) { - throw new BadRequestError('Must specify an image name when not using a build info entry') - } - } - - // TODO: consider - if using transient registry, prepend the snapshot name with the username - const snapshot = createSnapshotDto.buildInfo - ? await this.snapshotService.createFromBuildInfo(authContext.organization, createSnapshotDto) - : await this.snapshotService.createFromPull(authContext.organization, createSnapshotDto) - return SnapshotDto.fromSnapshot(snapshot) - } - - @Get('can-cleanup-image') - @ApiOperation({ - summary: 'Check if an image can be cleaned up', - operationId: 'canCleanupImage', - }) - @ApiQuery({ - name: 'imageName', - required: true, - type: String, - description: 'Image name with tag to check', - }) - @ApiResponse({ - status: 200, - description: 'Boolean indicating if image can be cleaned up', - type: Boolean, - }) - @RequiredSystemRole(SystemRole.ADMIN) - async canCleanupImage(@Query('imageName') imageName: string): Promise { - return this.snapshotService.canCleanupImage(imageName) - } - - @Get(':id') - @ApiOperation({ - summary: 'Get snapshot by ID or name', - operationId: 'getSnapshot', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID or name', - }) - @ApiResponse({ - status: 200, - description: 'The snapshot', - type: SnapshotDto, - }) - @ApiResponse({ - status: 404, - description: 'Snapshot not found', - }) - @UseGuards(SnapshotReadAccessGuard) - async getSnapshot( - @Param('id') snapshotIdOrName: string, - @AuthContext() authContext: OrganizationAuthContext, - ): Promise { - const snapshot = await this.snapshotService.getSnapshotWithRegions(snapshotIdOrName, authContext.organizationId) - return SnapshotDto.fromSnapshot(snapshot) - } - - @Delete(':id') - @ApiOperation({ - summary: 'Delete snapshot', - operationId: 'removeSnapshot', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiResponse({ - status: 200, - description: 'Snapshot has been deleted', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.DELETE_SNAPSHOTS]) - @UseGuards(SnapshotAccessGuard) - @Audit({ - action: AuditAction.DELETE, - targetType: AuditTarget.SNAPSHOT, - targetIdFromRequest: (req) => req.params.id, - }) - async removeSnapshot(@Param('id') snapshotId: string): Promise { - await this.snapshotService.removeSnapshot(snapshotId) - } - - @Get() - @ApiOperation({ - summary: 'List all snapshots', - operationId: 'getAllSnapshots', - }) - @ApiResponse({ - status: 200, - description: 'Paginated list of all snapshots', - type: PaginatedSnapshotsDto, - }) - async getAllSnapshots( - @AuthContext() authContext: OrganizationAuthContext, - @Query() queryParams: ListSnapshotsQueryDto, - ): Promise { - const { page, limit, name, sort, order } = queryParams - - const result = await this.snapshotService.getAllSnapshots( - authContext.organizationId, - page, - limit, - { name }, - { field: sort, direction: order }, - ) - - return { - items: result.items.map(SnapshotDto.fromSnapshot), - total: result.total, - page: result.page, - totalPages: result.totalPages, - } - } - - @Patch(':id/general') - @ApiOperation({ - summary: 'Set snapshot general status', - operationId: 'setSnapshotGeneralStatus', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiResponse({ - status: 200, - description: 'Snapshot general status has been set', - type: SnapshotDto, - }) - @RequiredSystemRole(SystemRole.ADMIN) - @Audit({ - action: AuditAction.SET_GENERAL_STATUS, - targetType: AuditTarget.SNAPSHOT, - targetIdFromRequest: (req) => req.params.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - general: req.body?.general, - }), - }, - }) - async setSnapshotGeneralStatus( - @Param('id') snapshotId: string, - @Body() dto: SetSnapshotGeneralStatusDto, - ): Promise { - const snapshot = await this.snapshotService.setSnapshotGeneralStatus(snapshotId, dto.general) - return SnapshotDto.fromSnapshot(snapshot) - } - - @Get(':id/build-logs') - @ApiOperation({ - summary: 'Get snapshot build logs', - operationId: 'getSnapshotBuildLogs', - deprecated: true, - description: 'This endpoint is deprecated. Use `getSnapshotBuildLogsUrl` instead.', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiQuery({ - name: 'follow', - required: false, - type: Boolean, - description: 'Whether to follow the logs stream', - }) - @UseGuards(SnapshotAccessGuard) - async getSnapshotBuildLogs( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - @Param('id') snapshotId: string, - @Query('follow', new ParseBoolPipe({ optional: true })) follow?: boolean, - ): Promise { - let snapshot = await this.snapshotService.getSnapshot(snapshotId) - - // Check if the snapshot has build info - if (!snapshot.buildInfo) { - throw new NotFoundException(`Snapshot ${snapshotId} has no build info`) - } - - if (snapshot.state == SnapshotState.ACTIVE) { - // Close the connection - res.end() - return - } - - // Retry until a runner is assigned or timeout after 30 seconds - const startTime = Date.now() - const timeoutMs = 30 * 1000 - - while (!snapshot.initialRunnerId) { - if (Date.now() - startTime > timeoutMs) { - throw new NotFoundException(`Timeout waiting for build runner assignment for snapshot ${snapshotId}`) - } - await new Promise((resolve) => setTimeout(resolve, 1000)) - snapshot = await this.snapshotService.getSnapshot(snapshotId) - } - - const runner = await this.runnerService.findOneOrFail(snapshot.initialRunnerId) - - if (!runner.apiUrl) { - throw new NotFoundException(`Build runner for snapshot ${snapshotId} has no API URL`) - } - - const logProxy = new LogProxy( - runner.apiUrl, - snapshot.buildInfo.snapshotRef, - runner.apiKey, - follow === true, - req, - res, - next, - ) - return logProxy.create() - } - - @Get(':id/build-logs-url') - @ApiOperation({ - summary: 'Get snapshot build logs URL', - operationId: 'getSnapshotBuildLogsUrl', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiResponse({ - status: 200, - description: 'The snapshot build logs URL', - type: UrlDto, - }) - @UseGuards(SnapshotAccessGuard) - async getSnapshotBuildLogsUrl(@Param('id') snapshotId: string): Promise { - let snapshot = await this.snapshotService.getSnapshot(snapshotId) - - // Check if the snapshot has build info - if (!snapshot.buildInfo) { - throw new NotFoundException(`Snapshot ${snapshotId} has no build info`) - } - - // Retry until a runner is assigned or timeout after 30 seconds - const startTime = Date.now() - const timeoutMs = 30 * 1000 - - while (!snapshot.initialRunnerId) { - if (Date.now() - startTime > timeoutMs) { - throw new NotFoundException(`Timeout waiting for build runner assignment for snapshot ${snapshotId}`) - } - await new Promise((resolve) => setTimeout(resolve, 1000)) - snapshot = await this.snapshotService.getSnapshot(snapshotId) - } - - const url = await this.snapshotService.getBuildLogsUrl(snapshot) - return new UrlDto(url) - } - - @Post(':id/activate') - @HttpCode(200) - @ApiOperation({ - summary: 'Activate a snapshot', - operationId: 'activateSnapshot', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiResponse({ - status: 200, - description: 'The snapshot has been successfully activated.', - type: SnapshotDto, - }) - @ApiResponse({ - status: 400, - description: 'Bad request - Snapshot is already active, not in inactive state, or has associated snapshot runners', - }) - @ApiResponse({ - status: 404, - description: 'Snapshot not found', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SNAPSHOTS]) - @UseGuards(SnapshotAccessGuard) - @Audit({ - action: AuditAction.ACTIVATE, - targetType: AuditTarget.SNAPSHOT, - targetIdFromRequest: (req) => req.params.id, - }) - async activateSnapshot( - @Param('id') snapshotId: string, - @AuthContext() authContext: OrganizationAuthContext, - ): Promise { - const snapshot = await this.snapshotService.activateSnapshot(snapshotId, authContext.organization) - return SnapshotDto.fromSnapshot(snapshot) - } - - @Post(':id/deactivate') - @HttpCode(204) - @ApiOperation({ - summary: 'Deactivate a snapshot', - operationId: 'deactivateSnapshot', - }) - @ApiParam({ - name: 'id', - description: 'Snapshot ID', - }) - @ApiResponse({ - status: 204, - description: 'The snapshot has been successfully deactivated.', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SNAPSHOTS]) - @UseGuards(SnapshotAccessGuard) - @Audit({ - action: AuditAction.DEACTIVATE, - targetType: AuditTarget.SNAPSHOT, - targetIdFromRequest: (req) => req.params.id, - }) - async deactivateSnapshot(@Param('id') snapshotId: string) { - await this.snapshotService.deactivateSnapshot(snapshotId) - } -} diff --git a/apps/api/src/sandbox/controllers/toolbox.deprecated.controller.ts b/apps/api/src/sandbox/controllers/toolbox.deprecated.controller.ts deleted file mode 100644 index 03c77b48d..000000000 --- a/apps/api/src/sandbox/controllers/toolbox.deprecated.controller.ts +++ /dev/null @@ -1,2166 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Controller, - Get, - Post, - Delete, - Body, - Param, - Request, - Logger, - UseGuards, - HttpCode, - UseInterceptors, - RawBodyRequest, - BadRequestException, - Res, - Next, -} from '@nestjs/common' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { - ApiOAuth2, - ApiResponse, - ApiQuery, - ApiOperation, - ApiConsumes, - ApiBody, - ApiTags, - ApiParam, - ApiHeader, - ApiBearerAuth, -} from '@nestjs/swagger' -import { - FileInfoDto, - MatchDto, - SearchFilesResponseDto, - ReplaceRequestDto, - ReplaceResultDto, - GitAddRequestDto, - GitBranchRequestDto, - GitDeleteBranchRequestDto, - GitCloneRequestDto, - GitCommitRequestDto, - GitCommitResponseDto, - GitRepoRequestDto, - GitStatusDto, - ListBranchResponseDto, - GitCommitInfoDto, - GitCheckoutRequestDto, - ExecuteRequestDto, - ExecuteResponseDto, - ProjectDirResponseDto, - CreateSessionRequestDto, - SessionExecuteRequestDto, - SessionExecuteResponseDto, - SessionDto, - CommandDto, - MousePositionDto, - MouseMoveRequestDto, - MouseMoveResponseDto, - MouseClickRequestDto, - MouseClickResponseDto, - MouseDragRequestDto, - MouseDragResponseDto, - MouseScrollRequestDto, - MouseScrollResponseDto, - KeyboardTypeRequestDto, - KeyboardPressRequestDto, - KeyboardHotkeyRequestDto, - ScreenshotResponseDto, - RegionScreenshotResponseDto, - CompressedScreenshotResponseDto, - DisplayInfoResponseDto, - WindowsResponseDto, - ComputerUseStartResponseDto, - ComputerUseStopResponseDto, - ComputerUseStatusResponseDto, - ProcessStatusResponseDto, - ProcessRestartResponseDto, - ProcessLogsResponseDto, - ProcessErrorsResponseDto, - UserHomeDirResponseDto, - WorkDirResponseDto, - PtyCreateRequestDto, - PtyCreateResponseDto, - PtySessionInfoDto, - PtyListResponseDto, - PtyResizeRequestDto, -} from '../dto/toolbox.deprecated.dto' -import { ToolboxService } from '../services/toolbox.deprecated.service' -import { ContentTypeInterceptor } from '../../common/interceptors/content-type.interceptors' -import { - CompletionListDto, - LspCompletionParamsDto, - LspDocumentRequestDto, - LspSymbolDto, - LspServerRequestDto, -} from '../dto/lsp.dto' -import { createProxyMiddleware, RequestHandler, fixRequestBody, Options } from 'http-proxy-middleware' -import { IncomingMessage } from 'http' -import { NextFunction } from 'express' -import { ServerResponse } from 'http' -import { SandboxAccessGuard } from '../guards/sandbox-access.guard' -import { CustomHeaders } from '../../common/constants/header.constants' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' -import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' -import followRedirects from 'follow-redirects' -import { UploadFileDto } from '../dto/upload-file.dto' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../../audit/decorators/audit.decorator' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { DownloadFilesDto } from '../dto/download-files.dto' -import { SkipThrottle } from '@nestjs/throttler' - -followRedirects.maxRedirects = 10 -followRedirects.maxBodyLength = 50 * 1024 * 1024 - -type RunnerInfo = { - apiKey: string - apiUrl: string -} -const RUNNER_INFO_CACHE_PREFIX = 'proxy:sandbox-runner-info:' -const RUNNER_INFO_CACHE_TTL = 2 * 60 // 2 minutes - -@ApiTags('toolbox') -@Controller('toolbox') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@SkipThrottle({ anonymous: true, authenticated: true }) -@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, SandboxAccessGuard) -@RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class ToolboxController { - private readonly logger = new Logger(ToolboxController.name) - private readonly toolboxProxy: RequestHandler< - RawBodyRequest, - ServerResponse, - NextFunction - > - private readonly toolboxStreamProxy: RequestHandler< - RawBodyRequest, - ServerResponse, - NextFunction - > - - constructor( - private readonly toolboxService: ToolboxService, - @InjectRedis() private readonly redis: Redis, - ) { - const commonProxyOptions: Options = { - router: async (req: RawBodyRequest) => { - // eslint-disable-next-line no-useless-escape - const sandboxId = req.url.match(/^\/api\/toolbox\/([^\/]+)\/toolbox/)?.[1] - try { - const runnerInfo = await this.getRunnerInfo(sandboxId) - - // @ts-expect-error - used later to set request headers - req._runnerApiKey = runnerInfo.apiKey - - return runnerInfo.apiUrl - } catch (err) { - // @ts-expect-error - used later to throw error - req._err = err - } - - // Must return a valid url - return 'http://target-error' - }, - pathRewrite: (path) => { - // eslint-disable-next-line no-useless-escape - const sandboxId = path.match(/^\/api\/toolbox\/([^\/]+)\/toolbox/)?.[1] - const routePath = path.split(`/api/toolbox/${sandboxId}/toolbox`)[1] - const newPath = `/sandboxes/${sandboxId}/toolbox${routePath}` - - // Handle files path which is served on /files/ in the daemon - // TODO: Circle back to this after daemon versioning - // We can then switch /files/ to /files and only perform this for older daemon versions - const url = new URL(`http://runner${newPath}`) - if (url.pathname.endsWith('/files')) { - url.pathname = url.pathname + '/' - return url.toString().replace('http://runner', '') - } - - return newPath - }, - changeOrigin: true, - autoRewrite: true, - proxyTimeout: 5 * 60 * 1000, - on: { - proxyReq: (proxyReq, req, res) => { - // @ts-expect-error - set when routing - if (req._err) { - res.writeHead(400, { 'Content-Type': 'application/json' }) - // @ts-expect-error - set when routing - res.end(JSON.stringify(req._err)) - return - } - - // @ts-expect-error - set when routing - const runnerApiKey = req._runnerApiKey - - try { - proxyReq.setHeader('Authorization', `Bearer ${runnerApiKey}`) - } catch { - // Ignore error - headers are already set - return - } - fixRequestBody(proxyReq, req) - }, - proxyRes: (proxyRes, req, res) => { - // console.log('proxyRes', proxyRes) - }, - }, - } - - this.toolboxProxy = createProxyMiddleware({ - ...commonProxyOptions, - followRedirects: true, - }) - - this.toolboxStreamProxy = createProxyMiddleware({ - ...commonProxyOptions, - followRedirects: false, - }) - } - - private async getRunnerInfo(sandboxId: string): Promise { - let runnerInfo: RunnerInfo | null = null - try { - const cached: { value: RunnerInfo } = JSON.parse(await this.redis.get(`${RUNNER_INFO_CACHE_PREFIX}${sandboxId}`)) - runnerInfo = cached.value - } catch { - // Ignore error and fetch from db - } - - if (!runnerInfo) { - const runner = await this.toolboxService.getRunner(sandboxId) - if (!runner.proxyUrl) { - throw new BadRequestException('Runner proxy URL not found') - } - - runnerInfo = { - apiKey: runner.apiKey, - apiUrl: runner.proxyUrl, - } - await this.redis.set( - `${RUNNER_INFO_CACHE_PREFIX}${sandboxId}`, - JSON.stringify({ value: runnerInfo }), - 'EX', - RUNNER_INFO_CACHE_TTL, - ) - } - - return runnerInfo - } - - @Get(':sandboxId/toolbox/project-dir') - @ApiOperation({ - summary: '[DEPRECATED] Get sandbox project dir', - operationId: 'getProjectDir_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Project directory retrieved successfully', - type: ProjectDirResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getProjectDir( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/user-home-dir') - @ApiOperation({ - summary: '[DEPRECATED] Get sandbox user home dir', - operationId: 'getUserHomeDir_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'User home directory retrieved successfully', - type: UserHomeDirResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getUserHomeDir( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/work-dir') - @ApiOperation({ - summary: '[DEPRECATED] Get sandbox work-dir', - operationId: 'getWorkDir_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Work-dir retrieved successfully', - type: WorkDirResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getWorkDir( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/files') - @ApiOperation({ - summary: '[DEPRECATED] List files', - operationId: 'listFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Files listed successfully', - type: [FileInfoDto], - }) - @ApiQuery({ name: 'path', type: String, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async listFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Delete(':sandboxId/toolbox/files') - @ApiOperation({ - summary: '[DEPRECATED] Delete file', - description: 'Delete file inside sandbox', - operationId: 'deleteFile_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File deleted successfully', - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiQuery({ name: 'recursive', type: Boolean, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_DELETE_FILE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - path: req.query.path, - }), - }, - }) - async deleteFile( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/files/download') - @ApiOperation({ - summary: '[DEPRECATED] Download file', - description: 'Download file from sandbox', - operationId: 'downloadFile_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File downloaded successfully', - schema: { - type: 'string', - format: 'binary', - }, - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_DOWNLOAD_FILE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - path: req.query.path, - }), - }, - }) - async downloadFile( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/files/bulk-download') - @ApiOperation({ - summary: '[DEPRECATED] Download multiple files', - description: 'Streams back a multipart/form-data bundle of the requested paths', - operationId: 'downloadFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'A multipart/form-data response with each file as a part', - schema: { - type: 'string', - format: 'binary', - }, - }) - @ApiBody({ - type: DownloadFilesDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async downloadFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/files/find') - @ApiOperation({ - summary: '[DEPRECATED] Search for text/pattern in files', - description: 'Search for text/pattern inside sandbox files', - operationId: 'findInFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Search completed successfully', - type: [MatchDto], - }) - @ApiQuery({ name: 'pattern', type: String, required: true }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async findInFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/files/folder') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Create folder', - description: 'Create folder inside sandbox', - operationId: 'createFolder_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Folder created successfully', - }) - @ApiQuery({ name: 'mode', type: String, required: true }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_CREATE_FOLDER, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - path: req.query.path, - mode: req.query.mode, - }), - }, - }) - async createFolder( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/files/info') - @ApiOperation({ - summary: '[DEPRECATED] Get file info', - description: 'Get file info inside sandbox', - operationId: 'getFileInfo_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File info retrieved successfully', - type: FileInfoDto, - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getFileInfo( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/files/move') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Move file', - description: 'Move file inside sandbox', - operationId: 'moveFile_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File moved successfully', - }) - @ApiQuery({ name: 'destination', type: String, required: true }) - @ApiQuery({ name: 'source', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_MOVE_FILE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - destination: req.query.destination, - source: req.query.source, - }), - }, - }) - async moveFile( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/files/permissions') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Set file permissions', - description: 'Set file owner/group/permissions inside sandbox', - operationId: 'setFilePermissions_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File permissions updated successfully', - }) - @ApiQuery({ name: 'mode', type: String, required: false }) - @ApiQuery({ name: 'group', type: String, required: false }) - @ApiQuery({ name: 'owner', type: String, required: false }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_SET_FILE_PERMISSIONS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - mode: req.query.mode, - group: req.query.group, - owner: req.query.owner, - path: req.query.path, - }), - }, - }) - async setFilePermissions( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/files/replace') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Replace in files', - description: 'Replace text/pattern in multiple files inside sandbox', - operationId: 'replaceInFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Text replaced successfully', - type: [ReplaceResultDto], - }) - @ApiBody({ - type: ReplaceRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_REPLACE_IN_FILES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - files: req.body?.files, - pattern: req.body?.pattern, - newValue: req.body?.newValue, - }), - }, - }) - async replaceInFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/files/search') - @ApiOperation({ - summary: '[DEPRECATED] Search files', - description: 'Search for files inside sandbox', - operationId: 'searchFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Search completed successfully', - type: SearchFilesResponseDto, - }) - @ApiQuery({ name: 'pattern', type: String, required: true }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async searchFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @HttpCode(200) - @Post(':sandboxId/toolbox/files/upload') - @ApiOperation({ - summary: '[DEPRECATED] Upload file', - description: 'Upload file inside sandbox', - operationId: 'uploadFile_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'File uploaded successfully', - }) - @ApiConsumes('multipart/form-data') - @ApiBody({ - schema: { - type: 'object', - properties: { - file: { - type: 'string', - format: 'binary', - }, - }, - }, - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_UPLOAD_FILE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - query: (req) => ({ - path: req.query.path, - }), - }, - }) - async uploadFile( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return this.toolboxProxy(req, res, next) - } - - @HttpCode(200) - @Post(':sandboxId/toolbox/files/bulk-upload') - @ApiOperation({ - summary: '[DEPRECATED] Upload multiple files', - description: 'Upload multiple files inside sandbox', - operationId: 'uploadFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Files uploaded successfully', - }) - @ApiConsumes('multipart/form-data') - @ApiBody({ type: [UploadFileDto] }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_BULK_UPLOAD_FILES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - }) - async uploadFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return this.toolboxStreamProxy(req, res, next) - } - - // Git operations - @Post(':sandboxId/toolbox/git/add') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Add files', - description: 'Add files to git commit', - operationId: 'gitAddFiles_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Files added to git successfully', - }) - @ApiBody({ - type: GitAddRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_ADD_FILES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - files: req.body?.files, - }), - }, - }) - async gitAddFiles( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/git/branches') - @ApiOperation({ - summary: '[DEPRECATED] Get branch list', - description: 'Get branch list from git repository', - operationId: 'gitListBranches_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Branch list retrieved successfully', - type: ListBranchResponseDto, - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async gitBranchList( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/branches') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Create branch', - description: 'Create branch on git repository', - operationId: 'gitCreateBranch_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Branch created successfully', - }) - @ApiBody({ - type: GitBranchRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_CREATE_BRANCH, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - name: req.body?.name, - }), - }, - }) - async gitCreateBranch( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Delete(':sandboxId/toolbox/git/branches') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Delete branch', - description: 'Delete branch on git repository', - operationId: 'gitDeleteBranch_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Branch deleted successfully', - }) - @ApiBody({ - type: GitDeleteBranchRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_DELETE_BRANCH, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - name: req.body?.name, - }), - }, - }) - async gitDeleteBranch( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/clone') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Clone repository', - description: 'Clone git repository', - operationId: 'gitCloneRepository_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Repository cloned successfully', - }) - @ApiBody({ - type: GitCloneRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_CLONE_REPOSITORY, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - url: req.body?.url, - path: req.body?.path, - username: req.body?.username, - password: req.body?.password ? MASKED_AUDIT_VALUE : undefined, - branch: req.body?.branch, - commit_id: req.body?.commit_id, - }), - }, - }) - async gitCloneRepository( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/commit') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Commit changes', - description: 'Commit changes to git repository', - operationId: 'gitCommitChanges_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Changes committed successfully', - type: GitCommitResponseDto, - }) - @ApiBody({ - type: GitCommitRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_COMMIT_CHANGES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - message: req.body?.message, - author: req.body?.author, - email: req.body?.email, - allow_empty: req.body?.allow_empty, - }), - }, - }) - async gitCommitChanges( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/git/history') - @ApiOperation({ - summary: '[DEPRECATED] Get commit history', - description: 'Get commit history from git repository', - operationId: 'gitGetHistory_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Commit history retrieved successfully', - type: [GitCommitInfoDto], - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async gitCommitHistory( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/pull') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Pull changes', - description: 'Pull changes from remote', - operationId: 'gitPullChanges_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Changes pulled successfully', - }) - @ApiBody({ - type: GitRepoRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_PULL_CHANGES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - username: req.body?.username, - password: req.body?.password ? MASKED_AUDIT_VALUE : undefined, - }), - }, - }) - async gitPullChanges( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/push') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Push changes', - description: 'Push changes to remote', - operationId: 'gitPushChanges_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Changes pushed successfully', - }) - @ApiBody({ - type: GitRepoRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_PUSH_CHANGES, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - username: req.body?.username, - password: req.body?.password ? MASKED_AUDIT_VALUE : undefined, - }), - }, - }) - async gitPushChanges( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/git/checkout') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Checkout branch', - description: 'Checkout branch or commit in git repository', - operationId: 'gitCheckoutBranch_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Branch checked out successfully', - }) - @ApiBody({ - type: GitCheckoutRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_GIT_CHECKOUT_BRANCH, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - path: req.body?.path, - branch: req.body?.branch, - }), - }, - }) - async gitCheckoutBranch( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/git/status') - @ApiOperation({ - summary: '[DEPRECATED] Get git status', - description: 'Get status from git repository', - operationId: 'gitGetStatus_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Git status retrieved successfully', - type: GitStatusDto, - }) - @ApiQuery({ name: 'path', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async gitStatus( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/process/execute') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Execute command', - description: 'Execute command synchronously inside sandbox', - operationId: 'executeCommand_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Command executed successfully', - type: ExecuteResponseDto, - }) - @Audit({ - action: AuditAction.TOOLBOX_EXECUTE_COMMAND, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - command: req.body?.command, - cwd: req.body?.cwd, - timeout: req.body?.timeout, - }), - }, - }) - async executeCommand( - @Param('sandboxId') sandboxId: string, - @Body() executeRequest: ExecuteRequestDto, - ): Promise { - const response = await this.toolboxService.forwardRequestToRunner( - sandboxId, - 'POST', - '/toolbox/process/execute', - executeRequest, - ) - - // TODO: use new proxy - can't use it now because of this - return { - exitCode: response.exitCode ?? response.code, - result: response.result, - } - } - - // Session management endpoints - @Get(':sandboxId/toolbox/process/session') - @ApiOperation({ - summary: '[DEPRECATED] List sessions', - description: 'List all active sessions in the sandbox', - operationId: 'listSessions_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Sessions retrieved successfully', - type: [SessionDto], - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async listSessions( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/process/session/:sessionId') - @ApiOperation({ - summary: '[DEPRECATED] Get session', - description: 'Get session by ID', - operationId: 'getSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Session retrieved successfully', - type: SessionDto, - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getSession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/process/session') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Create session', - description: 'Create a new session in the sandbox', - operationId: 'createSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - }) - @ApiBody({ - type: CreateSessionRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_CREATE_SESSION, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - body: (req: TypedRequest) => ({ - sessionId: req.body?.sessionId, - }), - }, - }) - async createSession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/process/session/:sessionId/exec') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Execute command in session', - description: 'Execute a command in a specific session', - operationId: 'executeSessionCommand_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Command executed successfully', - type: SessionExecuteResponseDto, - }) - @ApiResponse({ - status: 202, - description: 'Command accepted and is being processed', - type: SessionExecuteResponseDto, - }) - @ApiBody({ - type: SessionExecuteRequestDto, - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_SESSION_EXECUTE_COMMAND, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - params: (req) => ({ - sessionId: req.params.sessionId, - }), - body: (req: TypedRequest) => ({ - command: req.body?.command, - runAsync: req.body?.runAsync, - async: req.body?.async, - }), - }, - }) - async executeSessionCommand( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Delete(':sandboxId/toolbox/process/session/:sessionId') - @ApiOperation({ - summary: '[DEPRECATED] Delete session', - description: 'Delete a specific session', - operationId: 'deleteSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Session deleted successfully', - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_DELETE_SESSION, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - params: (req) => ({ - sessionId: req.params.sessionId, - }), - }, - }) - async deleteSession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/process/session/:sessionId/command/:commandId') - @ApiOperation({ - summary: '[DEPRECATED] Get session command', - description: 'Get session command by ID', - operationId: 'getSessionCommand_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Session command retrieved successfully', - type: CommandDto, - }) - @ApiParam({ name: 'commandId', type: String, required: true }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getSessionCommand( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/process/session/:sessionId/command/:commandId/logs') - @ApiOperation({ - summary: '[DEPRECATED] Get command logs', - description: 'Get logs for a specific command in a session', - operationId: 'getSessionCommandLogs_deprecated', - deprecated: true, - }) - // When follow is true, the response is an octet stream - @ApiResponse({ - status: 200, - description: 'Command log stream marked with stdout and stderr prefixes', - content: { - 'text/plain': { - schema: { - type: 'string', - }, - }, - }, - }) - @ApiQuery({ name: 'follow', type: Boolean, required: false, description: 'Whether to stream the logs' }) - @ApiParam({ name: 'commandId', type: String, required: true }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getSessionCommandLogs( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return this.toolboxProxy(req, res, next) - } - - // PTY endpoints - @Get(':sandboxId/toolbox/process/pty') - @ApiOperation({ - summary: '[DEPRECATED] List PTY sessions', - description: 'List all active PTY sessions in the sandbox', - operationId: 'listPTYSessions_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'PTY sessions retrieved successfully', - type: PtyListResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async listPtySessions( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/process/pty') - @HttpCode(201) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Create PTY session', - description: 'Create a new PTY session in the sandbox', - operationId: 'createPTYSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 201, - description: 'PTY session created successfully', - type: PtyCreateResponseDto, - }) - @ApiBody({ - type: PtyCreateRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async createPtySession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/process/pty/:sessionId') - @ApiOperation({ - summary: '[DEPRECATED] Get PTY session', - description: 'Get PTY session information by ID', - operationId: 'getPTYSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'PTY session retrieved successfully', - type: PtySessionInfoDto, - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getPtySession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/process/pty/:sessionId/resize') - @ApiOperation({ - summary: '[DEPRECATED] Resize PTY session', - description: 'Resize a PTY session', - operationId: 'resizePTYSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'PTY session resized successfully', - type: PtySessionInfoDto, - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @ApiBody({ - type: PtyResizeRequestDto, - }) - async resizePtySession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Delete(':sandboxId/toolbox/process/pty/:sessionId') - @ApiOperation({ - summary: '[DEPRECATED] Delete PTY session', - description: 'Delete a PTY session and terminate the associated process', - operationId: 'deletePTYSession_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'PTY session deleted successfully', - }) - @ApiParam({ name: 'sessionId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async deletePtySession( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/lsp/completions') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Get Lsp Completions', - description: - 'The Completion request is sent from the client to the server to compute completion items at a given cursor position.', - operationId: 'LspCompletions_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - type: CompletionListDto, - }) - @ApiBody({ - type: LspCompletionParamsDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getLspCompletions( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/lsp/did-close') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Call Lsp DidClose', - description: - 'The document close notification is sent from the client to the server when the document got closed in the client.', - operationId: 'LspDidClose_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - }) - @ApiBody({ - type: LspDocumentRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async lspDidClose( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/lsp/did-open') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Call Lsp DidOpen', - description: - 'The document open notification is sent from the client to the server to signal newly opened text documents.', - operationId: 'LspDidOpen_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - }) - @ApiBody({ - type: LspDocumentRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async lspDidOpen( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/lsp/document-symbols') - @ApiOperation({ - summary: '[DEPRECATED] Call Lsp DocumentSymbols', - description: 'The document symbol request is sent from the client to the server.', - operationId: 'LspDocumentSymbols_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - type: [LspSymbolDto], - }) - @ApiQuery({ name: 'uri', type: String, required: true }) - @ApiQuery({ name: 'pathToProject', type: String, required: true }) - @ApiQuery({ name: 'languageId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getLspDocumentSymbols( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/lsp/start') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Start Lsp server', - description: 'Start Lsp server process inside sandbox project', - operationId: 'LspStart_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - }) - @ApiBody({ - type: LspServerRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async startLspServer( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/lsp/stop') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Stop Lsp server', - description: 'Stop Lsp server process inside sandbox project', - operationId: 'LspStop_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - }) - @ApiBody({ - type: LspServerRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async stopLspServer( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/lsp/workspace-symbols') - @ApiOperation({ - summary: '[DEPRECATED] Call Lsp WorkspaceSymbols', - description: - 'The workspace symbol request is sent from the client to the server to list project-wide symbols matching the query string.', - operationId: 'LspWorkspaceSymbols_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'OK', - type: [LspSymbolDto], - }) - @ApiQuery({ name: 'query', type: String, required: true }) - @ApiQuery({ name: 'pathToProject', type: String, required: true }) - @ApiQuery({ name: 'languageId', type: String, required: true }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getLspWorkspaceSymbols( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - // Computer Use endpoints - - // Computer use management endpoints - @Post(':sandboxId/toolbox/computeruse/start') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Start computer use processes', - description: 'Start all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)', - operationId: 'startComputerUse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Computer use processes started successfully', - type: ComputerUseStartResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_COMPUTER_USE_START, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - }) - async startComputerUse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/stop') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Stop computer use processes', - description: 'Stop all VNC desktop processes (Xvfb, xfce4, x11vnc, novnc)', - operationId: 'stopComputerUse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Computer use processes stopped successfully', - type: ComputerUseStopResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_COMPUTER_USE_STOP, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - }) - async stopComputerUse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/status') - @ApiOperation({ - summary: '[DEPRECATED] Get computer use status', - description: 'Get status of all VNC desktop processes', - operationId: 'getComputerUseStatus_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Computer use status retrieved successfully', - type: ComputerUseStatusResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getComputerUseStatus( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/process/:processName/status') - @ApiOperation({ - summary: '[DEPRECATED] Get process status', - description: 'Get status of a specific VNC process', - operationId: 'getProcessStatus_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Process status retrieved successfully', - type: ProcessStatusResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @ApiParam({ name: 'processName', type: String, required: true }) - async getProcessStatus( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/process/:processName/restart') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Restart process', - description: 'Restart a specific VNC process', - operationId: 'restartProcess_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Process restarted successfully', - type: ProcessRestartResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @ApiParam({ name: 'processName', type: String, required: true }) - @Audit({ - action: AuditAction.TOOLBOX_COMPUTER_USE_RESTART_PROCESS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.sandboxId, - requestMetadata: { - params: (req) => ({ - processName: req.params.processName, - }), - }, - }) - async restartProcess( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/process/:processName/logs') - @ApiOperation({ - summary: '[DEPRECATED] Get process logs', - description: 'Get logs for a specific VNC process', - operationId: 'getProcessLogs_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Process logs retrieved successfully', - type: ProcessLogsResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @ApiParam({ name: 'processName', type: String, required: true }) - async getProcessLogs( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/process/:processName/errors') - @ApiOperation({ - summary: '[DEPRECATED] Get process errors', - description: 'Get error logs for a specific VNC process', - operationId: 'getProcessErrors_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Process errors retrieved successfully', - type: ProcessErrorsResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - @ApiParam({ name: 'processName', type: String, required: true }) - async getProcessErrors( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - // Mouse endpoints - @Get(':sandboxId/toolbox/computeruse/mouse/position') - @ApiOperation({ - summary: '[DEPRECATED] Get mouse position', - description: 'Get current mouse cursor position', - operationId: 'getMousePosition_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Mouse position retrieved successfully', - type: MousePositionDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getMousePosition( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/mouse/move') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Move mouse', - description: 'Move mouse cursor to specified coordinates', - operationId: 'moveMouse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Mouse moved successfully', - type: MouseMoveResponseDto, - }) - @ApiBody({ - type: MouseMoveRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async moveMouse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/mouse/click') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Click mouse', - description: 'Click mouse at specified coordinates', - operationId: 'clickMouse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Mouse clicked successfully', - type: MouseClickResponseDto, - }) - @ApiBody({ - type: MouseClickRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async clickMouse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/mouse/drag') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Drag mouse', - description: 'Drag mouse from start to end coordinates', - operationId: 'dragMouse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Mouse dragged successfully', - type: MouseDragResponseDto, - }) - @ApiBody({ - type: MouseDragRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async dragMouse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/mouse/scroll') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Scroll mouse', - description: 'Scroll mouse at specified coordinates', - operationId: 'scrollMouse_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Mouse scrolled successfully', - type: MouseScrollResponseDto, - }) - @ApiBody({ - type: MouseScrollRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async scrollMouse( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - // Keyboard endpoints - @Post(':sandboxId/toolbox/computeruse/keyboard/type') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Type text', - description: 'Type text using keyboard', - operationId: 'typeText_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Text typed successfully', - }) - @ApiBody({ - type: KeyboardTypeRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async typeText( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/keyboard/key') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Press key', - description: 'Press a key with optional modifiers', - operationId: 'pressKey_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Key pressed successfully', - }) - @ApiBody({ - type: KeyboardPressRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async pressKey( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Post(':sandboxId/toolbox/computeruse/keyboard/hotkey') - @HttpCode(200) - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Press hotkey', - description: 'Press a hotkey combination', - operationId: 'pressHotkey_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Hotkey pressed successfully', - }) - @ApiBody({ - type: KeyboardHotkeyRequestDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async pressHotkey( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - // Screenshot endpoints - @Get(':sandboxId/toolbox/computeruse/screenshot') - @ApiOperation({ - summary: '[DEPRECATED] Take screenshot', - description: 'Take a screenshot of the entire screen', - operationId: 'takeScreenshot_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Screenshot taken successfully', - type: ScreenshotResponseDto, - }) - @ApiQuery({ name: 'show_cursor', type: Boolean, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async takeScreenshot( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/screenshot/region') - @ApiOperation({ - summary: '[DEPRECATED] Take region screenshot', - description: 'Take a screenshot of a specific region', - operationId: 'takeRegionScreenshot_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Region screenshot taken successfully', - type: RegionScreenshotResponseDto, - }) - @ApiQuery({ name: 'x', type: Number, required: true }) - @ApiQuery({ name: 'y', type: Number, required: true }) - @ApiQuery({ name: 'width', type: Number, required: true }) - @ApiQuery({ name: 'height', type: Number, required: true }) - @ApiQuery({ name: 'show_cursor', type: Boolean, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async takeRegionScreenshot( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/screenshot/compressed') - @ApiOperation({ - summary: '[DEPRECATED] Take compressed screenshot', - description: 'Take a compressed screenshot with format, quality, and scale options', - operationId: 'takeCompressedScreenshot_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Compressed screenshot taken successfully', - type: CompressedScreenshotResponseDto, - }) - @ApiQuery({ name: 'show_cursor', type: Boolean, required: false }) - @ApiQuery({ name: 'format', type: String, required: false }) - @ApiQuery({ name: 'quality', type: Number, required: false }) - @ApiQuery({ name: 'scale', type: Number, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async takeCompressedScreenshot( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/screenshot/region/compressed') - @ApiOperation({ - summary: '[DEPRECATED] Take compressed region screenshot', - description: 'Take a compressed screenshot of a specific region', - operationId: 'takeCompressedRegionScreenshot_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Compressed region screenshot taken successfully', - type: CompressedScreenshotResponseDto, - }) - @ApiQuery({ name: 'x', type: Number, required: true }) - @ApiQuery({ name: 'y', type: Number, required: true }) - @ApiQuery({ name: 'width', type: Number, required: true }) - @ApiQuery({ name: 'height', type: Number, required: true }) - @ApiQuery({ name: 'show_cursor', type: Boolean, required: false }) - @ApiQuery({ name: 'format', type: String, required: false }) - @ApiQuery({ name: 'quality', type: Number, required: false }) - @ApiQuery({ name: 'scale', type: Number, required: false }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async takeCompressedRegionScreenshot( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - // Display endpoints - @Get(':sandboxId/toolbox/computeruse/display/info') - @ApiOperation({ - summary: '[DEPRECATED] Get display info', - description: 'Get information about displays', - operationId: 'getDisplayInfo_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Display info retrieved successfully', - type: DisplayInfoResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getDisplayInfo( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } - - @Get(':sandboxId/toolbox/computeruse/display/windows') - @ApiOperation({ - summary: '[DEPRECATED] Get windows', - description: 'Get list of open windows', - operationId: 'getWindows_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Windows list retrieved successfully', - type: WindowsResponseDto, - }) - @ApiParam({ name: 'sandboxId', type: String, required: true }) - async getWindows( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - ): Promise { - return await this.toolboxProxy(req, res, next) - } -} diff --git a/apps/api/src/sandbox/controllers/workspace.deprecated.controller.ts b/apps/api/src/sandbox/controllers/workspace.deprecated.controller.ts deleted file mode 100644 index cd50c2931..000000000 --- a/apps/api/src/sandbox/controllers/workspace.deprecated.controller.ts +++ /dev/null @@ -1,633 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Controller, - Get, - Post, - Delete, - Body, - Param, - Query, - Logger, - UseGuards, - HttpCode, - UseInterceptors, - Put, - NotFoundException, - ForbiddenException, - Res, - Request, - RawBodyRequest, - Next, - ParseBoolPipe, -} from '@nestjs/common' -import Redis from 'ioredis' -import { CombinedAuthGuard } from '../../auth/combined-auth.guard' -import { SandboxService as WorkspaceService } from '../services/sandbox.service' -import { - ApiOAuth2, - ApiResponse, - ApiQuery, - ApiOperation, - ApiParam, - ApiTags, - ApiHeader, - ApiBearerAuth, -} from '@nestjs/swagger' -import { SandboxLabelsDto as WorkspaceLabelsDto } from '../dto/sandbox.dto' -import { WorkspaceDto } from '../dto/workspace.deprecated.dto' -import { RunnerService } from '../services/runner.service' -import { SandboxState as WorkspaceState } from '../enums/sandbox-state.enum' -import { ContentTypeInterceptor } from '../../common/interceptors/content-type.interceptors' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { SandboxAccessGuard as WorkspaceAccessGuard } from '../guards/sandbox-access.guard' -import { CustomHeaders } from '../../common/constants/header.constants' -import { AuthContext } from '../../common/decorators/auth-context.decorator' -import { OrganizationAuthContext } from '../../common/interfaces/auth-context.interface' -import { RequiredOrganizationResourcePermissions } from '../../organization/decorators/required-organization-resource-permissions.decorator' -import { OrganizationResourcePermission } from '../../organization/enums/organization-resource-permission.enum' -import { OrganizationResourceActionGuard } from '../../organization/guards/organization-resource-action.guard' -import { WorkspacePortPreviewUrlDto } from '../dto/workspace-port-preview-url.deprecated.dto' -import { IncomingMessage, ServerResponse } from 'http' -import { NextFunction } from 'http-proxy-middleware/dist/types' -import { LogProxy } from '../proxy/log-proxy' -import { CreateWorkspaceDto } from '../dto/create-workspace.deprecated.dto' -import { TypedConfigService } from '../../config/typed-config.service' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { Audit, MASKED_AUDIT_VALUE, TypedRequest } from '../../audit/decorators/audit.decorator' -import { AuditAction } from '../../audit/enums/audit-action.enum' -import { AuditTarget } from '../../audit/enums/audit-target.enum' -import { AuthenticatedRateLimitGuard } from '../../common/guards/authenticated-rate-limit.guard' - -@ApiTags('workspace') -@Controller('workspace') -@ApiHeader(CustomHeaders.ORGANIZATION_ID) -@UseGuards(CombinedAuthGuard, OrganizationResourceActionGuard, AuthenticatedRateLimitGuard) -@ApiOAuth2(['openid', 'profile', 'email']) -@ApiBearerAuth() -export class WorkspaceController { - private readonly logger = new Logger(WorkspaceController.name) - - constructor( - @InjectRedis() private readonly redis: Redis, - private readonly runnerService: RunnerService, - private readonly workspaceService: WorkspaceService, - private readonly configService: TypedConfigService, - ) {} - - @Get() - @ApiOperation({ - summary: '[DEPRECATED] List all workspaces', - operationId: 'listWorkspaces_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'List of all workspacees', - type: [WorkspaceDto], - }) - @ApiQuery({ - name: 'verbose', - required: false, - type: Boolean, - description: 'Include verbose output', - }) - @ApiQuery({ - name: 'labels', - type: String, - required: false, - example: '{"label1": "value1", "label2": "value2"}', - description: 'JSON encoded labels to filter by', - }) - async listWorkspacees( - @AuthContext() authContext: OrganizationAuthContext, - @Query('verbose') verbose?: boolean, - @Query('labels') labelsQuery?: string, - ): Promise { - const labels = labelsQuery ? JSON.parse(labelsQuery) : {} - const workspacees = await this.workspaceService.findAllDeprecated(authContext.organizationId, labels) - const dtos = workspacees.map(async (workspace) => { - const dto = WorkspaceDto.fromSandbox(workspace) - return dto - }) - return await Promise.all(dtos) - } - - @Post() - @HttpCode(200) // for BoxLite Api compatibility - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Create a new workspace', - operationId: 'createWorkspace_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'The workspace has been successfully created.', - type: WorkspaceDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @Audit({ - action: AuditAction.CREATE, - targetType: AuditTarget.SANDBOX, - targetIdFromResult: (result: WorkspaceDto) => result?.id, - requestMetadata: { - body: (req: TypedRequest) => ({ - image: req.body?.image, - user: req.body?.user, - env: req.body?.env - ? Object.fromEntries(Object.keys(req.body?.env).map((key) => [key, MASKED_AUDIT_VALUE])) - : undefined, - labels: req.body?.labels, - public: req.body?.public, - class: req.body?.class, - target: req.body?.target, - cpu: req.body?.cpu, - gpu: req.body?.gpu, - memory: req.body?.memory, - disk: req.body?.disk, - autoStopInterval: req.body?.autoStopInterval, - autoArchiveInterval: req.body?.autoArchiveInterval, - volumes: req.body?.volumes, - buildInfo: req.body?.buildInfo, - }), - }, - }) - async createWorkspace( - @AuthContext() authContext: OrganizationAuthContext, - @Body() createWorkspaceDto: CreateWorkspaceDto, - ): Promise { - if (createWorkspaceDto.buildInfo) { - throw new ForbiddenException('Build info is not supported in this deprecated API - please upgrade your client') - } - - const organization = authContext.organization - - const workspace = WorkspaceDto.fromSandboxDto( - await this.workspaceService.createFromSnapshot( - { - ...createWorkspaceDto, - snapshot: createWorkspaceDto.image, - }, - organization, - true, - ), - ) - - // Wait for the workspace to start - const sandboxState = await this.waitForWorkspaceState( - workspace.id, - WorkspaceState.STARTED, - 30000, // 30 seconds timeout - ) - workspace.state = sandboxState - - return workspace - } - - @Get(':workspaceId') - @ApiOperation({ - summary: '[DEPRECATED] Get workspace details', - operationId: 'getWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiQuery({ - name: 'verbose', - required: false, - type: Boolean, - description: 'Include verbose output', - }) - @ApiResponse({ - status: 200, - description: 'Workspace details', - type: WorkspaceDto, - }) - @UseGuards(WorkspaceAccessGuard) - async getWorkspace( - @Param('workspaceId') workspaceId: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - @Query('verbose') verbose?: boolean, - ): Promise { - const workspace = await this.workspaceService.findOne(workspaceId, true) - - return WorkspaceDto.fromSandbox(workspace) - } - - @Delete(':workspaceId') - @ApiOperation({ - summary: '[DEPRECATED] Delete workspace', - operationId: 'deleteWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Workspace has been deleted', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.DELETE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.DELETE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - }) - async removeWorkspace( - @Param('workspaceId') workspaceId: string, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - @Query('force') force?: boolean, - ): Promise { - await this.workspaceService.destroy(workspaceId) - } - - @Post(':workspaceId/start') - @HttpCode(200) - @ApiOperation({ - summary: '[DEPRECATED] Start workspace', - operationId: 'startWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Workspace has been started', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.START, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - }) - async startWorkspace( - @AuthContext() authContext: OrganizationAuthContext, - @Param('workspaceId') workspaceId: string, - ): Promise { - await this.workspaceService.start(workspaceId, authContext.organization) - } - - @Post(':workspaceId/stop') - @HttpCode(200) // for BoxLite Api compatibility - @ApiOperation({ - summary: '[DEPRECATED] Stop workspace', - operationId: 'stopWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Workspace has been stopped', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.STOP, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - }) - async stopWorkspace(@Param('workspaceId') workspaceId: string): Promise { - await this.workspaceService.stop(workspaceId) - } - - @Put(':workspaceId/labels') - @UseInterceptors(ContentTypeInterceptor) - @ApiOperation({ - summary: '[DEPRECATED] Replace workspace labels', - operationId: 'replaceLabelsWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Labels have been successfully replaced', - type: WorkspaceLabelsDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.REPLACE_LABELS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - requestMetadata: { - body: (req: TypedRequest) => ({ - labels: req.body?.labels, - }), - }, - }) - async replaceLabels( - @Param('workspaceId') workspaceId: string, - @Body() labelsDto: WorkspaceLabelsDto, - ): Promise { - const { labels } = await this.workspaceService.replaceLabels(workspaceId, labelsDto.labels) - return { labels } - } - - @Post(':workspaceId/backup') - @ApiOperation({ - summary: '[DEPRECATED] Create workspace backup', - operationId: 'createBackupWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Workspace backup has been initiated', - type: WorkspaceDto, - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.CREATE_BACKUP, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - }) - async createBackup(@Param('workspaceId') workspaceId: string): Promise { - await this.workspaceService.createBackup(workspaceId) - } - - @Post(':workspaceId/public/:isPublic') - @ApiOperation({ - summary: '[DEPRECATED] Update public status', - operationId: 'updatePublicStatusWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiParam({ - name: 'isPublic', - description: 'Public status to set', - type: 'boolean', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.UPDATE_PUBLIC_STATUS, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - requestMetadata: { - params: (req) => ({ - isPublic: req.params.isPublic, - }), - }, - }) - async updatePublicStatus( - @Param('workspaceId') workspaceId: string, - @Param('isPublic') isPublic: boolean, - ): Promise { - await this.workspaceService.updatePublicStatus(workspaceId, isPublic) - } - - @Post(':workspaceId/autostop/:interval') - @ApiOperation({ - summary: '[DEPRECATED] Set workspace auto-stop interval', - operationId: 'setAutostopIntervalWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiParam({ - name: 'interval', - description: 'Auto-stop interval in minutes (0 to disable)', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Auto-stop interval has been set', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.SET_AUTO_STOP_INTERVAL, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - requestMetadata: { - params: (req) => ({ - interval: req.params.interval, - }), - }, - }) - async setAutostopInterval( - @Param('workspaceId') workspaceId: string, - @Param('interval') interval: number, - ): Promise { - await this.workspaceService.setAutostopInterval(workspaceId, interval) - } - - @Post(':workspaceId/autoarchive/:interval') - @ApiOperation({ - summary: '[DEPRECATED] Set workspace auto-archive interval', - operationId: 'setAutoArchiveIntervalWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiParam({ - name: 'interval', - description: 'Auto-archive interval in minutes (0 means the maximum interval will be used)', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Auto-archive interval has been set', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.SET_AUTO_ARCHIVE_INTERVAL, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - requestMetadata: { - params: (req) => ({ - interval: req.params.interval, - }), - }, - }) - async setAutoArchiveInterval( - @Param('workspaceId') workspaceId: string, - @Param('interval') interval: number, - ): Promise { - await this.workspaceService.setAutoArchiveInterval(workspaceId, interval) - } - - @Post(':workspaceId/archive') - @HttpCode(200) - @ApiOperation({ - summary: '[DEPRECATED] Archive workspace', - operationId: 'archiveWorkspace_deprecated', - deprecated: true, - }) - @ApiResponse({ - status: 200, - description: 'Workspace has been archived', - }) - @RequiredOrganizationResourcePermissions([OrganizationResourcePermission.WRITE_SANDBOXES]) - @UseGuards(WorkspaceAccessGuard) - @Audit({ - action: AuditAction.ARCHIVE, - targetType: AuditTarget.SANDBOX, - targetIdFromRequest: (req) => req.params.workspaceId, - }) - async archiveWorkspace(@Param('workspaceId') workspaceId: string): Promise { - await this.workspaceService.archive(workspaceId) - } - - @Get(':workspaceId/ports/:port/preview-url') - @ApiOperation({ - summary: '[DEPRECATED] Get preview URL for a workspace port', - operationId: 'getPortPreviewUrlWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiParam({ - name: 'port', - description: 'Port number to get preview URL for', - type: 'number', - }) - @ApiResponse({ - status: 200, - description: 'Preview URL for the specified port', - type: WorkspacePortPreviewUrlDto, - }) - @UseGuards(WorkspaceAccessGuard) - async getPortPreviewUrl( - @Param('workspaceId') workspaceId: string, - @Param('port') port: number, - ): Promise { - if (port < 1 || port > 65535) { - throw new BadRequestError('Invalid port') - } - - const proxyDomain = this.configService.getOrThrow('proxy.domain') - const proxyProtocol = this.configService.getOrThrow('proxy.protocol') - const workspace = await this.workspaceService.findOne(workspaceId) - if (!workspace) { - throw new NotFoundException(`Workspace with ID ${workspaceId} not found`) - } - - return { - url: `${proxyProtocol}://${port}-${workspaceId}.${proxyDomain}`, - token: workspace.authToken, - } - } - - @Get(':workspaceId/build-logs') - @ApiOperation({ - summary: '[DEPRECATED] Get build logs', - operationId: 'getBuildLogsWorkspace_deprecated', - deprecated: true, - }) - @ApiParam({ - name: 'workspaceId', - description: 'ID of the workspace', - type: 'string', - }) - @ApiResponse({ - status: 200, - description: 'Build logs stream', - }) - @ApiQuery({ - name: 'follow', - required: false, - type: Boolean, - description: 'Whether to follow the logs stream', - }) - @UseGuards(WorkspaceAccessGuard) - async getBuildLogs( - @Request() req: RawBodyRequest, - @Res() res: ServerResponse, - @Next() next: NextFunction, - @Param('workspaceId') workspaceId: string, - @Query('follow', new ParseBoolPipe({ optional: true })) follow?: boolean, - ): Promise { - const workspace = await this.workspaceService.findOne(workspaceId) - if (!workspace || !workspace.runnerId) { - throw new NotFoundException(`Workspace with ID ${workspaceId} not found or has no runner assigned`) - } - - if (!workspace.buildInfo) { - throw new NotFoundException(`Workspace with ID ${workspaceId} has no build info`) - } - - const runner = await this.runnerService.findOneOrFail(workspace.runnerId) - - if (!runner.apiUrl) { - throw new NotFoundException(`Runner for workspace ${workspaceId} has no API URL`) - } - - const logProxy = new LogProxy( - runner.apiUrl, - workspace.buildInfo.snapshotRef.split(':')[0], - runner.apiKey, - follow === true, - req, - res, - next, - ) - return logProxy.create() - } - - private async waitForWorkspaceState( - workspaceId: string, - desiredState: WorkspaceState, - timeout: number, - ): Promise { - const startTime = Date.now() - - let workspaceState: WorkspaceState - while (Date.now() - startTime < timeout) { - const workspace = await this.workspaceService.findOne(workspaceId) - workspaceState = workspace.state - if ( - workspaceState === desiredState || - workspaceState === WorkspaceState.ERROR || - workspaceState === WorkspaceState.BUILD_FAILED - ) { - return workspaceState - } - await new Promise((resolve) => setTimeout(resolve, 100)) // Wait 100 ms before checking again - } - - return workspaceState - } -} diff --git a/apps/api/src/sandbox/dto/build-info.dto.ts b/apps/api/src/sandbox/dto/build-info.dto.ts deleted file mode 100644 index 2b9a4b788..000000000 --- a/apps/api/src/sandbox/dto/build-info.dto.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'BuildInfo' }) -export class BuildInfoDto { - @ApiPropertyOptional({ - description: 'The Dockerfile content used for the build', - example: 'FROM node:14\nWORKDIR /app\nCOPY . .\nRUN npm install\nCMD ["npm", "start"]', - }) - dockerfileContent?: string - - @ApiPropertyOptional({ - description: 'The context hashes used for the build', - type: [String], - example: ['hash1', 'hash2'], - }) - contextHashes?: string[] - - @ApiProperty({ - description: 'The creation timestamp', - }) - createdAt: Date - - @ApiProperty({ - description: 'The last update timestamp', - }) - updatedAt: Date - - @ApiProperty({ - description: 'The snapshot reference', - example: 'boxlite-ai/sandbox:latest', - }) - snapshotRef: string -} diff --git a/apps/api/src/sandbox/dto/create-build-info.dto.ts b/apps/api/src/sandbox/dto/create-build-info.dto.ts deleted file mode 100644 index dc7971288..000000000 --- a/apps/api/src/sandbox/dto/create-build-info.dto.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { IsNotEmpty, IsOptional, IsString } from 'class-validator' - -@ApiSchema({ name: 'CreateBuildInfo' }) -export class CreateBuildInfoDto { - @ApiProperty({ - description: 'The Dockerfile content used for the build', - example: 'FROM node:14\nWORKDIR /app\nCOPY . .\nRUN npm install\nCMD ["npm", "start"]', - }) - @IsString() - @IsNotEmpty() - dockerfileContent: string - - @ApiPropertyOptional({ - description: 'The context hashes used for the build', - type: [String], - example: ['hash1', 'hash2'], - }) - @IsString({ each: true }) - @IsOptional() - contextHashes?: string[] -} diff --git a/apps/api/src/sandbox/dto/create-sandbox.dto.ts b/apps/api/src/sandbox/dto/create-sandbox.dto.ts deleted file mode 100644 index c4c211e67..000000000 --- a/apps/api/src/sandbox/dto/create-sandbox.dto.ts +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsEnum, IsObject, IsOptional, IsString, IsNumber, IsBoolean, IsArray } from 'class-validator' -import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { SandboxClass } from '../enums/sandbox-class.enum' -import { SandboxVolume } from './sandbox.dto' -import { CreateBuildInfoDto } from './create-build-info.dto' - -@ApiSchema({ name: 'CreateSandbox' }) -export class CreateSandboxDto { - @ApiPropertyOptional({ - description: 'The name of the sandbox. If not provided, the sandbox ID will be used as the name', - example: 'MySandbox', - }) - @IsOptional() - @IsString() - name?: string - - @ApiPropertyOptional({ - description: 'The ID or name of the snapshot used for the sandbox', - example: 'ubuntu-4vcpu-8ram-100gb', - }) - @IsOptional() - @IsString() - snapshot?: string - - @ApiPropertyOptional({ - description: 'The user associated with the project', - example: 'boxlite', - }) - @IsOptional() - @IsString() - user?: string - - @ApiPropertyOptional({ - description: 'Environment variables for the sandbox', - type: 'object', - additionalProperties: { type: 'string' }, - example: { NODE_ENV: 'production' }, - }) - @IsOptional() - @IsObject() - env?: { [key: string]: string } - - @ApiPropertyOptional({ - description: 'Labels for the sandbox', - type: 'object', - additionalProperties: { type: 'string' }, - example: { 'boxlite.io/public': 'true' }, - }) - @IsOptional() - @IsObject() - labels?: { [key: string]: string } - - @ApiPropertyOptional({ - description: 'Whether the sandbox http preview is publicly accessible', - example: false, - }) - @IsOptional() - @IsBoolean() - public?: boolean - - @ApiPropertyOptional({ - description: 'Whether to block all network access for the sandbox', - example: false, - }) - @IsOptional() - @IsBoolean() - networkBlockAll?: boolean - - @ApiPropertyOptional({ - description: 'Comma-separated list of allowed CIDR network addresses for the sandbox', - example: '192.168.1.0/16,10.0.0.0/24', - }) - @IsOptional() - @IsString() - networkAllowList?: string - - @ApiPropertyOptional({ - description: 'The sandbox class type', - enum: SandboxClass, - example: Object.values(SandboxClass)[0], - }) - @IsOptional() - @IsEnum(SandboxClass) - class?: SandboxClass - - @ApiPropertyOptional({ - description: 'The target (region) where the sandbox will be created', - example: 'us', - }) - @IsOptional() - @IsString() - target?: string - - @ApiPropertyOptional({ - description: 'CPU cores allocated to the sandbox', - example: 2, - type: 'integer', - }) - @IsOptional() - @IsNumber() - cpu?: number - - @ApiPropertyOptional({ - description: 'GPU units allocated to the sandbox', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - gpu?: number - - @ApiPropertyOptional({ - description: 'Memory allocated to the sandbox in GB', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - memory?: number - - @ApiPropertyOptional({ - description: 'Disk space allocated to the sandbox in GB', - example: 3, - type: 'integer', - }) - @IsOptional() - @IsNumber() - disk?: number - - @ApiPropertyOptional({ - description: 'Auto-stop interval in minutes (0 means disabled)', - example: 30, - type: 'integer', - }) - @IsOptional() - @IsNumber() - autoStopInterval?: number - - @ApiPropertyOptional({ - description: 'Auto-archive interval in minutes (0 means the maximum interval will be used)', - example: 7 * 24 * 60, - type: 'integer', - }) - @IsOptional() - @IsNumber() - autoArchiveInterval?: number - - @ApiPropertyOptional({ - description: - 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', - example: 30, - type: 'integer', - }) - @IsOptional() - @IsNumber() - autoDeleteInterval?: number - - @ApiPropertyOptional({ - description: 'Array of volumes to attach to the sandbox', - type: [SandboxVolume], - required: false, - }) - @IsOptional() - @IsArray() - volumes?: SandboxVolume[] - - @ApiPropertyOptional({ - description: 'Build information for the sandbox', - type: CreateBuildInfoDto, - }) - @IsOptional() - @IsObject() - buildInfo?: CreateBuildInfoDto -} diff --git a/apps/api/src/sandbox/dto/create-snapshot.dto.ts b/apps/api/src/sandbox/dto/create-snapshot.dto.ts deleted file mode 100644 index b47b088e3..000000000 --- a/apps/api/src/sandbox/dto/create-snapshot.dto.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { IsArray, IsObject, IsBoolean, IsNumber, IsOptional, IsString } from 'class-validator' -import { CreateBuildInfoDto } from './create-build-info.dto' - -@ApiSchema({ name: 'CreateSnapshot' }) -export class CreateSnapshotDto { - @ApiProperty({ - description: 'The name of the snapshot', - example: 'ubuntu-4vcpu-8ram-100gb', - }) - @IsString() - name: string - - @ApiPropertyOptional({ - description: 'The image name of the snapshot', - example: 'ubuntu:22.04', - }) - @IsOptional() - @IsString() - imageName?: string - - @ApiPropertyOptional({ - description: 'The entrypoint command for the snapshot', - example: 'sleep infinity', - }) - @IsString({ - each: true, - }) - @IsArray() - @IsOptional() - entrypoint?: string[] - - @ApiPropertyOptional({ - description: 'Whether the snapshot is general', - }) - @IsBoolean() - @IsOptional() - general?: boolean - - @ApiPropertyOptional({ - description: 'CPU cores allocated to the resulting sandbox', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - cpu?: number - - @ApiPropertyOptional({ - description: 'GPU units allocated to the resulting sandbox', - example: 0, - type: 'integer', - }) - @IsOptional() - @IsNumber() - gpu?: number - - @ApiPropertyOptional({ - description: 'Memory allocated to the resulting sandbox in GB', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - memory?: number - - @ApiPropertyOptional({ - description: 'Disk space allocated to the sandbox in GB', - example: 3, - type: 'integer', - }) - @IsOptional() - @IsNumber() - disk?: number - - @ApiPropertyOptional({ - description: 'Build information for the snapshot', - type: CreateBuildInfoDto, - }) - @IsOptional() - @IsObject() - buildInfo?: CreateBuildInfoDto - - @ApiPropertyOptional({ - description: - 'ID of the region where the snapshot will be available. Defaults to organization default region if not specified.', - }) - @IsOptional() - @IsString() - regionId?: string -} diff --git a/apps/api/src/sandbox/dto/create-workspace.deprecated.dto.ts b/apps/api/src/sandbox/dto/create-workspace.deprecated.dto.ts deleted file mode 100644 index 06c6160a0..000000000 --- a/apps/api/src/sandbox/dto/create-workspace.deprecated.dto.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsEnum, IsObject, IsOptional, IsString, IsNumber, IsBoolean } from 'class-validator' -import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { SandboxClass } from '../enums/sandbox-class.enum' -import { SandboxVolume } from './sandbox.dto' -import { CreateBuildInfoDto } from './create-build-info.dto' - -enum RunnerRegion { - EU = 'eu', - US = 'us', - ASIA = 'asia', -} -@ApiSchema({ name: 'CreateWorkspace' }) -export class CreateWorkspaceDto { - @ApiPropertyOptional({ - description: 'The image used for the workspace', - example: 'boxlite-ai/workspace:latest', - }) - @IsOptional() - @IsString() - image?: string - - @ApiPropertyOptional({ - description: 'The user associated with the project', - example: 'boxlite', - }) - @IsOptional() - @IsString() - user?: string - - @ApiPropertyOptional({ - description: 'Environment variables for the workspace', - type: 'object', - additionalProperties: { type: 'string' }, - example: { NODE_ENV: 'production' }, - }) - @IsOptional() - @IsObject() - env?: { [key: string]: string } - - @ApiPropertyOptional({ - description: 'Labels for the workspace', - type: 'object', - additionalProperties: { type: 'string' }, - example: { 'boxlite.io/public': 'true' }, - }) - @IsOptional() - @IsObject() - labels?: { [key: string]: string } - - @ApiPropertyOptional({ - description: 'Whether the workspace http preview is publicly accessible', - example: false, - }) - @IsOptional() - @IsBoolean() - public?: boolean - - @ApiPropertyOptional({ - description: 'The workspace class type', - enum: SandboxClass, - example: Object.values(SandboxClass)[0], - }) - @IsOptional() - @IsEnum(SandboxClass) - class?: SandboxClass - - @ApiPropertyOptional({ - description: 'The target (region) where the workspace will be created', - enum: RunnerRegion, - example: Object.values(RunnerRegion)[0], - }) - @IsOptional() - @IsEnum(RunnerRegion) - target?: RunnerRegion - - @ApiPropertyOptional({ - description: 'CPU cores allocated to the workspace', - example: 2, - type: 'integer', - }) - @IsOptional() - @IsNumber() - cpu?: number - - @ApiPropertyOptional({ - description: 'GPU units allocated to the workspace', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - gpu?: number - - @ApiPropertyOptional({ - description: 'Memory allocated to the workspace in GB', - example: 1, - type: 'integer', - }) - @IsOptional() - @IsNumber() - memory?: number - - @ApiPropertyOptional({ - description: 'Disk space allocated to the workspace in GB', - example: 3, - type: 'integer', - }) - @IsOptional() - @IsNumber() - disk?: number - - @ApiPropertyOptional({ - description: 'Auto-stop interval in minutes (0 means disabled)', - example: 30, - type: 'integer', - }) - @IsOptional() - @IsNumber() - autoStopInterval?: number - - @ApiPropertyOptional({ - description: 'Auto-archive interval in minutes (0 means the maximum interval will be used)', - example: 7 * 24 * 60, - type: 'integer', - }) - @IsOptional() - @IsNumber() - autoArchiveInterval?: number - - @ApiPropertyOptional({ - description: 'Array of volumes to attach to the workspace', - type: [SandboxVolume], - required: false, - }) - @IsOptional() - volumes?: SandboxVolume[] - - @ApiPropertyOptional({ - description: 'Build information for the workspace', - type: CreateBuildInfoDto, - }) - @IsOptional() - @IsObject() - buildInfo?: CreateBuildInfoDto -} diff --git a/apps/api/src/sandbox/dto/job-type-map.dto.ts b/apps/api/src/sandbox/dto/job-type-map.dto.ts deleted file mode 100644 index d53fb4fe7..000000000 --- a/apps/api/src/sandbox/dto/job-type-map.dto.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { JobType } from '../enums/job-type.enum' -import { ResourceType } from '../enums/resource-type.enum' - -/** - * Type-safe mapping between JobType and its corresponding ResourceType(s) + Payload - * This ensures compile-time safety when creating jobs - * resourceType is an array of allowed ResourceTypes - the user can supply any of them - */ -export interface JobTypeMap { - [JobType.CREATE_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.START_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.STOP_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.DESTROY_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.RESIZE_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.CREATE_BACKUP]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.BUILD_SNAPSHOT]: { - resourceType: [ResourceType.SANDBOX, ResourceType.SNAPSHOT] - } - [JobType.PULL_SNAPSHOT]: { - resourceType: [ResourceType.SNAPSHOT] - } - [JobType.REMOVE_SNAPSHOT]: { - resourceType: [ResourceType.SNAPSHOT] - } - [JobType.UPDATE_SANDBOX_NETWORK_SETTINGS]: { - resourceType: [ResourceType.SANDBOX] - } - [JobType.INSPECT_SNAPSHOT_IN_REGISTRY]: { - resourceType: [ResourceType.SNAPSHOT] - } - [JobType.RECOVER_SANDBOX]: { - resourceType: [ResourceType.SANDBOX] - } -} - -/** - * Helper type to extract the allowed resource types for a given JobType as a union - */ -export type ResourceTypeForJobType = JobTypeMap[T]['resourceType'][number] diff --git a/apps/api/src/sandbox/dto/list-sandboxes-query.dto.ts b/apps/api/src/sandbox/dto/list-sandboxes-query.dto.ts deleted file mode 100644 index c5a08b775..000000000 --- a/apps/api/src/sandbox/dto/list-sandboxes-query.dto.ts +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { IsBoolean, IsInt, IsOptional, IsString, IsArray, IsEnum, IsDate, Min } from 'class-validator' -import { Type } from 'class-transformer' -import { SandboxState } from '../enums/sandbox-state.enum' -import { ToArray } from '../../common/decorators/to-array.decorator' -import { PageNumber } from '../../common/decorators/page-number.decorator' -import { PageLimit } from '../../common/decorators/page-limit.decorator' - -export enum SandboxSortField { - ID = 'id', - NAME = 'name', - STATE = 'state', - SNAPSHOT = 'snapshot', - REGION = 'region', - UPDATED_AT = 'updatedAt', - CREATED_AT = 'createdAt', -} - -export enum SandboxSortDirection { - ASC = 'asc', - DESC = 'desc', -} - -export const DEFAULT_SANDBOX_SORT_FIELD = SandboxSortField.CREATED_AT -export const DEFAULT_SANDBOX_SORT_DIRECTION = SandboxSortDirection.DESC - -const VALID_QUERY_STATES = Object.values(SandboxState).filter((state) => state !== SandboxState.DESTROYED) - -@ApiSchema({ name: 'ListSandboxesQuery' }) -export class ListSandboxesQueryDto { - @PageNumber(1) - page = 1 - - @PageLimit(100) - limit = 100 - - @ApiProperty({ - name: 'id', - description: 'Filter by partial ID match', - required: false, - type: String, - example: 'abc123', - }) - @IsOptional() - @IsString() - id?: string - - @ApiProperty({ - name: 'name', - description: 'Filter by partial name match', - required: false, - type: String, - example: 'abc123', - }) - @IsOptional() - @IsString() - name?: string - - @ApiProperty({ - name: 'labels', - description: 'JSON encoded labels to filter by', - required: false, - type: String, - example: '{"label1": "value1", "label2": "value2"}', - }) - @IsOptional() - @IsString() - labels?: string - - @ApiProperty({ - name: 'includeErroredDeleted', - description: 'Include results with errored state and deleted desired state', - required: false, - type: Boolean, - default: false, - }) - @IsOptional() - @Type(() => Boolean) - @IsBoolean() - includeErroredDeleted?: boolean - - @ApiProperty({ - name: 'states', - description: 'List of states to filter by', - required: false, - enum: VALID_QUERY_STATES, - isArray: true, - }) - @IsOptional() - @ToArray() - @IsArray() - @IsEnum(VALID_QUERY_STATES, { - each: true, - message: `each value must be one of the following values: ${VALID_QUERY_STATES.join(', ')}`, - }) - states?: SandboxState[] - - @ApiProperty({ - name: 'snapshots', - description: 'List of snapshot names to filter by', - required: false, - type: [String], - }) - @IsOptional() - @ToArray() - @IsArray() - @IsString({ each: true }) - snapshots?: string[] - - @ApiProperty({ - name: 'regions', - description: 'List of regions to filter by', - required: false, - type: [String], - }) - @IsOptional() - @ToArray() - @IsArray() - @IsString({ each: true }) - regions?: string[] - - @ApiProperty({ - name: 'minCpu', - description: 'Minimum CPU', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - minCpu?: number - - @ApiProperty({ - name: 'maxCpu', - description: 'Maximum CPU', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - maxCpu?: number - - @ApiProperty({ - name: 'minMemoryGiB', - description: 'Minimum memory in GiB', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - minMemoryGiB?: number - - @ApiProperty({ - name: 'maxMemoryGiB', - description: 'Maximum memory in GiB', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - maxMemoryGiB?: number - - @ApiProperty({ - name: 'minDiskGiB', - description: 'Minimum disk space in GiB', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - minDiskGiB?: number - - @ApiProperty({ - name: 'maxDiskGiB', - description: 'Maximum disk space in GiB', - required: false, - type: Number, - minimum: 1, - }) - @IsOptional() - @Type(() => Number) - @IsInt() - @Min(1) - maxDiskGiB?: number - - @ApiProperty({ - name: 'lastEventAfter', - description: 'Include items with last event after this timestamp', - required: false, - type: String, - format: 'date-time', - example: '2024-01-01T00:00:00Z', - }) - @IsOptional() - @Type(() => Date) - @IsDate() - lastEventAfter?: Date - - @ApiProperty({ - name: 'lastEventBefore', - description: 'Include items with last event before this timestamp', - required: false, - type: String, - format: 'date-time', - example: '2024-12-31T23:59:59Z', - }) - @IsOptional() - @Type(() => Date) - @IsDate() - lastEventBefore?: Date - - @ApiProperty({ - name: 'sort', - description: 'Field to sort by', - required: false, - enum: SandboxSortField, - default: DEFAULT_SANDBOX_SORT_FIELD, - }) - @IsOptional() - @IsEnum(SandboxSortField) - sort = DEFAULT_SANDBOX_SORT_FIELD - - @ApiProperty({ - name: 'order', - description: 'Direction to sort by', - required: false, - enum: SandboxSortDirection, - default: DEFAULT_SANDBOX_SORT_DIRECTION, - }) - @IsOptional() - @IsEnum(SandboxSortDirection) - order = DEFAULT_SANDBOX_SORT_DIRECTION -} diff --git a/apps/api/src/sandbox/dto/list-snapshots-query.dto.ts b/apps/api/src/sandbox/dto/list-snapshots-query.dto.ts deleted file mode 100644 index 21544ed61..000000000 --- a/apps/api/src/sandbox/dto/list-snapshots-query.dto.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { IsOptional, IsString, IsEnum } from 'class-validator' -import { PageNumber } from '../../common/decorators/page-number.decorator' -import { PageLimit } from '../../common/decorators/page-limit.decorator' - -export enum SnapshotSortField { - NAME = 'name', - STATE = 'state', - LAST_USED_AT = 'lastUsedAt', - CREATED_AT = 'createdAt', -} - -export enum SnapshotSortDirection { - ASC = 'asc', - DESC = 'desc', -} - -@ApiSchema({ name: 'ListSnapshotsQuery' }) -export class ListSnapshotsQueryDto { - @PageNumber(1) - page = 1 - - @PageLimit(100) - limit = 100 - - @ApiProperty({ - name: 'name', - description: 'Filter by partial name match', - required: false, - type: String, - example: 'abc123', - }) - @IsOptional() - @IsString() - name?: string - - @ApiProperty({ - name: 'sort', - description: 'Field to sort by', - required: false, - enum: SnapshotSortField, - default: SnapshotSortField.LAST_USED_AT, - }) - @IsOptional() - @IsEnum(SnapshotSortField) - sort = SnapshotSortField.LAST_USED_AT - - @ApiProperty({ - name: 'order', - description: 'Direction to sort by', - required: false, - enum: SnapshotSortDirection, - default: SnapshotSortDirection.DESC, - }) - @IsOptional() - @IsEnum(SnapshotSortDirection) - order = SnapshotSortDirection.DESC -} diff --git a/apps/api/src/sandbox/dto/paginated-sandboxes.dto.ts b/apps/api/src/sandbox/dto/paginated-sandboxes.dto.ts deleted file mode 100644 index 7c8a90c8d..000000000 --- a/apps/api/src/sandbox/dto/paginated-sandboxes.dto.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { SandboxDto } from './sandbox.dto' - -@ApiSchema({ name: 'PaginatedSandboxes' }) -export class PaginatedSandboxesDto { - @ApiProperty({ type: [SandboxDto] }) - items: SandboxDto[] - - @ApiProperty() - total: number - - @ApiProperty() - page: number - - @ApiProperty() - totalPages: number -} diff --git a/apps/api/src/sandbox/dto/paginated-snapshots.dto.ts b/apps/api/src/sandbox/dto/paginated-snapshots.dto.ts deleted file mode 100644 index aeec78209..000000000 --- a/apps/api/src/sandbox/dto/paginated-snapshots.dto.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { SnapshotDto } from './snapshot.dto' - -@ApiSchema({ name: 'PaginatedSnapshots' }) -export class PaginatedSnapshotsDto { - @ApiProperty({ type: [SnapshotDto] }) - items: SnapshotDto[] - - @ApiProperty() - total: number - - @ApiProperty() - page: number - - @ApiProperty() - totalPages: number -} diff --git a/apps/api/src/sandbox/dto/registry-push-access-dto.ts b/apps/api/src/sandbox/dto/registry-push-access-dto.ts deleted file mode 100644 index c3e19d30e..000000000 --- a/apps/api/src/sandbox/dto/registry-push-access-dto.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty } from '@nestjs/swagger' - -export class RegistryPushAccessDto { - @ApiProperty({ - description: 'Temporary username for registry authentication', - example: 'temp-user-123', - }) - username: string - - @ApiProperty({ - description: 'Temporary secret for registry authentication', - example: 'eyJhbGciOiJIUzI1NiIs...', - }) - secret: string - - @ApiProperty({ - description: 'Registry URL', - example: 'registry.example.com', - }) - registryUrl: string - - @ApiProperty({ - description: 'Registry ID', - example: '123e4567-e89b-12d3-a456-426614174000', - }) - registryId: string - - @ApiProperty({ - description: 'Registry project ID', - example: 'library', - }) - project: string - - @ApiProperty({ - description: 'Token expiration time in ISO format', - example: '2023-12-31T23:59:59Z', - }) - expiresAt: string -} diff --git a/apps/api/src/sandbox/dto/resize-sandbox.dto.ts b/apps/api/src/sandbox/dto/resize-sandbox.dto.ts deleted file mode 100644 index 65697cc75..000000000 --- a/apps/api/src/sandbox/dto/resize-sandbox.dto.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsOptional, IsNumber, Min } from 'class-validator' -import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'ResizeSandbox' }) -export class ResizeSandboxDto { - @ApiPropertyOptional({ - description: 'CPU cores to allocate to the sandbox (minimum: 1)', - example: 2, - type: 'integer', - minimum: 1, - }) - @IsOptional() - @IsNumber() - @Min(1) - cpu?: number - - @ApiPropertyOptional({ - description: 'Memory in GB to allocate to the sandbox (minimum: 1)', - example: 4, - type: 'integer', - minimum: 1, - }) - @IsOptional() - @IsNumber() - @Min(1) - memory?: number - - @ApiPropertyOptional({ - description: 'Disk space in GB to allocate to the sandbox (can only be increased)', - example: 20, - type: 'integer', - minimum: 1, - }) - @IsOptional() - @IsNumber() - @Min(1) - disk?: number -} diff --git a/apps/api/src/sandbox/dto/runner-snapshot.dto.ts b/apps/api/src/sandbox/dto/runner-snapshot.dto.ts deleted file mode 100644 index dd42996be..000000000 --- a/apps/api/src/sandbox/dto/runner-snapshot.dto.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' - -export class RunnerSnapshotDto { - @ApiProperty({ - description: 'Runner snapshot ID', - example: '123e4567-e89b-12d3-a456-426614174000', - }) - runnerSnapshotId: string - - @ApiProperty({ - description: 'Runner ID', - example: '123e4567-e89b-12d3-a456-426614174000', - }) - runnerId: string - - @ApiPropertyOptional({ - description: 'Runner domain', - example: 'runner.example.com', - }) - runnerDomain?: string - - constructor(runnerSnapshotId: string, runnerId: string, runnerDomain: string | null) { - this.runnerSnapshotId = runnerSnapshotId - this.runnerId = runnerId - this.runnerDomain = runnerDomain ?? undefined - } -} diff --git a/apps/api/src/sandbox/dto/runner-status.dto.ts b/apps/api/src/sandbox/dto/runner-status.dto.ts deleted file mode 100644 index 6b63ce116..000000000 --- a/apps/api/src/sandbox/dto/runner-status.dto.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'RunnerStatus' }) -export class RunnerStatusDto { - @ApiProperty({ - description: 'Current CPU usage percentage', - example: 45.6, - }) - currentCpuUsagePercentage: number - - @ApiProperty({ - description: 'Current RAM usage percentage', - example: 68.2, - }) - currentMemoryUsagePercentage: number - - @ApiProperty({ - description: 'Current disk usage percentage', - example: 33.8, - }) - currentDiskUsagePercentage: number - - @ApiProperty({ - description: 'Current allocated CPU', - example: 4000, - }) - currentAllocatedCpu: number - - @ApiProperty({ - description: 'Current allocated memory', - example: 8000, - }) - currentAllocatedMemoryGiB: number - - @ApiProperty({ - description: 'Current allocated disk', - example: 50000, - }) - currentAllocatedDiskGiB: number - - @ApiProperty({ - description: 'Current snapshot count', - example: 12, - }) - currentSnapshotCount: number - - @ApiProperty({ - description: 'Runner status', - example: 'ok', - }) - status: string - - @ApiProperty({ - description: 'Runner version', - example: '0.0.1', - }) - version: string -} diff --git a/apps/api/src/sandbox/dto/sandbox.dto.ts b/apps/api/src/sandbox/dto/sandbox.dto.ts deleted file mode 100644 index d2af826d1..000000000 --- a/apps/api/src/sandbox/dto/sandbox.dto.ts +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { SandboxState } from '../enums/sandbox-state.enum' -import { IsEnum, IsOptional } from 'class-validator' -import { BackupState } from '../enums/backup-state.enum' -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { BuildInfoDto } from './build-info.dto' -import { SandboxClass } from '../enums/sandbox-class.enum' - -@ApiSchema({ name: 'SandboxVolume' }) -export class SandboxVolume { - @ApiProperty({ - description: 'The ID of the volume', - example: 'volume123', - }) - volumeId: string - - @ApiProperty({ - description: 'The mount path for the volume', - example: '/data', - }) - mountPath: string - - @ApiPropertyOptional({ - description: - 'Optional subpath within the volume to mount. When specified, only this S3 prefix will be accessible. When omitted, the entire volume is mounted.', - example: 'users/alice', - }) - subpath?: string -} - -@ApiSchema({ name: 'Sandbox' }) -export class SandboxDto { - @ApiProperty({ - description: 'The ID of the sandbox', - example: 'sandbox123', - }) - id: string - - @ApiProperty({ - description: 'The organization ID of the sandbox', - example: 'organization123', - }) - organizationId: string - - @ApiProperty({ - description: 'The name of the sandbox', - example: 'MySandbox', - }) - name: string - - @ApiPropertyOptional({ - description: 'The snapshot used for the sandbox', - example: 'boxlite-ai/sandbox:latest', - }) - snapshot: string - - @ApiProperty({ - description: 'The user associated with the project', - example: 'boxlite', - }) - user: string - - @ApiProperty({ - description: 'Environment variables for the sandbox', - type: 'object', - additionalProperties: { type: 'string' }, - example: { NODE_ENV: 'production' }, - }) - env: Record - - @ApiProperty({ - description: 'Labels for the sandbox', - type: 'object', - additionalProperties: { type: 'string' }, - example: { 'boxlite.io/public': 'true' }, - }) - labels: { [key: string]: string } - - @ApiProperty({ - description: 'Whether the sandbox http preview is public', - example: false, - }) - public: boolean - - @ApiProperty({ - description: 'Whether to block all network access for the sandbox', - example: false, - }) - networkBlockAll: boolean - - @ApiPropertyOptional({ - description: 'Comma-separated list of allowed CIDR network addresses for the sandbox', - example: '192.168.1.0/16,10.0.0.0/24', - }) - networkAllowList?: string - - @ApiProperty({ - description: 'The target environment for the sandbox', - example: 'local', - }) - target: string - - @ApiProperty({ - description: 'The CPU quota for the sandbox', - example: 2, - }) - cpu: number - - @ApiProperty({ - description: 'The GPU quota for the sandbox', - example: 0, - }) - gpu: number - - @ApiProperty({ - description: 'The memory quota for the sandbox', - example: 4, - }) - memory: number - - @ApiProperty({ - description: 'The disk quota for the sandbox', - example: 10, - }) - disk: number - - @ApiPropertyOptional({ - description: 'The state of the sandbox', - enum: SandboxState, - enumName: 'SandboxState', - example: Object.values(SandboxState)[0], - required: false, - }) - @IsEnum(SandboxState) - @IsOptional() - state?: SandboxState - - @ApiPropertyOptional({ - description: 'The desired state of the sandbox', - enum: SandboxDesiredState, - enumName: 'SandboxDesiredState', - example: Object.values(SandboxDesiredState)[0], - required: false, - }) - @IsEnum(SandboxDesiredState) - @IsOptional() - desiredState?: SandboxDesiredState - - @ApiPropertyOptional({ - description: 'The error reason of the sandbox', - example: 'The sandbox is not running', - required: false, - }) - @IsOptional() - errorReason?: string - - @ApiPropertyOptional({ - description: 'Whether the sandbox error is recoverable.', - example: true, - required: false, - }) - @IsOptional() - recoverable?: boolean - - @ApiPropertyOptional({ - description: 'The state of the backup', - enum: BackupState, - example: Object.values(BackupState)[0], - required: false, - }) - @IsEnum(BackupState) - @IsOptional() - backupState?: BackupState - - @ApiPropertyOptional({ - description: 'The creation timestamp of the last backup', - example: '2024-10-01T12:00:00Z', - required: false, - }) - @IsOptional() - backupCreatedAt?: string - - @ApiPropertyOptional({ - description: 'Auto-stop interval in minutes (0 means disabled)', - example: 30, - required: false, - }) - @IsOptional() - autoStopInterval?: number - - @ApiPropertyOptional({ - description: 'Auto-archive interval in minutes', - example: 7 * 24 * 60, - required: false, - }) - @IsOptional() - autoArchiveInterval?: number - - @ApiPropertyOptional({ - description: - 'Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)', - example: 30, - required: false, - }) - @IsOptional() - autoDeleteInterval?: number - - @ApiPropertyOptional({ - description: 'Array of volumes attached to the sandbox', - type: [SandboxVolume], - required: false, - }) - @IsOptional() - volumes?: SandboxVolume[] - - @ApiPropertyOptional({ - description: 'Build information for the sandbox', - type: BuildInfoDto, - required: false, - }) - @IsOptional() - buildInfo?: BuildInfoDto - - @ApiPropertyOptional({ - description: 'The creation timestamp of the sandbox', - example: '2024-10-01T12:00:00Z', - required: false, - }) - @IsOptional() - createdAt?: string - - @ApiPropertyOptional({ - description: 'The last update timestamp of the sandbox', - example: '2024-10-01T12:00:00Z', - required: false, - }) - @IsOptional() - updatedAt?: string - - @ApiPropertyOptional({ - description: 'The class of the sandbox', - enum: SandboxClass, - example: Object.values(SandboxClass)[0], - required: false, - deprecated: true, - }) - @IsEnum(SandboxClass) - @IsOptional() - class?: SandboxClass - - @ApiPropertyOptional({ - description: 'The version of the daemon running in the sandbox', - example: '1.0.0', - required: false, - }) - @IsOptional() - daemonVersion?: string - - @ApiPropertyOptional({ - description: 'The runner ID of the sandbox', - example: 'runner123', - required: false, - }) - @IsOptional() - runnerId?: string - - @ApiProperty({ - description: 'The toolbox proxy URL for the sandbox', - example: 'https://proxy.app.boxlite.io/toolbox', - }) - toolboxProxyUrl: string - - static fromSandbox(sandbox: Sandbox, toolboxProxyUrl: string): SandboxDto { - return { - id: sandbox.id, - organizationId: sandbox.organizationId, - name: sandbox.name, - target: sandbox.region, - snapshot: sandbox.snapshot, - user: sandbox.osUser, - env: sandbox.env, - cpu: sandbox.cpu, - gpu: sandbox.gpu, - memory: sandbox.mem, - disk: sandbox.disk, - public: sandbox.public, - networkBlockAll: sandbox.networkBlockAll, - networkAllowList: sandbox.networkAllowList, - labels: sandbox.labels, - volumes: sandbox.volumes, - state: this.getSandboxState(sandbox), - desiredState: sandbox.desiredState, - errorReason: sandbox.errorReason, - recoverable: sandbox.recoverable, - backupState: sandbox.backupState, - backupCreatedAt: sandbox.lastBackupAt ? new Date(sandbox.lastBackupAt).toISOString() : undefined, - autoStopInterval: sandbox.autoStopInterval, - autoArchiveInterval: sandbox.autoArchiveInterval, - autoDeleteInterval: sandbox.autoDeleteInterval, - class: sandbox.class, - createdAt: sandbox.createdAt ? new Date(sandbox.createdAt).toISOString() : undefined, - updatedAt: sandbox.updatedAt ? new Date(sandbox.updatedAt).toISOString() : undefined, - buildInfo: sandbox.buildInfo - ? { - dockerfileContent: sandbox.buildInfo.dockerfileContent, - contextHashes: sandbox.buildInfo.contextHashes, - createdAt: sandbox.buildInfo.createdAt, - updatedAt: sandbox.buildInfo.updatedAt, - snapshotRef: sandbox.buildInfo.snapshotRef, - } - : undefined, - daemonVersion: sandbox.daemonVersion, - runnerId: sandbox.runnerId, - toolboxProxyUrl, - } - } - - private static getSandboxState(sandbox: Sandbox): SandboxState { - switch (sandbox.state) { - case SandboxState.STARTED: - if (sandbox.desiredState === SandboxDesiredState.STOPPED) { - return SandboxState.STOPPING - } - if (sandbox.desiredState === SandboxDesiredState.DESTROYED) { - return SandboxState.DESTROYING - } - break - case SandboxState.STOPPED: - if (sandbox.desiredState === SandboxDesiredState.STARTED) { - return SandboxState.STARTING - } - if (sandbox.desiredState === SandboxDesiredState.DESTROYED) { - return SandboxState.DESTROYING - } - if (sandbox.desiredState === SandboxDesiredState.ARCHIVED) { - return SandboxState.ARCHIVING - } - break - case SandboxState.UNKNOWN: - if (sandbox.desiredState === SandboxDesiredState.STARTED) { - return SandboxState.CREATING - } - break - } - return sandbox.state - } -} - -@ApiSchema({ name: 'SandboxLabels' }) -export class SandboxLabelsDto { - @ApiProperty({ - description: 'Key-value pairs of labels', - example: { environment: 'dev', team: 'backend' }, - type: 'object', - additionalProperties: { type: 'string' }, - }) - labels: { [key: string]: string } -} diff --git a/apps/api/src/sandbox/dto/snapshot.dto.ts b/apps/api/src/sandbox/dto/snapshot.dto.ts deleted file mode 100644 index 879f74586..000000000 --- a/apps/api/src/sandbox/dto/snapshot.dto.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { Snapshot } from '../entities/snapshot.entity' -import { BuildInfoDto } from './build-info.dto' -import { IsOptional } from 'class-validator' - -export class SnapshotDto { - @ApiProperty() - id: string - - @ApiPropertyOptional() - organizationId?: string - - @ApiProperty() - general: boolean - - @ApiProperty() - name: string - - @ApiPropertyOptional() - imageName?: string - - @ApiProperty({ - enum: SnapshotState, - enumName: 'SnapshotState', - }) - state: SnapshotState - - @ApiProperty({ nullable: true }) - size?: number - - @ApiProperty({ nullable: true }) - entrypoint?: string[] - - @ApiProperty() - cpu: number - - @ApiProperty() - gpu: number - - @ApiProperty() - mem: number - - @ApiProperty() - disk: number - - @ApiProperty({ nullable: true }) - errorReason?: string - - @ApiProperty() - createdAt: Date - - @ApiProperty() - updatedAt: Date - - @ApiProperty({ nullable: true }) - lastUsedAt?: Date - - @ApiPropertyOptional({ - description: 'Build information for the snapshot', - type: BuildInfoDto, - }) - buildInfo?: BuildInfoDto - - @ApiPropertyOptional({ - description: 'IDs of regions where the snapshot is available', - type: [String], - }) - regionIds?: string[] - - @ApiPropertyOptional({ - description: 'The initial runner ID of the snapshot', - example: 'runner123', - required: false, - }) - @IsOptional() - initialRunnerId?: string - - @ApiPropertyOptional({ - description: 'The snapshot reference', - example: 'boxlite-ai/sandbox:latest', - required: false, - }) - @IsOptional() - ref?: string - - static fromSnapshot(snapshot: Snapshot): SnapshotDto { - return { - id: snapshot.id, - organizationId: snapshot.organizationId, - general: snapshot.general, - name: snapshot.name, - imageName: snapshot.imageName, - state: snapshot.state, - size: snapshot.size, - entrypoint: snapshot.entrypoint, - cpu: snapshot.cpu, - gpu: snapshot.gpu, - mem: snapshot.mem, - disk: snapshot.disk, - errorReason: snapshot.errorReason, - createdAt: snapshot.createdAt, - updatedAt: snapshot.updatedAt, - lastUsedAt: snapshot.lastUsedAt, - buildInfo: snapshot.buildInfo - ? { - dockerfileContent: snapshot.buildInfo.dockerfileContent, - contextHashes: snapshot.buildInfo.contextHashes, - createdAt: snapshot.buildInfo.createdAt, - updatedAt: snapshot.buildInfo.updatedAt, - snapshotRef: snapshot.buildInfo.snapshotRef, - } - : undefined, - regionIds: snapshot.snapshotRegions?.map((sr) => sr.regionId) ?? undefined, - initialRunnerId: snapshot.initialRunnerId, - ref: snapshot.ref, - } - } -} diff --git a/apps/api/src/sandbox/dto/toolbox.deprecated.dto.ts b/apps/api/src/sandbox/dto/toolbox.deprecated.dto.ts deleted file mode 100644 index fe52edc2c..000000000 --- a/apps/api/src/sandbox/dto/toolbox.deprecated.dto.ts +++ /dev/null @@ -1,984 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { IsString, IsBoolean, IsOptional, IsArray } from 'class-validator' - -@ApiSchema({ name: 'FileInfo' }) -export class FileInfoDto { - @ApiProperty() - name: string - - @ApiProperty() - isDir: boolean - - @ApiProperty() - size: number - - @ApiProperty() - modTime: string - - @ApiProperty() - mode: string - - @ApiProperty() - permissions: string - - @ApiProperty() - owner: string - - @ApiProperty() - group: string -} - -@ApiSchema({ name: 'Match' }) -export class MatchDto { - @ApiProperty() - file: string - - @ApiProperty() - line: number - - @ApiProperty() - content: string -} - -@ApiSchema({ name: 'SearchFilesResponse' }) -export class SearchFilesResponseDto { - @ApiProperty({ type: [String] }) - files: string[] -} - -@ApiSchema({ name: 'ReplaceRequest' }) -export class ReplaceRequestDto { - @ApiProperty({ type: [String] }) - files: string[] - - @ApiProperty() - pattern: string - - @ApiProperty() - newValue: string -} - -@ApiSchema({ name: 'ReplaceResult' }) -export class ReplaceResultDto { - @ApiPropertyOptional() - file?: string - - @ApiPropertyOptional() - success?: boolean - - @ApiPropertyOptional() - error?: string -} - -@ApiSchema({ name: 'GitAddRequest' }) -export class GitAddRequestDto { - @ApiProperty() - path: string - - @ApiProperty({ - type: [String], - description: 'files to add (use . for all files)', - }) - files: string[] -} - -@ApiSchema({ name: 'GitBranchRequest' }) -export class GitBranchRequestDto { - @ApiProperty() - path: string - - @ApiProperty() - name: string -} - -@ApiSchema({ name: 'GitDeleteBranchRequest' }) -export class GitDeleteBranchRequestDto { - @ApiProperty() - path: string - - @ApiProperty() - name: string -} - -@ApiSchema({ name: 'GitCloneRequest' }) -export class GitCloneRequestDto { - @ApiProperty() - url: string - - @ApiProperty() - path: string - - @ApiPropertyOptional() - username?: string - - @ApiPropertyOptional() - password?: string - - @ApiPropertyOptional() - branch?: string - - @ApiPropertyOptional() - commit_id?: string -} - -@ApiSchema({ name: 'GitCommitRequest' }) -export class GitCommitRequestDto { - @ApiProperty() - path: string - - @ApiProperty() - message: string - - @ApiProperty() - author: string - - @ApiProperty() - email: string - - @ApiPropertyOptional({ - description: 'Allow creating an empty commit when no changes are staged', - default: false, - }) - allow_empty?: boolean -} - -@ApiSchema({ name: 'GitCommitResponse' }) -export class GitCommitResponseDto { - @ApiProperty() - hash: string -} - -@ApiSchema({ name: 'GitCheckoutRequest' }) -export class GitCheckoutRequestDto { - @ApiProperty() - path: string - - @ApiProperty() - branch: string -} - -@ApiSchema({ name: 'GitRepoRequest' }) -export class GitRepoRequestDto { - @ApiProperty() - path: string - - @ApiPropertyOptional() - username?: string - - @ApiPropertyOptional() - password?: string -} - -@ApiSchema({ name: 'FileStatus' }) -export class FileStatusDto { - @ApiProperty() - name: string - - @ApiProperty() - staging: string - - @ApiProperty() - worktree: string - - @ApiProperty() - extra: string -} - -@ApiSchema({ name: 'GitStatus' }) -export class GitStatusDto { - @ApiProperty() - currentBranch: string - - @ApiProperty({ - type: [FileStatusDto], - }) - fileStatus: FileStatusDto[] - - @ApiPropertyOptional() - ahead?: number - - @ApiPropertyOptional() - behind?: number - - @ApiPropertyOptional() - branchPublished?: boolean -} - -@ApiSchema({ name: 'ListBranchResponse' }) -export class ListBranchResponseDto { - @ApiProperty({ type: [String] }) - branches: string[] -} - -@ApiSchema({ name: 'GitCommitInfo' }) -export class GitCommitInfoDto { - @ApiProperty() - hash: string - - @ApiProperty() - message: string - - @ApiProperty() - author: string - - @ApiProperty() - email: string - - @ApiProperty() - timestamp: string -} - -@ApiSchema({ name: 'ExecuteRequest' }) -export class ExecuteRequestDto { - @ApiProperty() - command: string - - @ApiPropertyOptional({ - description: 'Current working directory', - }) - cwd?: string - - @ApiPropertyOptional({ - description: 'Timeout in seconds, defaults to 10 seconds', - }) - timeout?: number -} - -@ApiSchema({ name: 'ExecuteResponse' }) -export class ExecuteResponseDto { - @ApiProperty({ - type: Number, - description: 'Exit code', - example: 0, - }) - exitCode: number - - @ApiProperty({ - type: String, - description: 'Command output', - example: 'Command output here', - }) - result: string -} - -@ApiSchema({ name: 'ProjectDirResponse' }) -export class ProjectDirResponseDto { - @ApiPropertyOptional() - dir?: string -} - -@ApiSchema({ name: 'UserHomeDirResponse' }) -export class UserHomeDirResponseDto { - @ApiPropertyOptional() - dir?: string -} - -@ApiSchema({ name: 'WorkDirResponse' }) -export class WorkDirResponseDto { - @ApiPropertyOptional() - dir?: string -} - -@ApiSchema({ name: 'CreateSessionRequest' }) -export class CreateSessionRequestDto { - @ApiProperty({ - description: 'The ID of the session', - example: 'session-123', - }) - @IsString() - sessionId: string -} - -@ApiSchema({ name: 'SessionExecuteRequest' }) -export class SessionExecuteRequestDto { - @ApiProperty({ - description: 'The command to execute', - example: 'ls -la', - }) - @IsString() - command: string - - @ApiPropertyOptional({ - description: 'Whether to execute the command asynchronously', - example: false, - }) - @IsBoolean() - @IsOptional() - runAsync?: boolean - - @ApiPropertyOptional({ - description: 'Deprecated: Use runAsync instead. Whether to execute the command asynchronously', - example: false, - deprecated: true, - }) - @IsBoolean() - @IsOptional() - async?: boolean - - constructor(partial: Partial) { - Object.assign(this, partial) - // Migrate async to runAsync if async is set and runAsync is not set - if (this.async !== undefined && this.runAsync === undefined) { - this.runAsync = this.async - } - } -} - -@ApiSchema({ name: 'SessionExecuteResponse' }) -export class SessionExecuteResponseDto { - @ApiPropertyOptional({ - description: 'The ID of the executed command', - example: 'cmd-123', - }) - @IsString() - @IsOptional() - cmdId?: string - - @ApiPropertyOptional({ - description: 'The output of the executed command marked with stdout and stderr prefixes', - example: 'total 20\ndrwxr-xr-x 4 user group 128 Mar 15 10:30 .', - }) - @IsString() - @IsOptional() - output?: string - - @ApiPropertyOptional({ - description: 'The exit code of the executed command', - example: 0, - }) - @IsOptional() - exitCode?: number -} - -@ApiSchema({ name: 'Command' }) -export class CommandDto { - @ApiProperty({ - description: 'The ID of the command', - example: 'cmd-123', - }) - @IsString() - id: string - - @ApiProperty({ - description: 'The command that was executed', - example: 'ls -la', - }) - @IsString() - command: string - - @ApiPropertyOptional({ - description: 'The exit code of the command', - example: 0, - }) - @IsOptional() - exitCode?: number -} - -@ApiSchema({ name: 'Session' }) -export class SessionDto { - @ApiProperty({ - description: 'The ID of the session', - example: 'session-123', - }) - @IsString() - sessionId: string - - @ApiProperty({ - description: 'The list of commands executed in this session', - type: [CommandDto], - nullable: true, - }) - @IsArray() - @IsOptional() - commands?: CommandDto[] | null -} - -// Computer Use DTOs -@ApiSchema({ name: 'MousePosition' }) -export class MousePositionDto { - @ApiProperty({ - description: 'The X coordinate of the mouse cursor position', - example: 100, - }) - x: number - - @ApiProperty({ - description: 'The Y coordinate of the mouse cursor position', - example: 200, - }) - y: number -} - -@ApiSchema({ name: 'MouseMoveRequest' }) -export class MouseMoveRequestDto { - @ApiProperty({ - description: 'The target X coordinate to move the mouse cursor to', - example: 150, - }) - x: number - - @ApiProperty({ - description: 'The target Y coordinate to move the mouse cursor to', - example: 250, - }) - y: number -} - -@ApiSchema({ name: 'MouseMoveResponse' }) -export class MouseMoveResponseDto { - @ApiProperty({ - description: 'The actual X coordinate where the mouse cursor ended up', - example: 150, - }) - x: number - - @ApiProperty({ - description: 'The actual Y coordinate where the mouse cursor ended up', - example: 250, - }) - y: number -} - -@ApiSchema({ name: 'MouseClickRequest' }) -export class MouseClickRequestDto { - @ApiProperty({ - description: 'The X coordinate where to perform the mouse click', - example: 100, - }) - x: number - - @ApiProperty({ - description: 'The Y coordinate where to perform the mouse click', - example: 200, - }) - y: number - - @ApiPropertyOptional({ - description: 'The mouse button to click (left, right, middle). Defaults to left', - example: 'left', - }) - button?: string - - @ApiPropertyOptional({ - description: 'Whether to perform a double-click instead of a single click', - example: false, - }) - double?: boolean -} - -@ApiSchema({ name: 'MouseClickResponse' }) -export class MouseClickResponseDto { - @ApiProperty({ - description: 'The actual X coordinate where the click occurred', - example: 100, - }) - x: number - - @ApiProperty({ - description: 'The actual Y coordinate where the click occurred', - example: 200, - }) - y: number -} - -@ApiSchema({ name: 'MouseDragRequest' }) -export class MouseDragRequestDto { - @ApiProperty({ - description: 'The starting X coordinate for the drag operation', - example: 100, - }) - startX: number - - @ApiProperty({ - description: 'The starting Y coordinate for the drag operation', - example: 200, - }) - startY: number - - @ApiProperty({ - description: 'The ending X coordinate for the drag operation', - example: 300, - }) - endX: number - - @ApiProperty({ - description: 'The ending Y coordinate for the drag operation', - example: 400, - }) - endY: number - - @ApiPropertyOptional({ - description: 'The mouse button to use for dragging (left, right, middle). Defaults to left', - example: 'left', - }) - button?: string -} - -@ApiSchema({ name: 'MouseDragResponse' }) -export class MouseDragResponseDto { - @ApiProperty({ - description: 'The actual X coordinate where the drag ended', - example: 300, - }) - x: number - - @ApiProperty({ - description: 'The actual Y coordinate where the drag ended', - example: 400, - }) - y: number -} - -@ApiSchema({ name: 'MouseScrollRequest' }) -export class MouseScrollRequestDto { - @ApiProperty({ - description: 'The X coordinate where to perform the scroll operation', - example: 100, - }) - x: number - - @ApiProperty({ - description: 'The Y coordinate where to perform the scroll operation', - example: 200, - }) - y: number - - @ApiProperty({ - description: 'The scroll direction (up, down)', - example: 'down', - }) - direction: string - - @ApiPropertyOptional({ - description: 'The number of scroll units to scroll. Defaults to 1', - example: 3, - }) - amount?: number -} - -@ApiSchema({ name: 'MouseScrollResponse' }) -export class MouseScrollResponseDto { - @ApiProperty({ - description: 'Whether the mouse scroll operation was successful', - example: true, - }) - success: boolean -} - -@ApiSchema({ name: 'KeyboardTypeRequest' }) -export class KeyboardTypeRequestDto { - @ApiProperty({ - description: 'The text to type using the keyboard', - example: 'Hello, World!', - }) - text: string - - @ApiPropertyOptional({ - description: 'Delay in milliseconds between keystrokes. Defaults to 0', - example: 100, - }) - delay?: number -} - -@ApiSchema({ name: 'KeyboardPressRequest' }) -export class KeyboardPressRequestDto { - @ApiProperty({ - description: 'The key to press (e.g., a, b, c, enter, space, etc.)', - example: 'enter', - }) - key: string - - @ApiPropertyOptional({ - description: 'Array of modifier keys to press along with the main key (ctrl, alt, shift, cmd)', - type: [String], - example: ['ctrl', 'shift'], - }) - modifiers?: string[] -} - -@ApiSchema({ name: 'KeyboardHotkeyRequest' }) -export class KeyboardHotkeyRequestDto { - @ApiProperty({ - description: 'The hotkey combination to press (e.g., "ctrl+c", "cmd+v", "alt+tab")', - example: 'ctrl+c', - }) - keys: string -} - -@ApiSchema({ name: 'ScreenshotResponse' }) -export class ScreenshotResponseDto { - @ApiProperty({ - description: 'Base64 encoded screenshot image data', - example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', - }) - screenshot: string - - @ApiPropertyOptional({ - description: 'The current cursor position when the screenshot was taken', - example: { x: 500, y: 300 }, - }) - cursorPosition?: { x: number; y: number } - - @ApiPropertyOptional({ - description: 'The size of the screenshot data in bytes', - example: 24576, - }) - sizeBytes?: number -} - -@ApiSchema({ name: 'RegionScreenshotRequest' }) -export class RegionScreenshotRequestDto { - @ApiProperty({ - description: 'The X coordinate of the top-left corner of the region to capture', - example: 100, - }) - x: number - - @ApiProperty({ - description: 'The Y coordinate of the top-left corner of the region to capture', - example: 100, - }) - y: number - - @ApiProperty({ - description: 'The width of the region to capture in pixels', - example: 800, - }) - width: number - - @ApiProperty({ - description: 'The height of the region to capture in pixels', - example: 600, - }) - height: number -} - -@ApiSchema({ name: 'RegionScreenshotResponse' }) -export class RegionScreenshotResponseDto { - @ApiProperty({ - description: 'Base64 encoded screenshot image data of the specified region', - example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', - }) - screenshot: string - - @ApiPropertyOptional({ - description: 'The current cursor position when the region screenshot was taken', - example: { x: 500, y: 300 }, - }) - cursorPosition?: { x: number; y: number } - - @ApiPropertyOptional({ - description: 'The size of the screenshot data in bytes', - example: 24576, - }) - sizeBytes?: number -} - -@ApiSchema({ name: 'CompressedScreenshotResponse' }) -export class CompressedScreenshotResponseDto { - @ApiProperty({ - description: 'Base64 encoded compressed screenshot image data', - example: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==', - }) - screenshot: string - - @ApiPropertyOptional({ - description: 'The current cursor position when the compressed screenshot was taken', - example: { x: 250, y: 150 }, - }) - cursorPosition?: { x: number; y: number } - - @ApiPropertyOptional({ - description: 'The size of the compressed screenshot data in bytes', - example: 12288, - }) - sizeBytes?: number -} - -@ApiSchema({ name: 'DisplayInfoResponse' }) -export class DisplayInfoResponseDto { - @ApiProperty({ - description: 'Array of display information for all connected displays', - type: [Object], - example: [ - { - id: 0, - x: 0, - y: 0, - width: 1920, - height: 1080, - is_active: true, - }, - ], - }) - displays: Array<{ id: number; x: number; y: number; width: number; height: number; is_active: boolean }> -} - -@ApiSchema({ name: 'WindowsResponse' }) -export class WindowsResponseDto { - @ApiProperty({ - description: 'Array of window information for all visible windows', - type: [Object], - example: [ - { - id: 12345, - title: 'Terminal', - }, - ], - }) - windows: Array<{ id: number; title: string }> - - @ApiProperty({ - description: 'The total number of windows found', - example: 5, - }) - count: number -} - -// Computer Use Management Response DTOs - -@ApiSchema({ name: 'ComputerUseStartResponse' }) -export class ComputerUseStartResponseDto { - @ApiProperty({ - description: 'A message indicating the result of starting computer use processes', - example: 'Computer use processes started successfully', - }) - message: string - - @ApiProperty({ - description: 'Status information about all VNC desktop processes after starting', - type: Object, - example: { - xvfb: { running: true, priority: 100, autoRestart: true, pid: 12345 }, - xfce4: { running: true, priority: 200, autoRestart: true, pid: 12346 }, - x11vnc: { running: true, priority: 300, autoRestart: true, pid: 12347 }, - novnc: { running: true, priority: 400, autoRestart: true, pid: 12348 }, - }, - }) - status: Record -} - -@ApiSchema({ name: 'ComputerUseStopResponse' }) -export class ComputerUseStopResponseDto { - @ApiProperty({ - description: 'A message indicating the result of stopping computer use processes', - example: 'Computer use processes stopped successfully', - }) - message: string - - @ApiProperty({ - description: 'Status information about all VNC desktop processes after stopping', - type: Object, - example: { - xvfb: { running: false, priority: 100, autoRestart: true }, - xfce4: { running: false, priority: 200, autoRestart: true }, - x11vnc: { running: false, priority: 300, autoRestart: true }, - novnc: { running: false, priority: 400, autoRestart: true }, - }, - }) - status: Record -} - -@ApiSchema({ name: 'ComputerUseStatusResponse' }) -export class ComputerUseStatusResponseDto { - @ApiProperty({ - description: 'Status of computer use services (active, partial, inactive, error)', - example: 'active', - enum: ['active', 'partial', 'inactive', 'error'], - }) - status: string -} - -@ApiSchema({ name: 'ProcessStatusResponse' }) -export class ProcessStatusResponseDto { - @ApiProperty({ - description: 'The name of the VNC process being checked', - example: 'xfce4', - }) - processName: string - - @ApiProperty({ - description: 'Whether the specified VNC process is currently running', - example: true, - }) - running: boolean -} - -@ApiSchema({ name: 'ProcessRestartResponse' }) -export class ProcessRestartResponseDto { - @ApiProperty({ - description: 'A message indicating the result of restarting the process', - example: 'Process xfce4 restarted successfully', - }) - message: string - - @ApiProperty({ - description: 'The name of the VNC process that was restarted', - example: 'xfce4', - }) - processName: string -} - -@ApiSchema({ name: 'ProcessLogsResponse' }) -export class ProcessLogsResponseDto { - @ApiProperty({ - description: 'The name of the VNC process whose logs were retrieved', - example: 'novnc', - }) - processName: string - - @ApiProperty({ - description: 'The log output from the specified VNC process', - example: '2024-01-15 10:30:45 [INFO] NoVNC server started on port 6080', - }) - logs: string -} - -@ApiSchema({ name: 'ProcessErrorsResponse' }) -export class ProcessErrorsResponseDto { - @ApiProperty({ - description: 'The name of the VNC process whose error logs were retrieved', - example: 'x11vnc', - }) - processName: string - - @ApiProperty({ - description: 'The error log output from the specified VNC process', - example: '2024-01-15 10:30:45 [ERROR] Failed to bind to port 5901', - }) - errors: string -} - -// PTY DTOs -@ApiSchema({ name: 'PtyCreateRequest' }) -export class PtyCreateRequestDto { - @ApiProperty({ - description: 'The unique identifier for the PTY session', - example: 'pty-session-12345', - }) - id: string - - @ApiPropertyOptional({ - description: "Starting directory for the PTY session, defaults to the sandbox's working directory", - example: '/home/user', - }) - cwd?: string - - @ApiPropertyOptional({ - description: 'Environment variables for the PTY session', - type: Object, - example: { TERM: 'xterm-256color', PS1: '\\u@boxlite:\\w$ ' }, - }) - envs?: Record - - @ApiPropertyOptional({ - description: 'Number of terminal columns', - example: 80, - }) - cols?: number - - @ApiPropertyOptional({ - description: 'Number of terminal rows', - example: 24, - }) - rows?: number - - @ApiPropertyOptional({ - description: 'Whether to start the PTY session lazily (only start when first client connects)', - example: false, - default: false, - }) - lazyStart?: boolean -} - -@ApiSchema({ name: 'PtyCreateResponse' }) -export class PtyCreateResponseDto { - @ApiProperty({ - description: 'The unique identifier for the created PTY session', - example: 'pty-session-12345', - }) - sessionId: string -} - -@ApiSchema({ name: 'PtySessionInfo' }) -export class PtySessionInfoDto { - @ApiProperty({ - description: 'The unique identifier for the PTY session', - example: 'pty-session-12345', - }) - id: string - - @ApiProperty({ - description: "Starting directory for the PTY session, defaults to the sandbox's working directory", - example: '/home/user', - }) - cwd: string - - @ApiProperty({ - description: 'Environment variables for the PTY session', - type: Object, - example: { TERM: 'xterm-256color', PS1: '\\u@boxlite:\\w$ ' }, - }) - envs: Record - - @ApiProperty({ - description: 'Number of terminal columns', - example: 80, - }) - cols: number - - @ApiProperty({ - description: 'Number of terminal rows', - example: 24, - }) - rows: number - - @ApiProperty({ - description: 'When the PTY session was created', - example: '2024-01-15T10:30:45Z', - }) - createdAt: string - - @ApiProperty({ - description: 'Whether the PTY session is currently active', - example: true, - }) - active: boolean - - @ApiProperty({ - description: 'Whether the PTY session uses lazy start (only start when first client connects)', - example: false, - default: false, - }) - lazyStart: boolean -} - -@ApiSchema({ name: 'PtyListResponse' }) -export class PtyListResponseDto { - @ApiProperty({ - description: 'List of active PTY sessions', - type: [PtySessionInfoDto], - }) - sessions: PtySessionInfoDto[] -} - -@ApiSchema({ name: 'PtyResizeRequest' }) -export class PtyResizeRequestDto { - @ApiProperty({ - description: 'Number of terminal columns', - example: 80, - }) - cols: number - - @ApiProperty({ - description: 'Number of terminal rows', - example: 24, - }) - rows: number -} diff --git a/apps/api/src/sandbox/dto/update-sandbox-network-settings.dto.ts b/apps/api/src/sandbox/dto/update-sandbox-network-settings.dto.ts deleted file mode 100644 index c252ebb31..000000000 --- a/apps/api/src/sandbox/dto/update-sandbox-network-settings.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { IsOptional, IsString, IsBoolean } from 'class-validator' -import { ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' - -@ApiSchema({ name: 'UpdateSandboxNetworkSettings' }) -export class UpdateSandboxNetworkSettingsDto { - @ApiPropertyOptional({ - description: 'Whether to block all network access for the sandbox', - example: false, - }) - @IsOptional() - @IsBoolean() - networkBlockAll?: boolean - - @ApiPropertyOptional({ - description: 'Comma-separated list of allowed CIDR network addresses for the sandbox', - example: '192.168.1.0/16,10.0.0.0/24', - }) - @IsOptional() - @IsString() - networkAllowList?: string -} diff --git a/apps/api/src/sandbox/dto/update-sandbox-state.dto.ts b/apps/api/src/sandbox/dto/update-sandbox-state.dto.ts deleted file mode 100644 index ae35c9b2d..000000000 --- a/apps/api/src/sandbox/dto/update-sandbox-state.dto.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger' -import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' -import { SandboxState } from '../enums/sandbox-state.enum' - -export class UpdateSandboxStateDto { - @IsEnum(SandboxState) - @ApiProperty({ - description: 'The new state for the sandbox', - enum: SandboxState, - example: SandboxState.STARTED, - }) - state: SandboxState - - @IsOptional() - @IsString() - @ApiPropertyOptional({ - description: 'Optional error message when reporting an error state', - example: 'Failed to pull snapshot image', - }) - errorReason?: string - - @IsOptional() - @IsBoolean() - @ApiPropertyOptional({ - description: 'Whether the sandbox is recoverable', - example: true, - }) - recoverable?: boolean -} diff --git a/apps/api/src/sandbox/dto/update-snapshot.dto.ts b/apps/api/src/sandbox/dto/update-snapshot.dto.ts deleted file mode 100644 index b7dfda6db..000000000 --- a/apps/api/src/sandbox/dto/update-snapshot.dto.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { IsBoolean } from 'class-validator' - -@ApiSchema({ name: 'SetSnapshotGeneralStatusDto' }) -export class SetSnapshotGeneralStatusDto { - @ApiProperty({ - description: 'Whether the snapshot is general', - example: true, - }) - @IsBoolean() - general: boolean -} diff --git a/apps/api/src/sandbox/dto/workspace-port-preview-url.deprecated.dto.ts b/apps/api/src/sandbox/dto/workspace-port-preview-url.deprecated.dto.ts deleted file mode 100644 index 6836fc3f3..000000000 --- a/apps/api/src/sandbox/dto/workspace-port-preview-url.deprecated.dto.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiSchema } from '@nestjs/swagger' -import { IsString } from 'class-validator' - -@ApiSchema({ name: 'WorkspacePortPreviewUrl' }) -export class WorkspacePortPreviewUrlDto { - @ApiProperty({ - description: 'Preview url', - example: 'https://123456-mysandbox.runner.com', - }) - @IsString() - url: string - - @ApiProperty({ - description: 'Access token', - example: 'ul67qtv-jl6wb9z5o3eii-ljqt9qed6l', - }) - @IsString() - token: string -} diff --git a/apps/api/src/sandbox/dto/workspace.deprecated.dto.ts b/apps/api/src/sandbox/dto/workspace.deprecated.dto.ts deleted file mode 100644 index ebb820091..000000000 --- a/apps/api/src/sandbox/dto/workspace.deprecated.dto.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' -import { SandboxDto } from './sandbox.dto' -import { IsEnum, IsOptional } from 'class-validator' -import { BackupState as SnapshotState } from '../enums/backup-state.enum' -import { Sandbox } from '../entities/sandbox.entity' - -@ApiSchema({ name: 'SandboxInfo' }) -export class SandboxInfoDto { - @ApiProperty({ - description: 'The creation timestamp of the project', - example: '2023-10-01T12:00:00Z', - }) - created: string - - @ApiProperty({ - description: 'Deprecated: The name of the sandbox', - example: 'MySandbox', - deprecated: true, - default: '', - }) - name: string - - @ApiPropertyOptional({ - description: 'Additional metadata provided by the provider', - example: '{"key": "value"}', - required: false, - }) - @IsOptional() - providerMetadata?: string -} - -@ApiSchema({ name: 'Workspace' }) -export class WorkspaceDto extends SandboxDto { - @ApiPropertyOptional({ - description: 'The image used for the workspace', - example: 'boxlite-ai/workspace:latest', - }) - image: string - - @ApiPropertyOptional({ - description: 'The state of the snapshot', - enum: SnapshotState, - example: Object.values(SnapshotState)[0], - required: false, - }) - @IsEnum(SnapshotState) - snapshotState?: SnapshotState - - @ApiPropertyOptional({ - description: 'The creation timestamp of the last snapshot', - example: '2024-10-01T12:00:00Z', - required: false, - }) - snapshotCreatedAt?: string - - @ApiPropertyOptional({ - description: 'Additional information about the sandbox', - type: SandboxInfoDto, - required: false, - }) - @IsOptional() - info?: SandboxInfoDto - - constructor() { - super() - } - - static fromSandbox(sandbox: Sandbox): WorkspaceDto { - // Send empty string for toolboxProxyUrl as it is not needed in deprecated DTO - const dto = super.fromSandbox(sandbox, '') - return this.fromSandboxDto(dto) - } - - static fromSandboxDto(sandboxDto: SandboxDto): WorkspaceDto { - return { - ...sandboxDto, - image: sandboxDto.snapshot, - snapshotState: sandboxDto.backupState, - snapshotCreatedAt: sandboxDto.backupCreatedAt, - info: { - name: sandboxDto.name, - created: sandboxDto.createdAt, - providerMetadata: JSON.stringify({ - state: sandboxDto.state, - region: sandboxDto.target, - class: sandboxDto.class, - updatedAt: sandboxDto.updatedAt, - lastSnapshot: sandboxDto.backupCreatedAt, - cpu: sandboxDto.cpu, - gpu: sandboxDto.gpu, - memory: sandboxDto.memory, - disk: sandboxDto.disk, - autoStopInterval: sandboxDto.autoStopInterval, - autoArchiveInterval: sandboxDto.autoArchiveInterval, - daemonVersion: sandboxDto.daemonVersion, - }), - }, - } - } -} diff --git a/apps/api/src/sandbox/entities/build-info.entity.ts b/apps/api/src/sandbox/entities/build-info.entity.ts deleted file mode 100644 index 215d23ab3..000000000 --- a/apps/api/src/sandbox/entities/build-info.entity.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn, UpdateDateColumn, BeforeInsert } from 'typeorm' -import { Snapshot } from './snapshot.entity' -import { Sandbox } from './sandbox.entity' -import { createHash } from 'crypto' - -export function generateBuildInfoHash(dockerfileContent: string, contextHashes: string[] = []): string { - const sortedContextHashes = [...contextHashes].sort() || [] - const combined = dockerfileContent + sortedContextHashes.join('') - const hash = createHash('sha256').update(combined).digest('hex') - return 'boxlite-' + hash + ':boxlite' -} - -@Entity() -export class BuildInfo { - @PrimaryColumn() - snapshotRef: string - - @Column({ type: 'text', nullable: true }) - dockerfileContent?: string - - @Column('simple-array', { nullable: true }) - contextHashes?: string[] - - @OneToMany(() => Snapshot, (snapshot) => snapshot.buildInfo) - snapshots: Snapshot[] - - @OneToMany(() => Sandbox, (sandbox) => sandbox.buildInfo) - sandboxes: Sandbox[] - - @Column({ type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP' }) - lastUsedAt: Date - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date - - @BeforeInsert() - generateHash() { - this.snapshotRef = generateBuildInfoHash(this.dockerfileContent, this.contextHashes) - } -} diff --git a/apps/api/src/sandbox/entities/sandbox-last-activity.entity.ts b/apps/api/src/sandbox/entities/sandbox-last-activity.entity.ts deleted file mode 100644 index 38e28bd5c..000000000 --- a/apps/api/src/sandbox/entities/sandbox-last-activity.entity.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from 'typeorm' -import { Sandbox } from './sandbox.entity' - -@Entity('sandbox_last_activity') -export class SandboxLastActivity { - @PrimaryColumn() - sandboxId: string - - @Column({ nullable: true, type: 'timestamp with time zone' }) - lastActivityAt?: Date - - @OneToOne(() => Sandbox, { onDelete: 'CASCADE' }) - @JoinColumn({ name: 'sandboxId' }) - sandbox?: Sandbox -} diff --git a/apps/api/src/sandbox/entities/sandbox.entity.ts b/apps/api/src/sandbox/entities/sandbox.entity.ts deleted file mode 100644 index be552fb63..000000000 --- a/apps/api/src/sandbox/entities/sandbox.entity.ts +++ /dev/null @@ -1,404 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Column, - CreateDateColumn, - Entity, - Index, - JoinColumn, - ManyToOne, - PrimaryColumn, - OneToOne, - Unique, - UpdateDateColumn, -} from 'typeorm' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { SandboxClass } from '../enums/sandbox-class.enum' -import { BackupState } from '../enums/backup-state.enum' -import { v4 as uuidv4 } from 'uuid' -import { SandboxVolume } from '../dto/sandbox.dto' -import { BuildInfo } from './build-info.entity' -import { nanoid } from 'nanoid' -import { SandboxLastActivity } from './sandbox-last-activity.entity' - -@Entity() -@Unique(['organizationId', 'name']) -@Index('sandbox_state_idx', ['state']) -@Index('sandbox_desiredstate_idx', ['desiredState']) -@Index('sandbox_snapshot_idx', ['snapshot']) -@Index('sandbox_runnerid_idx', ['runnerId']) -@Index('sandbox_runner_state_idx', ['runnerId', 'state']) -@Index('sandbox_organizationid_idx', ['organizationId']) -@Index('sandbox_region_idx', ['region']) -@Index('sandbox_resources_idx', ['cpu', 'mem', 'disk', 'gpu']) -@Index('sandbox_backupstate_idx', ['backupState']) -@Index('sandbox_runner_state_desired_idx', ['runnerId', 'state', 'desiredState'], { - where: '"pending" = false', -}) -@Index('sandbox_active_only_idx', ['id'], { - where: `"state" <> ALL (ARRAY['destroyed'::sandbox_state_enum, 'archived'::sandbox_state_enum])`, -}) -@Index('sandbox_pending_idx', ['id'], { - where: `"pending" = true`, -}) -@Index('idx_sandbox_authtoken', ['authToken']) -@Index('sandbox_labels_gin_full_idx', { synchronize: false }) -@Index('idx_sandbox_volumes_gin', { synchronize: false }) -export class Sandbox { - @PrimaryColumn({ default: () => 'uuid_generate_v4()' }) - id: string - - @Column({ - type: 'uuid', - }) - organizationId: string - - @Column() - name: string - - @Column() - region: string - - @Column({ - type: 'uuid', - nullable: true, - }) - runnerId?: string - - // this is the runnerId of the runner that was previously assigned to the sandbox - // if something goes wrong with new runner assignment, we can revert to the previous runner - @Column({ - type: 'uuid', - nullable: true, - }) - prevRunnerId?: string - - @Column({ - type: 'enum', - enum: SandboxClass, - default: SandboxClass.SMALL, - }) - class = SandboxClass.SMALL - - @Column({ - type: 'enum', - enum: SandboxState, - default: SandboxState.UNKNOWN, - }) - state = SandboxState.UNKNOWN - - @Column({ - type: 'enum', - enum: SandboxDesiredState, - default: SandboxDesiredState.STARTED, - }) - desiredState = SandboxDesiredState.STARTED - - @Column({ nullable: true }) - snapshot?: string - - @Column() - osUser: string - - @Column({ nullable: true }) - errorReason?: string - - @Column({ default: false, type: 'boolean' }) - recoverable = false - - @Column({ - type: 'jsonb', - default: {}, - }) - env: { [key: string]: string } = {} - - @Column({ default: false, type: 'boolean' }) - public = false - - @Column({ default: false, type: 'boolean' }) - networkBlockAll = false - - @Column({ nullable: true }) - networkAllowList?: string - - @Column('jsonb', { nullable: true }) - labels: { [key: string]: string } - - @Column({ nullable: true }) - backupRegistryId: string | null - - @Column({ nullable: true }) - backupSnapshot: string | null - - @Column({ nullable: true, type: 'timestamp with time zone' }) - lastBackupAt: Date | null - - @Column({ - type: 'enum', - enum: BackupState, - default: BackupState.NONE, - }) - backupState = BackupState.NONE - - @Column({ - type: 'text', - nullable: true, - }) - backupErrorReason: string | null - - @Column({ - type: 'jsonb', - default: [], - }) - existingBackupSnapshots: Array<{ - snapshotName: string - createdAt: Date - }> = [] - - @Column({ type: 'int', default: 2 }) - cpu = 2 - - @Column({ type: 'int', default: 0 }) - gpu = 0 - - @Column({ type: 'int', default: 4 }) - mem = 4 - - @Column({ type: 'int', default: 10 }) - disk = 10 - - @Column({ - type: 'jsonb', - default: [], - }) - volumes: SandboxVolume[] = [] - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date - - @OneToOne(() => SandboxLastActivity, (lastActivity) => lastActivity.sandbox) - lastActivityAt?: SandboxLastActivity - - // this is the interval in minutes after which the sandbox will be stopped if lastActivityAt is not updated - // if set to 0, auto stop will be disabled - @Column({ default: 15, type: 'int' }) - autoStopInterval: number | undefined = 15 - - // this is the interval in minutes after which a continuously stopped workspace will be automatically archived - @Column({ default: 7 * 24 * 60, type: 'int' }) - autoArchiveInterval: number | undefined = 7 * 24 * 60 - - // this is the interval in minutes after which a continuously stopped workspace will be automatically deleted - // if set to negative value, auto delete will be disabled - // if set to 0, sandbox will be immediately deleted upon stopping - @Column({ default: -1, type: 'int' }) - autoDeleteInterval: number | undefined = -1 - - @Column({ default: false, type: 'boolean' }) - pending: boolean | undefined = false - - @Column({ type: 'character varying' }) - authToken = nanoid(32).toLowerCase() - - @ManyToOne(() => BuildInfo, (buildInfo) => buildInfo.sandboxes, { - nullable: true, - }) - @JoinColumn() - buildInfo?: BuildInfo - - @Column({ nullable: true }) - daemonVersion?: string - - constructor(region: string, name?: string) { - this.id = uuidv4() - // Set name - use provided name or fallback to ID - this.name = name || this.id - this.region = region - } - - /** - * Helper method that returns the update data needed for a backup state update. - */ - static getBackupStateUpdate( - sandbox: Sandbox, - backupState: BackupState, - backupSnapshot?: string | null, - backupRegistryId?: string | null, - backupErrorReason?: string | null, - ): Partial { - const update: Partial = { - backupState, - } - switch (backupState) { - case BackupState.NONE: - update.backupSnapshot = null - break - case BackupState.COMPLETED: { - const now = new Date() - update.lastBackupAt = now - if (sandbox.backupSnapshot) { - update.existingBackupSnapshots = [ - ...sandbox.existingBackupSnapshots, - { - snapshotName: sandbox.backupSnapshot, - createdAt: now, - }, - ] - } - update.backupErrorReason = null - break - } - } - if (backupSnapshot !== undefined) { - update.backupSnapshot = backupSnapshot - } - if (backupRegistryId !== undefined) { - update.backupRegistryId = backupRegistryId - } - if (backupErrorReason !== undefined) { - update.backupErrorReason = backupErrorReason - } - return update - } - - /** - * Helper method that returns the update data needed for a soft delete operation. - */ - static getSoftDeleteUpdate(sandbox: Sandbox): Partial { - return { - pending: true, - desiredState: SandboxDesiredState.DESTROYED, - backupState: BackupState.NONE, - name: 'DESTROYED_' + sandbox.name + '_' + Date.now(), - } - } - - /** - * Asserts that the current entity state is valid. - */ - assertValid(): void { - this.validateDesiredStateTransition() - } - - private validateDesiredStateTransition(): void { - switch (this.desiredState) { - case SandboxDesiredState.STARTED: - if ( - [ - SandboxState.STARTED, - SandboxState.STOPPED, - SandboxState.STARTING, - SandboxState.ARCHIVED, - SandboxState.CREATING, - SandboxState.UNKNOWN, - SandboxState.RESTORING, - SandboxState.PENDING_BUILD, - SandboxState.BUILDING_SNAPSHOT, - SandboxState.PULLING_SNAPSHOT, - SandboxState.ARCHIVING, - SandboxState.ERROR, - SandboxState.BUILD_FAILED, - SandboxState.RESIZING, - ].includes(this.state) - ) { - break - } - throw new Error(`Sandbox ${this.id} is not in a valid state to be started. State: ${this.state}`) - case SandboxDesiredState.STOPPED: - if ( - [ - SandboxState.STARTED, - SandboxState.STOPPING, - SandboxState.STOPPED, - SandboxState.ERROR, - SandboxState.BUILD_FAILED, - SandboxState.RESIZING, - ].includes(this.state) - ) { - break - } - throw new Error(`Sandbox ${this.id} is not in a valid state to be stopped. State: ${this.state}`) - case SandboxDesiredState.ARCHIVED: - if ( - [ - SandboxState.ARCHIVED, - SandboxState.ARCHIVING, - SandboxState.STOPPED, - SandboxState.ERROR, - SandboxState.BUILD_FAILED, - ].includes(this.state) - ) { - break - } - throw new Error(`Sandbox ${this.id} is not in a valid state to be archived. State: ${this.state}`) - case SandboxDesiredState.DESTROYED: - if ( - [ - SandboxState.DESTROYED, - SandboxState.DESTROYING, - SandboxState.STOPPED, - SandboxState.STARTED, - SandboxState.ARCHIVED, - SandboxState.ERROR, - SandboxState.BUILD_FAILED, - SandboxState.ARCHIVING, - SandboxState.PENDING_BUILD, - ].includes(this.state) - ) { - break - } - throw new Error(`Sandbox ${this.id} is not in a valid state to be destroyed. State: ${this.state}`) - } - } - - /** - * Enforces domain invariants on the current entity state. - * - * @returns Additional field changes that invariant enforcement produced. - */ - enforceInvariants(): Partial { - const changes = this.getInvariantChanges() - Object.assign(this, changes) - return changes - } - - private getInvariantChanges(): Partial { - const changes: Partial = {} - - if (!this.pending && String(this.state) !== String(this.desiredState)) { - changes.pending = true - } - if (this.pending && String(this.state) === String(this.desiredState)) { - changes.pending = false - } - if ( - this.state === SandboxState.ERROR || - this.state === SandboxState.BUILD_FAILED || - this.desiredState === SandboxDesiredState.ARCHIVED - ) { - changes.pending = false - } - - if (this.state === SandboxState.DESTROYED || this.state === SandboxState.ARCHIVED) { - changes.runnerId = null - } - - if (this.state === SandboxState.DESTROYED) { - changes.backupState = BackupState.NONE - } - - return changes - } -} diff --git a/apps/api/src/sandbox/entities/snapshot-region.entity.ts b/apps/api/src/sandbox/entities/snapshot-region.entity.ts deleted file mode 100644 index 74c16d2f7..000000000 --- a/apps/api/src/sandbox/entities/snapshot-region.entity.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryColumn, UpdateDateColumn } from 'typeorm' -import { Snapshot } from './snapshot.entity' -import { Region } from '../../region/entities/region.entity' - -@Entity() -export class SnapshotRegion { - @PrimaryColumn('uuid') - snapshotId: string - - @PrimaryColumn() - regionId: string - - @ManyToOne(() => Snapshot, (snapshot) => snapshot.snapshotRegions, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'snapshotId' }) - snapshot: Snapshot - - @ManyToOne(() => Region, { - onDelete: 'CASCADE', - }) - @JoinColumn({ name: 'regionId' }) - region: Region - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date -} diff --git a/apps/api/src/sandbox/entities/snapshot-runner.entity.ts b/apps/api/src/sandbox/entities/snapshot-runner.entity.ts deleted file mode 100644 index 596e2a305..000000000 --- a/apps/api/src/sandbox/entities/snapshot-runner.entity.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm' -import { SnapshotRunnerState } from '../enums/snapshot-runner-state.enum' - -@Entity() -@Index('snapshot_runner_snapshotref_idx', ['snapshotRef']) -@Index('snapshot_runner_runnerid_snapshotref_idx', ['runnerId', 'snapshotRef']) -@Index('snapshot_runner_runnerid_idx', ['runnerId']) -@Index('snapshot_runner_state_idx', ['state']) -export class SnapshotRunner { - @PrimaryGeneratedColumn('uuid') - id: string - - @Column({ - type: 'enum', - enum: SnapshotRunnerState, - default: SnapshotRunnerState.PULLING_SNAPSHOT, - }) - state: SnapshotRunnerState - - @Column({ nullable: true }) - errorReason?: string - - @Column({ - // todo: remove default - default: '', - }) - snapshotRef: string - - @Column() - runnerId: string - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date -} diff --git a/apps/api/src/sandbox/entities/snapshot.entity.ts b/apps/api/src/sandbox/entities/snapshot.entity.ts deleted file mode 100644 index 38db08500..000000000 --- a/apps/api/src/sandbox/entities/snapshot.entity.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Column, - CreateDateColumn, - Entity, - Index, - JoinColumn, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, - Unique, - UpdateDateColumn, -} from 'typeorm' -import { SnapshotRunner } from './snapshot-runner.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { BuildInfo } from './build-info.entity' -import { SnapshotRegion } from './snapshot-region.entity' - -@Entity() -@Unique(['organizationId', 'name']) -@Index('snapshot_name_idx', ['name']) -@Index('snapshot_state_idx', ['state']) -export class Snapshot { - @PrimaryGeneratedColumn('uuid') - id: string - - @Column({ - nullable: true, - type: 'uuid', - }) - organizationId?: string - - // general snapshot is available to all organizations - @Column({ - type: 'boolean', - default: false, - }) - general = false - - @Column() - name: string - - @Column({ default: '' }) - imageName: string - - @Column({ nullable: true }) - ref?: string - - @Column({ - type: 'enum', - enum: SnapshotState, - default: SnapshotState.PENDING, - }) - state = SnapshotState.PENDING - - @Column({ nullable: true }) - errorReason?: string - - @Column({ type: 'float', nullable: true }) - size?: number - - @Column({ type: 'int', default: 1 }) - cpu = 1 - - @Column({ type: 'int', default: 0 }) - gpu = 0 - - @Column({ type: 'int', default: 1 }) - mem = 1 - - @Column({ type: 'int', default: 3 }) - disk = 3 - - @Column({ type: 'boolean', default: false }) - hideFromUsers = false - - @OneToMany(() => SnapshotRunner, (runner) => runner.snapshotRef) - runners: SnapshotRunner[] - - @Column({ array: true, type: 'text', nullable: true }) - entrypoint?: string[] - - @CreateDateColumn({ - type: 'timestamp with time zone', - }) - createdAt: Date - - @UpdateDateColumn({ - type: 'timestamp with time zone', - }) - updatedAt: Date - - @Column({ nullable: true }) - lastUsedAt?: Date - - @ManyToOne(() => BuildInfo, (buildInfo) => buildInfo.snapshots, { - nullable: true, - eager: true, - }) - @JoinColumn() - buildInfo?: BuildInfo - - @Column({ nullable: true }) - initialRunnerId?: string - - @OneToMany(() => SnapshotRegion, (snapshotRegion) => snapshotRegion.snapshot, { - cascade: true, - onDelete: 'CASCADE', - }) - snapshotRegions: SnapshotRegion[] -} diff --git a/apps/api/src/sandbox/enums/backup-state.enum.ts b/apps/api/src/sandbox/enums/backup-state.enum.ts deleted file mode 100644 index 343f11a32..000000000 --- a/apps/api/src/sandbox/enums/backup-state.enum.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum BackupState { - NONE = 'None', - PENDING = 'Pending', - IN_PROGRESS = 'InProgress', - COMPLETED = 'Completed', - ERROR = 'Error', -} diff --git a/apps/api/src/sandbox/enums/job-type.enum.ts b/apps/api/src/sandbox/enums/job-type.enum.ts deleted file mode 100644 index c8d790442..000000000 --- a/apps/api/src/sandbox/enums/job-type.enum.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum JobType { - CREATE_SANDBOX = 'CREATE_SANDBOX', - START_SANDBOX = 'START_SANDBOX', - STOP_SANDBOX = 'STOP_SANDBOX', - DESTROY_SANDBOX = 'DESTROY_SANDBOX', - RESIZE_SANDBOX = 'RESIZE_SANDBOX', - CREATE_BACKUP = 'CREATE_BACKUP', - BUILD_SNAPSHOT = 'BUILD_SNAPSHOT', - PULL_SNAPSHOT = 'PULL_SNAPSHOT', - RECOVER_SANDBOX = 'RECOVER_SANDBOX', - INSPECT_SNAPSHOT_IN_REGISTRY = 'INSPECT_SNAPSHOT_IN_REGISTRY', - REMOVE_SNAPSHOT = 'REMOVE_SNAPSHOT', - UPDATE_SANDBOX_NETWORK_SETTINGS = 'UPDATE_SANDBOX_NETWORK_SETTINGS', -} diff --git a/apps/api/src/sandbox/enums/sandbox-class.enum.ts b/apps/api/src/sandbox/enums/sandbox-class.enum.ts deleted file mode 100644 index c20e608d7..000000000 --- a/apps/api/src/sandbox/enums/sandbox-class.enum.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum SandboxClass { - SMALL = 'small', - MEDIUM = 'medium', - LARGE = 'large', -} - -export const SandboxClassData = { - [SandboxClass.SMALL]: { - cpu: 4, - memory: 8, - disk: 30, - }, - [SandboxClass.MEDIUM]: { - cpu: 8, - memory: 16, - disk: 60, - }, - [SandboxClass.LARGE]: { - cpu: 12, - memory: 24, - disk: 90, - }, -} diff --git a/apps/api/src/sandbox/enums/sandbox-desired-state.enum.ts b/apps/api/src/sandbox/enums/sandbox-desired-state.enum.ts deleted file mode 100644 index 2111398aa..000000000 --- a/apps/api/src/sandbox/enums/sandbox-desired-state.enum.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum SandboxDesiredState { - DESTROYED = 'destroyed', - STARTED = 'started', - STOPPED = 'stopped', - RESIZED = 'resized', - ARCHIVED = 'archived', -} diff --git a/apps/api/src/sandbox/enums/sandbox-state.enum.ts b/apps/api/src/sandbox/enums/sandbox-state.enum.ts deleted file mode 100644 index 72c4dacdf..000000000 --- a/apps/api/src/sandbox/enums/sandbox-state.enum.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum SandboxState { - CREATING = 'creating', - RESTORING = 'restoring', - DESTROYED = 'destroyed', - DESTROYING = 'destroying', - STARTED = 'started', - STOPPED = 'stopped', - STARTING = 'starting', - STOPPING = 'stopping', - ERROR = 'error', - BUILD_FAILED = 'build_failed', - PENDING_BUILD = 'pending_build', - BUILDING_SNAPSHOT = 'building_snapshot', - UNKNOWN = 'unknown', - PULLING_SNAPSHOT = 'pulling_snapshot', - ARCHIVED = 'archived', - ARCHIVING = 'archiving', - RESIZING = 'resizing', -} diff --git a/apps/api/src/sandbox/enums/snapshot-runner-state.enum.ts b/apps/api/src/sandbox/enums/snapshot-runner-state.enum.ts deleted file mode 100644 index d2357ba15..000000000 --- a/apps/api/src/sandbox/enums/snapshot-runner-state.enum.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum SnapshotRunnerState { - PULLING_SNAPSHOT = 'pulling_snapshot', - BUILDING_SNAPSHOT = 'building_snapshot', - READY = 'ready', - ERROR = 'error', - REMOVING = 'removing', -} diff --git a/apps/api/src/sandbox/enums/snapshot-state.enum.ts b/apps/api/src/sandbox/enums/snapshot-state.enum.ts deleted file mode 100644 index 4c8be2629..000000000 --- a/apps/api/src/sandbox/enums/snapshot-state.enum.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -export enum SnapshotState { - BUILDING = 'building', - PENDING = 'pending', - PULLING = 'pulling', - ACTIVE = 'active', - INACTIVE = 'inactive', - ERROR = 'error', - BUILD_FAILED = 'build_failed', - REMOVING = 'removing', -} diff --git a/apps/api/src/sandbox/errors/runner-api-error.ts b/apps/api/src/sandbox/errors/runner-api-error.ts deleted file mode 100644 index 7e2248635..000000000 --- a/apps/api/src/sandbox/errors/runner-api-error.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -export class RunnerApiError extends Error { - constructor( - message: string, - public readonly statusCode?: number, - public readonly code?: string, - ) { - super(message) - this.name = 'RunnerApiError' - } -} diff --git a/apps/api/src/sandbox/errors/sandbox-conflict.error.ts b/apps/api/src/sandbox/errors/sandbox-conflict.error.ts deleted file mode 100644 index 5ee22d29f..000000000 --- a/apps/api/src/sandbox/errors/sandbox-conflict.error.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ConflictException } from '@nestjs/common' - -export class SandboxConflictError extends ConflictException { - constructor() { - super('Sandbox was modified by another operation') - } -} diff --git a/apps/api/src/sandbox/errors/snapshot-state-error.ts b/apps/api/src/sandbox/errors/snapshot-state-error.ts deleted file mode 100644 index 9274e9cdd..000000000 --- a/apps/api/src/sandbox/errors/snapshot-state-error.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -export class SnapshotStateError extends Error { - constructor(public readonly errorReason: string) { - super(errorReason) - this.name = 'SnapshotStateError' - } -} diff --git a/apps/api/src/sandbox/events/sandbox-archived.event.ts b/apps/api/src/sandbox/events/sandbox-archived.event.ts deleted file mode 100644 index a8127ecba..000000000 --- a/apps/api/src/sandbox/events/sandbox-archived.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxArchivedEvent { - constructor(public readonly sandbox: Sandbox) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-backup-created.event.ts b/apps/api/src/sandbox/events/sandbox-backup-created.event.ts deleted file mode 100644 index decfc0155..000000000 --- a/apps/api/src/sandbox/events/sandbox-backup-created.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxBackupCreatedEvent { - constructor(public readonly sandbox: Sandbox) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-create.event.ts b/apps/api/src/sandbox/events/sandbox-create.event.ts deleted file mode 100644 index dd176d9e2..000000000 --- a/apps/api/src/sandbox/events/sandbox-create.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxCreatedEvent { - constructor(public readonly sandbox: Sandbox) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-desired-state-updated.event.ts b/apps/api/src/sandbox/events/sandbox-desired-state-updated.event.ts deleted file mode 100644 index d80ffa206..000000000 --- a/apps/api/src/sandbox/events/sandbox-desired-state-updated.event.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' - -export class SandboxDesiredStateUpdatedEvent { - constructor( - public readonly sandbox: Sandbox, - public readonly oldDesiredState: SandboxDesiredState, - public readonly newDesiredState: SandboxDesiredState, - ) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-destroyed.event.ts b/apps/api/src/sandbox/events/sandbox-destroyed.event.ts deleted file mode 100644 index cea2b31bf..000000000 --- a/apps/api/src/sandbox/events/sandbox-destroyed.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxDestroyedEvent { - constructor(public readonly sandbox: Sandbox) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-organization-updated.event.ts b/apps/api/src/sandbox/events/sandbox-organization-updated.event.ts deleted file mode 100644 index c9dcf2e38..000000000 --- a/apps/api/src/sandbox/events/sandbox-organization-updated.event.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxOrganizationUpdatedEvent { - constructor( - public readonly sandbox: Sandbox, - public readonly oldOrganizationId: string, - public readonly newOrganizationId: string, - ) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-public-status-updated.event.ts b/apps/api/src/sandbox/events/sandbox-public-status-updated.event.ts deleted file mode 100644 index dd8923561..000000000 --- a/apps/api/src/sandbox/events/sandbox-public-status-updated.event.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxPublicStatusUpdatedEvent { - constructor( - public readonly sandbox: Sandbox, - public readonly oldStatus: boolean, - public readonly newStatus: boolean, - ) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-started.event.ts b/apps/api/src/sandbox/events/sandbox-started.event.ts deleted file mode 100644 index 011745228..000000000 --- a/apps/api/src/sandbox/events/sandbox-started.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxStartedEvent { - constructor(public readonly sandbox: Sandbox) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-state-updated.event.ts b/apps/api/src/sandbox/events/sandbox-state-updated.event.ts deleted file mode 100644 index 1a95f57fa..000000000 --- a/apps/api/src/sandbox/events/sandbox-state-updated.event.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxState } from '../enums/sandbox-state.enum' - -export class SandboxStateUpdatedEvent { - constructor( - public readonly sandbox: Sandbox, - public readonly oldState: SandboxState, - public readonly newState: SandboxState, - ) {} -} diff --git a/apps/api/src/sandbox/events/sandbox-stopped.event.ts b/apps/api/src/sandbox/events/sandbox-stopped.event.ts deleted file mode 100644 index 74a91b56c..000000000 --- a/apps/api/src/sandbox/events/sandbox-stopped.event.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox } from '../entities/sandbox.entity' - -export class SandboxStoppedEvent { - constructor( - public readonly sandbox: Sandbox, - public readonly force?: boolean, - ) {} -} diff --git a/apps/api/src/sandbox/events/snapshot-activated.event.ts b/apps/api/src/sandbox/events/snapshot-activated.event.ts deleted file mode 100644 index fd03e0c89..000000000 --- a/apps/api/src/sandbox/events/snapshot-activated.event.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Snapshot } from '../entities/snapshot.entity' - -export class SnapshotActivatedEvent { - constructor(public readonly snapshot: Snapshot) {} -} diff --git a/apps/api/src/sandbox/events/snapshot-created.event.ts b/apps/api/src/sandbox/events/snapshot-created.event.ts deleted file mode 100644 index be46d8d88..000000000 --- a/apps/api/src/sandbox/events/snapshot-created.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Snapshot } from '../entities/snapshot.entity' - -export class SnapshotCreatedEvent { - constructor(public readonly snapshot: Snapshot) {} -} diff --git a/apps/api/src/sandbox/events/snapshot-removed.event.ts b/apps/api/src/sandbox/events/snapshot-removed.event.ts deleted file mode 100644 index 0b444f549..000000000 --- a/apps/api/src/sandbox/events/snapshot-removed.event.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Snapshot } from '../entities/snapshot.entity' - -export class SnapshotRemovedEvent { - constructor(public readonly snapshot: Snapshot) {} -} diff --git a/apps/api/src/sandbox/events/snapshot-state-updated.event.ts b/apps/api/src/sandbox/events/snapshot-state-updated.event.ts deleted file mode 100644 index de3426816..000000000 --- a/apps/api/src/sandbox/events/snapshot-state-updated.event.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' - -export class SnapshotStateUpdatedEvent { - constructor( - public readonly snapshot: Snapshot, - public readonly oldState: SnapshotState, - public readonly newState: SnapshotState, - ) {} -} diff --git a/apps/api/src/sandbox/guards/region-sandbox-access.guard.ts b/apps/api/src/sandbox/guards/region-sandbox-access.guard.ts deleted file mode 100644 index ba1dce3a7..000000000 --- a/apps/api/src/sandbox/guards/region-sandbox-access.guard.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, CanActivate, ExecutionContext, NotFoundException, ForbiddenException } from '@nestjs/common' -import { SandboxService } from '../services/sandbox.service' -import { BaseAuthContext } from '../../common/interfaces/auth-context.interface' -import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' -import { - isRegionSSHGatewayContext, - RegionSSHGatewayContext, -} from '../../common/interfaces/region-ssh-gateway.interface' - -@Injectable() -export class RegionSandboxAccessGuard implements CanActivate { - constructor(private readonly sandboxService: SandboxService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() - const sandboxId: string = request.params.sandboxId || request.params.id - - const authContext: BaseAuthContext = request.user - - if (!isRegionProxyContext(authContext) && !isRegionSSHGatewayContext(authContext)) { - return false - } - - try { - const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext - const sandboxRegionId = await this.sandboxService.getRegionId(sandboxId) - if (sandboxRegionId !== regionContext.regionId) { - throw new ForbiddenException(`Sandbox region ID does not match region ${regionContext.role} region ID`) - } - return true - } catch (error) { - if (!(error instanceof NotFoundException)) { - console.error(error) - } - throw new NotFoundException(`Sandbox with ID or name ${sandboxId} not found`) - } - } -} diff --git a/apps/api/src/sandbox/guards/sandbox-access.guard.ts b/apps/api/src/sandbox/guards/sandbox-access.guard.ts deleted file mode 100644 index 89acf16e1..000000000 --- a/apps/api/src/sandbox/guards/sandbox-access.guard.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, CanActivate, ExecutionContext, NotFoundException, ForbiddenException } from '@nestjs/common' -import { SandboxService } from '../services/sandbox.service' -import { OrganizationAuthContext, BaseAuthContext } from '../../common/interfaces/auth-context.interface' -import { isRunnerContext, RunnerContext } from '../../common/interfaces/runner-context.interface' -import { SystemRole } from '../../user/enums/system-role.enum' -import { isProxyContext } from '../../common/interfaces/proxy-context.interface' -import { isSshGatewayContext } from '../../common/interfaces/ssh-gateway-context.interface' -import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' -import { - isRegionSSHGatewayContext, - RegionSSHGatewayContext, -} from '../../common/interfaces/region-ssh-gateway.interface' - -@Injectable() -export class SandboxAccessGuard implements CanActivate { - constructor(private readonly sandboxService: SandboxService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() - // TODO: remove deprecated request.params.workspaceId param once we remove the deprecated workspace controller - const sandboxIdOrName: string = - request.params.sandboxIdOrName || request.params.sandboxId || request.params.id || request.params.workspaceId - - // TODO: initialize authContext safely - const authContext: BaseAuthContext = request.user - - try { - switch (true) { - case isRunnerContext(authContext): { - // For runner authentication, verify that the runner ID matches the sandbox's runner ID - const runnerContext = authContext as RunnerContext - const sandboxRunnerId = await this.sandboxService.getRunnerId(sandboxIdOrName) - if (sandboxRunnerId !== runnerContext.runnerId) { - throw new ForbiddenException('Runner ID does not match sandbox runner ID') - } - break - } - case isRegionProxyContext(authContext): - case isRegionSSHGatewayContext(authContext): { - // For region proxy/ssh gateway authentication, verify that the runner's region ID matches the region ID - const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext - const sandboxRegionId = await this.sandboxService.getRegionId(sandboxIdOrName) - if (sandboxRegionId !== regionContext.regionId) { - throw new ForbiddenException(`Sandbox region ID does not match region ${regionContext.role} region ID`) - } - break - } - case isProxyContext(authContext): - case isSshGatewayContext(authContext): - return true - default: { - // For user/organization authentication, check organization access - const orgAuthContext = authContext as OrganizationAuthContext - const sandboxOrganizationId = await this.sandboxService.getOrganizationId( - sandboxIdOrName, - orgAuthContext.organizationId, - ) - if (orgAuthContext.role !== SystemRole.ADMIN && sandboxOrganizationId !== orgAuthContext.organizationId) { - throw new ForbiddenException('Request organization ID does not match resource organization ID') - } - } - } - return true - } catch (error) { - if (!(error instanceof NotFoundException)) { - console.error(error) - } - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - } -} diff --git a/apps/api/src/sandbox/guards/snapshot-access.guard.ts b/apps/api/src/sandbox/guards/snapshot-access.guard.ts deleted file mode 100644 index 0668bea65..000000000 --- a/apps/api/src/sandbox/guards/snapshot-access.guard.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, CanActivate, ExecutionContext, ForbiddenException, NotFoundException } from '@nestjs/common' -import { SnapshotService } from '../services/snapshot.service' -import { - BaseAuthContext, - isOrganizationAuthContext, - OrganizationAuthContext, -} from '../../common/interfaces/auth-context.interface' -import { SystemRole } from '../../user/enums/system-role.enum' -import { Snapshot } from '../entities/snapshot.entity' -import { isSshGatewayContext } from '../../common/interfaces/ssh-gateway-context.interface' -import { isProxyContext } from '../../common/interfaces/proxy-context.interface' -import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' -import { - isRegionSSHGatewayContext, - RegionSSHGatewayContext, -} from '../../common/interfaces/region-ssh-gateway.interface' - -@Injectable() -export class SnapshotAccessGuard implements CanActivate { - constructor(private readonly snapshotService: SnapshotService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() - const snapshotId: string = request.params.snapshotId || request.params.id - - let snapshot: Snapshot - - // TODO: initialize authContext safely - const authContext: BaseAuthContext = request.user - - try { - snapshot = await this.snapshotService.getSnapshot(snapshotId) - } catch { - if (!isOrganizationAuthContext(authContext)) { - throw new NotFoundException(`Snapshot with ID ${snapshotId} not found`) - } - - // If not found by ID, try by name - snapshot = await this.snapshotService.getSnapshotByName(snapshotId, authContext.organizationId) - } - - try { - switch (true) { - case isRegionProxyContext(authContext): - case isRegionSSHGatewayContext(authContext): { - // For region proxy/ssh gateway authentication, verify that the runner's region ID matches the region ID - const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext - const isAvailable = await this.snapshotService.isAvailableInRegion(snapshot.id, regionContext.regionId) - if (!isAvailable) { - throw new NotFoundException(`Snapshot is not available in region ${regionContext.regionId}`) - } - break - } - case isProxyContext(authContext): - case isSshGatewayContext(authContext): - break - default: { - // For user/organization authentication, check organization access - const orgAuthContext = authContext as OrganizationAuthContext - if (orgAuthContext.role !== SystemRole.ADMIN && snapshot.organizationId !== orgAuthContext.organizationId) { - throw new ForbiddenException('Request organization ID does not match resource organization ID') - } - } - } - - request.snapshot = snapshot - - return true - } catch (error) { - if (!(error instanceof NotFoundException)) { - console.error(error) - } - throw new NotFoundException(`Snapshot with ID or name ${snapshotId} not found`) - } - } -} diff --git a/apps/api/src/sandbox/guards/snapshot-read-access.guard.ts b/apps/api/src/sandbox/guards/snapshot-read-access.guard.ts deleted file mode 100644 index 500d0258b..000000000 --- a/apps/api/src/sandbox/guards/snapshot-read-access.guard.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, CanActivate, ExecutionContext, ForbiddenException, NotFoundException } from '@nestjs/common' -import { SnapshotService } from '../services/snapshot.service' -import { - BaseAuthContext, - isOrganizationAuthContext, - OrganizationAuthContext, -} from '../../common/interfaces/auth-context.interface' -import { SystemRole } from '../../user/enums/system-role.enum' -import { Snapshot } from '../entities/snapshot.entity' -import { isSshGatewayContext } from '../../common/interfaces/ssh-gateway-context.interface' -import { isProxyContext } from '../../common/interfaces/proxy-context.interface' -import { isRegionProxyContext, RegionProxyContext } from '../../common/interfaces/region-proxy.interface' -import { - isRegionSSHGatewayContext, - RegionSSHGatewayContext, -} from '../../common/interfaces/region-ssh-gateway.interface' - -@Injectable() -export class SnapshotReadAccessGuard implements CanActivate { - constructor(private readonly snapshotService: SnapshotService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest() - const snapshotId: string = request.params.snapshotId || request.params.id - - let snapshot: Snapshot - - const authContext: BaseAuthContext = request.user - - try { - snapshot = await this.snapshotService.getSnapshot(snapshotId) - } catch { - if (!isOrganizationAuthContext(authContext)) { - throw new NotFoundException(`Snapshot with ID ${snapshotId} not found`) - } - - snapshot = await this.snapshotService.getSnapshotByName(snapshotId, authContext.organizationId) - } - - try { - switch (true) { - case isRegionProxyContext(authContext): - case isRegionSSHGatewayContext(authContext): { - const regionContext = authContext as RegionProxyContext | RegionSSHGatewayContext - const isAvailable = await this.snapshotService.isAvailableInRegion(snapshot.id, regionContext.regionId) - if (!isAvailable) { - throw new NotFoundException(`Snapshot is not available in region ${regionContext.regionId}`) - } - break - } - case isProxyContext(authContext): - case isSshGatewayContext(authContext): - break - default: { - const orgAuthContext = authContext as OrganizationAuthContext - if ( - orgAuthContext.role !== SystemRole.ADMIN && - snapshot.organizationId !== orgAuthContext.organizationId && - !snapshot.general - ) { - throw new ForbiddenException('Request organization ID does not match resource organization ID') - } - } - } - - request.snapshot = snapshot - - return true - } catch (error) { - if (!(error instanceof NotFoundException)) { - console.error(error) - } - throw new NotFoundException(`Snapshot with ID or name ${snapshotId} not found`) - } - } -} diff --git a/apps/api/src/sandbox/managers/backup.manager.ts b/apps/api/src/sandbox/managers/backup.manager.ts deleted file mode 100644 index 179bf560b..000000000 --- a/apps/api/src/sandbox/managers/backup.manager.ts +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common' -import { Cron, CronExpression } from '@nestjs/schedule' -import { In, IsNull, LessThan, Not, Or } from 'typeorm' -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { RunnerService } from '../services/runner.service' -import { RunnerState } from '../enums/runner-state.enum' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { DockerRegistryService } from '../../docker-registry/services/docker-registry.service' -import { BackupState } from '../enums/backup-state.enum' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/sandbox.constants' -import { fromAxiosError } from '../../common/utils/from-axios-error' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { OnEvent } from '@nestjs/event-emitter' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxDestroyedEvent } from '../events/sandbox-destroyed.event' -import { SandboxBackupCreatedEvent } from '../events/sandbox-backup-created.event' -import { SandboxArchivedEvent } from '../events/sandbox-archived.event' -import { RunnerAdapterFactory } from '../runner-adapter/runnerAdapter' -import { TypedConfigService } from '../../config/typed-config.service' - -import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' -import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' -import { setTimeout } from 'timers/promises' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { DockerRegistry } from '../../docker-registry/entities/docker-registry.entity' -import { SandboxService } from '../services/sandbox.service' -import { SandboxRepository } from '../repositories/sandbox.repository' - -@Injectable() -export class BackupManager implements TrackableJobExecutions, OnApplicationShutdown { - activeJobs = new Set() - - private readonly logger = new Logger(BackupManager.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - private readonly sandboxService: SandboxService, - private readonly runnerService: RunnerService, - private readonly runnerAdapterFactory: RunnerAdapterFactory, - private readonly dockerRegistryService: DockerRegistryService, - @InjectRedis() private readonly redis: Redis, - private readonly redisLockProvider: RedisLockProvider, - private readonly configService: TypedConfigService, - ) {} - - // on init - async onApplicationBootstrap() { - await this.adHocBackupCheck() - } - - async onApplicationShutdown() { - // wait for all active jobs to finish - while (this.activeJobs.size > 0) { - this.logger.log(`Waiting for ${this.activeJobs.size} active jobs to finish`) - await setTimeout(1000) - } - } - - // todo: make frequency configurable or more efficient - @Cron(CronExpression.EVERY_5_MINUTES, { name: 'ad-hoc-backup-check' }) - @TrackJobExecution() - @LogExecution('ad-hoc-backup-check') - @WithInstrumentation() - async adHocBackupCheck(): Promise { - const lockKey = 'ad-hoc-backup-check' - const hasLock = await this.redisLockProvider.lock(lockKey, 5 * 60) - if (!hasLock) { - return - } - - // Get all ready runners - const readyRunners = await this.runnerService.findAllReady() - - try { - // Process all runners in parallel - await Promise.all( - readyRunners.map(async (runner) => { - const sandboxes = await this.sandboxRepository.find({ - where: { - runnerId: runner.id, - organizationId: Not(SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION), - state: SandboxState.STARTED, - backupState: In([BackupState.NONE, BackupState.COMPLETED]), - lastBackupAt: Or(IsNull(), LessThan(new Date(Date.now() - 1 * 60 * 60 * 1000))), - autoDeleteInterval: Not(0), - }, - order: { - lastBackupAt: 'ASC', - }, - // todo: increase this number when backup is stable - take: 10, - }) - - await Promise.all( - sandboxes.map(async (sandbox) => { - const lockKey = `sandbox-backup-${sandbox.id}` - const hasLock = await this.redisLockProvider.lock(lockKey, 60) - if (!hasLock) { - return - } - - try { - // todo: remove the catch handler asap - await this.setBackupPending(sandbox).catch((error) => { - if (error instanceof BadRequestError && error.message === 'A backup is already in progress') { - return - } - this.logger.error(`Failed to create backup for sandbox ${sandbox.id}:`, fromAxiosError(error)) - }) - } catch (error) { - this.logger.error(`Error processing stop state for sandbox ${sandbox.id}:`, fromAxiosError(error)) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - }), - ) - } catch (error) { - this.logger.error(`Error processing backups: `, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'check-backup-states' }) - @TrackJobExecution() - @LogExecution('check-backup-states') - @WithInstrumentation() - async checkBackupStates(): Promise { - // lock the sync to only run one instance at a time - const lockKey = 'check-backup-states' - const hasLock = await this.redisLockProvider.lock(lockKey, 10) - if (!hasLock) { - return - } - - try { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('runner', 'r', 'r.id = sandbox.runnerId') - .where('sandbox.state IN (:...states)', { - states: [SandboxState.ARCHIVING, SandboxState.STARTED, SandboxState.STOPPED], - }) - .andWhere('sandbox.backupState IN (:...backupStates)', { - backupStates: [BackupState.PENDING, BackupState.IN_PROGRESS], - }) - .andWhere('r.state = :ready', { ready: RunnerState.READY }) - // Prioritize manual archival action, then auto-archive poller, then ad-hoc backup poller - .addSelect( - ` - CASE sandbox.state - WHEN :archiving THEN 1 - WHEN :stopped THEN 2 - WHEN :started THEN 3 - ELSE 999 - END - `, - 'state_priority', - ) - .setParameters({ - archiving: SandboxState.ARCHIVING, - stopped: SandboxState.STOPPED, - started: SandboxState.STARTED, - }) - .orderBy('state_priority', 'ASC') - .addOrderBy('sandbox.lastBackupAt', 'ASC', 'NULLS FIRST') // Process sandboxes with no backups first - .addOrderBy('sandbox.createdAt', 'ASC') // For equal lastBackupAt, process older sandboxes first - .take(100) - .getMany() - - await Promise.allSettled( - sandboxes.map(async (s) => { - const lockKey = `sandbox-backup-${s.id}` - const hasLock = await this.redisLockProvider.lock(lockKey, 60) - if (!hasLock) { - return - } - - try { - // get the latest sandbox state - const sandbox = await this.sandboxRepository.findOneByOrFail({ - id: s.id, - }) - - try { - switch (sandbox.backupState) { - case BackupState.PENDING: { - await this.handlePendingBackup(sandbox) - break - } - case BackupState.IN_PROGRESS: { - await this.checkBackupProgress(sandbox) - break - } - } - } catch (error) { - // if error, retry 10 times - const errorRetryKey = `${lockKey}-error-retry` - const errorRetryCount = await this.redis.get(errorRetryKey) - if (!errorRetryCount) { - await this.redis.setex(errorRetryKey, 300, '1') - } else if (parseInt(errorRetryCount) > 10) { - this.logger.error(`Error processing backup for sandbox ${sandbox.id}:`, fromAxiosError(error)) - await this.sandboxService.updateSandboxBackupState( - sandbox.id, - BackupState.ERROR, - undefined, - undefined, - fromAxiosError(error).message, - ) - } else { - await this.redis.setex(errorRetryKey, 300, errorRetryCount + 1) - } - } - } catch (error) { - this.logger.error(`Error processing backup for sandbox ${s.id}:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - } catch (error) { - this.logger.error(`Error processing backups: `, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'check-backup-states-errored-draining' }) - @TrackJobExecution() - @LogExecution('check-backup-states-errored-draining') - @WithInstrumentation() - async checkBackupStatesForErroredDraining(): Promise { - const lockKey = 'check-backup-states-errored-draining' - const hasLock = await this.redisLockProvider.lock(lockKey, 10) - if (!hasLock) { - return - } - - try { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('runner', 'r', 'r.id = sandbox.runnerId') - .where('sandbox.state = :error', { error: SandboxState.ERROR }) - .andWhere('sandbox.backupState IN (:...backupStates)', { - backupStates: [BackupState.PENDING, BackupState.IN_PROGRESS], - }) - .andWhere('r.state = :ready', { ready: RunnerState.READY }) - .andWhere('r."draining" = true') - .addOrderBy('sandbox.lastBackupAt', 'ASC', 'NULLS FIRST') - .addOrderBy('sandbox.createdAt', 'ASC') - .take(100) - .getMany() - - await Promise.allSettled( - sandboxes.map(async (s) => { - const lockKey = `sandbox-backup-${s.id}` - const hasLock = await this.redisLockProvider.lock(lockKey, 60) - if (!hasLock) { - return - } - - try { - const sandbox = await this.sandboxRepository.findOneByOrFail({ - id: s.id, - }) - - try { - switch (sandbox.backupState) { - case BackupState.PENDING: { - await this.handlePendingBackup(sandbox) - break - } - case BackupState.IN_PROGRESS: { - await this.checkBackupProgress(sandbox) - break - } - } - } catch (error) { - const errorRetryKey = `${lockKey}-error-retry` - const errorRetryCount = await this.redis.get(errorRetryKey) - if (!errorRetryCount) { - await this.redis.setex(errorRetryKey, 300, '1') - } else if (parseInt(errorRetryCount) > 10) { - this.logger.error( - `Error processing backup for errored sandbox ${sandbox.id} on draining runner:`, - fromAxiosError(error), - ) - await this.sandboxService.updateSandboxBackupState( - sandbox.id, - BackupState.ERROR, - undefined, - undefined, - fromAxiosError(error).message, - ) - } else { - await this.redis.setex(errorRetryKey, 300, errorRetryCount + 1) - } - } - } catch (error) { - this.logger.error(`Error processing backup for errored sandbox ${s.id} on draining runner:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - } catch (error) { - this.logger.error(`Error processing backups for errored sandboxes on draining runners: `, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-stop-state-create-backups' }) - @TrackJobExecution() - @LogExecution('sync-stop-state-create-backups') - @WithInstrumentation() - async syncStopStateCreateBackups(): Promise { - const lockKey = 'sync-stop-state-create-backups' - const hasLock = await this.redisLockProvider.lock(lockKey, 10) - if (!hasLock) { - return - } - - try { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('runner', 'r', 'r.id = sandbox.runnerId') - .where('sandbox.state IN (:...states)', { states: [SandboxState.ARCHIVING, SandboxState.STOPPED] }) - .andWhere('sandbox.backupState = :none', { none: BackupState.NONE }) - .andWhere('r.state = :ready', { ready: RunnerState.READY }) - .take(100) - .getMany() - - await Promise.allSettled( - sandboxes - .filter((sandbox) => sandbox.runnerId !== null) - .map(async (sandbox) => { - const lockKey = `sandbox-backup-${sandbox.id}` - const hasLock = await this.redisLockProvider.lock(lockKey, 30) - if (!hasLock) { - return - } - - try { - await this.setBackupPending(sandbox) - } catch (error) { - this.logger.error(`Error processing backup for sandbox ${sandbox.id}:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - } catch (error) { - this.logger.error(`Error processing backups: `, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - async setBackupPending(sandbox: Sandbox): Promise { - if (sandbox.backupState === BackupState.COMPLETED) { - return - } - - // Allow backups for STARTED sandboxes, STOPPED/ERROR sandboxes with runnerId, or ARCHIVING sandboxes - if ( - !( - sandbox.state === SandboxState.STARTED || - sandbox.state === SandboxState.ARCHIVING || - (sandbox.state === SandboxState.STOPPED && sandbox.runnerId) || - (sandbox.state === SandboxState.ERROR && sandbox.runnerId) - ) - ) { - throw new BadRequestError('Sandbox must be started, stopped, or errored with assigned runner to create a backup') - } - - if (sandbox.backupState === BackupState.IN_PROGRESS || sandbox.backupState === BackupState.PENDING) { - return - } - - let registry: DockerRegistry | null = null - - if (sandbox.backupRegistryId) { - registry = await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - } else { - registry = await this.dockerRegistryService.getAvailableBackupRegistry(sandbox.region) - } - - if (!registry) { - throw new BadRequestError('No backup registry configured') - } - // Generate backup snapshot name - const timestamp = new Date().toISOString().replace(/[:.]/g, '-') - const backupSnapshot = `${registry.url.replace('https://', '').replace('http://', '')}/${registry.project || 'boxlite'}/backup-${sandbox.id}:${timestamp}` - - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.PENDING, backupSnapshot, registry.id) - } - - private async checkBackupProgress(sandbox: Sandbox): Promise { - try { - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - // Get sandbox info from runner - const sandboxInfo = await runnerAdapter.sandboxInfo(sandbox.id) - - switch (sandboxInfo.backupState) { - case BackupState.COMPLETED: { - // Only accept completion if the runner-reported snapshot matches the DB snapshot - if (sandboxInfo.backupSnapshot && sandboxInfo.backupSnapshot !== sandbox.backupSnapshot) { - this.logger.warn( - `Ignoring stale backup completion for sandbox ${sandbox.id}: runner snapshot ${sandboxInfo.backupSnapshot} does not match DB snapshot ${sandbox.backupSnapshot}`, - ) - break - } - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.COMPLETED) - break - } - case BackupState.ERROR: { - // Only accept failure if the runner-reported snapshot matches the DB snapshot - if (sandboxInfo.backupSnapshot && sandboxInfo.backupSnapshot !== sandbox.backupSnapshot) { - this.logger.warn( - `Ignoring stale backup failure for sandbox ${sandbox.id}: runner snapshot ${sandboxInfo.backupSnapshot} does not match DB snapshot ${sandbox.backupSnapshot}`, - ) - break - } - await this.sandboxService.updateSandboxBackupState( - sandbox.id, - BackupState.ERROR, - undefined, - undefined, - sandboxInfo.backupErrorReason, - ) - break - } - // If backup state is none, retry the backup process by setting the backup state to pending - // This can happen if the runner is restarted or the operation is cancelled - case BackupState.NONE: { - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.PENDING) - break - } - // If still in progress or any other state, do nothing and wait for next sync - } - } catch (error) { - await this.sandboxService.updateSandboxBackupState( - sandbox.id, - BackupState.ERROR, - undefined, - undefined, - fromAxiosError(error).message, - ) - throw error - } - } - - private async deleteSandboxBackupRepositoryFromRegistry(sandbox: Sandbox): Promise { - const registry = await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - - try { - await this.dockerRegistryService.deleteSandboxRepository(sandbox.id, registry) - } catch (error) { - this.logger.error( - `Failed to delete backup repository ${sandbox.id} from registry ${registry.id}:`, - fromAxiosError(error), - ) - } - } - - private async handlePendingBackup(sandbox: Sandbox): Promise { - const lockKey = `runner-${sandbox.runnerId}-backup-lock` - try { - await this.redisLockProvider.waitForLock(lockKey, 10) - - const backupsInProgress = await this.sandboxRepository.count({ - where: { - runnerId: sandbox.runnerId, - backupState: BackupState.IN_PROGRESS, - }, - }) - if (backupsInProgress >= this.configService.getOrThrow('maxConcurrentBackupsPerRunner')) { - return - } - - const registry = await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - if (!registry) { - throw new Error('Registry not found') - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - // check if backup is already in progress on the runner - const runnerSandbox = await runnerAdapter.sandboxInfo(sandbox.id) - if (runnerSandbox.backupState === BackupState.IN_PROGRESS) { - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.IN_PROGRESS) - return - } - - // Initiate backup on runner - await runnerAdapter.createBackup(sandbox, sandbox.backupSnapshot, registry) - - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.IN_PROGRESS) - } catch (error) { - if (error.response?.status === 400 && error.response?.data?.message.includes('A backup is already in progress')) { - await this.sandboxService.updateSandboxBackupState(sandbox.id, BackupState.IN_PROGRESS) - return - } - await this.sandboxService.updateSandboxBackupState( - sandbox.id, - BackupState.ERROR, - undefined, - undefined, - fromAxiosError(error).message, - ) - throw error - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnEvent(SandboxEvents.ARCHIVED) - @TrackJobExecution() - private async handleSandboxArchivedEvent(event: SandboxArchivedEvent) { - this.setBackupPending(event.sandbox) - } - - @OnEvent(SandboxEvents.DESTROYED) - @TrackJobExecution() - private async handleSandboxDestroyedEvent(event: SandboxDestroyedEvent) { - this.deleteSandboxBackupRepositoryFromRegistry(event.sandbox) - } - - @OnEvent(SandboxEvents.BACKUP_CREATED) - @TrackJobExecution() - private async handleSandboxBackupCreatedEvent(event: SandboxBackupCreatedEvent) { - this.setBackupPending(event.sandbox) - } -} diff --git a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-archive.action.ts b/apps/api/src/sandbox/managers/sandbox-actions/sandbox-archive.action.ts deleted file mode 100644 index af6750bba..000000000 --- a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-archive.action.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable } from '@nestjs/common' -import { Sandbox } from '../../entities/sandbox.entity' -import { SandboxState } from '../../enums/sandbox-state.enum' -import { DONT_SYNC_AGAIN, SandboxAction, SyncState, SYNC_AGAIN } from './sandbox.action' -import { BackupState } from '../../enums/backup-state.enum' -import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' -import { RunnerService } from '../../services/runner.service' -import { SandboxRepository } from '../../repositories/sandbox.repository' -import { InjectRedis } from '@nestjs-modules/ioredis' -import Redis from 'ioredis' -import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' -import { EventEmitter2 } from '@nestjs/event-emitter' -import { SandboxEvents } from '../../constants/sandbox-events.constants' -import { SandboxBackupCreatedEvent } from '../../events/sandbox-backup-created.event' -import { WithSpan } from '../../../common/decorators/otel.decorator' - -@Injectable() -export class SandboxArchiveAction extends SandboxAction { - constructor( - protected runnerService: RunnerService, - protected runnerAdapterFactory: RunnerAdapterFactory, - protected sandboxRepository: SandboxRepository, - protected readonly redisLockProvider: RedisLockProvider, - @InjectRedis() private readonly redis: Redis, - private readonly eventEmitter: EventEmitter2, - ) { - super(runnerService, runnerAdapterFactory, sandboxRepository, redisLockProvider) - } - - @WithSpan() - async run(sandbox: Sandbox, lockCode: LockCode): Promise { - // Only proceed with archiving if the sandbox is in STOPPED, ARCHIVING or ERROR (runner draining) state. - // For all other states, do not proceed with archiving. - if ( - sandbox.state !== SandboxState.STOPPED && - sandbox.state !== SandboxState.ARCHIVING && - sandbox.state !== SandboxState.ERROR - ) { - return DONT_SYNC_AGAIN - } - - const lockKey = 'archive-lock-' + sandbox.runnerId - if (!(await this.redisLockProvider.lock(lockKey, 10))) { - return DONT_SYNC_AGAIN - } - - const isFromErrorState = sandbox.state === SandboxState.ERROR - - await this.redisLockProvider.unlock(lockKey) - - // if the backup state is error, we need to retry the backup - if (sandbox.backupState === BackupState.ERROR) { - const archiveErrorRetryKey = 'archive-error-retry-' + sandbox.id - const archiveErrorRetryCountRaw = await this.redis.get(archiveErrorRetryKey) - const archiveErrorRetryCount = archiveErrorRetryCountRaw ? parseInt(archiveErrorRetryCountRaw) : 0 - // if the archive error retry count is greater than 3, we need to mark the sandbox as error - if (archiveErrorRetryCount > 3) { - // Only transition to ERROR if not already in ERROR state - if (!isFromErrorState) { - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - 'Failed to archive sandbox after 3 retries', - ) - } - await this.redis.del(archiveErrorRetryKey) - return DONT_SYNC_AGAIN - } - await this.redis.setex('archive-error-retry-' + sandbox.id, 720, String(archiveErrorRetryCount + 1)) - - // recreate the backup to retry - this.eventEmitter.emit(SandboxEvents.BACKUP_CREATED, new SandboxBackupCreatedEvent(sandbox)) - - return DONT_SYNC_AGAIN - } - - if (sandbox.backupState !== BackupState.COMPLETED) { - return DONT_SYNC_AGAIN - } - - // when the backup is completed, destroy the sandbox on the runner - // and deassociate the sandbox from the runner - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - try { - const sandboxInfo = await runnerAdapter.sandboxInfo(sandbox.id) - if (sandboxInfo.state === SandboxState.DESTROYED) { - if (isFromErrorState) { - this.logger.warn(`Transitioning sandbox ${sandbox.id} from ERROR to ARCHIVED state (runner draining)`) - } - - await this.updateSandboxState(sandbox, SandboxState.ARCHIVED, lockCode, null) - return DONT_SYNC_AGAIN - } - - if (sandboxInfo.state !== SandboxState.DESTROYING) { - await runnerAdapter.destroySandbox(sandbox.id) - } - - return SYNC_AGAIN - } catch (error) { - // fail for errors other than sandbox not found or sandbox already destroyed - if ( - (error.response?.data?.statusCode === 400 && - error.response?.data?.message.includes('Sandbox already destroyed')) || - error.response?.status === 404 || - error.statusCode === 404 - ) { - // if the sandbox is already destroyed, do nothing - if (isFromErrorState) { - this.logger.warn(`Transitioning sandbox ${sandbox.id} from ERROR to ARCHIVED state (runner draining)`) - } - - await this.updateSandboxState(sandbox, SandboxState.ARCHIVED, lockCode, null) - return DONT_SYNC_AGAIN - } - - throw error - } - } -} diff --git a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-destroy.action.ts b/apps/api/src/sandbox/managers/sandbox-actions/sandbox-destroy.action.ts deleted file mode 100644 index 88233131a..000000000 --- a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-destroy.action.ts +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable } from '@nestjs/common' -import { Sandbox } from '../../entities/sandbox.entity' -import { SandboxState } from '../../enums/sandbox-state.enum' -import { DONT_SYNC_AGAIN, SandboxAction, SyncState, SYNC_AGAIN } from './sandbox.action' -import { RunnerState } from '../../enums/runner-state.enum' -import { RunnerService } from '../../services/runner.service' -import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' -import { SandboxRepository } from '../../repositories/sandbox.repository' -import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' -import { WithSpan } from '../../../common/decorators/otel.decorator' - -@Injectable() -export class SandboxDestroyAction extends SandboxAction { - constructor( - protected runnerService: RunnerService, - protected runnerAdapterFactory: RunnerAdapterFactory, - protected sandboxRepository: SandboxRepository, - protected redisLockProvider: RedisLockProvider, - ) { - super(runnerService, runnerAdapterFactory, sandboxRepository, redisLockProvider) - } - - @WithSpan() - async run(sandbox: Sandbox, lockCode: LockCode): Promise { - if (sandbox.state === SandboxState.DESTROYED) { - return DONT_SYNC_AGAIN - } - - if (sandbox.state === SandboxState.ARCHIVED || sandbox.state === SandboxState.PENDING_BUILD) { - await this.updateSandboxState(sandbox, SandboxState.DESTROYED, lockCode) - return DONT_SYNC_AGAIN - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - if (runner.state !== RunnerState.READY) { - return DONT_SYNC_AGAIN - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - try { - const sandboxInfo = await runnerAdapter.sandboxInfo(sandbox.id) - - if (sandboxInfo.state === SandboxState.DESTROYED) { - await this.updateSandboxState(sandbox, SandboxState.DESTROYED, lockCode) - return DONT_SYNC_AGAIN - } - - if (sandbox.state !== SandboxState.DESTROYING) { - await runnerAdapter.destroySandbox(sandbox.id) - await this.updateSandboxState(sandbox, SandboxState.DESTROYING, lockCode) - } - - return SYNC_AGAIN - } catch (error) { - // if the sandbox is not found on runner, it is already destroyed - if (error.response?.status === 404 || error.statusCode === 404) { - await this.updateSandboxState(sandbox, SandboxState.DESTROYED, lockCode) - return DONT_SYNC_AGAIN - } - - throw error - } - } -} diff --git a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-start.action.ts b/apps/api/src/sandbox/managers/sandbox-actions/sandbox-start.action.ts deleted file mode 100644 index dab612604..000000000 --- a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-start.action.ts +++ /dev/null @@ -1,867 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger, NotFoundException } from '@nestjs/common' -import { SandboxRepository } from '../../repositories/sandbox.repository' -import { RECOVERY_ERROR_SUBSTRINGS } from '../../constants/errors-for-recovery' -import { Sandbox } from '../../entities/sandbox.entity' -import { SandboxState } from '../../enums/sandbox-state.enum' -import { DONT_SYNC_AGAIN, SandboxAction, SYNC_AGAIN, SyncState } from './sandbox.action' -import { SANDBOX_BUILD_INFO_CACHE_TTL_MS } from '../../utils/sandbox-lookup-cache.util' -import { SnapshotRunnerState } from '../../enums/snapshot-runner-state.enum' -import { BackupState } from '../../enums/backup-state.enum' -import { RunnerState } from '../../enums/runner-state.enum' -import { BuildInfo } from '../../entities/build-info.entity' -import { SnapshotService } from '../../services/snapshot.service' -import { DockerRegistryService } from '../../../docker-registry/services/docker-registry.service' -import { DockerRegistry } from '../../../docker-registry/entities/docker-registry.entity' -import { RunnerService } from '../../services/runner.service' -import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' -import { SnapshotStateError } from '../../errors/snapshot-state-error' -import { Snapshot } from '../../entities/snapshot.entity' -import { OrganizationService } from '../../../organization/services/organization.service' -import { TypedConfigService } from '../../../config/typed-config.service' -import { Runner } from '../../entities/runner.entity' -import { Organization } from '../../../organization/entities/organization.entity' -import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' -import { InjectRedis } from '@nestjs-modules/ioredis' -import Redis from 'ioredis' -import { WithSpan } from '../../../common/decorators/otel.decorator' -import { SandboxActivityService } from '../../services/sandbox-activity.service' - -@Injectable() -export class SandboxStartAction extends SandboxAction { - protected readonly logger = new Logger(SandboxStartAction.name) - constructor( - protected runnerService: RunnerService, - protected runnerAdapterFactory: RunnerAdapterFactory, - protected sandboxRepository: SandboxRepository, - protected readonly snapshotService: SnapshotService, - protected readonly dockerRegistryService: DockerRegistryService, - protected readonly organizationService: OrganizationService, - protected readonly configService: TypedConfigService, - protected readonly redisLockProvider: RedisLockProvider, - @InjectRedis() private readonly redis: Redis, - private readonly sandboxActivityService: SandboxActivityService, - ) { - super(runnerService, runnerAdapterFactory, sandboxRepository, redisLockProvider) - } - - @WithSpan() - async run(sandbox: Sandbox, lockCode: LockCode): Promise { - // Load buildInfo only for states that need it — avoids a JOIN+DISTINCT in the - // shared syncInstanceState query that stop/destroy/archive paths never use. - if ( - sandbox.snapshot === null && - [SandboxState.PENDING_BUILD, SandboxState.BUILDING_SNAPSHOT, SandboxState.UNKNOWN].includes(sandbox.state) - ) { - await this.loadBuildInfo(sandbox) - } - - switch (sandbox.state) { - case SandboxState.PULLING_SNAPSHOT: { - if (!sandbox.runnerId) { - // Using the PULLING_SNAPSHOT state for the case where the runner isn't assigned yet as well - return this.handleUnassignedRunnerSandbox(sandbox, lockCode) - } else { - return this.handleRunnerSandboxStartedStateCheck(sandbox, lockCode) - } - } - case SandboxState.PENDING_BUILD: { - return this.handleUnassignedRunnerSandbox(sandbox, lockCode, true) - } - case SandboxState.BUILDING_SNAPSHOT: { - return this.handleRunnerSandboxBuildingSnapshotStateOnDesiredStateStart(sandbox, lockCode) - } - case SandboxState.UNKNOWN: { - return this.handleRunnerSandboxUnknownStateOnDesiredStateStart(sandbox, lockCode) - } - case SandboxState.ARCHIVED: - case SandboxState.ARCHIVING: - case SandboxState.STOPPED: { - return this.handleRunnerSandboxStoppedOrArchivedStateOnDesiredStateStart(sandbox, lockCode) - } - case SandboxState.RESTORING: - case SandboxState.CREATING: - case SandboxState.STARTING: { - return this.handleRunnerSandboxStartedStateCheck(sandbox, lockCode) - } - case SandboxState.ERROR: { - this.logger.error(`Sandbox ${sandbox.id} is in error state on desired state start`) - return DONT_SYNC_AGAIN - } - } - - return DONT_SYNC_AGAIN - } - - /** - * Loads the buildInfo relation for a sandbox. - * Uses QueryBuilder with getMany() to avoid the SELECT DISTINCT subquery - * that TypeORM generates when combining relations with findOne/LIMIT. - * Since sandbox.id is a PK and BuildInfo is @ManyToOne, at most one row is returned. - */ - private async loadBuildInfo(sandbox: Sandbox): Promise { - const [result] = await this.sandboxRepository - .createQueryBuilder('sandbox') - .leftJoinAndSelect('sandbox.buildInfo', 'buildInfo') - .where('sandbox.id = :id', { id: sandbox.id }) - .cache(`sandbox:buildInfo:${sandbox.id}`, SANDBOX_BUILD_INFO_CACHE_TTL_MS) - .getMany() - sandbox.buildInfo = result?.buildInfo ?? null - } - - private async handleRunnerSandboxBuildingSnapshotStateOnDesiredStateStart( - sandbox: Sandbox, - lockCode: LockCode, - ): Promise { - // Check for timeout - allow up to 60 minutes since the last sandbox update - const timeoutMinutes = 60 - const timeoutMs = timeoutMinutes * 60 * 1000 - - if (sandbox.updatedAt && Date.now() - sandbox.updatedAt.getTime() > timeoutMs) { - await this.updateSandboxState( - sandbox, - SandboxState.BUILD_FAILED, - lockCode, - undefined, - 'Timeout while building snapshot on runner', - ) - return DONT_SYNC_AGAIN - } - - const snapshotRunner = await this.runnerService.getSnapshotRunner(sandbox.runnerId, sandbox.buildInfo.snapshotRef) - if (snapshotRunner) { - switch (snapshotRunner.state) { - case SnapshotRunnerState.READY: { - // TODO: "UNKNOWN" should probably be changed to something else - await this.updateSandboxState(sandbox, SandboxState.UNKNOWN, lockCode) - return SYNC_AGAIN - } - case SnapshotRunnerState.ERROR: { - await this.updateSandboxState( - sandbox, - SandboxState.BUILD_FAILED, - lockCode, - undefined, - snapshotRunner.errorReason, - ) - return DONT_SYNC_AGAIN - } - } - } - if (!snapshotRunner || snapshotRunner.state === SnapshotRunnerState.BUILDING_SNAPSHOT) { - // Sleep for a second and go back to syncing instance state - await new Promise((resolve) => setTimeout(resolve, 1000)) - return SYNC_AGAIN - } - - return DONT_SYNC_AGAIN - } - - private async handleUnassignedRunnerSandbox( - sandbox: Sandbox, - lockCode: LockCode, - isBuild = false, - ): Promise { - // Get snapshot reference based on whether it's a pull or build operation - let snapshotRef: string - - if (isBuild) { - snapshotRef = sandbox.buildInfo.snapshotRef - } else { - const snapshot = await this.snapshotService.getSnapshotByName(sandbox.snapshot, sandbox.organizationId) - snapshotRef = snapshot.ref - } - - const declarativeBuildScoreThreshold = this.configService.get('runnerScore.thresholds.declarativeBuild') - - // Try to assign an available runner with the snapshot already available - try { - const runner = await this.runnerService.getRandomAvailableRunner({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - snapshotRef: snapshotRef, - ...(isBuild && - declarativeBuildScoreThreshold !== undefined && { - availabilityScoreThreshold: declarativeBuildScoreThreshold, - }), - }) - if (runner) { - await this.updateSandboxState(sandbox, SandboxState.UNKNOWN, lockCode, runner.id) - return SYNC_AGAIN - } - } catch { - // Continue to next assignment method - } - - // Try to assign an available runner that is currently processing the snapshot - const snapshotRunners = await this.runnerService.getSnapshotRunners(snapshotRef) - const targetState = isBuild ? SnapshotRunnerState.BUILDING_SNAPSHOT : SnapshotRunnerState.PULLING_SNAPSHOT - const targetSandboxState = isBuild ? SandboxState.BUILDING_SNAPSHOT : SandboxState.PULLING_SNAPSHOT - const errorSandboxState = isBuild ? SandboxState.BUILD_FAILED : SandboxState.ERROR - - for (const snapshotRunner of snapshotRunners) { - // Consider removing the runner usage rate check or improving it - const runner = await this.runnerService.findOneOrFail(snapshotRunner.runnerId) - - if (snapshotRunner.state === SnapshotRunnerState.ERROR) { - await this.updateSandboxState(sandbox, errorSandboxState, lockCode, runner.id, snapshotRunner.errorReason) - return DONT_SYNC_AGAIN - } - - if (runner.unschedulable || runner.draining || runner.state !== RunnerState.READY) { - continue - } - - if (declarativeBuildScoreThreshold === undefined || runner.availabilityScore >= declarativeBuildScoreThreshold) { - if (snapshotRunner.state === targetState) { - await this.updateSandboxState(sandbox, targetSandboxState, lockCode, runner.id) - return SYNC_AGAIN - } - } - } - - // Get excluded runner IDs based on operation type - const excludedRunnerIds = await (isBuild - ? this.runnerService.getRunnersWithMultipleSnapshotsBuilding() - : this.runnerService.getRunnersWithMultipleSnapshotsPulling()) - - // Try to assign an available runner to start processing the snapshot - let runner: Runner - - try { - runner = await this.runnerService.getRandomAvailableRunner({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - excludedRunnerIds: excludedRunnerIds, - ...(isBuild && - declarativeBuildScoreThreshold !== undefined && { - availabilityScoreThreshold: declarativeBuildScoreThreshold, - }), - }) - } catch { - // TODO: reconsider the timeout here - // No runners available, wait for 3 seconds and retry - await new Promise((resolve) => setTimeout(resolve, 3000)) - return SYNC_AGAIN - } - - if (isBuild) { - this.buildOnRunner(sandbox.buildInfo, runner, sandbox.organizationId) - await this.updateSandboxState(sandbox, SandboxState.BUILDING_SNAPSHOT, lockCode, runner.id) - } else { - const snapshot = await this.snapshotService.getSnapshotByName(sandbox.snapshot, sandbox.organizationId) - await this.runnerService.createSnapshotRunnerEntry(runner.id, snapshot.ref, SnapshotRunnerState.PULLING_SNAPSHOT) - this.pullSnapshotToRunner(snapshot, runner) - await this.updateSandboxState(sandbox, SandboxState.PULLING_SNAPSHOT, lockCode, runner.id) - } - - return SYNC_AGAIN - } - - async pullSnapshotToRunner(snapshot: Snapshot, runner: Runner) { - const internalRegistry = await this.dockerRegistryService.findInternalRegistryBySnapshotRef( - snapshot.ref, - runner.region, - ) - if (!internalRegistry) { - throw new Error('No internal registry found for sandbox snapshot') - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - // Fire the pull request (runner returns 202 immediately) - await runnerAdapter.pullSnapshot(snapshot.ref, internalRegistry) - - const pollTimeoutMs = 60 * 60 * 1_000 // 1 hour - const pollIntervalMs = 5 * 1_000 // 5 seconds - const startTime = Date.now() - - while (Date.now() - startTime < pollTimeoutMs) { - try { - await runnerAdapter.getSnapshotInfo(snapshot.ref) - return - } catch (err) { - if (err instanceof SnapshotStateError) { - throw err - } - } - await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)) - } - } - - // Initiates the snapshot build on the runner and creates an SnapshotRunner depending on the result - async buildOnRunner(buildInfo: BuildInfo, runner: Runner, organizationId: string) { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - const sourceRegistries = await this.dockerRegistryService.getSourceRegistriesForDockerfile( - buildInfo.dockerfileContent, - organizationId, - ) - - // Fire build request (runner returns 202 immediately) - await runnerAdapter.buildSnapshot( - buildInfo, - organizationId, - sourceRegistries.length > 0 ? sourceRegistries : undefined, - ) - - const pollTimeoutMs = 60 * 60 * 1_000 // 1 hour - const pollIntervalMs = 5 * 1_000 // 5 seconds - const startTime = Date.now() - - while (Date.now() - startTime < pollTimeoutMs) { - try { - await runnerAdapter.getSnapshotInfo(buildInfo.snapshotRef) - break - } catch (err) { - if (err instanceof SnapshotStateError) { - await this.runnerService.createSnapshotRunnerEntry( - runner.id, - buildInfo.snapshotRef, - SnapshotRunnerState.ERROR, - err.message, - ) - return - } - await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)) - } - } - - if (Date.now() - startTime >= pollTimeoutMs) { - await this.runnerService.createSnapshotRunnerEntry( - runner.id, - buildInfo.snapshotRef, - SnapshotRunnerState.ERROR, - 'Timeout while building', - ) - return - } - - const exists = await runnerAdapter.snapshotExists(buildInfo.snapshotRef) - let state = SnapshotRunnerState.BUILDING_SNAPSHOT - if (exists) { - state = SnapshotRunnerState.READY - } - - await this.runnerService.createSnapshotRunnerEntry(runner.id, buildInfo.snapshotRef, state) - } - - private async handleRunnerSandboxUnknownStateOnDesiredStateStart( - sandbox: Sandbox, - lockCode: LockCode, - ): Promise { - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - if (runner.state !== RunnerState.READY) { - return DONT_SYNC_AGAIN - } - - const organization = await this.organizationService.findOne(sandbox.organizationId) - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - let internalRegistry: DockerRegistry - let entrypoint: string[] - let snapshotRef: string - if (!sandbox.buildInfo) { - // get internal snapshot name - const snapshot = await this.snapshotService.getSnapshotByName(sandbox.snapshot, sandbox.organizationId) - snapshotRef = snapshot.ref - - internalRegistry = await this.dockerRegistryService.findInternalRegistryBySnapshotRef(snapshotRef, runner.region) - if (!internalRegistry) { - throw new Error('No registry found for snapshot') - } - - entrypoint = snapshot.entrypoint - } else { - snapshotRef = sandbox.buildInfo.snapshotRef - entrypoint = this.snapshotService.getEntrypointFromDockerfile(sandbox.buildInfo.dockerfileContent) - } - - const metadata = { - ...organization?.sandboxMetadata, - sandboxName: sandbox.name, - } - - const result = await runnerAdapter.createSandbox( - sandbox, - snapshotRef, - internalRegistry, - entrypoint, - metadata, - this.configService.get('sandboxOtel.endpointUrl'), - ) - - await this.updateSandboxState(sandbox, SandboxState.CREATING, lockCode, undefined, undefined, result?.daemonVersion) - // sync states again immediately for sandbox - return SYNC_AGAIN - } - - private async handleRunnerSandboxStoppedOrArchivedStateOnDesiredStateStart( - sandbox: Sandbox, - lockCode: LockCode, - ): Promise { - const organization = await this.organizationService.findOne(sandbox.organizationId) - - // check if sandbox is assigned to a runner and if that runner is unschedulable - // if it is, move sandbox to prevRunnerId, and set runnerId to null - // this will assign a new runner to the sandbox and restore the sandbox from the latest backup - if (sandbox.runnerId) { - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - const originalRunnerId = sandbox.runnerId // Store original value - - const startScoreThreshold = this.configService.get('runnerScore.thresholds.start') || 0 - - const shouldMoveToNewRunner = - (runner.unschedulable || runner.state != RunnerState.READY || runner.availabilityScore < startScoreThreshold) && - sandbox.backupState === BackupState.COMPLETED - - // if the runner is unschedulable/not ready and sandbox has a valid backup, move sandbox to a new runner - if (shouldMoveToNewRunner) { - sandbox.prevRunnerId = originalRunnerId - sandbox.runnerId = null - - await this.sandboxRepository.update( - sandbox.id, - { - updateData: { - prevRunnerId: originalRunnerId, - runnerId: null, - }, - }, - true, - ) - } - - // If the sandbox is on a runner and its backupState is COMPLETED - // but there are too many running sandboxes on that runner, move it to a less used runner - if (sandbox.backupState === BackupState.COMPLETED) { - if (runner.availabilityScore < this.configService.getOrThrow('runnerScore.thresholds.availability')) { - const availableRunners = await this.runnerService.findAvailableRunners({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - }) - const lessUsedRunners = availableRunners.filter((runner) => runner.id !== originalRunnerId) - - // temp workaround to move sandboxes to less used runner - if (lessUsedRunners.length > 0) { - sandbox.prevRunnerId = originalRunnerId - sandbox.runnerId = null - - await this.sandboxRepository.update( - sandbox.id, - { - updateData: { - prevRunnerId: originalRunnerId, - runnerId: null, - }, - }, - true, - ) - try { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - await runnerAdapter.destroySandbox(sandbox.id) - } catch (e) { - if (e.response?.status !== 404 && e.statusCode !== 404) { - this.logger.error(`Failed to cleanup sandbox ${sandbox.id} on previous runner ${runner.id}:`, e) - } - } - } - } - } - } - - if (sandbox.runnerId === null) { - // if sandbox has no runner, check if backup is completed - // if not, set sandbox to error - // if backup is completed, get random available runner and start sandbox - // use the backup to start the sandbox - - if (sandbox.backupState !== BackupState.COMPLETED) { - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - 'Sandbox has no runner and backup is not completed', - ) - return DONT_SYNC_AGAIN - } - - const syncCheck = await this.restoreSandboxOnNewRunner(sandbox, lockCode, organization, sandbox.prevRunnerId) - if (syncCheck !== null) { - return syncCheck - } - } else { - // if sandbox has runner, start sandbox - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - if (runner.state !== RunnerState.READY) { - return DONT_SYNC_AGAIN - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - const metadata: { [key: string]: string } = { ...organization?.sandboxMetadata } - if (sandbox.volumes?.length) { - metadata['volumes'] = JSON.stringify( - sandbox.volumes.map((v) => ({ volumeId: v.volumeId, mountPath: v.mountPath, subpath: v.subpath })), - ) - } - - try { - await runnerAdapter.startSandbox(sandbox.id, sandbox.authToken, metadata) - } catch (error) { - // Check against a list of substrings that should trigger an automatic recovery - if (error?.message) { - const matchesRecovery = RECOVERY_ERROR_SUBSTRINGS.some((substring) => - error.message.toLowerCase().includes(substring.toLowerCase()), - ) - if (matchesRecovery) { - try { - await this.restoreSandboxOnNewRunner(sandbox, lockCode, organization, sandbox.runnerId, true) - this.logger.warn(`Sandbox ${sandbox.id} transferred to a new runner`) - return SYNC_AGAIN - } catch (restoreError) { - this.logger.warn(`Sandbox ${sandbox.id} recovery attempt failed:`, restoreError.message) - } - } - } - throw error - } - - await this.updateSandboxState(sandbox, SandboxState.STARTING, lockCode) - return SYNC_AGAIN - } - - return SYNC_AGAIN - } - - // used to check if sandbox is started on runner and update sandbox state accordingly - // also used to handle the case where a sandbox is started on a runner and then transferred to a new runner - private async handleRunnerSandboxStartedStateCheck(sandbox: Sandbox, lockCode: LockCode): Promise { - // edge case when sandbox is being transferred to a new runner - if (!sandbox.runnerId) { - return SYNC_AGAIN - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - const sandboxInfo = await runnerAdapter.sandboxInfo(sandbox.id) - - switch (sandboxInfo.state) { - case SandboxState.STARTED: { - // if previous backup state is error or completed, set backup state to none - if ([BackupState.ERROR, BackupState.COMPLETED].includes(sandbox.backupState)) { - await this.updateSandboxState( - sandbox, - SandboxState.STARTED, - lockCode, - undefined, - undefined, - sandboxInfo.daemonVersion, - BackupState.NONE, - ) - return DONT_SYNC_AGAIN - } else { - await this.updateSandboxState( - sandbox, - SandboxState.STARTED, - lockCode, - undefined, - undefined, - sandboxInfo.daemonVersion, - ) - - // if sandbox was transferred to a new runner, remove it from the old runner - if (sandbox.prevRunnerId) { - await this.removeSandboxFromPreviousRunner(sandbox) - } - - return DONT_SYNC_AGAIN - } - } - case SandboxState.STARTING: - if (await this.checkTimeoutError(sandbox, 5, 'Timeout while starting sandbox')) { - return DONT_SYNC_AGAIN - } - break - case SandboxState.RESTORING: - if (await this.checkTimeoutError(sandbox, 30, 'Timeout while starting sandbox')) { - return DONT_SYNC_AGAIN - } - break - case SandboxState.CREATING: { - if (await this.checkTimeoutError(sandbox, 15, 'Timeout while creating sandbox')) { - return DONT_SYNC_AGAIN - } - break - } - case SandboxState.UNKNOWN: { - await this.updateSandboxState(sandbox, SandboxState.UNKNOWN, lockCode) - break - } - case SandboxState.ERROR: { - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - 'Sandbox entered error state on runner during startup wait loop', - ) - break - } - case SandboxState.PULLING_SNAPSHOT: { - if (await this.checkTimeoutError(sandbox, 30, 'Timeout while pulling snapshot')) { - return DONT_SYNC_AGAIN - } - await this.updateSandboxState(sandbox, SandboxState.PULLING_SNAPSHOT, lockCode) - break - } - case SandboxState.DESTROYED: { - this.logger.warn( - `Sandbox ${sandbox.id} is in destroyed state while starting on runner ${sandbox.runnerId}, prev runner ${sandbox.prevRunnerId}`, - ) - await this.checkTimeoutError( - sandbox, - 15, - 'Timeout while starting sandbox: Sandbox is in unknown state on runner', - ) - return DONT_SYNC_AGAIN - } - // also any other state that is not STARTED - default: { - this.logger.error(`Sandbox ${sandbox.id} is in unexpected state ${sandboxInfo.state}`) - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - `Sandbox is in unexpected state: ${sandboxInfo.state}`, - ) - break - } - } - - return SYNC_AGAIN - } - - private async checkTimeoutError(sandbox: Sandbox, timeoutMinutes: number, errorReason: string): Promise { - const lastActivityAt = await this.sandboxActivityService.getLastActivityAt(sandbox.id) - if (lastActivityAt && lastActivityAt.getTime() < Date.now() - 1000 * 60 * timeoutMinutes) { - const updateData: Partial = { - state: SandboxState.ERROR, - errorReason, - recoverable: false, - } - await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - return true - } - return false - } - - private async restoreSandboxOnNewRunner( - sandbox: Sandbox, - lockCode: LockCode, - organization: Organization, - excludedRunnerId: string, - isRecovery?: boolean, - ): Promise { - let lockKey: string | null = null - - // Recovery lock to prevent frequent automatic restore attempts - if (isRecovery) { - lockKey = `sandbox-${sandbox.id}-restored-cooldown` - const sixHoursInSeconds = 6 * 60 * 60 - const acquired = await this.redisLockProvider.lock(lockKey, sixHoursInSeconds) - if (!acquired) { - return null - } - } - - if (!sandbox.backupRegistryId) { - throw new Error('No registry found for backup') - } - - const registry = await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - if (!registry) { - throw new Error('No registry found for backup') - } - - // make sure we pick a runner that has the base snapshot - let baseSnapshot: Snapshot | null = null - if (sandbox.snapshot) { - try { - baseSnapshot = await this.snapshotService.getSnapshotByName(sandbox.snapshot, sandbox.organizationId) - } catch (e) { - if (e instanceof NotFoundException) { - // if the base snapshot is not found, we'll use any available runner later - } else { - if (isRecovery) { - return SYNC_AGAIN - } - // for all other errors, throw them - throw e - } - } - } - - const snapshotRef = baseSnapshot ? baseSnapshot.ref : null - - let availableRunners: Runner[] = [] - - const excludedRunnerIds: string[] = excludedRunnerId ? [excludedRunnerId] : [] - - const runnersWithBaseSnapshot: Runner[] = snapshotRef - ? await this.runnerService.findAvailableRunners({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - snapshotRef, - excludedRunnerIds, - }) - : [] - if (runnersWithBaseSnapshot.length > 0) { - availableRunners = runnersWithBaseSnapshot - } else { - // if no runner has the base snapshot, get all available runners - availableRunners = await this.runnerService.findAvailableRunners({ - regions: [sandbox.region], - excludedRunnerIds, - }) - } - - // check if we have any available runners after filtering - if (availableRunners.length === 0) { - // Sync state again later. Runners are unavailable - if (isRecovery) { - await this.redisLockProvider.unlock(lockKey) - } - return DONT_SYNC_AGAIN - } - - // get random runner from available runners - const randomRunnerIndex = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1) + min) - const runner = availableRunners[randomRunnerIndex(0, availableRunners.length - 1)] - - // verify the runner is still available and ready - if (!runner || runner.state !== RunnerState.READY || runner.unschedulable) { - this.logger.warn(`Selected runner ${runner?.id || 'null'} is no longer available, retrying sandbox assignment`) - if (isRecovery) { - await this.redisLockProvider.unlock(lockKey) - } - return SYNC_AGAIN - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - const existingBackups = sandbox.existingBackupSnapshots - .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) - .map((existingSnapshot) => existingSnapshot.snapshotName) - - let validBackup: string | null = null - let exists = false - - for (const existingBackup of existingBackups) { - try { - if (!validBackup && sandbox.backupSnapshot) { - // last snapshot is the current snapshot, so we don't need to check it - // just in case, we'll use the value from the backupSnapshot property - validBackup = sandbox.backupSnapshot - } else { - validBackup = existingBackup - } - - if (!validBackup) { - continue - } - - await runnerAdapter.inspectSnapshotInRegistry(validBackup, registry) - exists = true - break - } catch (error) { - this.logger.error(`Failed to check if backup snapshot ${validBackup} exists in registry ${registry.id}:`, error) - } - } - - const restoreBackupSnapshotRetryKey = `restore-backup-snapshot-retry-${sandbox.id}` - if (!exists) { - if (!isRecovery) { - // Check retry count - allow up to 3 attempts for transient issues - const retryCountRaw = await this.redis.get(restoreBackupSnapshotRetryKey) - const retryCount = retryCountRaw ? parseInt(retryCountRaw) : 0 - - if (retryCount < 3) { - // Increment retry count with 10 minute TTL, let syncStates cron pick up the retry later - await this.redis.setex(restoreBackupSnapshotRetryKey, 600, String(retryCount + 1)) - this.logger.warn( - `No valid backup snapshot found for sandbox ${sandbox.id}, retry attempt ${retryCount + 1}/3`, - ) - return DONT_SYNC_AGAIN - } - - // After 3 retries, error out and clear the retry counter - await this.redis.del(restoreBackupSnapshotRetryKey) - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - 'No valid backup snapshot found', - ) - } else { - throw new Error('No valid backup snapshot found') - } - return SYNC_AGAIN - } - - // Clear the retry counter on success - await this.redis.del(restoreBackupSnapshotRetryKey) - - await this.updateSandboxState(sandbox, SandboxState.RESTORING, lockCode, runner.id) - - const metadata = { - ...organization?.sandboxMetadata, - sandboxName: sandbox.name, - } - - await runnerAdapter.createSandbox( - sandbox, - validBackup, - registry, - undefined, - metadata, - this.configService.get('sandboxOtel.endpointUrl'), - ) - return null - } - - private async removeSandboxFromPreviousRunner(sandbox: Sandbox): Promise { - const runner = await this.runnerService.findOne(sandbox.prevRunnerId) - if (!runner) { - this.logger.warn(`Previously assigned runner ${sandbox.prevRunnerId} for sandbox ${sandbox.id} not found`) - - await this.sandboxRepository.update(sandbox.id, { updateData: { prevRunnerId: null } }, true) - return - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - try { - // First try to destroy the sandbox - await runnerAdapter.destroySandbox(sandbox.id) - } catch (error) { - if (error.response?.status !== 404 && error.statusCode !== 404) { - this.logger.error(`Failed to cleanup sandbox ${sandbox.id} on previous runner ${runner.id}:`, error) - throw error - } - } - - await this.sandboxRepository.update(sandbox.id, { updateData: { prevRunnerId: null } }, true) - } -} diff --git a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-stop.action.ts b/apps/api/src/sandbox/managers/sandbox-actions/sandbox-stop.action.ts deleted file mode 100644 index 4fe940bbd..000000000 --- a/apps/api/src/sandbox/managers/sandbox-actions/sandbox-stop.action.ts +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable } from '@nestjs/common' -import { Sandbox } from '../../entities/sandbox.entity' -import { SandboxState } from '../../enums/sandbox-state.enum' -import { DONT_SYNC_AGAIN, SandboxAction, SyncState, SYNC_AGAIN } from './sandbox.action' -import { BackupState } from '../../enums/backup-state.enum' -import { RunnerState } from '../../enums/runner-state.enum' -import { RunnerService } from '../../services/runner.service' -import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' -import { SandboxRepository } from '../../repositories/sandbox.repository' -import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' -import { WithSpan } from '../../../common/decorators/otel.decorator' - -@Injectable() -export class SandboxStopAction extends SandboxAction { - constructor( - protected runnerService: RunnerService, - protected runnerAdapterFactory: RunnerAdapterFactory, - protected sandboxRepository: SandboxRepository, - protected redisLockProvider: RedisLockProvider, - ) { - super(runnerService, runnerAdapterFactory, sandboxRepository, redisLockProvider) - } - - @WithSpan() - async run(sandbox: Sandbox, lockCode: LockCode, force?: boolean): Promise { - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - if (runner.state !== RunnerState.READY) { - return DONT_SYNC_AGAIN - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - if (sandbox.state === SandboxState.STARTED) { - // stop sandbox - await runnerAdapter.stopSandbox(sandbox.id, force) - await this.updateSandboxState(sandbox, SandboxState.STOPPING, lockCode) - - // sync states again immediately for sandbox - return SYNC_AGAIN - } - - if (sandbox.state !== SandboxState.STOPPING && sandbox.state !== SandboxState.ERROR) { - return DONT_SYNC_AGAIN - } - - const sandboxInfo = await runnerAdapter.sandboxInfo(sandbox.id) - - if (sandboxInfo.state === SandboxState.STOPPED) { - await this.updateSandboxState( - sandbox, - SandboxState.STOPPED, - lockCode, - undefined, - undefined, - undefined, - BackupState.NONE, - ) - return DONT_SYNC_AGAIN - } else if (sandboxInfo.state === SandboxState.ERROR) { - await this.updateSandboxState( - sandbox, - SandboxState.ERROR, - lockCode, - undefined, - 'Sandbox is in error state on runner', - ) - return DONT_SYNC_AGAIN - } - - return SYNC_AGAIN - } -} diff --git a/apps/api/src/sandbox/managers/sandbox-actions/sandbox.action.ts b/apps/api/src/sandbox/managers/sandbox-actions/sandbox.action.ts deleted file mode 100644 index eba2dedb6..000000000 --- a/apps/api/src/sandbox/managers/sandbox-actions/sandbox.action.ts +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { RunnerService } from '../../services/runner.service' -import { RunnerAdapterFactory } from '../../runner-adapter/runnerAdapter' -import { Sandbox } from '../../entities/sandbox.entity' -import { SandboxRepository } from '../../repositories/sandbox.repository' -import { SandboxState } from '../../enums/sandbox-state.enum' -import { BackupState } from '../../enums/backup-state.enum' -import { getStateChangeLockKey } from '../../utils/lock-key.util' -import { LockCode, RedisLockProvider } from '../../common/redis-lock.provider' - -export const SYNC_AGAIN = 'sync-again' -export const DONT_SYNC_AGAIN = 'dont-sync-again' -export type SyncState = typeof SYNC_AGAIN | typeof DONT_SYNC_AGAIN - -@Injectable() -export abstract class SandboxAction { - protected readonly logger = new Logger(SandboxAction.name) - - constructor( - protected readonly runnerService: RunnerService, - protected runnerAdapterFactory: RunnerAdapterFactory, - protected readonly sandboxRepository: SandboxRepository, - protected readonly redisLockProvider: RedisLockProvider, - ) {} - - abstract run(sandbox: Sandbox, lockCode: LockCode): Promise - - protected async updateSandboxState( - sandbox: Sandbox, - state: SandboxState, - expectedLockCode: LockCode, - runnerId?: string | null | undefined, - errorReason?: string, - daemonVersion?: string, - backupState?: BackupState, - recoverable?: boolean, - ) { - // check if the lock code is still valid - const lockKey = getStateChangeLockKey(sandbox.id) - const currentLockCode = await this.redisLockProvider.getCode(lockKey) - - if (currentLockCode === null) { - this.logger.warn( - `no lock code found - state update action expired - skipping - sandboxId: ${sandbox.id} - state: ${state}`, - ) - return - } - - if (expectedLockCode.getCode() !== currentLockCode.getCode()) { - this.logger.warn( - `lock code mismatch - state update action expired - skipping - sandboxId: ${sandbox.id} - state: ${state}`, - ) - return - } - - if (state !== SandboxState.ARCHIVED && !sandbox.pending) { - const err = new Error(`sandbox ${sandbox.id} is not in a pending state`) - this.logger.error(err) - return - } - - const updateData: Partial = { - state, - } - - if (runnerId !== undefined) { - updateData.runnerId = runnerId - } - - if (errorReason !== undefined) { - updateData.errorReason = errorReason - if (state === SandboxState.ERROR) { - updateData.recoverable = recoverable ?? false - } - } - - if (sandbox.state === SandboxState.ERROR && !sandbox.errorReason) { - updateData.errorReason = 'Sandbox is in error state during update' - updateData.recoverable = false - } - - if (daemonVersion !== undefined) { - updateData.daemonVersion = daemonVersion - } - - if (state == SandboxState.DESTROYED) { - updateData.backupState = BackupState.NONE - } - - if (backupState !== undefined) { - Object.assign(updateData, Sandbox.getBackupStateUpdate(sandbox, backupState)) - } - - if (recoverable !== undefined) { - updateData.recoverable = recoverable - } - - await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - } -} diff --git a/apps/api/src/sandbox/managers/sandbox.manager.ts b/apps/api/src/sandbox/managers/sandbox.manager.ts deleted file mode 100755 index a93068a9d..000000000 --- a/apps/api/src/sandbox/managers/sandbox.manager.ts +++ /dev/null @@ -1,962 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common' -import { Cron, CronExpression } from '@nestjs/schedule' -import { In, IsNull, Not } from 'typeorm' -import { randomUUID } from 'crypto' - -import { SandboxConflictError } from '../errors/sandbox-conflict.error' -import { JobConflictError } from '../errors/job-conflict.error' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { RunnerService } from '../services/runner.service' - -import { RedisLockProvider, LockCode } from '../common/redis-lock.provider' - -import { SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/sandbox.constants' - -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxStoppedEvent } from '../events/sandbox-stopped.event' -import { SandboxStartedEvent } from '../events/sandbox-started.event' -import { SandboxArchivedEvent } from '../events/sandbox-archived.event' -import { SandboxDestroyedEvent } from '../events/sandbox-destroyed.event' -import { SandboxCreatedEvent } from '../events/sandbox-create.event' - -import { WithInstrumentation, WithSpan } from '../../common/decorators/otel.decorator' - -import { SandboxStartAction } from './sandbox-actions/sandbox-start.action' -import { SandboxStopAction } from './sandbox-actions/sandbox-stop.action' -import { SandboxDestroyAction } from './sandbox-actions/sandbox-destroy.action' -import { SandboxArchiveAction } from './sandbox-actions/sandbox-archive.action' -import { SYNC_AGAIN, DONT_SYNC_AGAIN } from './sandbox-actions/sandbox.action' - -import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' -import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' -import { setTimeout } from 'timers/promises' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { getStateChangeLockKey } from '../utils/lock-key.util' -import { BackupState } from '../enums/backup-state.enum' -import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' -import { sanitizeSandboxError } from '../utils/sanitize-error.util' -import { Sandbox } from '../entities/sandbox.entity' -import { RunnerAdapterFactory } from '../runner-adapter/runnerAdapter' -import { DockerRegistryService } from '../../docker-registry/services/docker-registry.service' -import { OrganizationService } from '../../organization/services/organization.service' -import { TypedConfigService } from '../../config/typed-config.service' -import { BackupManager } from './backup.manager' -import { InjectRedis } from '@nestjs-modules/ioredis' -import Redis from 'ioredis' -import { InjectDataSource } from '@nestjs/typeorm' -import { DataSource } from 'typeorm' - -@Injectable() -export class SandboxManager implements TrackableJobExecutions, OnApplicationShutdown { - activeJobs = new Set() - - private readonly logger = new Logger(SandboxManager.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - private readonly runnerService: RunnerService, - private readonly redisLockProvider: RedisLockProvider, - private readonly sandboxStartAction: SandboxStartAction, - private readonly sandboxStopAction: SandboxStopAction, - private readonly sandboxDestroyAction: SandboxDestroyAction, - private readonly sandboxArchiveAction: SandboxArchiveAction, - private readonly configService: TypedConfigService, - private readonly dockerRegistryService: DockerRegistryService, - private readonly organizationService: OrganizationService, - private readonly runnerAdapterFactory: RunnerAdapterFactory, - private readonly backupManager: BackupManager, - @InjectRedis() private readonly redis: Redis, - @InjectDataSource() private readonly dataSource: DataSource, - ) {} - - async onApplicationShutdown() { - // wait for all active jobs to finish - while (this.activeJobs.size > 0) { - this.logger.log(`Waiting for ${this.activeJobs.size} active jobs to finish`) - await setTimeout(1000) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'auto-stop-check' }) - @TrackJobExecution() - @WithInstrumentation() - @LogExecution('auto-stop-check') - @WithInstrumentation() - async autostopCheck(): Promise { - const lockKey = 'auto-stop-check-worker-selected' - // lock the sync to only run one instance at a time - if (!(await this.redisLockProvider.lock(lockKey, 60))) { - return - } - - try { - const readyRunners = await this.runnerService.findAllReady() - - // Process all runners in parallel - await Promise.all( - readyRunners.map(async (runner) => { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('sandbox_last_activity', 'activity', 'activity."sandboxId" = sandbox.id') - .where('sandbox."runnerId" = :runnerId', { runnerId: runner.id }) - .andWhere('sandbox."organizationId" != :warmPoolOrg', { - warmPoolOrg: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - }) - .andWhere('sandbox.state = :state', { state: SandboxState.STARTED }) - .andWhere('sandbox."desiredState" = :desiredState', { - desiredState: SandboxDesiredState.STARTED, - }) - .andWhere('sandbox.pending != true') - .andWhere('sandbox."autoStopInterval" != 0') - .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoStopInterval"') - .orderBy('sandbox."lastBackupAt"', 'ASC') - .limit(100) - .getMany() - - await Promise.all( - sandboxes.map(async (sandbox) => { - const lockKey = getStateChangeLockKey(sandbox.id) - const acquired = await this.redisLockProvider.lock(lockKey, 30) - if (!acquired) { - return - } - - let updateData: Partial = {} - - // if auto-delete interval is 0, delete the sandbox immediately - if (sandbox.autoDeleteInterval === 0) { - updateData = Sandbox.getSoftDeleteUpdate(sandbox) - } else { - updateData.pending = true - updateData.desiredState = SandboxDesiredState.STOPPED - } - - try { - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - this.syncInstanceState(sandbox.id).catch(this.logger.error) - } catch (error) { - this.logger.error(`Error processing auto-stop state for sandbox ${sandbox.id}:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - }), - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'auto-archive-check' }) - @TrackJobExecution() - @LogExecution('auto-archive-check') - @WithInstrumentation() - async autoArchiveCheck(): Promise { - const lockKey = 'auto-archive-check-worker-selected' - // lock the sync to only run one instance at a time - if (!(await this.redisLockProvider.lock(lockKey, 60))) { - return - } - - try { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('sandbox_last_activity', 'activity', 'activity."sandboxId" = sandbox.id') - .where('sandbox."organizationId" != :warmPoolOrg', { - warmPoolOrg: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - }) - .andWhere('sandbox.state = :state', { state: SandboxState.STOPPED }) - .andWhere('sandbox."desiredState" = :desiredState', { - desiredState: SandboxDesiredState.STOPPED, - }) - .andWhere('sandbox.pending != true') - .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoArchiveInterval"') - .orderBy('sandbox."lastBackupAt"', 'ASC') - .limit(100) - .getMany() - - await Promise.all( - sandboxes.map(async (sandbox) => { - const lockKey = getStateChangeLockKey(sandbox.id) - const acquired = await this.redisLockProvider.lock(lockKey, 30) - if (!acquired) { - return - } - - try { - const updateData: Partial = { - desiredState: SandboxDesiredState.ARCHIVED, - } - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - this.syncInstanceState(sandbox.id).catch(this.logger.error) - } catch (error) { - this.logger.error(`Error processing auto-archive state for sandbox ${sandbox.id}:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'auto-delete-check' }) - @TrackJobExecution() - @LogExecution('auto-delete-check') - @WithInstrumentation() - async autoDeleteCheck(): Promise { - const lockKey = 'auto-delete-check-worker-selected' - // lock the sync to only run one instance at a time - if (!(await this.redisLockProvider.lock(lockKey, 60))) { - return - } - - try { - const readyRunners = await this.runnerService.findAllReady() - - // Process all runners in parallel - await Promise.all( - readyRunners.map(async (runner) => { - const sandboxes = await this.sandboxRepository - .createQueryBuilder('sandbox') - .innerJoin('sandbox_last_activity', 'activity', 'activity."sandboxId" = sandbox.id') - .where('sandbox."runnerId" = :runnerId', { runnerId: runner.id }) - .andWhere('sandbox."organizationId" != :warmPoolOrg', { - warmPoolOrg: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - }) - .andWhere('sandbox.state = :state', { state: SandboxState.STOPPED }) - .andWhere('sandbox."desiredState" = :desiredState', { - desiredState: SandboxDesiredState.STOPPED, - }) - .andWhere('sandbox.pending != true') - .andWhere('sandbox."autoDeleteInterval" >= 0') - .andWhere('activity."lastActivityAt" < NOW() - INTERVAL \'1 minute\' * sandbox."autoDeleteInterval"') - .orderBy('activity."lastActivityAt"', 'ASC') - .limit(100) - .getMany() - - await Promise.all( - sandboxes.map(async (sandbox) => { - const lockKey = getStateChangeLockKey(sandbox.id) - const acquired = await this.redisLockProvider.lock(lockKey, 30) - if (!acquired) { - return - } - - try { - const updateData = Sandbox.getSoftDeleteUpdate(sandbox) - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - this.syncInstanceState(sandbox.id).catch(this.logger.error) - } catch (error) { - this.logger.error(`Error processing auto-delete state for sandbox ${sandbox.id}:`, error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - }), - ) - }), - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'draining-runner-sandboxes-check' }) - @TrackJobExecution() - @LogExecution('draining-runner-sandboxes-check') - @WithInstrumentation() - async drainingRunnerSandboxesCheck(): Promise { - const lockKey = 'draining-runner-sandboxes-check' - const lockTtl = 10 * 60 // seconds (10 min) - if (!(await this.redisLockProvider.lock(lockKey, lockTtl))) { - return - } - - try { - const skip = (await this.redis.get('draining-runner-sandboxes-skip')) || 0 - - const drainingRunners = await this.runnerService.findDrainingPaginated(Number(skip), 10) - - this.logger.debug(`Checking ${drainingRunners.length} draining runners for sandbox migration (offset: ${skip})`) - - if (drainingRunners.length === 0) { - await this.redis.set('draining-runner-sandboxes-skip', 0) - return - } - - await this.redis.set('draining-runner-sandboxes-skip', Number(skip) + drainingRunners.length) - - await Promise.allSettled( - drainingRunners.map(async (runner) => { - try { - const sandboxes = await this.sandboxRepository.find({ - where: { - runnerId: runner.id, - state: SandboxState.STOPPED, - desiredState: SandboxDesiredState.STOPPED, - backupState: BackupState.COMPLETED, - backupSnapshot: Not(IsNull()), - }, - take: 100, - }) - - this.logger.debug( - `Found ${sandboxes.length} eligible sandboxes on draining runner ${runner.id} for migration`, - ) - - await Promise.allSettled( - sandboxes.map(async (sandbox) => { - const sandboxLockKey = getStateChangeLockKey(sandbox.id) - const hasSandboxLock = await this.redisLockProvider.lock(sandboxLockKey, 60) - if (!hasSandboxLock) { - return - } - - try { - const startScoreThreshold = this.configService.get('runnerScore.thresholds.start') || 0 - const targetRunner = await this.runnerService.getRandomAvailableRunner({ - snapshotRef: sandbox.backupSnapshot, - excludedRunnerIds: [runner.id], - availabilityScoreThreshold: startScoreThreshold, - }) - - await this.reassignSandbox(sandbox, runner.id, targetRunner.id) - } catch (e) { - this.logger.error(`Error migrating sandbox ${sandbox.id} from draining runner ${runner.id}`, e) - } finally { - await this.redisLockProvider.unlock(sandboxLockKey) - } - }), - ) - - // Archive ERROR sandboxes that have completed backups on this draining runner - await this.archiveErroredSandboxesOnDrainingRunner(runner.id) - - // Recover recoverable ERROR sandboxes in-place (expand disk) so they become STOPPED - await this.recoverRecoverableSandboxesOnDrainingRunner(runner.id) - - // Retry backups for non-started sandboxes with errored backup state - await this.retryErroredBackupsOnDrainingRunner(runner.id) - } catch (e) { - this.logger.error(`Error processing draining runner ${runner.id} for sandbox migration`, e) - } - }), - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - private async archiveErroredSandboxesOnDrainingRunner(runnerId: string): Promise { - const erroredSandboxes = await this.sandboxRepository.find({ - where: { - runnerId, - state: SandboxState.ERROR, - recoverable: false, - desiredState: Not(In([SandboxDesiredState.DESTROYED, SandboxDesiredState.ARCHIVED])), - backupState: BackupState.COMPLETED, - backupSnapshot: Not(IsNull()), - }, - take: 100, - }) - - if (erroredSandboxes.length === 0) { - return - } - - this.logger.debug( - `Found ${erroredSandboxes.length} errored sandboxes with completed backups on draining runner ${runnerId}`, - ) - - await Promise.allSettled( - erroredSandboxes.map(async (sandbox) => { - const sandboxLockKey = getStateChangeLockKey(sandbox.id) - const acquired = await this.redisLockProvider.lock(sandboxLockKey, 30) - if (!acquired) { - return - } - - try { - this.logger.warn( - `Setting desired state to ARCHIVED for errored sandbox ${sandbox.id} on draining runner ${runnerId} (previous desired state: ${sandbox.desiredState})`, - ) - const updateData: Partial = { - desiredState: SandboxDesiredState.ARCHIVED, - } - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { state: SandboxState.ERROR }, - }) - } catch (e) { - this.logger.error( - `Failed to set desired state to ARCHIVED for errored sandbox ${sandbox.id} on draining runner ${runnerId}`, - e, - ) - } finally { - await this.redisLockProvider.unlock(sandboxLockKey) - } - }), - ) - } - - private static readonly DRAINING_BACKUP_RETRY_TTL_SECONDS = 12 * 60 * 60 // 12 hours - private static readonly DRAINING_RECOVER_TTL_SECONDS = 12 * 60 * 60 // 12 hours - - private async retryErroredBackupsOnDrainingRunner(runnerId: string): Promise { - const erroredSandboxes = await this.sandboxRepository.find({ - where: [ - { - runnerId, - state: SandboxState.STOPPED, - recoverable: false, - desiredState: SandboxDesiredState.STOPPED, - backupState: BackupState.ERROR, - }, - { - runnerId, - state: SandboxState.ERROR, - recoverable: false, - backupState: In([BackupState.ERROR, BackupState.NONE]), - desiredState: Not(SandboxDesiredState.DESTROYED), - }, - ], - take: 100, - }) - - if (erroredSandboxes.length === 0) { - return - } - - this.logger.debug(`Found ${erroredSandboxes.length} sandboxes with errored backups on draining runner ${runnerId}`) - - await Promise.allSettled( - erroredSandboxes.map(async (sandbox) => { - const redisKey = `draining:backup-retry:${sandbox.id}` - - // Check if we've already retried within the last 12 hours - const alreadyRetried = await this.redis.exists(redisKey) - if (alreadyRetried) { - this.logger.debug( - `Skipping backup retry for sandbox ${sandbox.id} on draining runner ${runnerId} — already retried within 12 hours`, - ) - return - } - - try { - await this.backupManager.setBackupPending(sandbox) - await this.redis.set(redisKey, '1', 'EX', SandboxManager.DRAINING_BACKUP_RETRY_TTL_SECONDS) - this.logger.log(`Retried backup for sandbox ${sandbox.id} on draining runner ${runnerId}`) - } catch (e) { - this.logger.error(`Failed to retry backup for sandbox ${sandbox.id} on draining runner ${runnerId}`, e) - } - }), - ) - } - - private async recoverRecoverableSandboxesOnDrainingRunner(runnerId: string): Promise { - const recoverableSandboxes = await this.sandboxRepository.find({ - where: { - runnerId, - recoverable: true, - desiredState: Not(In([SandboxDesiredState.DESTROYED])), - backupSnapshot: Not(IsNull()), - }, - take: 100, - }) - - if (recoverableSandboxes.length === 0) { - return - } - - this.logger.debug(`Found ${recoverableSandboxes.length} recoverable sandboxes on draining runner ${runnerId}`) - - const runner = await this.runnerService.findOneOrFail(runnerId) - - if (runner.apiVersion === '2') { - this.logger.debug( - `Skipping recovery for sandboxes on draining runner ${runnerId} — not supported for runner API v2`, - ) - return - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - await Promise.allSettled( - recoverableSandboxes.map(async (sandbox) => { - const redisKey = `draining:recover:${sandbox.id}` - - // Check if we've already attempted recovery within the last 12 hours - const alreadyAttempted = await this.redis.exists(redisKey) - if (alreadyAttempted) { - this.logger.debug( - `Skipping recovery for sandbox ${sandbox.id} on draining runner ${runnerId} — already attempted within 12 hours`, - ) - return - } - - const sandboxLockKey = getStateChangeLockKey(sandbox.id) - const acquired = await this.redisLockProvider.lock(sandboxLockKey, 60) - if (!acquired) { - return - } - - try { - await runnerAdapter.recoverSandbox(sandbox) - - const updateData: Partial = { - state: SandboxState.STOPPED, - desiredState: SandboxDesiredState.STOPPED, - errorReason: null, - recoverable: false, - backupState: BackupState.NONE, - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - this.logger.log(`Recovered sandbox ${sandbox.id} on draining runner ${runnerId}`) - } catch (e) { - await this.redis.set(redisKey, '1', 'EX', SandboxManager.DRAINING_RECOVER_TTL_SECONDS) - this.logger.error(`Failed to recover sandbox ${sandbox.id} on draining runner ${runnerId}`, e) - } finally { - await this.redisLockProvider.unlock(sandboxLockKey) - } - }), - ) - } - - private async reassignSandbox(sandbox: Sandbox, oldRunnerId: string, newRunnerId: string): Promise { - this.logger.debug( - `Starting sandbox reassignment for ${sandbox.id} from runner ${oldRunnerId} to runner ${newRunnerId}`, - ) - - // Safety check: ensure sandbox is not pending - if (sandbox.pending) { - this.logger.warn( - `Sandbox ${sandbox.id} is pending, skipping reassignment from runner ${oldRunnerId} to runner ${newRunnerId}`, - ) - return - } - - if (!sandbox.backupRegistryId) { - throw new Error(`Sandbox ${sandbox.id} has no backup registry`) - } - - const registry = await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - if (!registry) { - throw new Error(`Registry ${sandbox.backupRegistryId} not found for sandbox ${sandbox.id}`) - } - - const organization = await this.organizationService.findOne(sandbox.organizationId) - - const metadata = { - ...organization?.sandboxMetadata, - sandboxName: sandbox.name, - } - - const newRunner = await this.runnerService.findOneOrFail(newRunnerId) - const newRunnerAdapter = await this.runnerAdapterFactory.create(newRunner) - - try { - // Pass undefined for entrypoint as the backup snapshot already has it baked in and use skipStart - await newRunnerAdapter.createSandbox( - sandbox, - sandbox.backupSnapshot, - registry, - undefined, - metadata, - undefined, - true, - ) - this.logger.debug(`Created sandbox ${sandbox.id} on new runner ${newRunnerId} with skipStart`) - } catch (e) { - this.logger.error(`Failed to create sandbox ${sandbox.id} on new runner ${newRunnerId}`, e) - throw e - } - - // Re-fetch sandbox from DB to get fresh state (the in-memory entity may be stale) - const freshSandbox = await this.sandboxRepository.findOne({ where: { id: sandbox.id } }) - if (!freshSandbox || freshSandbox.pending) { - this.logger.warn( - `Sandbox ${sandbox.id} is pending or missing, aborting reassignment from runner ${oldRunnerId} to runner ${newRunnerId}`, - ) - - // Roll back: remove the sandbox from the new runner since we won't complete the migration - try { - await newRunnerAdapter.destroySandbox(sandbox.id) - this.logger.debug(`Rolled back sandbox ${sandbox.id} creation on new runner ${newRunnerId}`) - } catch (rollbackErr) { - this.logger.error( - `Failed to roll back sandbox ${sandbox.id} on new runner ${newRunnerId} after pending check`, - rollbackErr, - ) - } - return - } - - // Update the sandbox to use the new runner; roll back on failure - try { - const updateData: Partial = { - prevRunnerId: sandbox.runnerId, - runnerId: newRunnerId, - } - await this.sandboxRepository.update( - sandbox.id, - { - updateData, - }, - true, - ) - } catch (e) { - this.logger.error(`Failed to update sandbox ${sandbox.id} runnerId to ${newRunnerId}, rolling back`, e) - - // Roll back: remove the sandbox from the new runner - try { - await newRunnerAdapter.destroySandbox(sandbox.id) - this.logger.debug(`Rolled back sandbox ${sandbox.id} creation on new runner ${newRunnerId}`) - } catch (rollbackErr) { - this.logger.error( - `Failed to roll back sandbox ${sandbox.id} on new runner ${newRunnerId} after DB update failure`, - rollbackErr, - ) - } - throw e - } - - this.logger.log(`Migrated sandbox ${sandbox.id} from draining runner ${oldRunnerId} to runner ${newRunnerId}`) - - // Best effort deletion of the sandbox on the old runner - try { - const oldRunner = await this.runnerService.findOne(oldRunnerId) - if (oldRunner) { - const oldRunnerAdapter = await this.runnerAdapterFactory.create(oldRunner) - await oldRunnerAdapter.destroySandbox(sandbox.id) - this.logger.debug(`Deleted sandbox ${sandbox.id} from old runner ${oldRunnerId}`) - } - } catch (e) { - this.logger.warn(`Best effort deletion failed for sandbox ${sandbox.id} on old runner ${oldRunnerId}`, e) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-states' }) - @TrackJobExecution() - @WithInstrumentation() - @LogExecution('sync-states') - async syncStates(): Promise { - const globalLockKey = 'sync-states' - const lockTtl = 10 * 60 // seconds (10 min) - if (!(await this.redisLockProvider.lock(globalLockKey, lockTtl))) { - return - } - - try { - const queryBuilder = this.sandboxRepository - .createQueryBuilder('sandbox') - .select(['sandbox.id']) - .leftJoin('sandbox_last_activity', 'activity', 'activity."sandboxId" = sandbox.id') - .where('sandbox.state NOT IN (:...excludedStates)', { - excludedStates: [ - SandboxState.DESTROYED, - SandboxState.ERROR, - SandboxState.BUILD_FAILED, - SandboxState.RESIZING, - ], - }) - .andWhere('sandbox."desiredState"::text != sandbox.state::text') - .andWhere('sandbox."desiredState"::text != :archived', { archived: SandboxDesiredState.ARCHIVED }) - .orderBy('activity."lastActivityAt"', 'DESC', 'NULLS LAST') - - const stream = await queryBuilder.stream() - let processedCount = 0 - const maxProcessPerRun = 1000 - const pendingProcesses: Promise[] = [] - - try { - await new Promise((resolve, reject) => { - stream.on('data', async (row: any) => { - if (processedCount >= maxProcessPerRun) { - resolve() - return - } - - const lockKey = getStateChangeLockKey(row.sandbox_id) - if (await this.redisLockProvider.isLocked(lockKey)) { - // Sandbox is already being processed, skip it - return - } - - // Process sandbox asynchronously but track the promise - const processPromise = this.syncInstanceState(row.sandbox_id).catch((err) => { - this.logger.error(`Error syncing sandbox state for ${row.sandbox_id}`, err) - }) - pendingProcesses.push(processPromise) - processedCount++ - - // Limit concurrent processing to avoid overwhelming the system - if (pendingProcesses.length >= 10) { - stream.pause() - Promise.allSettled(pendingProcesses.splice(0, pendingProcesses.length)) - .then(() => stream.resume()) - .catch(reject) - } - }) - - stream.on('end', () => { - Promise.allSettled(pendingProcesses) - .then(() => { - resolve() - }) - .catch(reject) - }) - - stream.on('error', reject) - }) - } finally { - if (!stream.destroyed) { - stream.destroy() - } - } - } finally { - await this.redisLockProvider.unlock(globalLockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-archived-desired-states' }) - @TrackJobExecution() - @LogExecution('sync-archived-desired-states') - @WithInstrumentation() - async syncArchivedDesiredStates(): Promise { - const lockKey = 'sync-archived-desired-states' - if (!(await this.redisLockProvider.lock(lockKey, 30))) { - return - } - - const sandboxes = await this.sandboxRepository.find({ - where: { - state: In([SandboxState.ARCHIVING, SandboxState.STOPPED, SandboxState.ERROR]), - desiredState: SandboxDesiredState.ARCHIVED, - }, - take: 100, - order: { - updatedAt: 'ASC', - }, - }) - - await Promise.all( - sandboxes.map(async (sandbox) => { - this.syncInstanceState(sandbox.id) - }), - ) - await this.redisLockProvider.unlock(lockKey) - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-archived-completed-states' }) - @TrackJobExecution() - @LogExecution('sync-archived-completed-states') - async syncArchivedCompletedStates(): Promise { - const lockKey = 'sync-archived-completed-states' - if (!(await this.redisLockProvider.lock(lockKey, 30))) { - return - } - - const sandboxes = await this.sandboxRepository.find({ - where: { - state: In([SandboxState.ARCHIVING, SandboxState.STOPPED, SandboxState.ERROR]), - desiredState: SandboxDesiredState.ARCHIVED, - backupState: BackupState.COMPLETED, - }, - take: 100, - order: { - updatedAt: 'ASC', - }, - }) - - await Promise.allSettled( - sandboxes.map(async (sandbox) => { - await this.syncInstanceState(sandbox.id) - }), - ) - await this.redisLockProvider.unlock(lockKey) - } - - /** - * Sync the state of a sandbox. - * - * Loop to handle SYNC_AGAIN without releasing the lock or re-fetching. - * The sandbox entity is mutated in-place by repository.update() on each iteration, - * and the lock guarantees no concurrent modification. - */ - async syncInstanceState(sandboxId: string, force?: boolean): Promise { - // Track the start time of the sync operation. - const startedAt = new Date() - - // Generate a random lock code to prevent race condition if sandbox action continues after the lock expires. - const lockCode = new LockCode(randomUUID()) - - // Prevent syncState cron from running multiple instances of the same sandbox. - const lockKey = getStateChangeLockKey(sandboxId) - const acquired = await this.redisLockProvider.lock(lockKey, 30, lockCode) - if (!acquired) { - return - } - - try { - const sandbox = await this.sandboxRepository.findOneOrFail({ - where: { id: sandboxId }, - }) - - while (new Date().getTime() - startedAt.getTime() <= 10000) { - if ( - [SandboxState.DESTROYED, SandboxState.BUILD_FAILED, SandboxState.RESIZING].includes(sandbox.state) || - (sandbox.state === SandboxState.ERROR && sandbox.desiredState !== SandboxDesiredState.ARCHIVED) - ) { - // Break sync loop if sandbox reaches a terminal state. - // However, should allow ERROR → ARCHIVED transition (e.g., during runner draining). - break - } - - if (String(sandbox.state) === String(sandbox.desiredState)) { - this.logger.warn( - `Sandbox ${sandboxId} is already in the desired state ${sandbox.desiredState}, skipping sync`, - ) - // Break sync loop if sandbox is already in the desired state. - break - } - - // Rely on the sandbox action to return SYNC_AGAIN or DONT_SYNC_AGAIN to continue/break the sync loop. - let syncState = DONT_SYNC_AGAIN - - try { - switch (sandbox.desiredState) { - case SandboxDesiredState.STARTED: { - syncState = await this.sandboxStartAction.run(sandbox, lockCode) - break - } - case SandboxDesiredState.STOPPED: { - syncState = await this.sandboxStopAction.run(sandbox, lockCode, force) - break - } - case SandboxDesiredState.DESTROYED: { - syncState = await this.sandboxDestroyAction.run(sandbox, lockCode) - break - } - case SandboxDesiredState.ARCHIVED: { - syncState = await this.sandboxArchiveAction.run(sandbox, lockCode) - break - } - } - } catch (error) { - if (error instanceof SandboxConflictError) { - this.logger.warn( - `Sandbox ${sandboxId} was modified by another operation during sync, skipping error transition`, - ) - break - } - - if (error instanceof JobConflictError) { - this.logger.debug(`Job already in progress for sandbox ${sandboxId}, skipping`) - break - } - - this.logger.error(`Error processing desired state for sandbox ${sandboxId}:`, error) - - const { recoverable, errorReason } = sanitizeSandboxError(error) - - const updateData: Partial = { - state: SandboxState.ERROR, - errorReason, - recoverable, - } - - // Update sandbox to error state without safeguards - await this.sandboxRepository.updateWhere(sandboxId, { updateData, whereCondition: {} }) - - // Break sync loop since sandbox is in error state. - break - } - - // Do not sync again for v2 runners - // Job completion will update the sandbox state - if (sandbox.runnerId && (await this.runnerService.getRunnerApiVersion(sandbox.runnerId)) === '2') { - break - } - - // Break sync loop if sandbox action returned DONT_SYNC_AGAIN. - if (syncState !== SYNC_AGAIN) { - break - } - } - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @OnAsyncEvent({ - event: SandboxEvents.ARCHIVED, - }) - @TrackJobExecution() - @WithSpan() - private async handleSandboxArchivedEvent(event: SandboxArchivedEvent) { - await this.syncInstanceState(event.sandbox.id) - } - - @OnAsyncEvent({ - event: SandboxEvents.DESTROYED, - }) - @TrackJobExecution() - @WithSpan() - private async handleSandboxDestroyedEvent(event: SandboxDestroyedEvent) { - await this.syncInstanceState(event.sandbox.id) - } - - @OnAsyncEvent({ - event: SandboxEvents.STARTED, - }) - @TrackJobExecution() - @WithSpan() - private async handleSandboxStartedEvent(event: SandboxStartedEvent) { - await this.syncInstanceState(event.sandbox.id) - } - - @OnAsyncEvent({ - event: SandboxEvents.STOPPED, - }) - @TrackJobExecution() - @WithSpan() - private async handleSandboxStoppedEvent(event: SandboxStoppedEvent) { - await this.syncInstanceState(event.sandbox.id, event.force) - } - - @OnAsyncEvent({ - event: SandboxEvents.CREATED, - }) - @TrackJobExecution() - @WithSpan() - private async handleSandboxCreatedEvent(event: SandboxCreatedEvent) { - await this.syncInstanceState(event.sandbox.id) - } -} diff --git a/apps/api/src/sandbox/managers/snapshot.manager.ts b/apps/api/src/sandbox/managers/snapshot.manager.ts deleted file mode 100644 index a9c0b2111..000000000 --- a/apps/api/src/sandbox/managers/snapshot.manager.ts +++ /dev/null @@ -1,1264 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger, NotFoundException, OnApplicationShutdown } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Cron, CronExpression } from '@nestjs/schedule' -import { In, IsNull, Not, Repository } from 'typeorm' -import { DockerRegistryService } from '../../docker-registry/services/docker-registry.service' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { SnapshotRunner } from '../entities/snapshot-runner.entity' -import { Runner } from '../entities/runner.entity' -import { DockerRegistry } from '../../docker-registry/entities/docker-registry.entity' -import { RunnerState } from '../enums/runner-state.enum' -import { SnapshotRunnerState } from '../enums/snapshot-runner-state.enum' -import { v4 as uuidv4 } from 'uuid' -import { RunnerNotReadyError } from '../errors/runner-not-ready.error' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { OrganizationService } from '../../organization/services/organization.service' -import { BuildInfo } from '../entities/build-info.entity' -import { fromAxiosError } from '../../common/utils/from-axios-error' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { RunnerService } from '../services/runner.service' -import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' -import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' -import { setTimeout as sleep } from 'timers/promises' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { RunnerAdapterFactory, RunnerSnapshotInfo, SnapshotDigestResponse } from '../runner-adapter/runnerAdapter' -import { SnapshotStateError } from '../errors/snapshot-state-error' -import { SnapshotEvents } from '../constants/snapshot-events' -import { SnapshotCreatedEvent } from '../events/snapshot-created.event' -import { SnapshotService } from '../services/snapshot.service' -import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' -import { parseDockerImage } from '../../common/utils/docker-image.util' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { BackupState } from '../enums/backup-state.enum' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { SnapshotActivatedEvent } from '../events/snapshot-activated.event' -import { TypedConfigService } from '../../config/typed-config.service' -import { createBoxLiteInternalSnapshotRef } from '../utils/snapshot-ref.util' - -const SYNC_AGAIN = 'sync-again' -const DONT_SYNC_AGAIN = 'dont-sync-again' -const DEFAULT_SNAPSHOT_DEACTIVATION_TIMEOUT_MINUTES = 14 * 24 * 60 // 14 days -type SyncState = typeof SYNC_AGAIN | typeof DONT_SYNC_AGAIN - -@Injectable() -export class SnapshotManager implements TrackableJobExecutions, OnApplicationShutdown { - activeJobs = new Set() - - private readonly logger = new Logger(SnapshotManager.name) - // generate a unique instance id used to ensure only one instance of the worker is handing the - // snapshot activation - private readonly instanceId = uuidv4() - - constructor( - @InjectRedis() private readonly redis: Redis, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(SnapshotRunner) - private readonly snapshotRunnerRepository: Repository, - @InjectRepository(Runner) - private readonly runnerRepository: Repository, - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(BuildInfo) - private readonly buildInfoRepository: Repository, - private readonly runnerService: RunnerService, - private readonly dockerRegistryService: DockerRegistryService, - private readonly runnerAdapterFactory: RunnerAdapterFactory, - private readonly redisLockProvider: RedisLockProvider, - private readonly organizationService: OrganizationService, - private readonly snapshotService: SnapshotService, - private readonly configService: TypedConfigService, - ) {} - - async onApplicationShutdown() { - // wait for all active jobs to finish - while (this.activeJobs.size > 0) { - this.logger.log(`Waiting for ${this.activeJobs.size} active jobs to finish`) - await sleep(1000) - } - } - - @Cron(CronExpression.EVERY_5_SECONDS, { name: 'sync-runner-snapshots', waitForCompletion: true }) - @TrackJobExecution() - @LogExecution('sync-runner-snapshots') - @WithInstrumentation() - async syncRunnerSnapshots() { - const lockKey = 'sync-runner-snapshots-lock' - const lockTtl = 10 * 60 // seconds (10 min) - if (!(await this.redisLockProvider.lock(lockKey, lockTtl))) { - return - } - - const skip = (await this.redis.get('sync-runner-snapshots-skip')) || 0 - - const snapshots = await this.snapshotRepository - .createQueryBuilder('snapshot') - .innerJoin('organization', 'org', 'org.id = snapshot.organizationId') - .where('snapshot.state = :snapshotState', { snapshotState: SnapshotState.ACTIVE }) - .andWhere('org.suspended = false') - .orderBy('snapshot.createdAt', 'ASC') - .take(100) - .skip(Number(skip)) - .getMany() - - if (snapshots.length === 0) { - await this.redisLockProvider.unlock(lockKey) - await this.redis.set('sync-runner-snapshots-skip', 0) - return - } - - await this.redis.set('sync-runner-snapshots-skip', Number(skip) + snapshots.length) - - const results = await Promise.allSettled( - snapshots.map(async (snapshot) => { - const regions = await this.snapshotService.getSnapshotRegions(snapshot.id) - - const sharedRegionIds = regions.filter((r) => r.organizationId === null).map((r) => r.id) - const organizationRegionIds = regions - .filter((r) => r.organizationId === snapshot.organizationId) - .map((r) => r.id) - - return this.propagateSnapshotToRunners(snapshot, sharedRegionIds, organizationRegionIds) - }), - ) - - // Log all promise errors - results.forEach((result) => { - if (result.status === 'rejected') { - this.logger.error(`Error propagating snapshot to runners: ${fromAxiosError(result.reason)}`) - } - }) - - await this.redisLockProvider.unlock(lockKey) - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'sync-runner-snapshot-states', waitForCompletion: true }) - @TrackJobExecution() - @LogExecution('sync-runner-snapshot-states') - @WithInstrumentation() - async syncRunnerSnapshotStates() { - // this approach is not ideal, as if the number of runners is large, this will take a long time - // also, if some snapshots stuck in a "pulling" state, they will infest the queue - // todo: find a better approach - - const lockKey = 'sync-runner-snapshot-states-lock' - if (!(await this.redisLockProvider.lock(lockKey, 30))) { - return - } - - const runnerSnapshots = await this.snapshotRunnerRepository - .createQueryBuilder('snapshotRunner') - .where({ - state: In([ - SnapshotRunnerState.PULLING_SNAPSHOT, - SnapshotRunnerState.BUILDING_SNAPSHOT, - SnapshotRunnerState.REMOVING, - ]), - }) - .orderBy('RANDOM()') - .take(100) - .getMany() - - await Promise.allSettled( - runnerSnapshots.map((snapshotRunner) => { - return this.syncRunnerSnapshotState(snapshotRunner).catch((err) => { - if (err.code !== 'ECONNRESET') { - if (err instanceof RunnerNotReadyError) { - this.logger.debug( - `Runner ${snapshotRunner.runnerId} is not ready while trying to sync snapshot runner ${snapshotRunner.id}: ${err}`, - ) - return - } - this.logger.error(`Error syncing runner snapshot state ${snapshotRunner.id}: ${fromAxiosError(err)}`) - this.snapshotRunnerRepository.update(snapshotRunner.id, { - state: SnapshotRunnerState.ERROR, - errorReason: fromAxiosError(err).message, - }) - } - }) - }), - ) - - await this.redisLockProvider.unlock(lockKey) - } - - async syncRunnerSnapshotState(snapshotRunner: SnapshotRunner): Promise { - const runner = await this.runnerService.findOne(snapshotRunner.runnerId) - if (!runner) { - // cleanup the snapshot runner record if the runner is not found - // this can happen if the runner is deleted from the database without cleaning up the snapshot runners - await this.snapshotRunnerRepository.delete(snapshotRunner.id) - this.logger.warn( - `Runner ${snapshotRunner.runnerId} not found while trying to process snapshot runner ${snapshotRunner.id}. Snapshot runner has been removed.`, - ) - return - } - - if (runner.state !== RunnerState.READY) { - // todo: handle timeout policy - // for now just remove the snapshot runner record if the runner is not ready - await this.snapshotRunnerRepository.delete(snapshotRunner.id) - - throw new RunnerNotReadyError(`Runner ${runner.id} is not ready`) - } - - switch (snapshotRunner.state) { - case SnapshotRunnerState.PULLING_SNAPSHOT: - await this.handleSnapshotRunnerStatePullingSnapshot(snapshotRunner, runner) - break - case SnapshotRunnerState.BUILDING_SNAPSHOT: - await this.handleSnapshotRunnerStateBuildingSnapshot(snapshotRunner, runner) - break - case SnapshotRunnerState.REMOVING: - await this.handleSnapshotRunnerStateRemoving(snapshotRunner, runner) - break - } - } - - async propagateSnapshotToRunners(snapshot: Snapshot, sharedRegionIds: string[], organizationRegionIds: string[]) { - // todo: remove try catch block and implement error handling - try { - // get all runners in the regions to propagate to - const runners = await this.runnerRepository.find({ - where: { - state: RunnerState.READY, - unschedulable: Not(true), - region: In([...sharedRegionIds, ...organizationRegionIds]), - }, - }) - - const sharedRunners = runners.filter((runner) => sharedRegionIds.includes(runner.region)) - const sharedRunnerIds = sharedRunners.map((runner) => runner.id) - - const organizationRunners = runners.filter((runner) => organizationRegionIds.includes(runner.region)) - const organizationRunnerIds = organizationRunners.map((runner) => runner.id) - - // get all runners where the snapshot is already propagated to (or in progress) - const sharedSnapshotRunners = await this.snapshotRunnerRepository.find({ - where: { - snapshotRef: snapshot.ref, - state: In([SnapshotRunnerState.READY, SnapshotRunnerState.PULLING_SNAPSHOT]), - runnerId: In(sharedRunnerIds), - }, - }) - const sharedSnapshotRunnersDistinctRunnersIds = new Set( - sharedSnapshotRunners.map((snapshotRunner) => snapshotRunner.runnerId), - ) - - const organizationSnapshotRunners = await this.snapshotRunnerRepository.find({ - where: { - snapshotRef: snapshot.ref, - state: In([SnapshotRunnerState.READY, SnapshotRunnerState.PULLING_SNAPSHOT]), - runnerId: In(organizationRunnerIds), - }, - }) - const organizationSnapshotRunnersDistinctRunnersIds = new Set( - organizationSnapshotRunners.map((snapshotRunner) => snapshotRunner.runnerId), - ) - - // get all runners where the snapshot is not propagated to - const unallocatedSharedRunners = sharedRunners.filter( - (runner) => !sharedSnapshotRunnersDistinctRunnersIds.has(runner.id), - ) - const unallocatedOrganizationRunners = organizationRunners.filter( - (runner) => !organizationSnapshotRunnersDistinctRunnersIds.has(runner.id), - ) - - const runnersToPropagateTo: Runner[] = [] - - // propagate the snapshot to all organization runners - runnersToPropagateTo.push(...unallocatedOrganizationRunners) - - // respect the propagation limit for shared runners - const sharedRunnersPropagateLimit = Math.max( - 0, - Math.ceil(sharedRunners.length / 3) - sharedSnapshotRunnersDistinctRunnersIds.size, - ) - runnersToPropagateTo.push( - ...unallocatedSharedRunners.sort(() => Math.random() - 0.5).slice(0, sharedRunnersPropagateLimit), - ) - - if (runnersToPropagateTo.length === 0) { - return - } - - // regionId -> registry - const internalRegistriesMap = new Map() - - for (const regionId of [...sharedRegionIds, ...organizationRegionIds]) { - const registry = await this.dockerRegistryService.findInternalRegistryBySnapshotRef(snapshot.ref, regionId) - if (registry) { - internalRegistriesMap.set(regionId, registry) - } - } - - const results = await Promise.allSettled( - runnersToPropagateTo.map(async (runner) => { - const internalRegistry = internalRegistriesMap.get(runner.region) - if (!internalRegistry) { - throw new Error(`No internal registry found for snapshot ${snapshot.ref} in region ${runner.region}`) - } - - const snapshotRunner = await this.runnerService.getSnapshotRunner(runner.id, snapshot.ref) - - try { - if (!snapshotRunner) { - await this.runnerService.createSnapshotRunnerEntry( - runner.id, - snapshot.ref, - SnapshotRunnerState.PULLING_SNAPSHOT, - ) - await this.pullSnapshotRunner(runner, snapshot.ref, internalRegistry) - } else if (snapshotRunner.state === SnapshotRunnerState.PULLING_SNAPSHOT) { - await this.handleSnapshotRunnerStatePullingSnapshot(snapshotRunner, runner) - } - } catch (err) { - this.logger.error(`Error propagating snapshot to runner ${runner.id}: ${fromAxiosError(err)}`) - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = err.message - await this.snapshotRunnerRepository.update(snapshotRunner.id, snapshotRunner) - } - }), - ) - - results.forEach((result) => { - if (result.status === 'rejected') { - this.logger.error(result.reason) - } - }) - } catch (err) { - this.logger.error(err) - } - } - - async pullSnapshotRunner( - runner: Runner, - snapshotRef: string, - registry?: DockerRegistry, - destinationRegistry?: DockerRegistry, - destinationRef?: string, - ) { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - // Runner returns immediately; polling for completion is handled by syncRunnerSnapshotStates cron - await runnerAdapter.pullSnapshot(snapshotRef, registry, destinationRegistry, destinationRef) - } - - async handleSnapshotRunnerStatePullingSnapshot(snapshotRunner: SnapshotRunner, runner: Runner) { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - try { - await runnerAdapter.getSnapshotInfo(snapshotRunner.snapshotRef) - snapshotRunner.state = SnapshotRunnerState.READY - await this.snapshotRunnerRepository.save(snapshotRunner) - return - } catch (err) { - if (err instanceof SnapshotStateError) { - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = err.errorReason - await this.snapshotRunnerRepository.save(snapshotRunner) - return - } - } - - const timeoutMinutes = 60 - const timeoutMs = timeoutMinutes * 60 * 1000 - if (Date.now() - snapshotRunner.updatedAt.getTime() > timeoutMs) { - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = 'Timeout while pulling snapshot to runner' - await this.snapshotRunnerRepository.save(snapshotRunner) - return - } - - const retryTimeoutMinutes = 10 - const retryTimeoutMs = retryTimeoutMinutes * 60 * 1000 - if (Date.now() - snapshotRunner.createdAt.getTime() > retryTimeoutMs) { - const internalRegistry = await this.dockerRegistryService.findInternalRegistryBySnapshotRef( - snapshotRunner.snapshotRef, - runner.region, - ) - if (!internalRegistry) { - throw new Error( - `No internal registry found for snapshot ${snapshotRunner.snapshotRef} in region ${runner.region}`, - ) - } - await this.pullSnapshotRunner(runner, snapshotRunner.snapshotRef, internalRegistry) - return - } - } - - async handleSnapshotRunnerStateBuildingSnapshot(snapshotRunner: SnapshotRunner, runner: Runner) { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - try { - await runnerAdapter.getSnapshotInfo(snapshotRunner.snapshotRef) - snapshotRunner.state = SnapshotRunnerState.READY - await this.snapshotRunnerRepository.save(snapshotRunner) - return - } catch (err) { - if (err instanceof SnapshotStateError) { - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = err.errorReason - await this.snapshotRunnerRepository.save(snapshotRunner) - return - } - } - } - - // Pulls stopped sandboxes' backup snapshots to another runner to prepare for reassignment during draining - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'migrate-draining-runner-snapshots', waitForCompletion: true }) - @TrackJobExecution() - @LogExecution('migrate-draining-runner-snapshots') - @WithInstrumentation() - private async handleMigrateDrainingRunnerSnapshots() { - const lockKey = 'migrate-draining-runner-snapshots' - const hasLock = await this.redisLockProvider.lock(lockKey, 60) - if (!hasLock) { - return - } - - try { - const drainingRunners = await this.runnerRepository.find({ - where: { - draining: true, - state: RunnerState.READY, - }, - }) - - this.logger.debug(`Checking ${drainingRunners.length} draining runners for snapshot migration`) - - await Promise.allSettled( - drainingRunners.map(async (runner) => { - try { - const sandboxes = await this.sandboxRepository.find({ - where: { - runnerId: runner.id, - state: SandboxState.STOPPED, - desiredState: SandboxDesiredState.STOPPED, - backupState: BackupState.COMPLETED, - backupSnapshot: Not(IsNull()), - }, - take: 100, - }) - - this.logger.debug( - `Found ${sandboxes.length} eligible sandboxes on draining runner ${runner.id} for snapshot migration`, - ) - - await Promise.allSettled( - sandboxes.map(async (sandbox) => { - const sandboxLockKey = `draining-runner-snapshot-migration:${sandbox.id}` - const hasSandboxLock = await this.redisLockProvider.lock(sandboxLockKey, 3600) - if (!hasSandboxLock) { - return - } - - try { - // Get an available runner in the same region with the same class - const targetRunner = await this.runnerService.getRandomAvailableRunner({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - excludedRunnerIds: [runner.id], - }) - - // Check if snapshot runner entry already exists - const existingEntry = await this.runnerService.getSnapshotRunner( - targetRunner.id, - sandbox.backupSnapshot, - ) - if (existingEntry) { - if (existingEntry.state === SnapshotRunnerState.ERROR) { - // Clean up the failed entry so we can retry - this.logger.warn( - `Removing ERROR snapshot runner entry ${existingEntry.id} for runner ${targetRunner.id} and snapshot ${sandbox.backupSnapshot} to allow retry`, - ) - await this.snapshotRunnerRepository.delete(existingEntry.id) - } else { - this.logger.debug( - `Snapshot runner entry already exists for runner ${targetRunner.id} and snapshot ${sandbox.backupSnapshot} (state: ${existingEntry.state})`, - ) - // Do not unlock to avoid duplicates - return - } - } - - // Find the backup registry to use as source for the pull - const registry = sandbox.backupRegistryId - ? await this.dockerRegistryService.findOne(sandbox.backupRegistryId) - : await this.dockerRegistryService.findInternalRegistryBySnapshotRef( - sandbox.backupSnapshot, - targetRunner.region, - ) - - if (!registry) { - this.logger.warn( - `No registry found for backup snapshot ${sandbox.backupSnapshot} of sandbox ${sandbox.id}`, - ) - await this.redisLockProvider.unlock(sandboxLockKey) - return - } - - // Create snapshot runner entry on the target runner - await this.runnerService.createSnapshotRunnerEntry( - targetRunner.id, - sandbox.backupSnapshot, - SnapshotRunnerState.PULLING_SNAPSHOT, - ) - await this.pullSnapshotRunner(targetRunner, sandbox.backupSnapshot, registry) - - this.logger.log( - `Created snapshot runner entry for sandbox ${sandbox.id} backup ${sandbox.backupSnapshot} on runner ${targetRunner.id} (migrating from draining runner ${runner.id})`, - ) - await this.redisLockProvider.unlock(sandboxLockKey) - } catch (e) { - if (e instanceof BadRequestError && e.message === 'No available runners') { - this.logger.warn( - `No available runners found in region ${sandbox.region} for sandbox ${sandbox.id} snapshot migration`, - ) - } else { - this.logger.error(`Error migrating snapshot for sandbox ${sandbox.id}`, e) - } - await this.redisLockProvider.unlock(sandboxLockKey) - } - }), - ) - } catch (e) { - this.logger.error(`Error processing draining runner ${runner.id} for snapshot migration`, e) - } - }), - ) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'check-snapshot-cleanup' }) - @TrackJobExecution() - @LogExecution('check-snapshot-cleanup') - @WithInstrumentation() - async checkSnapshotCleanup() { - const lockKey = 'check-snapshot-cleanup-lock' - if (!(await this.redisLockProvider.lock(lockKey, 30))) { - return - } - - const snapshots = await this.snapshotRepository.find({ - where: { - state: SnapshotState.REMOVING, - }, - }) - - await Promise.all( - snapshots.map(async (snapshot) => { - const countActiveSnapshots = await this.snapshotRepository.count({ - where: { - state: SnapshotState.ACTIVE, - ref: snapshot.ref, - }, - }) - - // Only remove snapshot runners if no other snapshots depend on them - if (countActiveSnapshots === 0) { - await this.snapshotRunnerRepository.update( - { - snapshotRef: snapshot.ref, - }, - { - state: SnapshotRunnerState.REMOVING, - }, - ) - } - - await this.snapshotRepository.remove(snapshot) - }), - ) - - await this.redisLockProvider.unlock(lockKey) - } - - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'check-snapshot-state' }) - @TrackJobExecution() - @LogExecution('check-snapshot-state') - @WithInstrumentation() - async checkSnapshotState() { - // the first time the snapshot is created it needs to be pushed to the internal registry - // before propagating to the runners - // this cron job will process the snapshot states until the snapshot is active (or error) - - // get all snapshots - const snapshots = await this.snapshotRepository.find({ - where: { - state: Not(In([SnapshotState.ACTIVE, SnapshotState.ERROR, SnapshotState.BUILD_FAILED, SnapshotState.INACTIVE])), - }, - }) - - await Promise.all( - snapshots.map(async (snapshot) => { - this.syncSnapshotState(snapshot.id) - }), - ) - } - - async syncSnapshotState(snapshotId: string): Promise { - const lockKey = `sync-snapshot-state-${snapshotId}` - if (!(await this.redisLockProvider.lock(lockKey, 720))) { - return - } - - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if ( - !snapshot || - [SnapshotState.ACTIVE, SnapshotState.ERROR, SnapshotState.BUILD_FAILED, SnapshotState.INACTIVE].includes( - snapshot.state, - ) - ) { - await this.redisLockProvider.unlock(lockKey) - return - } - - let syncState = DONT_SYNC_AGAIN - - try { - switch (snapshot.state) { - case SnapshotState.PENDING: - syncState = await this.handleSnapshotStatePending(snapshot) - break - case SnapshotState.PULLING: - case SnapshotState.BUILDING: - syncState = await this.handleCheckInitialRunnerSnapshot(snapshot) - break - case SnapshotState.REMOVING: - syncState = await this.handleSnapshotStateRemoving(snapshot) - break - } - } catch (error) { - if (error.code === 'ECONNRESET') { - syncState = SYNC_AGAIN - } else { - const message = error.message || String(error) - await this.updateSnapshotState(snapshot.id, SnapshotState.ERROR, message) - } - } - - await this.redisLockProvider.unlock(lockKey) - if (syncState === SYNC_AGAIN) { - this.syncSnapshotState(snapshotId) - } - } - - async handleSnapshotRunnerStateRemoving(snapshotRunner: SnapshotRunner, runner: Runner) { - if (!runner) { - // generally this should not happen - // in case the runner has been deleted from the database, delete the snapshot runner record - const errorMessage = `Runner not found while trying to remove snapshot ${snapshotRunner.snapshotRef} from runner ${snapshotRunner.runnerId}` - this.logger.warn(errorMessage) - - this.snapshotRunnerRepository.delete(snapshotRunner.id).catch((err) => { - this.logger.error(fromAxiosError(err)) - }) - return - } - if (!snapshotRunner.snapshotRef) { - // this should never happen - // remove the snapshot runner record (it will be recreated again by the snapshot propagation job) - this.logger.warn(`Internal snapshot name not found for snapshot runner ${snapshotRunner.id}`) - this.snapshotRunnerRepository.delete(snapshotRunner.id).catch((err) => { - this.logger.error(fromAxiosError(err)) - }) - return - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - const exists = await runnerAdapter.snapshotExists(snapshotRunner.snapshotRef) - if (!exists) { - await this.snapshotRunnerRepository.delete(snapshotRunner.id) - } else { - // just in case the snapshot is still there - runnerAdapter.removeSnapshot(snapshotRunner.snapshotRef).catch((err) => { - // this should not happen, and is not critical - // if the runner can not remove the snapshot, just delete the snapshot runner record - this.snapshotRunnerRepository.delete(snapshotRunner.id).catch((err) => { - this.logger.error(fromAxiosError(err)) - }) - // and log the error for tracking - const errorMessage = `Failed to do just in case remove snapshot ${snapshotRunner.snapshotRef} from runner ${runner.id}: ${fromAxiosError(err)}` - this.logger.warn(errorMessage) - }) - } - } - - async handleSnapshotStateRemoving(snapshot: Snapshot): Promise { - const snapshotRunnerItems = await this.snapshotRunnerRepository.find({ - where: { - snapshotRef: snapshot.ref, - }, - }) - - if (snapshotRunnerItems.length === 0) { - await this.snapshotRepository.remove(snapshot) - } - - return DONT_SYNC_AGAIN - } - - async handleCheckInitialRunnerSnapshot(snapshot: Snapshot): Promise { - // Check for timeout - allow up to 30 minutes - const timeoutMinutes = 30 - const timeoutMs = timeoutMinutes * 60 * 1000 - if (Date.now() - snapshot.updatedAt.getTime() > timeoutMs) { - await this.updateSnapshotState(snapshot.id, SnapshotState.ERROR, 'Timeout processing snapshot on initial runner') - return DONT_SYNC_AGAIN - } - - // Check if the snapshot ref is already set and it is already on the runner - const snapshotRunner = await this.snapshotRunnerRepository.findOne({ - where: { - snapshotRef: snapshot.ref, - runnerId: snapshot.initialRunnerId, - }, - }) - - if (snapshot.ref && snapshotRunner) { - if (snapshotRunner.state === SnapshotRunnerState.READY && snapshot.size != null) { - await this.updateSnapshotState(snapshot.id, SnapshotState.ACTIVE) - return DONT_SYNC_AGAIN - } else if (snapshotRunner.state === SnapshotRunnerState.ERROR) { - await this.snapshotRunnerRepository.delete(snapshotRunner.id) - } - } - - const runner = await this.runnerService.findOneOrFail(snapshot.initialRunnerId) - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - const initialImageRefOnRunner = snapshot.buildInfo ? snapshot.buildInfo.snapshotRef : snapshot.ref - - let snapshotInfoResponse: RunnerSnapshotInfo - try { - snapshotInfoResponse = await runnerAdapter.getSnapshotInfo(initialImageRefOnRunner) - } catch (error) { - if (error instanceof SnapshotStateError) { - throw error - } else { - return DONT_SYNC_AGAIN - } - } - - const internalRegistry = await this.dockerRegistryService.getAvailableInternalRegistry(runner.region) - if (!internalRegistry) { - throw new Error('No internal registry found for snapshot') - } - - const digestSyncState = await this.processSnapshotDigest( - snapshot, - internalRegistry, - snapshotInfoResponse.hash, - snapshotInfoResponse.sizeGB, - snapshotInfoResponse.entrypoint, - ) - - if (digestSyncState === DONT_SYNC_AGAIN) { - return DONT_SYNC_AGAIN - } - - let inspectedSnapshotDigest: SnapshotDigestResponse | undefined - try { - inspectedSnapshotDigest = await runnerAdapter.inspectSnapshotInRegistry(snapshot.ref, internalRegistry) - } catch (error) { - this.logger.error(`Failed to inspect snapshot ${snapshot.ref} in registry: ${error}`) - return DONT_SYNC_AGAIN - } - - if (snapshot.size == null && typeof inspectedSnapshotDigest?.sizeGB === 'number') { - await this.processSnapshotDigest( - snapshot, - internalRegistry, - snapshotInfoResponse.hash, - inspectedSnapshotDigest.sizeGB, - snapshotInfoResponse.entrypoint, - ) - } - - try { - await runnerAdapter.removeSnapshot(initialImageRefOnRunner) - } catch (error) { - this.logger.error(`Failed to remove snapshot ${snapshot.imageName}: ${fromAxiosError(error)}`) - } - - // For pull snapshots, best effort cleanup the original image now that we've computed the ref from it - // Only cleanup if there's no other snapshot in processing state using the same image - if (!snapshot.buildInfo) { - try { - const anotherSnapshot = await this.snapshotRepository.findOne({ - where: { - imageName: snapshot.imageName, - id: Not(snapshot.id), - state: Not(In([SnapshotState.ACTIVE, SnapshotState.INACTIVE])), - }, - }) - if (!anotherSnapshot) { - await runnerAdapter.removeSnapshot(snapshot.imageName) - } - } catch (err) { - this.logger.error(`Failed to cleanup original image ${snapshot.imageName}: ${fromAxiosError(err)}`) - } - } - - if (snapshotRunner) { - snapshotRunner.state = SnapshotRunnerState.READY - await this.snapshotRunnerRepository.save(snapshotRunner) - } else { - await this.runnerService.createSnapshotRunnerEntry(runner.id, snapshot.ref, SnapshotRunnerState.READY) - } - await this.updateSnapshotState(snapshot.id, SnapshotState.ACTIVE) - - // Best effort removal of old snapshot from transient registry - const transientRegistry = await this.dockerRegistryService.findTransientRegistryBySnapshotImageName( - snapshot.imageName, - runner.region, - ) - if (transientRegistry) { - try { - await this.dockerRegistryService.removeImage(snapshot.imageName, transientRegistry.id) - } catch (error) { - if (error.statusCode === 404) { - // image not found, just return - return DONT_SYNC_AGAIN - } - this.logger.error('Failed to remove transient image:', fromAxiosError(error)) - } - } - - return DONT_SYNC_AGAIN - } - - async processPullOnInitialRunner(snapshot: Snapshot, runner: Runner) { - // Check for timeout - allow up to 30 minutes - const timeoutMinutes = 30 - const timeoutMs = timeoutMinutes * 60 * 1000 - if (Date.now() - snapshot.updatedAt.getTime() > timeoutMs) { - await this.updateSnapshotState( - snapshot.id, - SnapshotState.ERROR, - 'Timeout processing snapshot pull on initial runner', - ) - return DONT_SYNC_AGAIN - } - - let sourceRegistry = await this.dockerRegistryService.findSourceRegistryBySnapshotImageName( - snapshot.imageName, - runner.region, - snapshot.organizationId, - ) - if (!sourceRegistry) { - sourceRegistry = await this.dockerRegistryService.getDefaultDockerHubRegistry() - } - const destinationRegistry = await this.dockerRegistryService.getAvailableInternalRegistry(runner.region) - - // Fire pull request (runner returns 202 immediately) - // Post-processing (digest, cleanup) is handled by handleCheckInitialRunnerSnapshot on the next poll cycle - try { - await this.pullSnapshotRunner( - runner, - snapshot.imageName, - sourceRegistry, - destinationRegistry ?? undefined, - snapshot.ref ? snapshot.ref : undefined, - ) - } catch (err) { - // Validation errors are still returned synchronously - await this.updateSnapshotState(snapshot.id, SnapshotState.ERROR, err.message) - throw err - } - } - - async processBuildOnRunner(snapshot: Snapshot, runner: Runner) { - try { - const registry = await this.dockerRegistryService.getAvailableInternalRegistry(runner.region) - - const sourceRegistries = await this.dockerRegistryService.getSourceRegistriesForDockerfile( - snapshot.buildInfo.dockerfileContent, - snapshot.organizationId, - ) - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - registry.url = registry.url.replace(/^(https?:\/\/)/, '') - // Runner returns immediately; polling for completion is handled by handleCheckInitialRunnerSnapshot - await runnerAdapter.buildSnapshot( - snapshot.buildInfo, - snapshot.organizationId, - sourceRegistries.length > 0 ? sourceRegistries : undefined, - registry ?? undefined, - true, - ) - } catch (err) { - this.logger.error(`Error building snapshot ${snapshot.name}: ${fromAxiosError(err)}`) - await this.updateSnapshotState(snapshot.id, SnapshotState.BUILD_FAILED, fromAxiosError(err).message) - } - } - - async handleSnapshotStatePending(snapshot: Snapshot): Promise { - let initialRunner: Runner | undefined = undefined - - if (!snapshot.initialRunnerId) { - // TODO: get only runners where the base snapshot is available (extract from buildInfo) - const excludedRunnerIds = snapshot.buildInfo - ? await this.runnerService.getRunnersWithMultipleSnapshotsBuilding() - : await this.runnerService.getRunnersWithMultipleSnapshotsPulling() - - try { - const regions = await this.snapshotService.getSnapshotRegions(snapshot.id) - if (!regions.length) { - throw new Error('No regions found for snapshot') - } - - initialRunner = await this.runnerService.getRandomAvailableRunner({ - regions: regions.map((region) => region.id), - excludedRunnerIds: excludedRunnerIds, - }) - } catch (error) { - this.logger.warn(`Failed to get initial runner: ${fromAxiosError(error)}`) - } - - if (!initialRunner) { - // No runners available, retry later - return DONT_SYNC_AGAIN - } - - snapshot.initialRunnerId = initialRunner.id - await this.snapshotRepository.save(snapshot) - } else { - initialRunner = await this.runnerService.findOneOrFail(snapshot.initialRunnerId) - } - - if (snapshot.buildInfo) { - await this.updateSnapshotState(snapshot.id, SnapshotState.BUILDING) - await this.runnerService.createSnapshotRunnerEntry( - initialRunner.id, - snapshot.buildInfo.snapshotRef, - SnapshotRunnerState.BUILDING_SNAPSHOT, - ) - await this.processBuildOnRunner(snapshot, initialRunner) - } else { - if (!snapshot.ref) { - const runnerAdapter = await this.runnerAdapterFactory.create(initialRunner) - const registry = await this.dockerRegistryService.findRegistryByImageName( - snapshot.imageName, - initialRunner.region, - snapshot.organizationId, - ) - - const image = parseDockerImage(snapshot.imageName) - if (registry && !image.registry) { - image.registry = registry.url.replace(/^(https?:\/\/)/, '') - } - const imageName = image.getFullName() - - const internalRegistry = await this.dockerRegistryService.getAvailableInternalRegistry(initialRunner.region) - if (!internalRegistry) { - throw new Error('No internal registry found for snapshot') - } - - const snapshotDigestResponse = await runnerAdapter.inspectSnapshotInRegistry(imageName, registry) - const digestSyncState = await this.processSnapshotDigest( - snapshot, - internalRegistry, - snapshotDigestResponse.hash, - snapshotDigestResponse.sizeGB, - ) - - if (digestSyncState === DONT_SYNC_AGAIN) { - return DONT_SYNC_AGAIN - } - - await this.snapshotRepository.save(snapshot) - } - - await this.updateSnapshotState(snapshot.id, SnapshotState.PULLING) - await this.runnerService.createSnapshotRunnerEntry( - initialRunner.id, - snapshot.ref, - SnapshotRunnerState.PULLING_SNAPSHOT, - ) - await this.processPullOnInitialRunner(snapshot, initialRunner) - } - - return SYNC_AGAIN - } - - private async updateSnapshotState(snapshotId: string, state: SnapshotState, errorReason?: string, size?: number) { - const partialUpdate: Partial = { - state, - } - - if (state === SnapshotState.ACTIVE) { - partialUpdate.lastUsedAt = new Date() - } - - if (errorReason !== undefined) { - partialUpdate.errorReason = errorReason - } - - if (size !== undefined) { - partialUpdate.size = size - } - - const result = await this.snapshotRepository.update( - { - id: snapshotId, - }, - partialUpdate, - ) - - if (!result.affected) { - throw new NotFoundException(`Snapshot with ID ${snapshotId} not found`) - } - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'cleanup-old-buildinfo-snapshot-runners' }) - @TrackJobExecution() - @LogExecution('cleanup-old-buildinfo-snapshot-runners') - @WithInstrumentation() - async cleanupOldBuildInfoSnapshotRunners() { - const lockKey = 'cleanup-old-buildinfo-snapshots-lock' - if (!(await this.redisLockProvider.lock(lockKey, 300))) { - return - } - - try { - const staleEntries = await this.snapshotRunnerRepository - .createQueryBuilder('sr') - .select('sr.id') - .innerJoin(BuildInfo, 'bi', 'sr."snapshotRef" = bi."snapshotRef"') - .where('sr.state = :readyState', { readyState: SnapshotRunnerState.READY }) - .andWhere( - `bi.lastUsedAt < now() - interval '${this.configService.getOrThrow('buildInfoSnapshotRunnerStalenessDays')} days'`, - ) - .andWhere( - `sr.updatedAt < now() - interval '${this.configService.getOrThrow('buildInfoSnapshotRunnerStalenessDays')} days'`, - ) - .andWhere("sr.snapshotRef LIKE 'boxlite-%'") - .limit(500) - .getMany() - - if (staleEntries.length === 0) { - return - } - - const ids = staleEntries.map((sr) => sr.id) - const result = await this.snapshotRunnerRepository.update( - { id: In(ids) }, - { state: SnapshotRunnerState.REMOVING }, - ) - - if (result.affected > 0) { - this.logger.debug(`Marked ${result.affected} SnapshotRunners for removal due to unused BuildInfo`) - } - } catch (error) { - this.logger.error(`Failed to mark old BuildInfo SnapshotRunners for removal: ${fromAxiosError(error)}`) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_MINUTES, { name: 'deactivate-old-snapshots' }) - @TrackJobExecution() - @LogExecution('deactivate-old-snapshots') - @WithInstrumentation() - async deactivateOldSnapshots() { - const lockKey = 'deactivate-old-snapshots-lock' - if (!(await this.redisLockProvider.lock(lockKey, 300))) { - return - } - - try { - const cutoff = `NOW() - INTERVAL '1 minute' * COALESCE(org."snapshot_deactivation_timeout_minutes", ${DEFAULT_SNAPSHOT_DEACTIVATION_TIMEOUT_MINUTES})` - - const oldSnapshots = await this.snapshotRepository - .createQueryBuilder('snapshot') - .leftJoin('organization', 'org', `org."id" = snapshot."organizationId"`) - .where('snapshot.general = false') - .andWhere('snapshot.state = :snapshotState', { snapshotState: SnapshotState.ACTIVE }) - .andWhere(`(snapshot."lastUsedAt" IS NULL OR snapshot."lastUsedAt" < ${cutoff})`) - .andWhere(`snapshot."createdAt" < ${cutoff}`) - .andWhere( - `NOT EXISTS ( - SELECT 1 FROM snapshot s - WHERE s."ref" = snapshot."ref" - AND s.state = :activeState - AND (s."lastUsedAt" >= ${cutoff} OR s."createdAt" >= ${cutoff}) - )`, - { - activeState: SnapshotState.ACTIVE, - }, - ) - .take(100) - .getMany() - - if (oldSnapshots.length === 0) { - return - } - - // Deactivate the snapshots - const snapshotIds = oldSnapshots.map((snapshot) => snapshot.id) - await this.snapshotRepository.update({ id: In(snapshotIds) }, { state: SnapshotState.INACTIVE }) - - // Get internal names of deactivated snapshots - const refs = oldSnapshots.map((snapshot) => snapshot.ref).filter((name) => name) // Filter out null/undefined values - - if (refs.length > 0) { - // Set associated SnapshotRunner records to REMOVING state - const result = await this.snapshotRunnerRepository.update( - { snapshotRef: In(refs) }, - { state: SnapshotRunnerState.REMOVING }, - ) - - this.logger.debug( - `Deactivated ${oldSnapshots.length} snapshots and marked ${result.affected} SnapshotRunners for removal`, - ) - } - } catch (error) { - this.logger.error(`Failed to deactivate old snapshots: ${fromAxiosError(error)}`) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - @Cron(CronExpression.EVERY_10_MINUTES, { name: 'cleanup-inactive-snapshots-from-runners' }) - @TrackJobExecution() - @LogExecution('cleanup-inactive-snapshots-from-runners') - @WithInstrumentation() - async cleanupInactiveSnapshotsFromRunners() { - const lockKey = 'cleanup-inactive-snapshots-from-runners-lock' - if (!(await this.redisLockProvider.lock(lockKey, 300))) { - return - } - - try { - // Only fetch inactive snapshots that have associated snapshot runner entries - const queryResult = await this.snapshotRepository - .createQueryBuilder('snapshot') - .select('snapshot."ref"') - .where('snapshot.state = :snapshotState', { snapshotState: SnapshotState.INACTIVE }) - .andWhere('snapshot."ref" IS NOT NULL') - .andWhereExists( - this.snapshotRunnerRepository - .createQueryBuilder('snapshot_runner') - .select('1') - .where('snapshot_runner."snapshotRef" = snapshot."ref"') - .andWhere('snapshot_runner.state != :snapshotRunnerState', { - snapshotRunnerState: SnapshotRunnerState.REMOVING, - }), - ) - .andWhere( - () => { - const query = this.snapshotRepository - .createQueryBuilder('s') - .select('1') - .where('s."ref" = snapshot."ref"') - .andWhere('s.state = :snapshotState') - return `NOT EXISTS (${query.getQuery()})` - }, - { - snapshotState: SnapshotState.ACTIVE, - }, - ) - .take(100) - .getRawMany() - - const inactiveSnapshotRefs = queryResult.map((result) => result.ref) - - if (inactiveSnapshotRefs.length > 0) { - // Set associated SnapshotRunner records to REMOVING state - const result = await this.snapshotRunnerRepository.update( - { snapshotRef: In(inactiveSnapshotRefs) }, - { state: SnapshotRunnerState.REMOVING }, - ) - - this.logger.debug(`Marked ${result.affected} SnapshotRunners for removal`) - } - } catch (error) { - this.logger.error(`Failed to cleanup inactive snapshots from runners: ${fromAxiosError(error)}`) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - private async processSnapshotDigest( - snapshot: Snapshot, - internalRegistry: DockerRegistry, - hash: string, - sizeGB?: number, - entrypoint?: string[] | string, - ): Promise { - let shouldSave = false - - if (!snapshot.ref) { - shouldSave = true - snapshot.ref = createBoxLiteInternalSnapshotRef(internalRegistry.url, internalRegistry.project, hash) - } - - if (snapshot.size == null && typeof sizeGB === 'number') { - shouldSave = true - - const organization = await this.organizationService.findOne(snapshot.organizationId) - if (!organization) { - throw new NotFoundException(`Organization with ID ${snapshot.organizationId} not found`) - } - - const MAX_SIZE_GB = organization.maxSnapshotSize - - snapshot.size = sizeGB - - if (sizeGB > MAX_SIZE_GB) { - await this.updateSnapshotState( - snapshot.id, - SnapshotState.ERROR, - `Snapshot size (${sizeGB.toFixed(2)}GB) exceeds maximum allowed size of ${MAX_SIZE_GB}GB`, - sizeGB, - ) - return DONT_SYNC_AGAIN - } - } - - // If entrypoint is not explicitly set, set it from snapshotInfoResponse - if (!snapshot.entrypoint) { - if (entrypoint && entrypoint.length > 0) { - shouldSave = true - if (Array.isArray(entrypoint)) { - snapshot.entrypoint = entrypoint - } else { - snapshot.entrypoint = [entrypoint] - } - } - } - - if (shouldSave) { - await this.snapshotRepository.save(snapshot) - } - } - - @OnAsyncEvent({ - event: SnapshotEvents.CREATED, - }) - private async handleSnapshotCreatedEvent(event: SnapshotCreatedEvent) { - await this.syncSnapshotState(event.snapshot.id) - } - - @OnAsyncEvent({ - event: SnapshotEvents.ACTIVATED, - }) - private async handleSnapshotActivatedEvent(event: SnapshotActivatedEvent) { - await this.syncSnapshotState(event.snapshot.id) - } -} diff --git a/apps/api/src/sandbox/proxy/log-proxy.ts b/apps/api/src/sandbox/proxy/log-proxy.ts deleted file mode 100644 index e456b98b5..000000000 --- a/apps/api/src/sandbox/proxy/log-proxy.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Logger } from '@nestjs/common' -import { createProxyMiddleware, fixRequestBody, Options } from 'http-proxy-middleware' -import { IncomingMessage, ServerResponse } from 'http' -import { NextFunction } from 'express' - -export class LogProxy { - private readonly logger = new Logger(LogProxy.name) - - constructor( - private readonly targetUrl: string, - private readonly snapshotRef: string, - private readonly authToken: string, - private readonly follow: boolean, - private readonly req: IncomingMessage, - private readonly res: ServerResponse, - private readonly next: NextFunction, - ) {} - - create() { - const proxyOptions: Options = { - target: this.targetUrl, - secure: false, - changeOrigin: true, - autoRewrite: true, - pathRewrite: () => `/snapshots/logs?snapshotRef=${this.snapshotRef}&follow=${this.follow}`, - on: { - proxyReq: (proxyReq: any, req: any) => { - proxyReq.setHeader('Authorization', `Bearer ${this.authToken}`) - proxyReq.setHeader('Accept', 'application/octet-stream') - fixRequestBody(proxyReq, req) - }, - }, - proxyTimeout: 5 * 60 * 1000, - } - - return createProxyMiddleware(proxyOptions)(this.req, this.res, this.next) - } -} diff --git a/apps/api/src/sandbox/repositories/sandbox.repository.ts b/apps/api/src/sandbox/repositories/sandbox.repository.ts deleted file mode 100644 index 1c43d4cd9..000000000 --- a/apps/api/src/sandbox/repositories/sandbox.repository.ts +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { DataSource, EntityManager, FindOptionsWhere } from 'typeorm' -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxLastActivity } from '../entities/sandbox-last-activity.entity' -import { Injectable, Logger, NotFoundException } from '@nestjs/common' -import { SandboxConflictError } from '../errors/sandbox-conflict.error' -import { InjectDataSource } from '@nestjs/typeorm' -import { EventEmitter2 } from '@nestjs/event-emitter' -import { BaseRepository } from '../../common/repositories/base.repository' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxStateUpdatedEvent } from '../events/sandbox-state-updated.event' -import { SandboxDesiredStateUpdatedEvent } from '../events/sandbox-desired-state-updated.event' -import { SandboxPublicStatusUpdatedEvent } from '../events/sandbox-public-status-updated.event' -import { SandboxOrganizationUpdatedEvent } from '../events/sandbox-organization-updated.event' -import { SandboxLookupCacheInvalidationService } from '../services/sandbox-lookup-cache-invalidation.service' - -@Injectable() -export class SandboxRepository extends BaseRepository { - private readonly logger = new Logger(SandboxRepository.name) - - constructor( - @InjectDataSource() dataSource: DataSource, - eventEmitter: EventEmitter2, - private readonly sandboxLookupCacheInvalidationService: SandboxLookupCacheInvalidationService, - ) { - super(dataSource, eventEmitter, Sandbox) - } - - async insert(sandbox: Sandbox): Promise { - const now = new Date() - if (!sandbox.createdAt) { - sandbox.createdAt = now - } - if (!sandbox.updatedAt) { - sandbox.updatedAt = now - } - - sandbox.assertValid() - sandbox.enforceInvariants() - - await this.dataSource.transaction(async (entityManager) => { - await entityManager.insert(Sandbox, sandbox) - await this.upsertLastActivity(entityManager, sandbox.id, sandbox.createdAt) - }) - - this.invalidateLookupCacheOnInsert(sandbox) - - return sandbox - } - - /** - * @param id - The ID of the sandbox to update. - * @param params.updateData - The partial data to update. - * - * @returns `void` because a raw update is performed. - */ - async update(id: string, params: { updateData: Partial }, raw: true): Promise - /** - * @param id - The ID of the sandbox to update. - * @param params.updateData - The partial data to update. - * @param params.entity - Optional pre-fetched sandbox to use instead of fetching from the database. - * - * @returns The updated sandbox. - */ - async update(id: string, params: { updateData: Partial; entity?: Sandbox }, raw?: false): Promise - async update( - id: string, - params: { updateData: Partial; entity?: Sandbox }, - raw = false, - ): Promise { - const { updateData, entity } = params - - if (raw) { - await this.repository.update(id, updateData) - return - } - - const sandbox = entity ?? (await this.findOneBy({ id })) - if (!sandbox) { - throw new NotFoundException('Sandbox not found') - } - - const previousSandbox = { ...sandbox } - - Object.assign(sandbox, updateData) - sandbox.assertValid() - const invariantChanges = sandbox.enforceInvariants() - - await this.dataSource.transaction(async (entityManager) => { - const result = await entityManager.update( - Sandbox, - { - id: previousSandbox.id, - state: previousSandbox.state, - desiredState: previousSandbox.desiredState, - pending: previousSandbox.pending, - organizationId: previousSandbox.organizationId, - }, - { ...updateData, ...invariantChanges }, - ) - if (!result.affected) { - throw new SandboxConflictError() - } - sandbox.updatedAt = new Date() - - if (previousSandbox.state !== sandbox.state || previousSandbox.organizationId !== sandbox.organizationId) { - await this.upsertLastActivity(entityManager, id, sandbox.updatedAt) - } - }) - - this.emitUpdateEvents(sandbox, previousSandbox) - this.invalidateLookupCacheOnUpdate(sandbox, previousSandbox) - - return sandbox - } - - /** - * Partially updates a sandbox in the database and optionally emits a corresponding event based on the changes. - * - * Performs the update in a transaction with a pessimistic write lock to ensure consistency. - * - * @param id - The ID of the sandbox to update. - * @param params.updateData - The partial data to update. - * @param params.whereCondition - The where condition to use for the update. - * - * @throws {SandboxConflictError} if the sandbox was modified by another operation - */ - async updateWhere( - id: string, - params: { - updateData: Partial - whereCondition: FindOptionsWhere - }, - ): Promise { - const { updateData, whereCondition } = params - - return this.manager.transaction(async (entityManager) => { - const whereClause = { - ...whereCondition, - id, - } - - const sandbox = await entityManager.findOne(Sandbox, { - where: whereClause, - lock: { mode: 'pessimistic_write' }, - relations: [], - loadEagerRelations: false, - }) - - if (!sandbox) { - throw new SandboxConflictError() - } - - const previousSandbox = { ...sandbox } - - Object.assign(sandbox, updateData) - sandbox.assertValid() - const invariantChanges = sandbox.enforceInvariants() - - await entityManager.update(Sandbox, id, { ...updateData, ...invariantChanges }) - sandbox.updatedAt = new Date() - - if (previousSandbox.state !== sandbox.state || previousSandbox.organizationId !== sandbox.organizationId) { - await this.upsertLastActivity(entityManager, id, sandbox.updatedAt) - } - - this.emitUpdateEvents(sandbox, previousSandbox) - this.invalidateLookupCacheOnUpdate(sandbox, previousSandbox) - - return sandbox - }) - } - - /** - * Upserts the last activity for a sandbox. - */ - private async upsertLastActivity( - entityManager: EntityManager, - sandboxId: string, - lastActivityAt: Date, - ): Promise { - await entityManager.upsert(SandboxLastActivity, { sandboxId, lastActivityAt }, ['sandboxId']) - } - - /** - * Invalidates the sandbox lookup cache for the inserted sandbox. - */ - private invalidateLookupCacheOnInsert(sandbox: Sandbox): void { - try { - this.sandboxLookupCacheInvalidationService.invalidateOrgId({ - sandboxId: sandbox.id, - organizationId: sandbox.organizationId, - name: sandbox.name, - }) - } catch (error) { - this.logger.warn( - `Failed to enqueue sandbox lookup cache invalidation on insert (id, organizationId, name) for ${sandbox.id}: ${error instanceof Error ? error.message : String(error)}`, - ) - } - } - - /** - * Invalidates the sandbox lookup cache for the updated sandbox. - */ - private invalidateLookupCacheOnUpdate( - updatedSandbox: Sandbox, - previousSandbox: Pick, - ): void { - try { - this.sandboxLookupCacheInvalidationService.invalidate({ - sandboxId: updatedSandbox.id, - organizationId: updatedSandbox.organizationId, - previousOrganizationId: previousSandbox.organizationId, - name: updatedSandbox.name, - previousName: previousSandbox.name, - }) - } catch (error) { - this.logger.warn( - `Failed to enqueue sandbox lookup cache invalidation on update (id, organizationId, name) for ${updatedSandbox.id}: ${error instanceof Error ? error.message : String(error)}`, - ) - } - - try { - if (updatedSandbox.authToken !== previousSandbox.authToken) { - this.sandboxLookupCacheInvalidationService.invalidate({ - authToken: updatedSandbox.authToken, - }) - } - } catch (error) { - this.logger.warn( - `Failed to enqueue sandbox lookup cache invalidation on update (authToken) for ${updatedSandbox.id}: ${error instanceof Error ? error.message : String(error)}`, - ) - } - } - - /** - * Emits events based on the changes made to a sandbox. - */ - private emitUpdateEvents( - updatedSandbox: Sandbox, - previousSandbox: Pick, - ): void { - if (previousSandbox.state !== updatedSandbox.state) { - this.eventEmitter.emit( - SandboxEvents.STATE_UPDATED, - new SandboxStateUpdatedEvent(updatedSandbox, previousSandbox.state, updatedSandbox.state), - ) - } - - if (previousSandbox.desiredState !== updatedSandbox.desiredState) { - this.eventEmitter.emit( - SandboxEvents.DESIRED_STATE_UPDATED, - new SandboxDesiredStateUpdatedEvent(updatedSandbox, previousSandbox.desiredState, updatedSandbox.desiredState), - ) - } - - if (previousSandbox.public !== updatedSandbox.public) { - this.eventEmitter.emit( - SandboxEvents.PUBLIC_STATUS_UPDATED, - new SandboxPublicStatusUpdatedEvent(updatedSandbox, previousSandbox.public, updatedSandbox.public), - ) - } - - if (previousSandbox.organizationId !== updatedSandbox.organizationId) { - this.eventEmitter.emit( - SandboxEvents.ORGANIZATION_UPDATED, - new SandboxOrganizationUpdatedEvent( - updatedSandbox, - previousSandbox.organizationId, - updatedSandbox.organizationId, - ), - ) - } - } -} diff --git a/apps/api/src/sandbox/runner-adapter/runnerAdapter.ts b/apps/api/src/sandbox/runner-adapter/runnerAdapter.ts deleted file mode 100644 index 689cf1238..000000000 --- a/apps/api/src/sandbox/runner-adapter/runnerAdapter.ts +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { Runner } from '../entities/runner.entity' -import { ModuleRef } from '@nestjs/core' -import { RunnerAdapterV0 } from './runnerAdapter.v0' -import { RunnerAdapterV2 } from './runnerAdapter.v2' -import { BuildInfo } from '../entities/build-info.entity' -import { DockerRegistry } from '../../docker-registry/entities/docker-registry.entity' -import { Sandbox } from '../entities/sandbox.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { BackupState } from '../enums/backup-state.enum' -import { RunnerServiceInfo } from '../common/runner-service-info' - -export interface RunnerSandboxInfo { - state: SandboxState - daemonVersion?: string - backupState?: BackupState - backupSnapshot?: string - backupErrorReason?: string -} - -export interface RunnerSnapshotInfo { - name: string - sizeGB: number - entrypoint: string[] - cmd: string[] - hash: string -} - -export interface SnapshotDigestResponse { - hash: string - sizeGB: number -} - -export interface RunnerMetrics { - currentAllocatedCpu?: number - currentAllocatedDiskGiB?: number - currentAllocatedMemoryGiB?: number - currentCpuUsagePercentage?: number - currentDiskUsagePercentage?: number - currentMemoryUsagePercentage?: number - currentSnapshotCount?: number - currentStartedSandboxes?: number -} - -export interface RunnerInfo { - serviceHealth?: RunnerServiceInfo[] - metrics?: RunnerMetrics - appVersion?: string -} - -export interface StartSandboxResponse { - daemonVersion: string -} - -export interface RunnerAdapter { - init(runner: Runner): Promise - - healthCheck(signal?: AbortSignal): Promise - - runnerInfo(signal?: AbortSignal): Promise - - sandboxInfo(sandboxId: string): Promise - createSandbox( - sandbox: Sandbox, - snapshotRef: string, - registry?: DockerRegistry, - entrypoint?: string[], - metadata?: { [key: string]: string }, - otelEndpoint?: string, - skipStart?: boolean, - ): Promise - startSandbox( - sandboxId: string, - authToken: string, - metadata?: { [key: string]: string }, - skipStart?: boolean, - ): Promise - stopSandbox(sandboxId: string, force?: boolean): Promise - destroySandbox(sandboxId: string): Promise - createBackup(sandbox: Sandbox, backupSnapshotName: string, registry?: DockerRegistry): Promise - - removeSnapshot(snapshotName: string): Promise - buildSnapshot( - buildInfo: BuildInfo, - organizationId?: string, - sourceRegistries?: DockerRegistry[], - registry?: DockerRegistry, - pushToInternalRegistry?: boolean, - ): Promise - pullSnapshot( - snapshotName: string, - registry?: DockerRegistry, - destinationRegistry?: DockerRegistry, - destinationRef?: string, - newTag?: string, - ): Promise - snapshotExists(snapshotRef: string): Promise - getSnapshotInfo(snapshotName: string): Promise - inspectSnapshotInRegistry(snapshotName: string, registry?: DockerRegistry): Promise - - updateNetworkSettings( - sandboxId: string, - networkBlockAll?: boolean, - networkAllowList?: string, - networkLimitEgress?: boolean, - ): Promise - - recoverSandbox(sandbox: Sandbox): Promise - - resizeSandbox(sandboxId: string, cpu?: number, memory?: number, disk?: number): Promise -} - -@Injectable() -export class RunnerAdapterFactory { - private readonly logger = new Logger(RunnerAdapterFactory.name) - - constructor(private moduleRef: ModuleRef) {} - - async create(runner: Runner): Promise { - switch (runner.apiVersion) { - case '0': { - const adapter = await this.moduleRef.create(RunnerAdapterV0) - await adapter.init(runner) - return adapter - } - case '2': { - const adapter = await this.moduleRef.create(RunnerAdapterV2) - await adapter.init(runner) - return adapter - } - default: - throw new Error(`Unsupported runner version: ${runner.apiVersion}`) - } - } -} diff --git a/apps/api/src/sandbox/runner-adapter/runnerAdapter.v0.ts b/apps/api/src/sandbox/runner-adapter/runnerAdapter.v0.ts deleted file mode 100644 index 158a5ce4d..000000000 --- a/apps/api/src/sandbox/runner-adapter/runnerAdapter.v0.ts +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import axios, { AxiosError } from 'axios' -import axiosDebug from 'axios-debug-log' -import axiosRetry from 'axios-retry' - -import { Injectable, Logger } from '@nestjs/common' -import { - RunnerAdapter, - RunnerInfo, - RunnerSandboxInfo, - RunnerSnapshotInfo, - StartSandboxResponse, - SnapshotDigestResponse, -} from './runnerAdapter' -import { SnapshotStateError } from '../errors/snapshot-state-error' -import { Runner } from '../entities/runner.entity' -import { - Configuration, - SandboxApi, - EnumsSandboxState, - SnapshotsApi, - EnumsBackupState, - DefaultApi, - CreateSandboxDTO, - BuildSnapshotRequestDTO, - CreateBackupDTO, - PullSnapshotRequestDTO, - ToolboxApi, - UpdateNetworkSettingsDTO, - RecoverSandboxDTO, -} from '@boxlite-ai/runner-api-client' -import { Sandbox } from '../entities/sandbox.entity' -import { BuildInfo } from '../entities/build-info.entity' -import { DockerRegistry } from '../../docker-registry/entities/docker-registry.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { BackupState } from '../enums/backup-state.enum' -import { RunnerApiError } from '../errors/runner-api-error' - -const isDebugEnabled = process.env.DEBUG === 'true' - -// Network error codes that should trigger a retry -const RETRYABLE_NETWORK_ERROR_CODES = ['ECONNRESET', 'ETIMEDOUT'] - -@Injectable() -export class RunnerAdapterV0 implements RunnerAdapter { - private readonly logger = new Logger(RunnerAdapterV0.name) - private sandboxApiClient: SandboxApi - private snapshotApiClient: SnapshotsApi - private runnerApiClient: DefaultApi - private toolboxApiClient: ToolboxApi - - private convertSandboxState(state: EnumsSandboxState): SandboxState { - switch (state) { - case EnumsSandboxState.SandboxStateCreating: - return SandboxState.CREATING - case EnumsSandboxState.SandboxStateRestoring: - return SandboxState.RESTORING - case EnumsSandboxState.SandboxStateDestroyed: - return SandboxState.DESTROYED - case EnumsSandboxState.SandboxStateDestroying: - return SandboxState.DESTROYING - case EnumsSandboxState.SandboxStateStarted: - return SandboxState.STARTED - case EnumsSandboxState.SandboxStateStopped: - return SandboxState.STOPPED - case EnumsSandboxState.SandboxStateStarting: - return SandboxState.STARTING - case EnumsSandboxState.SandboxStateStopping: - return SandboxState.STOPPING - case EnumsSandboxState.SandboxStateError: - return SandboxState.ERROR - case EnumsSandboxState.SandboxStatePullingSnapshot: - return SandboxState.PULLING_SNAPSHOT - default: - return SandboxState.UNKNOWN - } - } - - private convertBackupState(state: EnumsBackupState): BackupState { - switch (state) { - case EnumsBackupState.BackupStatePending: - return BackupState.PENDING - case EnumsBackupState.BackupStateInProgress: - return BackupState.IN_PROGRESS - case EnumsBackupState.BackupStateCompleted: - return BackupState.COMPLETED - case EnumsBackupState.BackupStateFailed: - return BackupState.ERROR - default: - return BackupState.NONE - } - } - - public async init(runner: Runner): Promise { - if (!runner.apiUrl) { - throw new Error('Runner API URL is required') - } - - const axiosInstance = axios.create({ - baseURL: runner.apiUrl, - headers: { - Authorization: `Bearer ${runner.apiKey}`, - }, - timeout: 1 * 60 * 60 * 1000, // 1 hour - }) - - const retryErrorMap = new WeakMap() - - // Configure axios-retry to handle network errors - axiosRetry(axiosInstance, { - retries: 3, - retryDelay: axiosRetry.exponentialDelay, - retryCondition: (error) => { - // Check if error code or message matches any retryable error - const matchedErrorCode = RETRYABLE_NETWORK_ERROR_CODES.find( - (code) => - (error as any).code === code || error.message?.includes(code) || (error as any).cause?.code === code, - ) - - if (matchedErrorCode) { - retryErrorMap.set(error, matchedErrorCode) - return true - } - - return false - }, - onRetry: (retryCount, error, requestConfig) => { - this.logger.warn( - `Retrying request due to ${retryErrorMap.get(error)} (attempt ${retryCount}): ${requestConfig.method?.toUpperCase()} ${requestConfig.url}`, - ) - }, - }) - - axiosInstance.interceptors.response.use( - (response) => { - return response - }, - (error) => { - const errorMessage = error.response?.data?.message || error.response?.data || error.message || String(error) - const statusCode = error.response?.data?.statusCode || error.response?.status || error.status - const code = error.response?.data?.code || (error as any).code || (error as any).cause?.code || '' - - throw new RunnerApiError(String(errorMessage), statusCode, code) - }, - ) - - if (isDebugEnabled) { - axiosDebug.addLogger(axiosInstance) - } - - this.sandboxApiClient = new SandboxApi(new Configuration(), '', axiosInstance) - this.snapshotApiClient = new SnapshotsApi(new Configuration(), '', axiosInstance) - this.runnerApiClient = new DefaultApi(new Configuration(), '', axiosInstance) - this.toolboxApiClient = new ToolboxApi(new Configuration(), '', axiosInstance) - } - - async healthCheck(signal?: AbortSignal): Promise { - const response = await this.runnerApiClient.healthCheck({ signal }) - if (response.data.status !== 'ok') { - throw new Error('Runner is not healthy') - } - } - - async runnerInfo(signal?: AbortSignal): Promise { - const response = await this.runnerApiClient.runnerInfo({ signal }) - return { - serviceHealth: response.data.serviceHealth, - metrics: response.data.metrics, - appVersion: response.data.appVersion, - } - } - - async sandboxInfo(sandboxId: string): Promise { - const sandboxInfo = await this.sandboxApiClient.info(sandboxId) - return { - state: this.convertSandboxState(sandboxInfo.data.state), - backupState: this.convertBackupState(sandboxInfo.data.backupState), - backupSnapshot: sandboxInfo.data.backupSnapshot, - backupErrorReason: sandboxInfo.data.backupError, - daemonVersion: sandboxInfo.data.daemonVersion, - } - } - - async createSandbox( - sandbox: Sandbox, - snapshotRef: string, - registry?: DockerRegistry, - entrypoint?: string[], - metadata?: { [key: string]: string }, - otelEndpoint?: string, - skipStart?: boolean, - ): Promise { - const createSandboxDto: CreateSandboxDTO = { - id: sandbox.id, - userId: sandbox.organizationId, - snapshot: snapshotRef, - osUser: sandbox.osUser, - cpuQuota: sandbox.cpu, - gpuQuota: sandbox.gpu, - memoryQuota: sandbox.mem, - storageQuota: sandbox.disk, - env: sandbox.env, - registry: registry - ? { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - : undefined, - entrypoint: entrypoint, - volumes: sandbox.volumes?.map((volume) => ({ - volumeId: volume.volumeId, - mountPath: volume.mountPath, - subpath: volume.subpath, - })), - networkBlockAll: sandbox.networkBlockAll, - networkAllowList: sandbox.networkAllowList, - metadata: metadata, - authToken: sandbox.authToken, - otelEndpoint, - skipStart: skipStart, - organizationId: sandbox.organizationId, - regionId: sandbox.region, - } - - const response = await this.sandboxApiClient.create(createSandboxDto) - - if (!response?.data?.daemonVersion) { - return undefined - } - - return { - daemonVersion: response.data.daemonVersion, - } - } - - async startSandbox( - sandboxId: string, - authToken: string, - metadata?: { [key: string]: string }, - ): Promise { - const response = await this.sandboxApiClient.start(sandboxId, authToken, metadata) - - if (!response?.data?.daemonVersion) { - return undefined - } - - return { - daemonVersion: response.data.daemonVersion, - } - } - - async stopSandbox(sandboxId: string, force?: boolean): Promise { - await this.sandboxApiClient.stop(sandboxId, { force }) - } - - async destroySandbox(sandboxId: string): Promise { - await this.sandboxApiClient.destroy(sandboxId) - } - - async createBackup(sandbox: Sandbox, backupSnapshotName: string, registry?: DockerRegistry): Promise { - const request: CreateBackupDTO = { - snapshot: backupSnapshotName, - registry: undefined, - } - - if (registry) { - request.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - await this.sandboxApiClient.createBackup(sandbox.id, request) - } - - async buildSnapshot( - buildInfo: BuildInfo, - organizationId?: string, - sourceRegistries?: DockerRegistry[], - registry?: DockerRegistry, - pushToInternalRegistry?: boolean, - ): Promise { - const request: BuildSnapshotRequestDTO = { - snapshot: buildInfo.snapshotRef, - dockerfile: buildInfo.dockerfileContent, - organizationId: organizationId, - context: buildInfo.contextHashes, - pushToInternalRegistry: pushToInternalRegistry, - } - - if (sourceRegistries) { - request.sourceRegistries = sourceRegistries.map((sourceRegistry) => ({ - project: sourceRegistry.project, - url: sourceRegistry.url.replace(/^(https?:\/\/)/, ''), - username: sourceRegistry.username, - password: sourceRegistry.password, - })) - } - - if (registry) { - request.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - await this.snapshotApiClient.buildSnapshot(request) - } - - async removeSnapshot(snapshotName: string): Promise { - await this.snapshotApiClient.removeSnapshot(snapshotName) - } - - async pullSnapshot( - snapshotName: string, - registry?: DockerRegistry, - destinationRegistry?: DockerRegistry, - destinationRef?: string, - newTag?: string, - ): Promise { - const request: PullSnapshotRequestDTO = { - snapshot: snapshotName, - newTag, - } - - if (registry) { - request.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - if (destinationRegistry) { - request.destinationRegistry = { - project: destinationRegistry.project, - url: destinationRegistry.url.replace(/^(https?:\/\/)/, ''), - username: destinationRegistry.username, - password: destinationRegistry.password, - } - } - - if (destinationRef) { - request.destinationRef = destinationRef - } - - await this.snapshotApiClient.pullSnapshot(request) - } - - async snapshotExists(snapshotName: string): Promise { - const response = await this.snapshotApiClient.snapshotExists(snapshotName) - return response.data.exists - } - - async getSnapshotInfo(snapshotName: string): Promise { - try { - const response = await this.snapshotApiClient.getSnapshotInfo(snapshotName) - - return { - name: response.data.name || '', - sizeGB: response.data.sizeGB, - entrypoint: response.data.entrypoint, - cmd: response.data.cmd, - hash: response.data.hash, - } - } catch (err) { - if (err instanceof RunnerApiError && err.statusCode === 422) { - throw new SnapshotStateError(err.message) - } - throw err - } - } - - async inspectSnapshotInRegistry(snapshotName: string, registry?: DockerRegistry): Promise { - const response = await this.snapshotApiClient.inspectSnapshotInRegistry({ - snapshot: snapshotName, - registry: registry - ? { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - : undefined, - }) - - return { - hash: response.data.hash, - sizeGB: response.data.sizeGB, - } - } - - async updateNetworkSettings( - sandboxId: string, - networkBlockAll?: boolean, - networkAllowList?: string, - networkLimitEgress?: boolean, - ): Promise { - const updateNetworkSettingsDto: UpdateNetworkSettingsDTO = { - networkBlockAll: networkBlockAll, - networkAllowList: networkAllowList, - networkLimitEgress: networkLimitEgress, - } - - await this.sandboxApiClient.updateNetworkSettings(sandboxId, updateNetworkSettingsDto) - } - - async recoverSandbox(sandbox: Sandbox): Promise { - const recoverSandboxDTO: RecoverSandboxDTO = { - userId: sandbox.organizationId, - snapshot: sandbox.snapshot, - osUser: sandbox.osUser, - cpuQuota: sandbox.cpu, - gpuQuota: sandbox.gpu, - memoryQuota: sandbox.mem, - storageQuota: sandbox.disk, - env: sandbox.env, - volumes: sandbox.volumes?.map((volume) => ({ - volumeId: volume.volumeId, - mountPath: volume.mountPath, - subpath: volume.subpath, - })), - networkBlockAll: sandbox.networkBlockAll, - networkAllowList: sandbox.networkAllowList, - errorReason: sandbox.errorReason, - backupErrorReason: sandbox.backupErrorReason, - } - await this.sandboxApiClient.recover(sandbox.id, recoverSandboxDTO) - } - - async resizeSandbox(sandboxId: string, cpu?: number, memory?: number, disk?: number): Promise { - await this.sandboxApiClient.resize(sandboxId, { cpu, memory, disk }) - } -} diff --git a/apps/api/src/sandbox/runner-adapter/runnerAdapter.v2.ts b/apps/api/src/sandbox/runner-adapter/runnerAdapter.v2.ts deleted file mode 100644 index 33bf2a497..000000000 --- a/apps/api/src/sandbox/runner-adapter/runnerAdapter.v2.ts +++ /dev/null @@ -1,539 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository, IsNull, Not } from 'typeorm' -import { - RunnerAdapter, - RunnerInfo, - RunnerSandboxInfo, - RunnerSnapshotInfo, - StartSandboxResponse, - SnapshotDigestResponse, -} from './runnerAdapter' -import { Runner } from '../entities/runner.entity' -import { Sandbox } from '../entities/sandbox.entity' -import { Job } from '../entities/job.entity' -import { BuildInfo } from '../entities/build-info.entity' -import { DockerRegistry } from '../../docker-registry/entities/docker-registry.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { JobType } from '../enums/job-type.enum' -import { JobStatus } from '../enums/job-status.enum' -import { ResourceType } from '../enums/resource-type.enum' -import { JobService } from '../services/job.service' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { - CreateSandboxDTO, - CreateBackupDTO, - BuildSnapshotRequestDTO, - PullSnapshotRequestDTO, - UpdateNetworkSettingsDTO, - InspectSnapshotInRegistryRequest, - RecoverSandboxDTO, -} from '@boxlite-ai/runner-api-client' -import { SnapshotStateError } from '../errors/snapshot-state-error' - -/** - * RunnerAdapterV2 implements RunnerAdapter for v2 runners. - * Instead of making direct API calls to the runner, it creates jobs in the database - * that the v2 runner polls and processes asynchronously. - */ -@Injectable() -export class RunnerAdapterV2 implements RunnerAdapter { - private readonly logger = new Logger(RunnerAdapterV2.name) - private runner: Runner - - constructor( - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Job) - private readonly jobRepository: Repository, - private readonly jobService: JobService, - ) {} - - async init(runner: Runner): Promise { - this.runner = runner - } - - async healthCheck(_signal?: AbortSignal): Promise { - throw new Error('healthCheck is not supported for V2 runners') - } - - async runnerInfo(_signal?: AbortSignal): Promise { - throw new Error('runnerInfo is not supported for V2 runners') - } - - async sandboxInfo(sandboxId: string): Promise { - // Query the sandbox entity - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, - }) - - if (!sandbox) { - throw new Error(`Sandbox ${sandboxId} not found`) - } - - // Query for any incomplete jobs for this sandbox to determine transitional state - const incompleteJob = await this.jobRepository.findOne({ - where: { - resourceType: ResourceType.SANDBOX, - resourceId: sandboxId, - completedAt: IsNull(), - }, - order: { createdAt: 'DESC' }, - }) - - let state = sandbox.state - - let daemonVersion: string | undefined = undefined - - // If there's an incomplete job, infer the transitional state from job type - if (incompleteJob) { - state = this.inferStateFromJob(incompleteJob, sandbox) - daemonVersion = incompleteJob.getResultMetadata()?.daemonVersion - } else { - // Look for latest job for this sandbox - const latestJob = await this.jobRepository.findOne({ - where: { - resourceType: ResourceType.SANDBOX, - resourceId: sandboxId, - }, - order: { createdAt: 'DESC' }, - }) - if (latestJob) { - state = this.inferStateFromJob(latestJob, sandbox) - daemonVersion = latestJob.getResultMetadata()?.daemonVersion - } - } - - return { - state, - backupState: sandbox.backupState, - backupErrorReason: sandbox.backupErrorReason, - daemonVersion, - } - } - - private inferStateFromJob(job: Job, sandbox: Sandbox): SandboxState { - // Map job types to transitional states - switch (job.type) { - case JobType.CREATE_SANDBOX: - return job.status === JobStatus.COMPLETED ? SandboxState.STARTED : SandboxState.CREATING - case JobType.START_SANDBOX: - return job.status === JobStatus.COMPLETED ? SandboxState.STARTED : SandboxState.STARTING - case JobType.STOP_SANDBOX: - return job.status === JobStatus.COMPLETED ? SandboxState.STOPPED : SandboxState.STOPPING - case JobType.DESTROY_SANDBOX: - return job.status === JobStatus.COMPLETED ? SandboxState.DESTROYED : SandboxState.DESTROYING - default: - // For other job types (backup, etc.), return current sandbox state - return sandbox.state - } - } - - async createSandbox( - sandbox: Sandbox, - snapshotRef: string, - registry?: DockerRegistry, - entrypoint?: string[], - metadata?: { [key: string]: string }, - otelEndpoint?: string, - skipStart?: boolean, - ): Promise { - const payload: CreateSandboxDTO = { - id: sandbox.id, - userId: sandbox.organizationId, - snapshot: snapshotRef, - osUser: sandbox.osUser, - cpuQuota: sandbox.cpu, - gpuQuota: sandbox.gpu, - memoryQuota: sandbox.mem, - storageQuota: sandbox.disk, - env: sandbox.env, - registry: registry - ? { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - : undefined, - entrypoint: entrypoint, - volumes: sandbox.volumes?.map((volume) => ({ - volumeId: volume.volumeId, - mountPath: volume.mountPath, - subpath: volume.subpath, - })), - networkBlockAll: sandbox.networkBlockAll, - networkAllowList: sandbox.networkAllowList, - metadata: metadata, - authToken: sandbox.authToken, - otelEndpoint: otelEndpoint, - skipStart: skipStart, - organizationId: sandbox.organizationId, - regionId: sandbox.region, - } - - await this.jobService.createJob( - null, - JobType.CREATE_SANDBOX, - this.runner.id, - ResourceType.SANDBOX, - sandbox.id, - payload, - ) - - this.logger.debug(`Created CREATE_SANDBOX job for sandbox ${sandbox.id} on runner ${this.runner.id}`) - - // Daemon version will be set in the job result metadata - return undefined - } - - async startSandbox( - sandboxId: string, - authToken: string, - metadata?: { [key: string]: string }, - ): Promise { - await this.jobService.createJob(null, JobType.START_SANDBOX, this.runner.id, ResourceType.SANDBOX, sandboxId, { - authToken, - metadata, - }) - - this.logger.debug(`Created START_SANDBOX job for sandbox ${sandboxId} on runner ${this.runner.id}`) - - // Daemon version will be set in the job result metadata - return undefined - } - - async stopSandbox(sandboxId: string, force?: boolean): Promise { - await this.jobService.createJob(null, JobType.STOP_SANDBOX, this.runner.id, ResourceType.SANDBOX, sandboxId, { - force, - }) - - this.logger.debug(`Created STOP_SANDBOX job for sandbox ${sandboxId} on runner ${this.runner.id}`) - } - - async destroySandbox(sandboxId: string): Promise { - await this.jobService.createJob(null, JobType.DESTROY_SANDBOX, this.runner.id, ResourceType.SANDBOX, sandboxId) - - this.logger.debug(`Created DESTROY_SANDBOX job for sandbox ${sandboxId} on runner ${this.runner.id}`) - } - - async recoverSandbox(sandbox: Sandbox): Promise { - const recoverSandboxDTO: RecoverSandboxDTO = { - userId: sandbox.organizationId, - snapshot: sandbox.snapshot, - osUser: sandbox.osUser, - cpuQuota: sandbox.cpu, - gpuQuota: sandbox.gpu, - memoryQuota: sandbox.mem, - storageQuota: sandbox.disk, - env: sandbox.env, - volumes: sandbox.volumes?.map((volume) => ({ - volumeId: volume.volumeId, - mountPath: volume.mountPath, - subpath: volume.subpath, - })), - networkBlockAll: sandbox.networkBlockAll, - networkAllowList: sandbox.networkAllowList, - errorReason: sandbox.errorReason, - backupErrorReason: sandbox.backupErrorReason, - } - await this.jobService.createJob( - null, - JobType.RECOVER_SANDBOX, - this.runner.id, - ResourceType.SANDBOX, - sandbox.id, - recoverSandboxDTO, - ) - - this.logger.debug(`Created RECOVER_SANDBOX job for sandbox ${sandbox.id} on runner ${this.runner.id}`) - } - - async createBackup(sandbox: Sandbox, backupSnapshotName: string, registry?: DockerRegistry): Promise { - const payload: CreateBackupDTO = { - snapshot: backupSnapshotName, - registry: undefined, - } - - if (registry) { - payload.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - await this.jobService.createJob( - null, - JobType.CREATE_BACKUP, - this.runner.id, - ResourceType.SANDBOX, - sandbox.id, - payload, - ) - - this.logger.debug(`Created CREATE_BACKUP job for sandbox ${sandbox.id} on runner ${this.runner.id}`) - } - - async buildSnapshot( - buildInfo: BuildInfo, - organizationId?: string, - sourceRegistries?: DockerRegistry[], - registry?: DockerRegistry, - pushToInternalRegistry?: boolean, - ): Promise { - const payload: BuildSnapshotRequestDTO = { - snapshot: buildInfo.snapshotRef, - dockerfile: buildInfo.dockerfileContent, - organizationId: organizationId, - context: buildInfo.contextHashes, - pushToInternalRegistry: pushToInternalRegistry, - } - - if (sourceRegistries) { - payload.sourceRegistries = sourceRegistries.map((sourceRegistry) => ({ - project: sourceRegistry.project, - url: sourceRegistry.url.replace(/^(https?:\/\/)/, ''), - username: sourceRegistry.username, - password: sourceRegistry.password, - })) - } - - if (registry) { - payload.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - await this.jobService.createJob( - null, - JobType.BUILD_SNAPSHOT, - this.runner.id, - ResourceType.SNAPSHOT, - buildInfo.snapshotRef, - payload, - ) - - this.logger.debug(`Created BUILD_SNAPSHOT job for ${buildInfo.snapshotRef} on runner ${this.runner.id}`) - } - - async pullSnapshot( - snapshotName: string, - registry?: DockerRegistry, - destinationRegistry?: DockerRegistry, - destinationRef?: string, - newTag?: string, - ): Promise { - const payload: PullSnapshotRequestDTO = { - snapshot: snapshotName, - newTag, - } - - if (registry) { - payload.registry = { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - } - - if (destinationRegistry) { - payload.destinationRegistry = { - project: destinationRegistry.project, - url: destinationRegistry.url.replace(/^(https?:\/\/)/, ''), - username: destinationRegistry.username, - password: destinationRegistry.password, - } - } - - if (destinationRef) { - payload.destinationRef = destinationRef - } - - await this.jobService.createJob( - null, - JobType.PULL_SNAPSHOT, - this.runner.id, - ResourceType.SNAPSHOT, - destinationRef || snapshotName, - payload, - ) - - this.logger.debug(`Created PULL_SNAPSHOT job for ${snapshotName} on runner ${this.runner.id}`) - } - - async removeSnapshot(snapshotName: string): Promise { - await this.jobService.createJob(null, JobType.REMOVE_SNAPSHOT, this.runner.id, ResourceType.SNAPSHOT, snapshotName) - - this.logger.debug(`Created REMOVE_SNAPSHOT job for ${snapshotName} on runner ${this.runner.id}`) - } - - async snapshotExists(snapshotRef: string): Promise { - // Find the latest job for this snapshot on this runner - // Do not include INSPECT_SNAPSHOT_IN_REGISTRY - const latestJob = await this.jobRepository.findOne({ - where: [ - { - runnerId: this.runner.id, - resourceType: ResourceType.SNAPSHOT, - resourceId: snapshotRef, - type: Not(JobType.INSPECT_SNAPSHOT_IN_REGISTRY), - }, - ], - order: { createdAt: 'DESC' }, - }) - - // If no job exists, snapshot doesn't exist - if (!latestJob) { - return false - } - - // If the latest job is a REMOVE_SNAPSHOT, the snapshot no longer exists - if (latestJob.type === JobType.REMOVE_SNAPSHOT) { - return false - } - - // If the latest job is PULL_SNAPSHOT or BUILD_SNAPSHOT, check if it completed successfully - if (latestJob.type === JobType.PULL_SNAPSHOT || latestJob.type === JobType.BUILD_SNAPSHOT) { - return latestJob.status === JobStatus.COMPLETED - } - - // For any other job type, snapshot doesn't exist - return false - } - - async getSnapshotInfo(snapshotRef: string): Promise { - const latestJob = await this.jobRepository.findOne({ - where: [ - { - runnerId: this.runner.id, - resourceType: ResourceType.SNAPSHOT, - resourceId: snapshotRef, - type: Not(JobType.INSPECT_SNAPSHOT_IN_REGISTRY), - }, - ], - order: { createdAt: 'DESC' }, - }) - - if (!latestJob) { - throw new Error(`Snapshot ${snapshotRef} not found on runner ${this.runner.id}`) - } - - const metadata = latestJob.getResultMetadata() - - switch (latestJob.status) { - case JobStatus.COMPLETED: - if (latestJob.type === JobType.PULL_SNAPSHOT || latestJob.type === JobType.BUILD_SNAPSHOT) { - return { - name: latestJob.resourceId, - sizeGB: metadata?.sizeGB, - entrypoint: metadata?.entrypoint, - cmd: metadata?.cmd, - hash: metadata?.hash, - } - } - throw new Error( - `Snapshot ${snapshotRef} is in an unknown state (${latestJob.status}) on runner ${this.runner.id}`, - ) - case JobStatus.FAILED: - throw new SnapshotStateError( - latestJob.errorMessage || `Snapshot ${snapshotRef} failed on runner ${this.runner.id}`, - ) - default: - throw new Error( - `Snapshot ${snapshotRef} is in an unknown state (${latestJob.status}) on runner ${this.runner.id}`, - ) - } - } - - async inspectSnapshotInRegistry(snapshotName: string, registry?: DockerRegistry): Promise { - const payload: InspectSnapshotInRegistryRequest = { - snapshot: snapshotName, - registry: registry - ? { - project: registry.project, - url: registry.url.replace(/^(https?:\/\/)/, ''), - username: registry.username, - password: registry.password, - } - : undefined, - } - - const job = await this.jobService.createJob( - null, - JobType.INSPECT_SNAPSHOT_IN_REGISTRY, - this.runner.id, - ResourceType.SNAPSHOT, - snapshotName, - payload, - ) - - this.logger.debug(`Created INSPECT_SNAPSHOT_IN_REGISTRY job for ${snapshotName} on runner ${this.runner.id}`) - - const waitTimeout = 30 * 1000 // 30 seconds - const completedJob = await this.jobService.waitJobCompletion(job.id, waitTimeout) - - if (!completedJob) { - throw new Error(`Snapshot ${snapshotName} not found in registry on runner ${this.runner.id}`) - } - - if (completedJob.status !== JobStatus.COMPLETED) { - throw new Error( - `Snapshot ${snapshotName} failed to inspect in registry on runner ${this.runner.id}. Error: ${completedJob.errorMessage}`, - ) - } - - const resultMetadata = completedJob.getResultMetadata() - - return { - hash: resultMetadata?.hash, - sizeGB: resultMetadata?.sizeGB, - } - } - - async updateNetworkSettings( - sandboxId: string, - networkBlockAll?: boolean, - networkAllowList?: string, - networkLimitEgress?: boolean, - ): Promise { - const payload: UpdateNetworkSettingsDTO = { - networkBlockAll: networkBlockAll, - networkAllowList: networkAllowList, - networkLimitEgress: networkLimitEgress, - } - - await this.jobService.createJob( - null, - JobType.UPDATE_SANDBOX_NETWORK_SETTINGS, - this.runner.id, - ResourceType.SANDBOX, - sandboxId, - payload, - ) - - this.logger.debug( - `Created UPDATE_SANDBOX_NETWORK_SETTINGS job for sandbox ${sandboxId} on runner ${this.runner.id}`, - ) - } - - async resizeSandbox(sandboxId: string, cpu?: number, memory?: number, disk?: number): Promise { - await this.jobService.createJob(null, JobType.RESIZE_SANDBOX, this.runner.id, ResourceType.SANDBOX, sandboxId, { - cpu, - memory, - disk, - }) - - this.logger.debug(`Created RESIZE_SANDBOX job for sandbox ${sandboxId} on runner ${this.runner.id}`) - } -} diff --git a/apps/api/src/sandbox/sandbox.module.ts b/apps/api/src/sandbox/sandbox.module.ts deleted file mode 100644 index 0a2964678..000000000 --- a/apps/api/src/sandbox/sandbox.module.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Module } from '@nestjs/common' -import { DataSource } from 'typeorm' -import { SandboxController } from './controllers/sandbox.controller' -import { SandboxService } from './services/sandbox.service' -import { TypeOrmModule } from '@nestjs/typeorm' -import { Sandbox } from './entities/sandbox.entity' -import { UserModule } from '../user/user.module' -import { RunnerService } from './services/runner.service' -import { Runner } from './entities/runner.entity' -import { RunnerController } from './controllers/runner.controller' -import { ToolboxService } from './services/toolbox.deprecated.service' -import { DockerRegistryModule } from '../docker-registry/docker-registry.module' -import { SandboxManager } from './managers/sandbox.manager' -import { ToolboxController } from './controllers/toolbox.deprecated.controller' -import { Snapshot } from './entities/snapshot.entity' -import { SnapshotController } from './controllers/snapshot.controller' -import { SnapshotService } from './services/snapshot.service' -import { SnapshotManager } from './managers/snapshot.manager' -import { SnapshotRunner } from './entities/snapshot-runner.entity' -import { DockerRegistry } from '../docker-registry/entities/docker-registry.entity' -import { RedisLockProvider } from './common/redis-lock.provider' -import { OrganizationModule } from '../organization/organization.module' -import { SandboxWarmPoolService } from './services/sandbox-warm-pool.service' -import { WarmPool } from './entities/warm-pool.entity' -import { PreviewController } from './controllers/preview.controller' -import { SnapshotSubscriber } from './subscribers/snapshot.subscriber' -import { VolumeController } from './controllers/volume.controller' -import { VolumeService } from './services/volume.service' -import { VolumeManager } from './managers/volume.manager' -import { Volume } from './entities/volume.entity' -import { BuildInfo } from './entities/build-info.entity' -import { BackupManager } from './managers/backup.manager' -import { VolumeSubscriber } from './subscribers/volume.subscriber' -import { RunnerSubscriber } from './subscribers/runner.subscriber' -import { WorkspaceController } from './controllers/workspace.deprecated.controller' -import { RunnerAdapterFactory } from './runner-adapter/runnerAdapter' -import { SandboxStartAction } from './managers/sandbox-actions/sandbox-start.action' -import { SandboxStopAction } from './managers/sandbox-actions/sandbox-stop.action' -import { SandboxDestroyAction } from './managers/sandbox-actions/sandbox-destroy.action' -import { SandboxArchiveAction } from './managers/sandbox-actions/sandbox-archive.action' -import { SshAccess } from './entities/ssh-access.entity' -import { SandboxRepository } from './repositories/sandbox.repository' -import { ProxyCacheInvalidationService } from './services/proxy-cache-invalidation.service' -import { RegionModule } from '../region/region.module' -import { Region } from '../region/entities/region.entity' -import { SnapshotRegion } from './entities/snapshot-region.entity' -import { JobController } from './controllers/job.controller' -import { JobService } from './services/job.service' -import { JobStateHandlerService } from './services/job-state-handler.service' -import { Job } from './entities/job.entity' -import { SandboxLookupCacheInvalidationService } from './services/sandbox-lookup-cache-invalidation.service' -import { SandboxAccessGuard } from './guards/sandbox-access.guard' -import { RunnerAccessGuard } from './guards/runner-access.guard' -import { RegionRunnerAccessGuard } from './guards/region-runner-access.guard' -import { RegionSandboxAccessGuard } from './guards/region-sandbox-access.guard' -import { ProxyGuard } from './guards/proxy.guard' -import { SshGatewayGuard } from './guards/ssh-gateway.guard' -import { EventEmitter2 } from '@nestjs/event-emitter' -import { SandboxLastActivity } from './entities/sandbox-last-activity.entity' -import { SandboxActivityService } from './services/sandbox-activity.service' -import { SandboxStateWaiterService } from './services/sandbox-state-waiter.service' - -@Module({ - imports: [ - UserModule, - DockerRegistryModule, - OrganizationModule, - RegionModule, - TypeOrmModule.forFeature([ - Sandbox, - Runner, - Snapshot, - BuildInfo, - SnapshotRunner, - SnapshotRegion, - DockerRegistry, - WarmPool, - Volume, - SshAccess, - Region, - Job, - SandboxLastActivity, - ]), - ], - controllers: [ - SandboxController, - RunnerController, - ToolboxController, - SnapshotController, - WorkspaceController, - PreviewController, - VolumeController, - JobController, - ], - providers: [ - SandboxService, - SandboxManager, - BackupManager, - SandboxWarmPoolService, - RunnerService, - ToolboxService, - SnapshotService, - ProxyCacheInvalidationService, - SandboxLookupCacheInvalidationService, - SnapshotManager, - RedisLockProvider, - SnapshotSubscriber, - VolumeService, - VolumeManager, - VolumeSubscriber, - RunnerSubscriber, - RunnerAdapterFactory, - SandboxStartAction, - SandboxStopAction, - SandboxDestroyAction, - SandboxArchiveAction, - JobService, - JobStateHandlerService, - SandboxActivityService, - SandboxStateWaiterService, - SandboxAccessGuard, - RunnerAccessGuard, - RegionRunnerAccessGuard, - RegionSandboxAccessGuard, - ProxyGuard, - SshGatewayGuard, - { - provide: SandboxRepository, - inject: [DataSource, EventEmitter2, SandboxLookupCacheInvalidationService], - useFactory: ( - dataSource: DataSource, - eventEmitter: EventEmitter2, - sandboxLookupCacheInvalidationService: SandboxLookupCacheInvalidationService, - ) => new SandboxRepository(dataSource, eventEmitter, sandboxLookupCacheInvalidationService), - }, - ], - exports: [ - SandboxService, - RunnerService, - RedisLockProvider, - SnapshotService, - VolumeService, - VolumeManager, - SandboxRepository, - RunnerAdapterFactory, - SandboxActivityService, - SandboxStateWaiterService, - ], -}) -export class SandboxModule {} diff --git a/apps/api/src/sandbox/services/job-state-handler.service.ts b/apps/api/src/sandbox/services/job-state-handler.service.ts deleted file mode 100644 index 4085e2710..000000000 --- a/apps/api/src/sandbox/services/job-state-handler.service.ts +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository } from 'typeorm' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotRunner } from '../entities/snapshot-runner.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { SnapshotRunnerState } from '../enums/snapshot-runner-state.enum' -import { JobStatus } from '../enums/job-status.enum' -import { JobType } from '../enums/job-type.enum' -import { Job } from '../entities/job.entity' -import { BackupState } from '../enums/backup-state.enum' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { sanitizeSandboxError } from '../utils/sanitize-error.util' -import { OrganizationUsageService } from '../../organization/services/organization-usage.service' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { Sandbox } from '../entities/sandbox.entity' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { ResourceType } from '../enums/resource-type.enum' -import { getStateChangeLockKey } from '../utils/lock-key.util' - -/** - * Service for handling entity state updates based on job completion (v2 runners only). - * This service listens to job status changes and updates entity states accordingly. - */ -@Injectable() -export class JobStateHandlerService { - private readonly logger = new Logger(JobStateHandlerService.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(SnapshotRunner) - private readonly snapshotRunnerRepository: Repository, - private readonly organizationUsageService: OrganizationUsageService, - private readonly redisLockProvider: RedisLockProvider, - ) {} - - /** - * Handle job completion and update entity state accordingly. - * Called when a job status is updated to COMPLETED or FAILED. - */ - async handleJobCompletion(job: Job): Promise { - if (job.status !== JobStatus.COMPLETED && job.status !== JobStatus.FAILED) { - return - } - - if (!job.resourceId) { - return - } - - switch (job.type) { - case JobType.CREATE_SANDBOX: - await this.handleCreateSandboxJobCompletion(job) - break - case JobType.START_SANDBOX: - await this.handleStartSandboxJobCompletion(job) - break - case JobType.STOP_SANDBOX: - await this.handleStopSandboxJobCompletion(job) - break - case JobType.DESTROY_SANDBOX: - await this.handleDestroySandboxJobCompletion(job) - break - case JobType.RESIZE_SANDBOX: - await this.handleResizeSandboxJobCompletion(job) - break - case JobType.PULL_SNAPSHOT: - await this.handlePullSnapshotJobCompletion(job) - break - case JobType.BUILD_SNAPSHOT: - await this.handleBuildSnapshotJobCompletion(job) - break - case JobType.REMOVE_SNAPSHOT: - await this.handleRemoveSnapshotJobCompletion(job) - break - case JobType.CREATE_BACKUP: - await this.handleCreateBackupJobCompletion(job) - break - case JobType.RECOVER_SANDBOX: - await this.handleRecoverSandboxJobCompletion(job) - break - default: - break - } - - switch (job.resourceType) { - case ResourceType.SANDBOX: { - const lockKey = getStateChangeLockKey(job.resourceId) - this.redisLockProvider - .unlock(lockKey) - .catch((error) => this.logger.error(`Error unlocking Redis lock for sandbox ${job.resourceId}:`, error)) // Clean up lock after job completion - break - } - default: - break - } - } - - private async handleCreateSandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for CREATE_SANDBOX job ${job.id}`) - return - } - - if (sandbox.desiredState !== SandboxDesiredState.STARTED) { - this.logger.error( - `Sandbox ${sandboxId} is not in desired state STARTED for CREATE_SANDBOX job ${job.id}. Desired state: ${sandbox.desiredState}`, - ) - return - } - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `CREATE_SANDBOX job ${job.id} completed successfully, marking sandbox ${sandboxId} as STARTED`, - ) - updateData.state = SandboxState.STARTED - updateData.errorReason = null - const metadata = job.getResultMetadata() - if (metadata?.daemonVersion && typeof metadata.daemonVersion === 'string') { - updateData.daemonVersion = metadata.daemonVersion - } - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`CREATE_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - updateData.state = SandboxState.ERROR - const { recoverable, errorReason } = sanitizeSandboxError(job.errorMessage) - updateData.errorReason = errorReason || 'Failed to create sandbox' - updateData.recoverable = recoverable - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling CREATE_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handleStartSandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for START_SANDBOX job ${job.id}`) - return - } - - if (sandbox.desiredState !== SandboxDesiredState.STARTED) { - this.logger.error( - `Sandbox ${sandboxId} is not in desired state STARTED for START_SANDBOX job ${job.id}. Desired state: ${sandbox.desiredState}`, - ) - return - } - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug(`START_SANDBOX job ${job.id} completed successfully, marking sandbox ${sandboxId} as STARTED`) - updateData.state = SandboxState.STARTED - updateData.errorReason = null - const metadata = job.getResultMetadata() - if (metadata?.daemonVersion && typeof metadata.daemonVersion === 'string') { - updateData.daemonVersion = metadata.daemonVersion - } - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`START_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - updateData.state = SandboxState.ERROR - const { recoverable, errorReason } = sanitizeSandboxError(job.errorMessage) - updateData.errorReason = errorReason || 'Failed to start sandbox' - updateData.recoverable = recoverable - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling START_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handleStopSandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for STOP_SANDBOX job ${job.id}`) - return - } - - if (sandbox.desiredState !== SandboxDesiredState.STOPPED) { - this.logger.error( - `Sandbox ${sandboxId} is not in desired state STOPPED for STOP_SANDBOX job ${job.id}. Desired state: ${sandbox.desiredState}`, - ) - return - } - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug(`STOP_SANDBOX job ${job.id} completed successfully, marking sandbox ${sandboxId} as STOPPED`) - updateData.state = SandboxState.STOPPED - updateData.errorReason = null - Object.assign(updateData, Sandbox.getBackupStateUpdate(sandbox, BackupState.NONE)) - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`STOP_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - updateData.state = SandboxState.ERROR - const { recoverable, errorReason } = sanitizeSandboxError(job.errorMessage) - updateData.errorReason = errorReason || 'Failed to stop sandbox' - updateData.recoverable = recoverable - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling STOP_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handleDestroySandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for DESTROY_SANDBOX job ${job.id}`) - return - } - const updateData: Partial = {} - - if (sandbox.desiredState === SandboxDesiredState.DESTROYED) { - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `DESTROY_SANDBOX job ${job.id} completed successfully, marking sandbox ${sandboxId} as DESTROYED`, - ) - updateData.state = SandboxState.DESTROYED - updateData.errorReason = null - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`DESTROY_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - updateData.state = SandboxState.ERROR - const { recoverable, errorReason } = sanitizeSandboxError(job.errorMessage) - updateData.errorReason = errorReason || 'Failed to destroy sandbox' - updateData.recoverable = recoverable - } - } else if ( - sandbox.desiredState === SandboxDesiredState.ARCHIVED && - sandbox.backupState === BackupState.COMPLETED - ) { - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `DESTROY_SANDBOX job ${job.id} completed during archiving, marking sandbox ${sandboxId} as ARCHIVED`, - ) - } else if (job.status === JobStatus.FAILED) { - this.logger.warn( - `DESTROY_SANDBOX job ${job.id} failed during archiving for sandbox ${sandboxId}: ${job.errorMessage}. Marking as ARCHIVED since backup is complete.`, - ) - } - updateData.state = SandboxState.ARCHIVED - updateData.errorReason = null - } else { - return - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling DESTROY_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handlePullSnapshotJobCompletion(job: Job): Promise { - const snapshotRef = job.resourceId - const runnerId = job.runnerId - if (!snapshotRef || !runnerId) return - - try { - const snapshotRunner = await this.snapshotRunnerRepository.findOne({ - where: { snapshotRef, runnerId }, - }) - - if (!snapshotRunner) { - this.logger.warn(`SnapshotRunner not found for snapshot ${snapshotRef} on runner ${runnerId}`) - return - } - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `PULL_SNAPSHOT job ${job.id} completed successfully, marking SnapshotRunner ${snapshotRunner.id} as READY`, - ) - snapshotRunner.state = SnapshotRunnerState.READY - snapshotRunner.errorReason = null - - // Check if this is the initial runner for a snapshot and update the snapshot state - const snapshot = await this.snapshotRepository.findOne({ - where: { initialRunnerId: runnerId, ref: snapshotRef }, - }) - if (snapshot && (snapshot.state === SnapshotState.PULLING || snapshot.state === SnapshotState.BUILDING)) { - this.logger.debug(`Marking snapshot ${snapshot.id} as ACTIVE after initial pull completed`) - snapshot.state = SnapshotState.ACTIVE - snapshot.errorReason = null - snapshot.lastUsedAt = new Date() - await this.snapshotRepository.save(snapshot) - } - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`PULL_SNAPSHOT job ${job.id} failed for snapshot ${snapshotRef}: ${job.errorMessage}`) - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = job.errorMessage || 'Failed to pull snapshot' - - // Check if this is the initial runner for a snapshot and update the snapshot state - const snapshot = await this.snapshotRepository.findOne({ - where: { initialRunnerId: runnerId, ref: snapshotRef }, - }) - if (snapshot && snapshot.state === SnapshotState.PULLING) { - this.logger.error(`Marking snapshot ${snapshot.id} as ERROR after initial pull failed`) - snapshot.state = SnapshotState.ERROR - snapshot.errorReason = job.errorMessage || 'Failed to pull snapshot on initial runner' - await this.snapshotRepository.save(snapshot) - } - } - - await this.snapshotRunnerRepository.save(snapshotRunner) - } catch (error) { - this.logger.error(`Error handling PULL_SNAPSHOT job completion for snapshot ${snapshotRef}:`, error) - } - } - - private async handleBuildSnapshotJobCompletion(job: Job): Promise { - const snapshotRef = job.resourceId - const runnerId = job.runnerId - if (!snapshotRef || !runnerId) return - - try { - // For BUILD_SNAPSHOT, find snapshot by buildInfo.snapshotRef - const snapshot = await this.snapshotRepository - .createQueryBuilder('snapshot') - .leftJoinAndSelect('snapshot.buildInfo', 'buildInfo') - .where('snapshot.initialRunnerId = :runnerId', { runnerId }) - .andWhere('buildInfo.snapshotRef = :snapshotRef', { snapshotRef }) - .getOne() - - // Update SnapshotRunner state - const snapshotRunner = await this.snapshotRunnerRepository.findOne({ - where: { snapshotRef, runnerId }, - }) - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug(`BUILD_SNAPSHOT job ${job.id} completed successfully for snapshot ref ${snapshotRef}`) - - if (snapshot?.state === SnapshotState.BUILDING) { - snapshot.state = SnapshotState.ACTIVE - snapshot.errorReason = null - snapshot.lastUsedAt = new Date() - await this.snapshotRepository.save(snapshot) - this.logger.debug(`Marked snapshot ${snapshot.id} as ACTIVE after build completed`) - } - - if (snapshotRunner) { - snapshotRunner.state = SnapshotRunnerState.READY - snapshotRunner.errorReason = null - await this.snapshotRunnerRepository.save(snapshotRunner) - } - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`BUILD_SNAPSHOT job ${job.id} failed for snapshot ref ${snapshotRef}: ${job.errorMessage}`) - - if (snapshot?.state === SnapshotState.BUILDING) { - snapshot.state = SnapshotState.ERROR - snapshot.errorReason = job.errorMessage || 'Failed to build snapshot' - await this.snapshotRepository.save(snapshot) - } - - if (snapshotRunner) { - snapshotRunner.state = SnapshotRunnerState.ERROR - snapshotRunner.errorReason = job.errorMessage || 'Failed to build snapshot' - await this.snapshotRunnerRepository.save(snapshotRunner) - } - } - } catch (error) { - this.logger.error(`Error handling BUILD_SNAPSHOT job completion for snapshot ref ${snapshotRef}:`, error) - } - } - - private async handleRemoveSnapshotJobCompletion(job: Job): Promise { - const snapshotRef = job.resourceId - const runnerId = job.runnerId - if (!snapshotRef || !runnerId) return - - try { - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `REMOVE_SNAPSHOT job ${job.id} completed successfully for snapshot ${snapshotRef} on runner ${runnerId}`, - ) - const affected = await this.snapshotRunnerRepository.delete({ snapshotRef, runnerId }) - if (affected.affected && affected.affected > 0) { - this.logger.debug( - `Removed ${affected.affected} snapshot runners for snapshot ${snapshotRef} on runner ${runnerId}`, - ) - } - } else if (job.status === JobStatus.FAILED) { - this.logger.error( - `REMOVE_SNAPSHOT job ${job.id} failed for snapshot ${snapshotRef} on runner ${runnerId}: ${job.errorMessage}`, - ) - } - } catch (error) { - this.logger.error(`Error handling REMOVE_SNAPSHOT job completion for snapshot ${snapshotRef}:`, error) - } - } - - private async handleCreateBackupJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for CREATE_BACKUP job ${job.id}`) - return - } - - // Parse the job payload to get the snapshot this job was for. - // Old v2 runners may not include snapshot in the payload, so we only - // perform stale-snapshot checks when the field is present. - const jobSnapshot = job.getPayload<{ snapshot?: string }>()?.snapshot - - // Ignore stale backup results if the job's snapshot doesn't match the current DB snapshot. - // Old v2 runners may not include snapshot in the payload — skip this check for them. - if (jobSnapshot && jobSnapshot !== sandbox.backupSnapshot) { - this.logger.warn( - `Ignoring stale backup ${job.status} for sandbox ${sandboxId}: job snapshot ${jobSnapshot} does not match DB snapshot ${sandbox.backupSnapshot}`, - ) - return - } - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `CREATE_BACKUP job ${job.id} completed successfully, marking sandbox ${sandboxId} as BACKUP_COMPLETED`, - ) - Object.assign(updateData, Sandbox.getBackupStateUpdate(sandbox, BackupState.COMPLETED)) - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`CREATE_BACKUP job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - Object.assign( - updateData, - Sandbox.getBackupStateUpdate(sandbox, BackupState.ERROR, undefined, undefined, job.errorMessage), - ) - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling CREATE_BACKUP job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handleRecoverSandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for RECOVER_SANDBOX job ${job.id}`) - return - } - - if (sandbox.desiredState !== SandboxDesiredState.STARTED) { - this.logger.error( - `Sandbox ${sandboxId} is not in desired state STARTED for RECOVER_SANDBOX job ${job.id}. Desired state: ${sandbox.desiredState}`, - ) - return - } - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug( - `RECOVER_SANDBOX job ${job.id} completed successfully, marking sandbox ${sandboxId} as STARTED`, - ) - updateData.state = SandboxState.STARTED - updateData.errorReason = null - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`RECOVER_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - updateData.state = SandboxState.ERROR - updateData.errorReason = job.errorMessage || 'Failed to recover sandbox' - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling RECOVER_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } - - private async handleResizeSandboxJobCompletion(job: Job): Promise { - const sandboxId = job.resourceId - if (!sandboxId) return - - try { - const sandbox = await this.sandboxRepository.findOne({ where: { id: sandboxId } }) - if (!sandbox) { - this.logger.warn(`Sandbox ${sandboxId} not found for RESIZE_SANDBOX job ${job.id}`) - return - } - - if (sandbox.state !== SandboxState.RESIZING) { - this.logger.warn( - `Sandbox ${sandboxId} is not in RESIZING state for RESIZE_SANDBOX job ${job.id}. State: ${sandbox.state}`, - ) - return - } - - // Determine the previous state (STARTED or STOPPED based on desiredState) - const previousState = - sandbox.desiredState === SandboxDesiredState.STARTED - ? SandboxState.STARTED - : sandbox.desiredState === SandboxDesiredState.STOPPED - ? SandboxState.STOPPED - : null - - if (!previousState) { - this.logger.error( - `Sandbox ${sandboxId} has unexpected desiredState ${sandbox.desiredState} for RESIZE_SANDBOX job ${job.id}`, - ) - return - } - - // Calculate deltas before updating sandbox - const payload = job.payload as { cpu?: number; memory?: number; disk?: number } - - // For cold resize (previousState === STOPPED), cpu/memory don't affect org quota. - const isHotResize = previousState === SandboxState.STARTED - const cpuDeltaForQuota = isHotResize ? (payload.cpu ?? sandbox.cpu) - sandbox.cpu : 0 - const memDeltaForQuota = isHotResize ? (payload.memory ?? sandbox.mem) - sandbox.mem : 0 - const diskDeltaForQuota = (payload.disk ?? sandbox.disk) - sandbox.disk // Disk only increases - - const updateData: Partial = {} - - if (job.status === JobStatus.COMPLETED) { - this.logger.debug(`RESIZE_SANDBOX job ${job.id} completed successfully for sandbox ${sandboxId}`) - - // Update sandbox resources - updateData.cpu = payload.cpu ?? sandbox.cpu - updateData.mem = payload.memory ?? sandbox.mem - updateData.disk = payload.disk ?? sandbox.disk - updateData.state = previousState - - // Apply usage change (handles both positive and negative deltas) - await this.organizationUsageService.applyResizeUsageChange( - sandbox.organizationId, - sandbox.region, - cpuDeltaForQuota, - memDeltaForQuota, - diskDeltaForQuota, - ) - return - } else if (job.status === JobStatus.FAILED) { - this.logger.error(`RESIZE_SANDBOX job ${job.id} failed for sandbox ${sandboxId}: ${job.errorMessage}`) - - // Rollback pending usage (all deltas were tracked, including negative) - await this.organizationUsageService.decrementPendingSandboxUsage( - sandbox.organizationId, - sandbox.region, - cpuDeltaForQuota !== 0 ? cpuDeltaForQuota : undefined, - memDeltaForQuota !== 0 ? memDeltaForQuota : undefined, - diskDeltaForQuota !== 0 ? diskDeltaForQuota : undefined, - ) - - updateData.state = previousState - } - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandbox }) - } catch (error) { - this.logger.error(`Error handling RESIZE_SANDBOX job completion for sandbox ${sandboxId}:`, error) - } - } -} diff --git a/apps/api/src/sandbox/services/proxy-cache-invalidation.service.ts b/apps/api/src/sandbox/services/proxy-cache-invalidation.service.ts deleted file mode 100644 index 8b9d36334..000000000 --- a/apps/api/src/sandbox/services/proxy-cache-invalidation.service.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Injectable, Logger } from '@nestjs/common' -import { OnEvent } from '@nestjs/event-emitter' -import Redis from 'ioredis' - -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxArchivedEvent } from '../events/sandbox-archived.event' - -@Injectable() -export class ProxyCacheInvalidationService { - private readonly logger = new Logger(ProxyCacheInvalidationService.name) - private static readonly RUNNER_INFO_CACHE_PREFIX = 'proxy:sandbox-runner-info:' - - constructor(@InjectRedis() private readonly redis: Redis) {} - - @OnEvent(SandboxEvents.ARCHIVED) - async handleSandboxArchived(event: SandboxArchivedEvent): Promise { - await this.invalidateRunnerCache(event.sandbox.id) - } - - private async invalidateRunnerCache(sandboxId: string): Promise { - try { - await this.redis.del(`${ProxyCacheInvalidationService.RUNNER_INFO_CACHE_PREFIX}${sandboxId}`) - this.logger.debug(`Invalidated sandbox runner cache for ${sandboxId}`) - } catch (error) { - this.logger.warn(`Failed to invalidate runner cache for sandbox ${sandboxId}: ${error.message}`) - } - } -} diff --git a/apps/api/src/sandbox/services/sandbox-activity.service.ts b/apps/api/src/sandbox/services/sandbox-activity.service.ts deleted file mode 100644 index 7c56d4759..000000000 --- a/apps/api/src/sandbox/services/sandbox-activity.service.ts +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { InjectRedis } from '@nestjs-modules/ioredis' -import Redis from 'ioredis' -import { InjectDataSource } from '@nestjs/typeorm' -import { DataSource, IsNull, Raw } from 'typeorm' -import { Cron, CronExpression } from '@nestjs/schedule' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { SandboxLastActivity } from '../entities/sandbox-last-activity.entity' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { TypedConfigService } from '../../config/typed-config.service' - -const REDIS_ACTIVITY_KEY = 'sandbox:activity' - -interface SandboxActivityUpdate { - sandboxId: string - lastActivityAt: Date -} - -@Injectable() -export class SandboxActivityService { - private readonly logger = new Logger(SandboxActivityService.name) - - constructor( - @InjectRedis() private readonly redis: Redis, - @InjectDataSource() private readonly dataSource: DataSource, - private readonly redisLockProvider: RedisLockProvider, - private readonly configService: TypedConfigService, - ) {} - - /** - * Buffers a last activity timestamp in Redis (throttled to once per configured throttle TTL). - * - * Relies on the periodic flush to the database. - */ - async updateLastActivityAt(sandboxId: string, lastActivityAt: Date): Promise { - const lockKey = `sandbox:update-last-activity:${sandboxId}` - const acquired = await this.redisLockProvider.lock( - lockKey, - this.configService.getOrThrow('sandboxActivity.throttleTtlSeconds'), - ) - if (!acquired) { - return - } - await this.redis.zadd(REDIS_ACTIVITY_KEY, lastActivityAt.getTime(), sandboxId) - } - - /** - * Read the last activity timestamp for a sandbox. - * - * Checks Redis buffer first, falls back to the database. - */ - async getLastActivityAt(sandboxId: string): Promise { - const score = await this.redis.zscore(REDIS_ACTIVITY_KEY, sandboxId) - if (score !== null) { - return new Date(Number(score)) - } - - const row = await this.dataSource.getRepository(SandboxLastActivity).findOne({ where: { sandboxId } }) - - return row?.lastActivityAt ?? null - } - - /** - * Flush buffered activity timestamps from Redis to the database in bulk. - * Processes entries in batches to avoid oversized transactions. - * - * Frequency must be < 1min to prevent unintended auto-lifecycle actions. - */ - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'flush-activity-to-db' }) - @LogExecution('flush-activity-to-db') - @WithInstrumentation() - async flushActivityToDb(): Promise { - const lockKey = 'flush-activity-to-db-lock' - const lockTtl = 30 - const acquired = await this.redisLockProvider.lock(lockKey, lockTtl) - if (!acquired) { - return - } - - try { - let totalFlushed = 0 - - const batchSize = this.configService.getOrThrow('sandboxActivity.flushBatchSize') - const maxScore = Date.now() - - const entries = await this.redis.zrangebyscore(REDIS_ACTIVITY_KEY, '-inf', maxScore, 'WITHSCORES') - - if (entries.length === 0) { - return - } - - const updates: SandboxActivityUpdate[] = [] - for (let i = 0; i < entries.length; i += 2) { - updates.push({ - sandboxId: entries[i], - lastActivityAt: new Date(Number(entries[i + 1])), - }) - } - - for (let offset = 0; offset < updates.length; offset += batchSize) { - const batch = updates.slice(offset, offset + batchSize) - await this.bulkUpsertActivity(batch) - totalFlushed += batch.length - } - - await this.redis.zremrangebyscore(REDIS_ACTIVITY_KEY, '-inf', maxScore) - - if (totalFlushed > 0) { - this.logger.debug(`Flushed ${totalFlushed} activity timestamps to the database`) - } - } catch (error) { - this.logger.error('Error flushing activity timestamps to the database:', error) - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - /** - * Builds a query to upsert activity timestamps into the database. - * - * Uses a conditional upsert that only updates when the incoming timestamp is newer, preventing updates to stale buffered values. - */ - private buildUpsertQuery(values: SandboxActivityUpdate | SandboxActivityUpdate[]) { - return this.dataSource - .createQueryBuilder() - .insert() - .into(SandboxLastActivity) - .values(values) - .orUpdate(['lastActivityAt'], ['sandboxId'], { - overwriteCondition: { - where: [ - { lastActivityAt: IsNull() }, - { lastActivityAt: Raw((alias) => `${alias} < EXCLUDED."lastActivityAt"`) }, - ], - }, - }) - } - - /** - * Bulk upserts activity timestamps into the database. - * - * In case of FK violations, falls back to individual upserts to skip deleted sandbox(es). - */ - private async bulkUpsertActivity(updates: SandboxActivityUpdate[]): Promise { - if (updates.length === 0) { - this.logger.debug('No activity updates to flush') - return - } - - try { - await this.buildUpsertQuery(updates).execute() - } catch (bulkUpsertError) { - if (bulkUpsertError.code === '23503') { - this.logger.warn( - 'Bulk upsert for activity timestamps failed with FK violation, falling back to individual upserts', - ) - for (const update of updates) { - try { - await this.buildUpsertQuery(update).execute() - } catch (error) { - if (error.code === '23503') { - this.logger.warn(`Skipping activity flush for sandbox ${update.sandboxId} (deleted)`) - } else { - throw error - } - } - } - } else { - throw bulkUpsertError - } - } - } -} diff --git a/apps/api/src/sandbox/services/sandbox-lookup-cache-invalidation.service.ts b/apps/api/src/sandbox/services/sandbox-lookup-cache-invalidation.service.ts deleted file mode 100644 index 6bffc4e6e..000000000 --- a/apps/api/src/sandbox/services/sandbox-lookup-cache-invalidation.service.ts +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger } from '@nestjs/common' -import { DataSource } from 'typeorm' -import { - sandboxLookupCacheKeyByAuthToken, - sandboxLookupCacheKeyById, - sandboxLookupCacheKeyByName, - sandboxOrgIdCacheKeyById, - sandboxOrgIdCacheKeyByName, -} from '../utils/sandbox-lookup-cache.util' - -type InvalidateSandboxLookupCacheArgs = - | { - sandboxId: string - organizationId: string - name: string - previousOrganizationId?: string | null - previousName?: string | null - } - | { - authToken: string - } - -@Injectable() -export class SandboxLookupCacheInvalidationService { - private readonly logger = new Logger(SandboxLookupCacheInvalidationService.name) - - constructor(private readonly dataSource: DataSource) {} - - invalidate(args: InvalidateSandboxLookupCacheArgs): void { - const cache = this.dataSource.queryResultCache - if (!cache) { - return - } - - if ('authToken' in args) { - cache - .remove([sandboxLookupCacheKeyByAuthToken({ authToken: args.authToken })]) - .then(() => this.logger.debug(`Invalidated sandbox lookup cache for authToken ${args.authToken}`)) - .catch((error) => - this.logger.warn( - `Failed to invalidate sandbox lookup cache for authToken ${args.authToken}: ${error instanceof Error ? error.message : String(error)}`, - ), - ) - return - } - - const organizationIds = Array.from( - new Set( - [args.organizationId, args.previousOrganizationId].filter((id): id is string => - Boolean(id && id.trim().length > 0), - ), - ), - ) - const names = Array.from( - new Set([args.name, args.previousName].filter((n): n is string => Boolean(n && n.trim().length > 0))), - ) - - const cacheIds: string[] = [] - for (const organizationId of organizationIds) { - for (const returnDestroyed of [false, true]) { - cacheIds.push( - sandboxLookupCacheKeyById({ - organizationId, - returnDestroyed, - sandboxId: args.sandboxId, - }), - ) - for (const sandboxName of names) { - cacheIds.push( - sandboxLookupCacheKeyByName({ - organizationId, - returnDestroyed, - sandboxName, - }), - ) - } - } - } - - if (cacheIds.length === 0) { - return - } - - cache - .remove(cacheIds) - .then(() => this.logger.debug(`Invalidated sandbox lookup cache for ${args.sandboxId}`)) - .catch((error) => - this.logger.warn( - `Failed to invalidate sandbox lookup cache for ${args.sandboxId}: ${error instanceof Error ? error.message : String(error)}`, - ), - ) - } - - invalidateOrgId(args: { - sandboxId: string - organizationId: string - name: string - previousOrganizationId?: string | null - previousName?: string | null - }): void { - const cache = this.dataSource.queryResultCache - if (!cache) { - return - } - - const organizationIds = Array.from( - new Set( - [args.organizationId, args.previousOrganizationId].filter((id): id is string => - Boolean(id && id.trim().length > 0), - ), - ), - ) - const names = Array.from( - new Set([args.name, args.previousName].filter((n): n is string => Boolean(n && n.trim().length > 0))), - ) - - const cacheIds: string[] = [] - for (const organizationId of organizationIds) { - cacheIds.push( - sandboxOrgIdCacheKeyById({ - organizationId, - sandboxId: args.sandboxId, - }), - ) - for (const sandboxName of names) { - cacheIds.push( - sandboxOrgIdCacheKeyByName({ - organizationId, - sandboxName, - }), - ) - } - } - - // Also invalidate the "no org" variants (when organizationId was not provided to getOrganizationId) - cacheIds.push(sandboxOrgIdCacheKeyById({ sandboxId: args.sandboxId })) - for (const sandboxName of names) { - cacheIds.push(sandboxOrgIdCacheKeyByName({ sandboxName })) - } - - cache - .remove(cacheIds) - .then(() => this.logger.debug(`Invalidated sandbox orgId cache for ${args.sandboxId}`)) - .catch((error) => - this.logger.warn( - `Failed to invalidate sandbox orgId cache for ${args.sandboxId}: ${error instanceof Error ? error.message : String(error)}`, - ), - ) - } -} diff --git a/apps/api/src/sandbox/services/sandbox-state-waiter.service.ts b/apps/api/src/sandbox/services/sandbox-state-waiter.service.ts deleted file mode 100644 index 8b7d54620..000000000 --- a/apps/api/src/sandbox/services/sandbox-state-waiter.service.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common' -import Redis from 'ioredis' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { SANDBOX_EVENT_CHANNEL } from '../../common/constants/constants' -import { SandboxDto } from '../dto/sandbox.dto' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SandboxStateUpdatedEvent } from '../events/sandbox-state-updated.event' -import { SandboxService } from './sandbox.service' - -@Injectable() -export class SandboxStateWaiterService implements OnModuleDestroy { - private readonly logger = new Logger(SandboxStateWaiterService.name) - private readonly callbacks = new Map void>() - private readonly redisSubscriber: Redis - - constructor( - private readonly sandboxService: SandboxService, - @InjectRedis() private readonly redis: Redis, - ) { - this.redisSubscriber = this.redis.duplicate() - this.redisSubscriber.subscribe(SANDBOX_EVENT_CHANNEL) - this.redisSubscriber.on('message', (channel, message) => { - if (channel !== SANDBOX_EVENT_CHANNEL) { - return - } - - try { - const event = JSON.parse(message) as SandboxStateUpdatedEvent - const callback = this.callbacks.get(event.sandbox.id) - if (callback) { - callback(event) - } - } catch (error) { - this.logger.error('Failed to parse sandbox state updated event:', error) - } - }) - } - - async onModuleDestroy() { - await this.redisSubscriber.quit() - } - - async waitForStarted( - sandboxId: string, - organizationId: string, - timeoutSeconds: number, - ): Promise { - const current = await this.sandboxService.findOneByIdOrName( - sandboxId, - organizationId, - ) - - if (current.state === SandboxState.STARTED) { - return this.sandboxService.toSandboxDto(current) - } - - this.assertNotFailed(current.state, current.errorReason) - - return new Promise((resolve, reject) => { - let latestSandbox = current - let timeout: NodeJS.Timeout - - const finish = async (sandbox = latestSandbox) => { - this.callbacks.delete(sandboxId) - clearTimeout(timeout) - resolve(await this.sandboxService.toSandboxDto(sandbox)) - } - - const fail = (error: Error) => { - this.callbacks.delete(sandboxId) - clearTimeout(timeout) - reject(error) - } - - const handleStateUpdated = (event: SandboxStateUpdatedEvent) => { - if (event.sandbox.id !== sandboxId) { - return - } - - latestSandbox = event.sandbox - - if (event.sandbox.state === SandboxState.STARTED) { - finish(event.sandbox).catch(fail) - return - } - - try { - this.assertNotFailed(event.sandbox.state, event.sandbox.errorReason) - } catch (error) { - fail(error) - } - } - - this.callbacks.set(sandboxId, handleStateUpdated) - - this.sandboxService - .findOneByIdOrName(sandboxId, organizationId) - .then((sandbox) => { - latestSandbox = sandbox - if (sandbox.state === SandboxState.STARTED) { - return finish(sandbox) - } - this.assertNotFailed(sandbox.state, sandbox.errorReason) - }) - .catch(fail) - - timeout = setTimeout(() => { - finish().catch(fail) - }, timeoutSeconds * 1000) - }) - } - - private assertNotFailed(state: SandboxState, errorReason?: string | null) { - if (state === SandboxState.ERROR || state === SandboxState.BUILD_FAILED) { - throw new BadRequestError( - `Sandbox failed to start: ${errorReason || 'Unknown error'}`, - ) - } - } -} diff --git a/apps/api/src/sandbox/services/sandbox-warm-pool.service.ts b/apps/api/src/sandbox/services/sandbox-warm-pool.service.ts deleted file mode 100644 index 9de133338..000000000 --- a/apps/api/src/sandbox/services/sandbox-warm-pool.service.ts +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Inject, Injectable, Logger } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Cron, CronExpression } from '@nestjs/schedule' -import { FindOptionsWhere, In, MoreThan, Not, Repository } from 'typeorm' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { Sandbox } from '../entities/sandbox.entity' -import { SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/sandbox.constants' -import { WarmPool } from '../entities/warm-pool.entity' -import { EventEmitter2, OnEvent } from '@nestjs/event-emitter' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxOrganizationUpdatedEvent } from '../events/sandbox-organization-updated.event' -import { ConfigService } from '@nestjs/config' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { SandboxClass } from '../enums/sandbox-class.enum' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { SandboxState } from '../enums/sandbox-state.enum' -import { Runner } from '../entities/runner.entity' -import { WarmPoolTopUpRequested } from '../events/warmpool-topup-requested.event' -import { WarmPoolEvents } from '../constants/warmpool-events.constants' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { isValidUuid } from '../../common/utils/uuid' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' - -export type FetchWarmPoolSandboxParams = { - snapshot: string | Snapshot - target: string - class: SandboxClass - cpu: number - mem: number - disk: number - gpu: number - osUser: string - env: { [key: string]: string } - organizationId: string - state: string -} - -@Injectable() -export class SandboxWarmPoolService { - private readonly logger = new Logger(SandboxWarmPoolService.name) - - constructor( - @InjectRepository(WarmPool) - private readonly warmPoolRepository: Repository, - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(Runner) - private readonly runnerRepository: Repository, - private readonly redisLockProvider: RedisLockProvider, - private readonly configService: ConfigService, - @Inject(EventEmitter2) - private eventEmitter: EventEmitter2, - @InjectRedis() private readonly redis: Redis, - ) {} - - // on init - async onApplicationBootstrap() { - // await this.adHocBackupCheck() - } - - async fetchWarmPoolSandbox(params: FetchWarmPoolSandboxParams): Promise { - // validate snapshot - let snapshot: Snapshot | null = null - if (typeof params.snapshot === 'string') { - const sandboxSnapshot = params.snapshot || this.configService.get('DEFAULT_SNAPSHOT') - - const snapshotFilter: FindOptionsWhere[] = [ - { organizationId: params.organizationId, name: sandboxSnapshot, state: SnapshotState.ACTIVE }, - { general: true, name: sandboxSnapshot, state: SnapshotState.ACTIVE }, - ] - - if (isValidUuid(sandboxSnapshot)) { - snapshotFilter.push( - { organizationId: params.organizationId, id: sandboxSnapshot, state: SnapshotState.ACTIVE }, - { general: true, id: sandboxSnapshot, state: SnapshotState.ACTIVE }, - ) - } - - snapshot = await this.snapshotRepository.findOne({ - where: snapshotFilter, - }) - if (!snapshot) { - throw new BadRequestError( - `Snapshot ${sandboxSnapshot} not found. Did you add it through the BoxLite Dashboard?`, - ) - } - } else { - snapshot = params.snapshot - } - - // check if sandbox is warm pool - const warmPoolItem = await this.warmPoolRepository.findOne({ - where: { - snapshot: snapshot.name, - target: params.target, - class: params.class, - cpu: params.cpu, - mem: params.mem, - disk: params.disk, - gpu: params.gpu, - osUser: params.osUser, - env: params.env, - pool: MoreThan(0), - }, - }) - if (warmPoolItem) { - const availabilityScoreThreshold = this.configService.getOrThrow('runnerScore.thresholds.availability') - - // Build subquery to find excluded runners (unschedulable OR low score) - const excludedRunnersSubquery = this.runnerRepository - .createQueryBuilder('runner') - .select('runner.id') - .where('runner.region = :region') - .andWhere('(runner.unschedulable = true OR runner.availabilityScore < :scoreThreshold)') - - const queryBuilder = this.sandboxRepository - .createQueryBuilder('sandbox') - .where('sandbox.class = :class', { class: warmPoolItem.class }) - .andWhere('sandbox.cpu = :cpu', { cpu: warmPoolItem.cpu }) - .andWhere('sandbox.mem = :mem', { mem: warmPoolItem.mem }) - .andWhere('sandbox.disk = :disk', { disk: warmPoolItem.disk }) - .andWhere('sandbox.snapshot = :snapshot', { snapshot: snapshot.name }) - .andWhere('sandbox.osUser = :osUser', { osUser: warmPoolItem.osUser }) - .andWhere('sandbox.env = :env', { env: warmPoolItem.env }) - .andWhere('sandbox.organizationId = :organizationId', { - organizationId: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - }) - .andWhere('sandbox.region = :region', { region: warmPoolItem.target }) - .andWhere('sandbox.state = :state', { state: SandboxState.STARTED }) - .andWhere(`sandbox.runnerId NOT IN (${excludedRunnersSubquery.getQuery()})`) - .setParameters({ - region: warmPoolItem.target, - scoreThreshold: availabilityScoreThreshold, - }) - - const candidateLimit = this.configService.getOrThrow('warmPool.candidateLimit') - const warmPoolSandboxes = await queryBuilder.orderBy('RANDOM()').take(candidateLimit).getMany() - - // make sure we only release warm pool sandbox once - let warmPoolSandbox: Sandbox | null = null - for (const sandbox of warmPoolSandboxes) { - const lockKey = `sandbox-warm-pool-${sandbox.id}` - if (!(await this.redisLockProvider.lock(lockKey, 10))) { - continue - } - - warmPoolSandbox = sandbox - break - } - - return warmPoolSandbox - } - - // no warm pool config exists for this snapshot — cache it so callers can skip - await this.redis.set(`warm-pool:skip:${snapshot.id}`, '1', 'EX', 60) - - return null - } - - // todo: make frequency configurable or more efficient - @Cron(CronExpression.EVERY_10_SECONDS, { name: 'warm-pool-check' }) - @LogExecution('warm-pool-check') - @WithInstrumentation() - async warmPoolCheck(): Promise { - const warmPoolItems = await this.warmPoolRepository.find() - - await Promise.all( - warmPoolItems.map(async (warmPoolItem) => { - const lockKey = `warm-pool-lock-${warmPoolItem.id}` - if (!(await this.redisLockProvider.lock(lockKey, 720))) { - return - } - - const sandboxCount = await this.sandboxRepository.count({ - where: { - snapshot: warmPoolItem.snapshot, - organizationId: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - class: warmPoolItem.class, - osUser: warmPoolItem.osUser, - env: warmPoolItem.env, - region: warmPoolItem.target, - cpu: warmPoolItem.cpu, - gpu: warmPoolItem.gpu, - mem: warmPoolItem.mem, - disk: warmPoolItem.disk, - desiredState: SandboxDesiredState.STARTED, - state: Not(In([SandboxState.ERROR, SandboxState.BUILD_FAILED])), - }, - }) - - const missingCount = warmPoolItem.pool - sandboxCount - if (missingCount > 0) { - const promises = [] - this.logger.debug(`Creating ${missingCount} sandboxes for warm pool id ${warmPoolItem.id}`) - - for (let i = 0; i < missingCount; i++) { - promises.push( - this.eventEmitter.emitAsync(WarmPoolEvents.TOPUP_REQUESTED, new WarmPoolTopUpRequested(warmPoolItem)), - ) - } - - // Wait for all promises to settle before releasing the lock. Otherwise, another worker could start creating sandboxes - await Promise.allSettled(promises) - } - - await this.redisLockProvider.unlock(lockKey) - }), - ) - } - - @OnEvent(SandboxEvents.ORGANIZATION_UPDATED) - async handleSandboxOrganizationUpdated(event: SandboxOrganizationUpdatedEvent) { - if (event.newOrganizationId === SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION) { - return - } - const warmPoolItem = await this.warmPoolRepository.findOne({ - where: { - snapshot: event.sandbox.snapshot, - class: event.sandbox.class, - cpu: event.sandbox.cpu, - mem: event.sandbox.mem, - disk: event.sandbox.disk, - target: event.sandbox.region, - env: event.sandbox.env, - gpu: event.sandbox.gpu, - osUser: event.sandbox.osUser, - }, - }) - - if (!warmPoolItem) { - return - } - - const sandboxCount = await this.sandboxRepository.count({ - where: { - snapshot: warmPoolItem.snapshot, - organizationId: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - class: warmPoolItem.class, - osUser: warmPoolItem.osUser, - env: warmPoolItem.env, - region: warmPoolItem.target, - cpu: warmPoolItem.cpu, - gpu: warmPoolItem.gpu, - mem: warmPoolItem.mem, - disk: warmPoolItem.disk, - desiredState: SandboxDesiredState.STARTED, - state: Not(In([SandboxState.ERROR, SandboxState.BUILD_FAILED])), - }, - }) - - if (warmPoolItem.pool <= sandboxCount) { - return - } - - if (warmPoolItem) { - this.eventEmitter.emit(WarmPoolEvents.TOPUP_REQUESTED, new WarmPoolTopUpRequested(warmPoolItem)) - } - } -} diff --git a/apps/api/src/sandbox/services/sandbox.service.ts b/apps/api/src/sandbox/services/sandbox.service.ts deleted file mode 100644 index 68427cc04..000000000 --- a/apps/api/src/sandbox/services/sandbox.service.ts +++ /dev/null @@ -1,2186 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ForbiddenException, Injectable, Logger, NotFoundException, ConflictException } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Not, Repository, LessThan, In, JsonContains, FindOptionsWhere, ILike } from 'typeorm' -import { Sandbox } from '../entities/sandbox.entity' -import { CreateSandboxDto } from '../dto/create-sandbox.dto' -import { ResizeSandboxDto } from '../dto/resize-sandbox.dto' -import { SandboxState } from '../enums/sandbox-state.enum' -import { SandboxClass } from '../enums/sandbox-class.enum' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' -import { RunnerService } from './runner.service' -import { SandboxError } from '../../exceptions/sandbox-error.exception' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { Cron, CronExpression } from '@nestjs/schedule' -import { BackupState } from '../enums/backup-state.enum' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../constants/sandbox.constants' -import { SandboxWarmPoolService } from './sandbox-warm-pool.service' -import { EventEmitter2, OnEvent } from '@nestjs/event-emitter' -import { WarmPoolEvents } from '../constants/warmpool-events.constants' -import { WarmPoolTopUpRequested } from '../events/warmpool-topup-requested.event' -import { Runner } from '../entities/runner.entity' -import { Organization } from '../../organization/entities/organization.entity' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxStateUpdatedEvent } from '../events/sandbox-state-updated.event' -import { BuildInfo } from '../entities/build-info.entity' -import { generateBuildInfoHash as generateBuildSnapshotRef } from '../entities/build-info.entity' -import { SandboxBackupCreatedEvent } from '../events/sandbox-backup-created.event' -import { SandboxDestroyedEvent } from '../events/sandbox-destroyed.event' -import { SandboxStartedEvent } from '../events/sandbox-started.event' -import { SandboxStoppedEvent } from '../events/sandbox-stopped.event' -import { SandboxArchivedEvent } from '../events/sandbox-archived.event' -import { OrganizationService } from '../../organization/services/organization.service' -import { OrganizationEvents } from '../../organization/constants/organization-events.constant' -import { OrganizationSuspendedSandboxStoppedEvent } from '../../organization/events/organization-suspended-sandbox-stopped.event' -import { TypedConfigService } from '../../config/typed-config.service' -import { WarmPool } from '../entities/warm-pool.entity' -import { SandboxDto, SandboxVolume } from '../dto/sandbox.dto' -import { isValidUuid } from '../../common/utils/uuid' -import { RunnerAdapterFactory } from '../runner-adapter/runnerAdapter' -import { validateNetworkAllowList } from '../utils/network-validation.util' -import { OrganizationUsageService } from '../../organization/services/organization-usage.service' -import { SshAccess } from '../entities/ssh-access.entity' -import { SshAccessDto, SshAccessValidationDto } from '../dto/ssh-access.dto' -import { VolumeService } from './volume.service' -import { PaginatedList } from '../../common/interfaces/paginated-list.interface' -import { - SandboxSortField, - SandboxSortDirection, - DEFAULT_SANDBOX_SORT_FIELD, - DEFAULT_SANDBOX_SORT_DIRECTION, -} from '../dto/list-sandboxes-query.dto' -import { createRangeFilter } from '../../common/utils/range-filter' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { - UPGRADE_TIER_MESSAGE, - ARCHIVE_SANDBOXES_MESSAGE, - PER_SANDBOX_LIMIT_MESSAGE, -} from '../../common/constants/error-messages' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { customAlphabet as customNanoid, nanoid, urlAlphabet } from 'nanoid' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { validateMountPaths, validateSubpaths } from '../utils/volume-mount-path-validation.util' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { PortPreviewUrlDto, SignedPortPreviewUrlDto } from '../dto/port-preview-url.dto' -import { RegionService } from '../../region/services/region.service' -import { DefaultRegionRequiredException } from '../../organization/exceptions/DefaultRegionRequiredException' -import { SnapshotService } from './snapshot.service' -import { RegionType } from '../../region/enums/region-type.enum' -import { SandboxCreatedEvent } from '../events/sandbox-create.event' -import { InjectRedis } from '@nestjs-modules/ioredis' -import { Redis } from 'ioredis' -import { - SANDBOX_LOOKUP_CACHE_TTL_MS, - SANDBOX_ORG_ID_CACHE_TTL_MS, - TOOLBOX_PROXY_URL_CACHE_TTL_S, - sandboxLookupCacheKeyById, - sandboxLookupCacheKeyByName, - sandboxOrgIdCacheKeyById, - sandboxOrgIdCacheKeyByName, - toolboxProxyUrlCacheKey, -} from '../utils/sandbox-lookup-cache.util' -import { SandboxLookupCacheInvalidationService } from './sandbox-lookup-cache-invalidation.service' -import { Region } from '../../region/entities/region.entity' -import { SandboxActivityService } from './sandbox-activity.service' - -const DEFAULT_CPU = 1 -const DEFAULT_MEMORY = 1 -const DEFAULT_DISK = 3 -const DEFAULT_GPU = 0 - -@Injectable() -export class SandboxService { - private readonly logger = new Logger(SandboxService.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(Runner) - private readonly runnerRepository: Repository, - @InjectRepository(BuildInfo) - private readonly buildInfoRepository: Repository, - @InjectRepository(SshAccess) - private readonly sshAccessRepository: Repository, - private readonly runnerService: RunnerService, - private readonly volumeService: VolumeService, - private readonly configService: TypedConfigService, - private readonly warmPoolService: SandboxWarmPoolService, - private readonly eventEmitter: EventEmitter2, - private readonly organizationService: OrganizationService, - private readonly runnerAdapterFactory: RunnerAdapterFactory, - private readonly organizationUsageService: OrganizationUsageService, - private readonly redisLockProvider: RedisLockProvider, - @InjectRedis() private readonly redis: Redis, - private readonly regionService: RegionService, - private readonly snapshotService: SnapshotService, - private readonly sandboxLookupCacheInvalidationService: SandboxLookupCacheInvalidationService, - private readonly sandboxActivityService: SandboxActivityService, - ) {} - - protected getLockKey(id: string): string { - return `sandbox:${id}:state-change` - } - - private assertSandboxNotErrored(sandbox: Sandbox): void { - if ([SandboxState.ERROR, SandboxState.BUILD_FAILED].includes(sandbox.state)) { - throw new SandboxError('Sandbox is in an errored state') - } - } - - private async validateOrganizationQuotas( - organization: Organization, - region: Region, - cpu: number, - memory: number, - disk: number, - excludeSandboxId?: string, - ): Promise<{ - pendingCpuIncremented: boolean - pendingMemoryIncremented: boolean - pendingDiskIncremented: boolean - }> { - // validate per-sandbox quotas - if (cpu > organization.maxCpuPerSandbox) { - throw new ForbiddenException( - `CPU request ${cpu} exceeds maximum allowed per sandbox (${organization.maxCpuPerSandbox}).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (memory > organization.maxMemoryPerSandbox) { - throw new ForbiddenException( - `Memory request ${memory}GB exceeds maximum allowed per sandbox (${organization.maxMemoryPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (disk > organization.maxDiskPerSandbox) { - throw new ForbiddenException( - `Disk request ${disk}GB exceeds maximum allowed per sandbox (${organization.maxDiskPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - - // e.g. region belonging to an organization - if (!region.enforceQuotas) { - return { - pendingCpuIncremented: false, - pendingMemoryIncremented: false, - pendingDiskIncremented: false, - } - } - - const regionQuota = await this.organizationService.getRegionQuota(organization.id, region.id) - - if (!regionQuota) { - if (region.regionType === RegionType.SHARED) { - // region is public, but the organization does not have a quota for it - throw new ForbiddenException(`Region ${region.id} is not available to the organization`) - } else { - // region is not public, respond as if the region was not found - throw new NotFoundException('Region not found') - } - } - - // validate usage quotas - const { - cpuIncremented: pendingCpuIncremented, - memoryIncremented: pendingMemoryIncremented, - diskIncremented: pendingDiskIncremented, - } = await this.organizationUsageService.incrementPendingSandboxUsage( - organization.id, - region.id, - cpu, - memory, - disk, - excludeSandboxId, - ) - - const usageOverview = await this.organizationUsageService.getSandboxUsageOverview( - organization.id, - region.id, - excludeSandboxId, - ) - - try { - const upgradeTierMessage = UPGRADE_TIER_MESSAGE(this.configService.getOrThrow('dashboardUrl')) - - if (usageOverview.currentCpuUsage + usageOverview.pendingCpuUsage > regionQuota.totalCpuQuota) { - throw new ForbiddenException( - `Total CPU limit exceeded. Maximum allowed: ${regionQuota.totalCpuQuota}.\n${upgradeTierMessage}`, - ) - } - - if (usageOverview.currentMemoryUsage + usageOverview.pendingMemoryUsage > regionQuota.totalMemoryQuota) { - throw new ForbiddenException( - `Total memory limit exceeded. Maximum allowed: ${regionQuota.totalMemoryQuota}GiB.\n${upgradeTierMessage}`, - ) - } - - if (usageOverview.currentDiskUsage + usageOverview.pendingDiskUsage > regionQuota.totalDiskQuota) { - throw new ForbiddenException( - `Total disk limit exceeded. Maximum allowed: ${regionQuota.totalDiskQuota}GiB.\n${ARCHIVE_SANDBOXES_MESSAGE}\n${upgradeTierMessage}`, - ) - } - } catch (error) { - await this.rollbackPendingUsage( - organization.id, - region.id, - pendingCpuIncremented ? cpu : undefined, - pendingMemoryIncremented ? memory : undefined, - pendingDiskIncremented ? disk : undefined, - ) - throw error - } - - return { - pendingCpuIncremented, - pendingMemoryIncremented, - pendingDiskIncremented, - } - } - - async rollbackPendingUsage( - organizationId: string, - regionId: string, - pendingCpuIncrement?: number, - pendingMemoryIncrement?: number, - pendingDiskIncrement?: number, - ): Promise { - if (!pendingCpuIncrement && !pendingMemoryIncrement && !pendingDiskIncrement) { - return - } - - try { - await this.organizationUsageService.decrementPendingSandboxUsage( - organizationId, - regionId, - pendingCpuIncrement, - pendingMemoryIncrement, - pendingDiskIncrement, - ) - } catch (error) { - this.logger.error(`Error rolling back pending sandbox usage: ${error}`) - } - } - - async archive(sandboxIdOrName: string, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - this.assertSandboxNotErrored(sandbox) - - if (String(sandbox.state) !== String(sandbox.desiredState)) { - throw new SandboxError('State change in progress') - } - - if (sandbox.state !== SandboxState.STOPPED) { - throw new SandboxError('Sandbox is not stopped') - } - - if (sandbox.pending) { - throw new SandboxError('Sandbox state change in progress') - } - - if (sandbox.autoDeleteInterval === 0) { - throw new SandboxError('Ephemeral sandboxes cannot be archived') - } - - const updateData: Partial = { - state: SandboxState.ARCHIVING, - desiredState: SandboxDesiredState.ARCHIVED, - } - - const updatedSandbox = await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: SandboxState.STOPPED }, - }) - - this.eventEmitter.emit(SandboxEvents.ARCHIVED, new SandboxArchivedEvent(updatedSandbox)) - return updatedSandbox - } - - async createForWarmPool(warmPoolItem: WarmPool): Promise { - const sandbox = new Sandbox(warmPoolItem.target) - - sandbox.organizationId = SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION - - sandbox.class = warmPoolItem.class - sandbox.snapshot = warmPoolItem.snapshot - // TODO: default user should be configurable - sandbox.osUser = 'boxlite' - sandbox.env = warmPoolItem.env || {} - - sandbox.cpu = warmPoolItem.cpu - sandbox.gpu = warmPoolItem.gpu - sandbox.mem = warmPoolItem.mem - sandbox.disk = warmPoolItem.disk - - const snapshot = await this.snapshotRepository.findOne({ - where: [ - { organizationId: sandbox.organizationId, name: sandbox.snapshot, state: SnapshotState.ACTIVE }, - { general: true, name: sandbox.snapshot, state: SnapshotState.ACTIVE }, - ], - }) - if (!snapshot) { - throw new BadRequestError(`Snapshot ${sandbox.snapshot} not found while creating warm pool sandbox`) - } - - const runner = await this.runnerService.getRandomAvailableRunner({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - snapshotRef: snapshot.ref, - }) - - sandbox.runnerId = runner.id - sandbox.pending = true - - await this.sandboxRepository.insert(sandbox) - return sandbox - } - - async createFromSnapshot( - createSandboxDto: CreateSandboxDto, - organization: Organization, - useSandboxResourceParams_deprecated?: boolean, - ): Promise { - let pendingCpuIncrement: number | undefined - let pendingMemoryIncrement: number | undefined - let pendingDiskIncrement: number | undefined - - const region = await this.getValidatedOrDefaultRegion(organization, createSandboxDto.target) - - try { - const sandboxClass = this.getValidatedOrDefaultClass(createSandboxDto.class) - - let snapshotIdOrName = createSandboxDto.snapshot - - if (!createSandboxDto.snapshot?.trim()) { - snapshotIdOrName = this.configService.getOrThrow('defaultSnapshot') - } - - const snapshotFilter: FindOptionsWhere[] = [ - { organizationId: organization.id, name: snapshotIdOrName }, - { general: true, name: snapshotIdOrName }, - ] - - if (isValidUuid(snapshotIdOrName)) { - snapshotFilter.push( - { organizationId: organization.id, id: snapshotIdOrName }, - { general: true, id: snapshotIdOrName }, - ) - } - - const snapshots = await this.snapshotRepository.find({ - where: snapshotFilter, - }) - - if (snapshots.length === 0) { - throw new BadRequestError( - `Snapshot ${snapshotIdOrName} not found. Did you add it through the BoxLite Dashboard?`, - ) - } - - let snapshot = snapshots.find((s) => s.state === SnapshotState.ACTIVE) - - if (!snapshot) { - snapshot = snapshots[0] - } - - if (!(await this.snapshotService.isAvailableInRegion(snapshot.id, region.id))) { - throw new BadRequestError(`Snapshot ${snapshotIdOrName} is not available in region ${region.id}`) - } - - if (snapshot.state !== SnapshotState.ACTIVE) { - throw new BadRequestError(`Snapshot ${snapshotIdOrName} is ${snapshot.state}`) - } - - if (!snapshot.ref) { - throw new BadRequestError('Snapshot ref is not defined') - } - - let cpu = snapshot.cpu - let mem = snapshot.mem - let disk = snapshot.disk - let gpu = snapshot.gpu - - // Remove the deprecated behavior in a future release - if (useSandboxResourceParams_deprecated) { - if (createSandboxDto.cpu) { - cpu = createSandboxDto.cpu - } - if (createSandboxDto.memory) { - mem = createSandboxDto.memory - } - if (createSandboxDto.disk) { - disk = createSandboxDto.disk - } - if (createSandboxDto.gpu) { - gpu = createSandboxDto.gpu - } - } - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const { pendingCpuIncremented, pendingMemoryIncremented, pendingDiskIncremented } = - await this.validateOrganizationQuotas(organization, region, cpu, mem, disk) - - if (pendingCpuIncremented) { - pendingCpuIncrement = cpu - } - if (pendingMemoryIncremented) { - pendingMemoryIncrement = mem - } - if (pendingDiskIncremented) { - pendingDiskIncrement = disk - } - - if (!createSandboxDto.volumes || createSandboxDto.volumes.length === 0) { - const skipWarmPool = (await this.redis.exists(`warm-pool:skip:${snapshot.id}`)) === 1 - - if (!skipWarmPool) { - const warmPoolSandbox = await this.warmPoolService.fetchWarmPoolSandbox({ - organizationId: organization.id, - snapshot, - target: region.id, - class: createSandboxDto.class, - cpu: cpu, - mem: mem, - disk: disk, - gpu: gpu, - osUser: createSandboxDto.user, - env: createSandboxDto.env, - state: SandboxState.STARTED, - }) - - if (warmPoolSandbox) { - return await this.assignWarmPoolSandbox(warmPoolSandbox, createSandboxDto, organization) - } - } - } else { - const volumeIdOrNames = createSandboxDto.volumes.map((v) => v.volumeId) - await this.volumeService.validateVolumes(organization.id, volumeIdOrNames) - } - - const runner = await this.runnerService.getRandomAvailableRunner({ - regions: [region.id], - sandboxClass, - snapshotRef: snapshot.ref, - }) - - const sandbox = new Sandbox(region.id, createSandboxDto.name) - - sandbox.organizationId = organization.id - - // TODO: make configurable - sandbox.class = sandboxClass - sandbox.snapshot = snapshot.name - // TODO: default user should be configurable - sandbox.osUser = createSandboxDto.user || 'boxlite' - sandbox.env = createSandboxDto.env || {} - sandbox.labels = createSandboxDto.labels || {} - - sandbox.cpu = cpu - sandbox.gpu = gpu - sandbox.mem = mem - sandbox.disk = disk - - sandbox.public = createSandboxDto.public || false - - if (createSandboxDto.networkBlockAll !== undefined) { - sandbox.networkBlockAll = createSandboxDto.networkBlockAll - } - - if (createSandboxDto.networkAllowList !== undefined) { - sandbox.networkAllowList = this.resolveNetworkAllowList(createSandboxDto.networkAllowList) - } - - if (createSandboxDto.autoStopInterval !== undefined) { - sandbox.autoStopInterval = this.resolveAutoStopInterval(createSandboxDto.autoStopInterval) - } - - if (createSandboxDto.autoArchiveInterval !== undefined) { - sandbox.autoArchiveInterval = this.resolveAutoArchiveInterval(createSandboxDto.autoArchiveInterval) - } - - if (createSandboxDto.autoDeleteInterval !== undefined) { - sandbox.autoDeleteInterval = createSandboxDto.autoDeleteInterval - } - - if (createSandboxDto.volumes !== undefined) { - sandbox.volumes = this.resolveVolumes(createSandboxDto.volumes) - } - - sandbox.runnerId = runner.id - sandbox.pending = true - - const insertedSandbox = await this.sandboxRepository.insert(sandbox) - - this.eventEmitter - .emitAsync(SandboxEvents.CREATED, new SandboxCreatedEvent(insertedSandbox)) - .catch((err) => this.logger.error('Failed to emit SandboxCreatedEvent', err)) - - return this.toSandboxDto(insertedSandbox) - } catch (error) { - await this.rollbackPendingUsage( - organization.id, - region.id, - pendingCpuIncrement, - pendingMemoryIncrement, - pendingDiskIncrement, - ) - - if (error.code === '23505') { - throw new ConflictException(`Sandbox with name ${createSandboxDto.name} already exists`) - } - - throw error - } - } - - private async assignWarmPoolSandbox( - warmPoolSandbox: Sandbox, - createSandboxDto: CreateSandboxDto, - organization: Organization, - ): Promise { - const now = new Date() - const updateData: Partial = { - public: createSandboxDto.public || false, - labels: createSandboxDto.labels || {}, - organizationId: organization.id, - createdAt: now, - } - - if (createSandboxDto.name) { - updateData.name = createSandboxDto.name - } - - if (createSandboxDto.autoStopInterval !== undefined) { - updateData.autoStopInterval = this.resolveAutoStopInterval(createSandboxDto.autoStopInterval) - } - - if (createSandboxDto.autoArchiveInterval !== undefined) { - updateData.autoArchiveInterval = this.resolveAutoArchiveInterval(createSandboxDto.autoArchiveInterval) - } - - if (createSandboxDto.autoDeleteInterval !== undefined) { - updateData.autoDeleteInterval = createSandboxDto.autoDeleteInterval - } - - if (createSandboxDto.networkBlockAll !== undefined) { - updateData.networkBlockAll = createSandboxDto.networkBlockAll - } - - if (createSandboxDto.networkAllowList !== undefined) { - updateData.networkAllowList = this.resolveNetworkAllowList(createSandboxDto.networkAllowList) - } - - if (!warmPoolSandbox.runnerId) { - throw new SandboxError('Runner not found for warm pool sandbox') - } - - if ( - createSandboxDto.networkBlockAll !== undefined || - createSandboxDto.networkAllowList !== undefined || - organization.sandboxLimitedNetworkEgress - ) { - const runner = await this.runnerService.findOneOrFail(warmPoolSandbox.runnerId) - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - await runnerAdapter.updateNetworkSettings( - warmPoolSandbox.id, - createSandboxDto.networkBlockAll, - createSandboxDto.networkAllowList, - organization.sandboxLimitedNetworkEgress, - ) - } - - const updatedSandbox = await this.sandboxRepository.update(warmPoolSandbox.id, { - updateData, - entity: warmPoolSandbox, - }) - - // Defensive invalidation of orgId cache since the sandbox moved from unassigned to a real organization - this.sandboxLookupCacheInvalidationService.invalidateOrgId({ - sandboxId: warmPoolSandbox.id, - organizationId: organization.id, - name: warmPoolSandbox.name, - previousOrganizationId: SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION, - }) - - // Treat this as a newly started sandbox - this.eventEmitter.emit( - SandboxEvents.STATE_UPDATED, - new SandboxStateUpdatedEvent(updatedSandbox, SandboxState.STARTED, SandboxState.STARTED), - ) - return this.toSandboxDto(updatedSandbox) - } - - async createFromBuildInfo(createSandboxDto: CreateSandboxDto, organization: Organization): Promise { - let pendingCpuIncrement: number | undefined - let pendingMemoryIncrement: number | undefined - let pendingDiskIncrement: number | undefined - - const region = await this.getValidatedOrDefaultRegion(organization, createSandboxDto.target) - - try { - const sandboxClass = this.getValidatedOrDefaultClass(createSandboxDto.class) - - const cpu = createSandboxDto.cpu || DEFAULT_CPU - const mem = createSandboxDto.memory || DEFAULT_MEMORY - const disk = createSandboxDto.disk || DEFAULT_DISK - const gpu = createSandboxDto.gpu || DEFAULT_GPU - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const { pendingCpuIncremented, pendingMemoryIncremented, pendingDiskIncremented } = - await this.validateOrganizationQuotas(organization, region, cpu, mem, disk) - - if (pendingCpuIncremented) { - pendingCpuIncrement = cpu - } - if (pendingMemoryIncremented) { - pendingMemoryIncrement = mem - } - if (pendingDiskIncremented) { - pendingDiskIncrement = disk - } - - if (createSandboxDto.volumes && createSandboxDto.volumes.length > 0) { - const volumeIdOrNames = createSandboxDto.volumes.map((v) => v.volumeId) - await this.volumeService.validateVolumes(organization.id, volumeIdOrNames) - } - - const sandbox = new Sandbox(region.id, createSandboxDto.name) - - sandbox.organizationId = organization.id - - sandbox.class = sandboxClass - sandbox.osUser = createSandboxDto.user || 'boxlite' - sandbox.env = createSandboxDto.env || {} - sandbox.labels = createSandboxDto.labels || {} - - sandbox.cpu = cpu - sandbox.gpu = gpu - sandbox.mem = mem - sandbox.disk = disk - sandbox.public = createSandboxDto.public || false - - if (createSandboxDto.networkBlockAll !== undefined) { - sandbox.networkBlockAll = createSandboxDto.networkBlockAll - } - - if (createSandboxDto.networkAllowList !== undefined) { - sandbox.networkAllowList = this.resolveNetworkAllowList(createSandboxDto.networkAllowList) - } - - if (createSandboxDto.autoStopInterval !== undefined) { - sandbox.autoStopInterval = this.resolveAutoStopInterval(createSandboxDto.autoStopInterval) - } - - if (createSandboxDto.autoArchiveInterval !== undefined) { - sandbox.autoArchiveInterval = this.resolveAutoArchiveInterval(createSandboxDto.autoArchiveInterval) - } - - if (createSandboxDto.autoDeleteInterval !== undefined) { - sandbox.autoDeleteInterval = createSandboxDto.autoDeleteInterval - } - - if (createSandboxDto.volumes !== undefined) { - sandbox.volumes = this.resolveVolumes(createSandboxDto.volumes) - } - - const buildInfoSnapshotRef = generateBuildSnapshotRef( - createSandboxDto.buildInfo.dockerfileContent, - createSandboxDto.buildInfo.contextHashes, - ) - - // Check if buildInfo with the same snapshotRef already exists - const existingBuildInfo = await this.buildInfoRepository.findOne({ - where: { snapshotRef: buildInfoSnapshotRef }, - }) - - if (existingBuildInfo) { - sandbox.buildInfo = existingBuildInfo - if (await this.redisLockProvider.lock(`build-info:${existingBuildInfo.snapshotRef}:update`, 60)) { - await this.buildInfoRepository.update(sandbox.buildInfo.snapshotRef, { lastUsedAt: new Date() }) - } - } else { - const buildInfoEntity = this.buildInfoRepository.create({ - ...createSandboxDto.buildInfo, - }) - await this.buildInfoRepository.save(buildInfoEntity) - sandbox.buildInfo = buildInfoEntity - } - - let runner: Runner - - try { - const declarativeBuildScoreThreshold = this.configService.get('runnerScore.thresholds.declarativeBuild') - runner = await this.runnerService.getRandomAvailableRunner({ - regions: [sandbox.region], - sandboxClass: sandbox.class, - snapshotRef: sandbox.buildInfo.snapshotRef, - ...(declarativeBuildScoreThreshold !== undefined && { - availabilityScoreThreshold: declarativeBuildScoreThreshold, - }), - }) - sandbox.runnerId = runner.id - } catch (error) { - if ( - error instanceof BadRequestError == false || - error.message !== 'No available runners' || - !sandbox.buildInfo - ) { - throw error - } - sandbox.state = SandboxState.PENDING_BUILD - } - - sandbox.pending = true - - const insertedSandbox = await this.sandboxRepository.insert(sandbox) - - this.eventEmitter - .emitAsync(SandboxEvents.CREATED, new SandboxCreatedEvent(insertedSandbox)) - .catch((err) => this.logger.error('Failed to emit SandboxCreatedEvent', err)) - - return this.toSandboxDto(insertedSandbox) - } catch (error) { - await this.rollbackPendingUsage( - organization.id, - region.id, - pendingCpuIncrement, - pendingMemoryIncrement, - pendingDiskIncrement, - ) - - if (error.code === '23505') { - throw new ConflictException(`Sandbox with name ${createSandboxDto.name} already exists`) - } - - throw error - } - } - - async createBackup(sandboxIdOrName: string, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - if (sandbox.autoDeleteInterval === 0) { - throw new SandboxError('Ephemeral sandboxes cannot be backed up') - } - - if (![BackupState.COMPLETED, BackupState.NONE].includes(sandbox.backupState)) { - throw new SandboxError('Sandbox backup is already in progress') - } - - this.eventEmitter.emit(SandboxEvents.BACKUP_CREATED, new SandboxBackupCreatedEvent(sandbox)) - - return sandbox - } - - async findAllDeprecated( - organizationId: string, - labels?: { [key: string]: string }, - includeErroredDestroyed?: boolean, - ): Promise { - const baseFindOptions: FindOptionsWhere = { - organizationId, - ...(labels ? { labels: JsonContains(labels) } : {}), - } - - const where: FindOptionsWhere[] = [ - { - ...baseFindOptions, - state: Not(In([SandboxState.DESTROYED, SandboxState.ERROR, SandboxState.BUILD_FAILED])), - }, - { - ...baseFindOptions, - state: In([SandboxState.ERROR, SandboxState.BUILD_FAILED]), - ...(includeErroredDestroyed ? {} : { desiredState: Not(SandboxDesiredState.DESTROYED) }), - }, - ] - - return this.sandboxRepository.find({ where }) - } - - async findAll( - organizationId: string, - page = 1, - limit = 10, - filters?: { - id?: string - name?: string - labels?: { [key: string]: string } - includeErroredDestroyed?: boolean - states?: SandboxState[] - snapshots?: string[] - regionIds?: string[] - minCpu?: number - maxCpu?: number - minMemoryGiB?: number - maxMemoryGiB?: number - minDiskGiB?: number - maxDiskGiB?: number - lastEventAfter?: Date - lastEventBefore?: Date - }, - sort?: { - field?: SandboxSortField - direction?: SandboxSortDirection - }, - ): Promise> { - const pageNum = Number(page) - const limitNum = Number(limit) - - const { - id, - name, - labels, - includeErroredDestroyed, - states, - snapshots, - regionIds, - minCpu, - maxCpu, - minMemoryGiB, - maxMemoryGiB, - minDiskGiB, - maxDiskGiB, - lastEventAfter, - lastEventBefore, - } = filters || {} - - const { field: sortField = DEFAULT_SANDBOX_SORT_FIELD, direction: sortDirection = DEFAULT_SANDBOX_SORT_DIRECTION } = - sort || {} - - const baseFindOptions: FindOptionsWhere = { - organizationId, - ...(id ? { id: ILike(`${id}%`) } : {}), - ...(name ? { name: ILike(`${name}%`) } : {}), - ...(labels ? { labels: JsonContains(labels) } : {}), - ...(snapshots ? { snapshot: In(snapshots) } : {}), - ...(regionIds ? { region: In(regionIds) } : {}), - } - - baseFindOptions.cpu = createRangeFilter(minCpu, maxCpu) - baseFindOptions.mem = createRangeFilter(minMemoryGiB, maxMemoryGiB) - baseFindOptions.disk = createRangeFilter(minDiskGiB, maxDiskGiB) - baseFindOptions.updatedAt = createRangeFilter(lastEventAfter, lastEventBefore) - - const statesToInclude = (states || Object.values(SandboxState)).filter((state) => state !== SandboxState.DESTROYED) - const errorStates = [SandboxState.ERROR, SandboxState.BUILD_FAILED] - - const nonErrorStatesToInclude = statesToInclude.filter((state) => !errorStates.includes(state)) - const errorStatesToInclude = statesToInclude.filter((state) => errorStates.includes(state)) - - const where: FindOptionsWhere[] = [] - - if (nonErrorStatesToInclude.length > 0) { - where.push({ - ...baseFindOptions, - state: In(nonErrorStatesToInclude), - }) - } - - if (errorStatesToInclude.length > 0) { - where.push({ - ...baseFindOptions, - state: In(errorStatesToInclude), - ...(includeErroredDestroyed ? {} : { desiredState: Not(SandboxDesiredState.DESTROYED) }), - }) - } - - const [items, total] = await this.sandboxRepository.findAndCount({ - where, - order: { - [sortField]: { - direction: sortDirection, - nulls: 'LAST', - }, - ...(sortField !== SandboxSortField.CREATED_AT && { createdAt: 'DESC' }), - }, - skip: (pageNum - 1) * limitNum, - take: limitNum, - }) - - return { - items, - total, - page: pageNum, - totalPages: Math.ceil(total / limitNum), - } - } - - private getExpectedDesiredStateForState(state: SandboxState): SandboxDesiredState | undefined { - switch (state) { - case SandboxState.STARTED: - return SandboxDesiredState.STARTED - case SandboxState.STOPPED: - return SandboxDesiredState.STOPPED - case SandboxState.ARCHIVED: - return SandboxDesiredState.ARCHIVED - case SandboxState.DESTROYED: - return SandboxDesiredState.DESTROYED - default: - return undefined - } - } - - private hasValidDesiredState(state: SandboxState): boolean { - return this.getExpectedDesiredStateForState(state) !== undefined - } - - async findByRunnerId( - runnerId: string, - states?: SandboxState[], - skipReconcilingSandboxes?: boolean, - ): Promise { - const where: FindOptionsWhere = { runnerId } - if (states && states.length > 0) { - // Validate that all states have corresponding desired states - states.forEach((state) => { - if (!this.hasValidDesiredState(state)) { - throw new BadRequestError(`State ${state} does not have a corresponding desired state`) - } - }) - where.state = In(states) - } - - let sandboxes = await this.sandboxRepository.find({ where }) - - if (skipReconcilingSandboxes) { - sandboxes = sandboxes.filter((sandbox) => { - const expectedDesiredState = this.getExpectedDesiredStateForState(sandbox.state) - return expectedDesiredState !== undefined && expectedDesiredState === sandbox.desiredState - }) - } - - return sandboxes - } - - async findOneByIdOrName( - sandboxIdOrName: string, - organizationId: string, - returnDestroyed?: boolean, - ): Promise { - const stateFilter = returnDestroyed ? {} : { state: Not(SandboxState.DESTROYED) } - const relations: ['buildInfo'] = ['buildInfo'] - - // Try lookup by ID first - let sandbox = await this.sandboxRepository.findOne({ - where: { - id: sandboxIdOrName, - organizationId, - ...stateFilter, - }, - relations, - cache: { - id: sandboxLookupCacheKeyById({ organizationId, returnDestroyed, sandboxId: sandboxIdOrName }), - milliseconds: SANDBOX_LOOKUP_CACHE_TTL_MS, - }, - }) - - // Fallback to lookup by name - if (!sandbox) { - sandbox = await this.sandboxRepository.findOne({ - where: { - name: sandboxIdOrName, - organizationId, - ...stateFilter, - }, - relations, - cache: { - id: sandboxLookupCacheKeyByName({ organizationId, returnDestroyed, sandboxName: sandboxIdOrName }), - milliseconds: SANDBOX_LOOKUP_CACHE_TTL_MS, - }, - }) - } - - if ( - !sandbox || - (!returnDestroyed && - [SandboxState.ERROR, SandboxState.BUILD_FAILED].includes(sandbox.state) && - sandbox.desiredState === SandboxDesiredState.DESTROYED) - ) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - - return sandbox - } - - async findOne(sandboxId: string, returnDestroyed?: boolean): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { - id: sandboxId, - ...(returnDestroyed ? {} : { state: Not(SandboxState.DESTROYED) }), - }, - }) - - if ( - !sandbox || - (!returnDestroyed && - [SandboxState.ERROR, SandboxState.BUILD_FAILED].includes(sandbox.state) && - sandbox.desiredState === SandboxDesiredState.DESTROYED) - ) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - return sandbox - } - - async getOrganizationId(sandboxIdOrName: string, organizationId?: string): Promise { - let sandbox = await this.sandboxRepository.findOne({ - where: { - id: sandboxIdOrName, - ...(organizationId ? { organizationId: organizationId } : {}), - }, - select: ['organizationId'], - cache: { - id: sandboxOrgIdCacheKeyById({ organizationId, sandboxId: sandboxIdOrName }), - milliseconds: SANDBOX_ORG_ID_CACHE_TTL_MS, - }, - }) - - if (!sandbox && organizationId) { - sandbox = await this.sandboxRepository.findOne({ - where: { - name: sandboxIdOrName, - organizationId: organizationId, - }, - select: ['organizationId'], - cache: { - id: sandboxOrgIdCacheKeyByName({ organizationId, sandboxName: sandboxIdOrName }), - milliseconds: SANDBOX_ORG_ID_CACHE_TTL_MS, - }, - }) - } - - if (!sandbox || !sandbox.organizationId) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - - return sandbox.organizationId - } - - async getRunnerId(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { - id: sandboxId, - }, - select: ['runnerId'], - loadEagerRelations: false, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - return sandbox.runnerId || null - } - - async getRegionId(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { - id: sandboxId, - }, - select: ['region'], - loadEagerRelations: false, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - return sandbox.region - } - - async getPortPreviewUrl(sandboxIdOrName: string, organizationId: string, port: number): Promise { - if (port < 1 || port > 65535) { - throw new BadRequestError('Invalid port') - } - - const proxyDomain = this.configService.getOrThrow('proxy.domain') - const proxyProtocol = this.configService.getOrThrow('proxy.protocol') - - const where: FindOptionsWhere = { - organizationId: organizationId, - state: Not(SandboxState.DESTROYED), - } - - const sandbox = await this.sandboxRepository.findOne({ - where: [ - { - id: sandboxIdOrName, - ...where, - }, - { - name: sandboxIdOrName, - ...where, - }, - ], - cache: { - id: `sandbox:${sandboxIdOrName}:organization:${organizationId}`, - milliseconds: 1000, - }, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - - let url = `${proxyProtocol}://${port}-${sandbox.id}.${proxyDomain}` - - const region = await this.regionService.findOne(sandbox.region, true) - if (region && region.proxyUrl) { - // Insert port and sandbox.id into the custom proxy URL - url = region.proxyUrl.replace(/(https?:\/)(\/)/, `$1/${port}-${sandbox.id}.`) - } - - return { - sandboxId: sandbox.id, - url, - token: sandbox.authToken, - } - } - - async getSignedPortPreviewUrl( - sandboxIdOrName: string, - organizationId: string, - port: number, - expiresInSeconds = 60, - ): Promise { - if (port < 1 || port > 65535) { - throw new BadRequestError('Invalid port') - } - - if (expiresInSeconds < 1 || expiresInSeconds > 60 * 60 * 24) { - throw new BadRequestError('expiresInSeconds must be between 1 second and 24 hours') - } - - const proxyDomain = this.configService.getOrThrow('proxy.domain') - const proxyProtocol = this.configService.getOrThrow('proxy.protocol') - - const where: FindOptionsWhere = { - organizationId: organizationId, - state: Not(SandboxState.DESTROYED), - } - - const sandbox = await this.sandboxRepository.findOne({ - where: [ - { - id: sandboxIdOrName, - ...where, - }, - { - name: sandboxIdOrName, - ...where, - }, - ], - cache: { - id: `sandbox:${sandboxIdOrName}:organization:${organizationId}`, - milliseconds: 1000, - }, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - - const token = customNanoid(urlAlphabet.replace('_', '').replace('-', ''))(16).toLocaleLowerCase() - - const lockKey = `sandbox:signed-preview-url-token:${port}:${token}` - await this.redis.setex(lockKey, expiresInSeconds, sandbox.id) - - let url = `${proxyProtocol}://${port}-${token}.${proxyDomain}` - - const region = await this.regionService.findOne(sandbox.region, true) - if (region && region.proxyUrl) { - // Insert port and sandbox.id into the custom proxy URL - url = region.proxyUrl.replace(/(https?:\/)(\/)/, `$1/${port}-${token}.`) - } - - return { - sandboxId: sandbox.id, - port, - token, - url, - } - } - - async getSandboxIdFromSignedPreviewUrlToken(token: string, port: number): Promise { - const lockKey = `sandbox:signed-preview-url-token:${port}:${token}` - const sandboxId = await this.redis.get(lockKey) - if (!sandboxId) { - throw new ForbiddenException('Invalid or expired token') - } - return sandboxId - } - - async expireSignedPreviewUrlToken( - sandboxIdOrName: string, - organizationId: string, - token: string, - port: number, - ): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID or name ${sandboxIdOrName} not found`) - } - - const lockKey = `sandbox:signed-preview-url-token:${port}:${token}` - await this.redis.del(lockKey) - } - - async destroy(sandboxIdOrName: string, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - if (sandbox.pending && sandbox.state !== SandboxState.PENDING_BUILD) { - throw new SandboxError('Sandbox state change in progress') - } - - const updateData = Sandbox.getSoftDeleteUpdate(sandbox) - - const updatedSandbox = await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: sandbox.pending, state: sandbox.state }, - }) - - this.eventEmitter.emit(SandboxEvents.DESTROYED, new SandboxDestroyedEvent(updatedSandbox)) - return updatedSandbox - } - - async start(sandboxIdOrName: string, organization: Organization): Promise { - let pendingCpuIncrement: number | undefined - let pendingMemoryIncrement: number | undefined - let pendingDiskIncrement: number | undefined - - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organization.id) - - const region = await this.regionService.findOne(sandbox.region) - if (!region) { - throw new NotFoundException(`Region with ID ${sandbox.region} not found`) - } - - try { - if (sandbox.state === SandboxState.STARTED && sandbox.desiredState === SandboxDesiredState.STARTED) { - return sandbox - } - - this.assertSandboxNotErrored(sandbox) - - if (String(sandbox.state) !== String(sandbox.desiredState)) { - // Allow start of stopped | archived and archiving | archived sandboxes - if ( - sandbox.desiredState !== SandboxDesiredState.ARCHIVED || - (sandbox.state !== SandboxState.STOPPED && sandbox.state !== SandboxState.ARCHIVING) - ) { - throw new SandboxError('State change in progress') - } - } - - if (![SandboxState.STOPPED, SandboxState.ARCHIVED, SandboxState.ARCHIVING].includes(sandbox.state)) { - throw new SandboxError('Sandbox is not in valid state') - } - - if (sandbox.pending) { - throw new SandboxError('Sandbox state change in progress') - } - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const { pendingCpuIncremented, pendingMemoryIncremented, pendingDiskIncremented } = - await this.validateOrganizationQuotas(organization, region, sandbox.cpu, sandbox.mem, sandbox.disk, sandbox.id) - - if (pendingCpuIncremented) { - pendingCpuIncrement = sandbox.cpu - } - if (pendingMemoryIncremented) { - pendingMemoryIncrement = sandbox.mem - } - if (pendingDiskIncremented) { - pendingDiskIncrement = sandbox.disk - } - - const updateData: Partial = { - pending: true, - desiredState: SandboxDesiredState.STARTED, - authToken: nanoid(32).toLocaleLowerCase(), - } - - const updatedSandbox = await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - this.eventEmitter.emit(SandboxEvents.STARTED, new SandboxStartedEvent(updatedSandbox)) - - return updatedSandbox - } catch (error) { - await this.rollbackPendingUsage( - organization.id, - sandbox.region, - pendingCpuIncrement, - pendingMemoryIncrement, - pendingDiskIncrement, - ) - throw error - } - } - - async stop(sandboxIdOrName: string, organizationId?: string, force?: boolean): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - this.assertSandboxNotErrored(sandbox) - - if (String(sandbox.state) !== String(sandbox.desiredState)) { - throw new SandboxError('State change in progress') - } - - if (sandbox.state !== SandboxState.STARTED) { - throw new SandboxError('Sandbox is not started') - } - - if (sandbox.pending) { - throw new SandboxError('Sandbox state change in progress') - } - - const updateData: Partial = { - pending: true, - desiredState: sandbox.autoDeleteInterval === 0 ? SandboxDesiredState.DESTROYED : SandboxDesiredState.STOPPED, - } - - const updatedSandbox = await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: sandbox.state }, - }) - - if (sandbox.autoDeleteInterval === 0) { - this.eventEmitter.emit(SandboxEvents.DESTROYED, new SandboxDestroyedEvent(updatedSandbox)) - } else { - this.eventEmitter.emit(SandboxEvents.STOPPED, new SandboxStoppedEvent(updatedSandbox, force)) - } - - return updatedSandbox - } - - async recover(sandboxIdOrName: string, organization: Organization): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organization.id) - - if (sandbox.state !== SandboxState.ERROR) { - throw new BadRequestError('Sandbox must be in error state to recover') - } - - if (sandbox.pending) { - throw new SandboxError('Sandbox state change in progress') - } - - // Validate runner exists - if (!sandbox.runnerId) { - throw new NotFoundException(`Sandbox with ID ${sandbox.id} does not have a runner`) - } - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - if (runner.apiVersion === '2') { - // TODO: we need "recovering" state that can be set after calling recover - // Once in recovering, we abort further processing and let the manager/job handler take care of it - // (Also, since desiredState would be STARTED, we need to check the quota) - throw new ForbiddenException('Recovering sandboxes with runner API version 2 is not supported') - } - - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - try { - await runnerAdapter.recoverSandbox(sandbox) - } catch (error) { - if (error instanceof Error && error.message.includes('storage cannot be further expanded')) { - const errorMsg = `Sandbox storage cannot be further expanded. Maximum expansion of ${(sandbox.disk * 0.1).toFixed(2)}GB (10% of original ${sandbox.disk.toFixed(2)}GB) has been reached. Please contact support for further assistance.` - throw new ForbiddenException(errorMsg) - } - throw error - } - - const updateData: Partial = { - state: SandboxState.STOPPED, - desiredState: SandboxDesiredState.STOPPED, - errorReason: null, - recoverable: false, - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { state: SandboxState.ERROR }, - }) - - // Now that sandbox is in STOPPED state, use the normal start flow - // This handles quota validation, pending usage, event emission, etc. - return await this.start(sandbox.id, organization) - } - - async resize(sandboxIdOrName: string, resizeDto: ResizeSandboxDto, organization: Organization): Promise { - let pendingCpuIncrement: number | undefined - let pendingMemoryIncrement: number | undefined - let pendingDiskIncrement: number | undefined - - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organization.id) - - const region = await this.regionService.findOne(sandbox.region) - if (!region) { - throw new NotFoundException(`Region with ID ${sandbox.region} not found`) - } - - try { - // Validate sandbox is in a valid state for resize - if (sandbox.state !== SandboxState.STARTED && sandbox.state !== SandboxState.STOPPED) { - throw new BadRequestError('Sandbox must be in started or stopped state to resize') - } - - if (sandbox.pending) { - throw new SandboxError('Sandbox state change in progress') - } - - // If no resize parameters provided, throw error - if (resizeDto.cpu === undefined && resizeDto.memory === undefined && resizeDto.disk === undefined) { - throw new BadRequestError('No resource changes specified - sandbox is already at the desired configuration') - } - - // Disk resize requires stopped sandbox (cold resize only) - if (resizeDto.disk !== undefined && sandbox.state !== SandboxState.STOPPED) { - throw new BadRequestError('Disk resize can only be performed on a stopped sandbox') - } - - // Hot resize (sandbox is running): only CPU and memory can be increased - const isHotResize = sandbox.state === SandboxState.STARTED - - // Validate hot resize constraints - if (isHotResize) { - if (resizeDto.cpu !== undefined && resizeDto.cpu < sandbox.cpu) { - throw new BadRequestError('Sandbox must be in stopped state to decrease the number of CPU cores') - } - - if (resizeDto.memory !== undefined && resizeDto.memory < sandbox.mem) { - throw new BadRequestError('Sandbox must be in stopped state to decrease memory') - } - } - - // Disk can only be increased (never decreased) - if (resizeDto.disk !== undefined && resizeDto.disk < sandbox.disk) { - throw new BadRequestError('Sandbox disk size cannot be decreased') - } - - // Calculate new resource values - const newCpu = resizeDto.cpu ?? sandbox.cpu - const newMem = resizeDto.memory ?? sandbox.mem - const newDisk = resizeDto.disk ?? sandbox.disk - - // Throw if nothing actually changes - if (newCpu === sandbox.cpu && newMem === sandbox.mem && newDisk === sandbox.disk) { - throw new BadRequestError('No resource changes specified - sandbox is already at the desired configuration') - } - - // Validate organization quotas for the new resource values - this.organizationService.assertOrganizationIsNotSuspended(organization) - - // Validate per-sandbox quotas with total new values - if (newCpu > organization.maxCpuPerSandbox) { - throw new ForbiddenException( - `CPU request ${newCpu} exceeds maximum allowed per sandbox (${organization.maxCpuPerSandbox}).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (newMem > organization.maxMemoryPerSandbox) { - throw new ForbiddenException( - `Memory request ${newMem}GB exceeds maximum allowed per sandbox (${organization.maxMemoryPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (newDisk > organization.maxDiskPerSandbox) { - throw new ForbiddenException( - `Disk request ${newDisk}GB exceeds maximum allowed per sandbox (${organization.maxDiskPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - - // For cold resize, cpu/memory don't affect quota until sandbox is STARTED. - // For hot resize, track all deltas (positive reserves quota, negative frees quota for others). - const cpuDeltaForQuota = isHotResize ? newCpu - sandbox.cpu : 0 - const memDeltaForQuota = isHotResize ? newMem - sandbox.mem : 0 - const diskDeltaForQuota = newDisk - sandbox.disk // Disk only increases (validated at start of method) - - // Validate and track pending for any non-zero quota changes - if (cpuDeltaForQuota !== 0 || memDeltaForQuota !== 0 || diskDeltaForQuota !== 0) { - const { pendingCpuIncremented, pendingMemoryIncremented, pendingDiskIncremented } = - await this.validateOrganizationQuotas( - organization, - region, - cpuDeltaForQuota, - memDeltaForQuota, - diskDeltaForQuota, - ) - - if (pendingCpuIncremented) { - pendingCpuIncrement = cpuDeltaForQuota - } - if (pendingMemoryIncremented) { - pendingMemoryIncrement = memDeltaForQuota - } - if (pendingDiskIncremented) { - pendingDiskIncrement = diskDeltaForQuota - } - } - - // Get runner and validate before changing state - if (!sandbox.runnerId) { - throw new BadRequestError('Sandbox has no runner assigned') - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - // Capture the previous state before transitioning to RESIZING (STARTED or STOPPED) - const previousState = - sandbox.state === SandboxState.STARTED - ? SandboxState.STARTED - : sandbox.state === SandboxState.STOPPED - ? SandboxState.STOPPED - : null - - if (!previousState) { - throw new BadRequestError('Sandbox must be in started or stopped state to resize') - } - - // Now transition to RESIZING state - const updateData: Partial = { - state: SandboxState.RESIZING, - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: previousState }, - }) - - try { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - - await runnerAdapter.resizeSandbox(sandbox.id, resizeDto.cpu, resizeDto.memory, resizeDto.disk) - - // For V0 runners, update resources immediately (subscriber emits STATE_UPDATED) - // For V2 runners, job handler will update resources on completion - if (runner.apiVersion === '0') { - const updateData: Partial = { - cpu: newCpu, - mem: newMem, - disk: newDisk, - state: previousState, - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { state: SandboxState.RESIZING }, - }) - - // Apply the usage change (increments current, decrements pending) - // Only apply deltas for quotas that were validated/pending-incremented - await this.organizationUsageService.applyResizeUsageChange( - organization.id, - sandbox.region, - cpuDeltaForQuota, - memDeltaForQuota, - diskDeltaForQuota, - ) - } - - return await this.findOneByIdOrName(sandbox.id, organization.id) - } catch (error) { - // Return to previous state on error - const updateData: Partial = { - state: previousState, - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { state: SandboxState.RESIZING }, - }) - - throw error - } - } catch (error) { - await this.rollbackPendingUsage( - organization.id, - sandbox.region, - pendingCpuIncrement, - pendingMemoryIncrement, - pendingDiskIncrement, - ) - throw error - } - } - - async updatePublicStatus(sandboxIdOrName: string, isPublic: boolean, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - const updateData: Partial = { - public: isPublic, - } - - return await this.sandboxRepository.update(sandbox.id, { - updateData, - entity: sandbox, - }) - } - - async updateLastActivityAt(sandboxId: string, lastActivityAt: Date): Promise { - await this.sandboxActivityService.updateLastActivityAt(sandboxId, lastActivityAt) - } - - async getToolboxProxyUrl(sandboxId: string): Promise { - const sandbox = await this.findOne(sandboxId) - return this.resolveToolboxProxyUrl(sandbox.region) - } - - async toSandboxDto(sandbox: Sandbox): Promise { - const toolboxProxyUrl = await this.resolveToolboxProxyUrl(sandbox.region) - return SandboxDto.fromSandbox(sandbox, toolboxProxyUrl) - } - - async toSandboxDtos(sandboxes: Sandbox[]): Promise { - const urlMap = await this.resolveToolboxProxyUrls(sandboxes.map((s) => s.region)) - return sandboxes.map((s) => { - const url = urlMap.get(s.region) - if (!url) { - throw new NotFoundException(`Toolbox proxy URL not resolved for region ${s.region}`) - } - return SandboxDto.fromSandbox(s, url) - }) - } - - async resolveToolboxProxyUrl(regionId: string): Promise { - const cacheKey = toolboxProxyUrlCacheKey(regionId) - const cached = await this.redis.get(cacheKey) - if (cached) { - return cached - } - - const region = await this.regionService.findOne(regionId) - const url = region?.toolboxProxyUrl - ? region.toolboxProxyUrl.replace(/\/+$/, '') + '/toolbox' - : this.configService.getOrThrow('proxy.toolboxUrl') - - this.redis.setex(cacheKey, TOOLBOX_PROXY_URL_CACHE_TTL_S, url).catch((err) => { - this.logger.warn(`Failed to cache toolbox proxy URL for region ${regionId}: ${err.message}`) - }) - return url - } - - async resolveToolboxProxyUrls(regionIds: string[]): Promise> { - const unique = [...new Set(regionIds)] - const result = new Map() - - const pipeline = this.redis.pipeline() - for (const id of unique) { - pipeline.get(toolboxProxyUrlCacheKey(id)) - } - const cached = await pipeline.exec() - - const uncached: string[] = [] - for (let i = 0; i < unique.length; i++) { - const err = cached?.[i]?.[0] - if (err) { - this.logger.warn(`Failed to get cached toolbox proxy URL for region ${unique[i]}: ${err.message}`) - } - const val = cached?.[i]?.[1] as string | null - if (val) { - result.set(unique[i], val) - } else { - uncached.push(unique[i]) - } - } - - if (uncached.length > 0) { - const regions = await this.regionService.findByIds(uncached) - const regionMap = new Map(regions.map((r) => [r.id, r])) - const fallback = this.configService.getOrThrow('proxy.toolboxUrl') - const setPipeline = this.redis.pipeline() - for (const id of uncached) { - const region = regionMap.get(id) - const url = region?.toolboxProxyUrl ? region.toolboxProxyUrl.replace(/\/+$/, '') + '/toolbox' : fallback - result.set(id, url) - setPipeline.setex(toolboxProxyUrlCacheKey(id), TOOLBOX_PROXY_URL_CACHE_TTL_S, url) - } - const setResults = await setPipeline.exec() - setResults?.forEach(([err], i) => { - if (err) { - this.logger.warn(`Failed to cache toolbox proxy URL for region ${uncached[i]}: ${err.message}`) - } - }) - } - - return result - } - - async getBuildLogsUrl(sandboxIdOrName: string, organizationId: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - if (!sandbox.buildInfo?.snapshotRef) { - throw new NotFoundException(`Sandbox ${sandboxIdOrName} has no build info`) - } - - const region = await this.regionService.findOne(sandbox.region, true) - - if (!region) { - throw new NotFoundException(`Region for runner for sandbox ${sandboxIdOrName} not found`) - } - - if (!region.proxyUrl) { - return `${this.configService.getOrThrow('proxy.protocol')}://${this.configService.getOrThrow('proxy.domain')}/sandboxes/${sandbox.id}/build-logs` - } - - return region.proxyUrl + '/sandboxes/' + sandbox.id + '/build-logs' - } - - private async getValidatedOrDefaultRegion(organization: Organization, regionIdOrName?: string): Promise { - if (!organization.defaultRegionId) { - throw new DefaultRegionRequiredException() - } - - regionIdOrName = regionIdOrName?.trim() - - if (!regionIdOrName) { - const region = await this.regionService.findOne(organization.defaultRegionId) - if (!region) { - throw new NotFoundException('Default region not found') - } - return region - } - - const region = - (await this.regionService.findOneByName(regionIdOrName, organization.id)) ?? - (await this.regionService.findOneByName(regionIdOrName, null)) ?? - (await this.regionService.findOne(regionIdOrName)) - - if (!region) { - throw new NotFoundException('Region not found') - } - - return region - } - - private getValidatedOrDefaultClass(sandboxClass: SandboxClass): SandboxClass { - if (!sandboxClass) { - return SandboxClass.SMALL - } - - if (Object.values(SandboxClass).includes(sandboxClass)) { - return sandboxClass - } else { - throw new BadRequestError('Invalid class') - } - } - - async replaceLabels( - sandboxIdOrName: string, - labels: { [key: string]: string }, - organizationId?: string, - ): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - // Replace all labels - const updateData: Partial = { - labels, - } - - return await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - } - - @Cron(CronExpression.EVERY_SECOND, { name: 'cleanup-destroyed-sandboxes' }) - @LogExecution('cleanup-destroyed-sandboxes') - @WithInstrumentation() - async cleanupDestroyedSandboxes() { - const twentyFourHoursAgo = new Date() - twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24) - - const destroyedSandboxs = await this.sandboxRepository.delete({ - state: SandboxState.DESTROYED, - updatedAt: LessThan(twentyFourHoursAgo), - }) - - if (destroyedSandboxs.affected > 0) { - this.logger.debug(`Cleaned up ${destroyedSandboxs.affected} destroyed sandboxes`) - } - } - - @Cron(CronExpression.EVERY_10_MINUTES, { name: 'cleanup-build-failed-sandboxes' }) - @LogExecution('cleanup-build-failed-sandboxes') - @WithInstrumentation() - async cleanupBuildFailedSandboxes() { - const twentyFourHoursAgo = new Date() - twentyFourHoursAgo.setHours(twentyFourHoursAgo.getHours() - 24) - - const destroyedSandboxs = await this.sandboxRepository.delete({ - state: SandboxState.BUILD_FAILED, - desiredState: SandboxDesiredState.DESTROYED, - updatedAt: LessThan(twentyFourHoursAgo), - }) - - if (destroyedSandboxs.affected > 0) { - this.logger.debug(`Cleaned up ${destroyedSandboxs.affected} build failed sandboxes`) - } - } - - @Cron(CronExpression.EVERY_SECOND, { name: 'cleanup-stale-build-failed-sandboxes' }) - @LogExecution('cleanup-stale-build-failed-sandboxes') - @WithInstrumentation() - async cleanupStaleBuildFailedSandboxes() { - const sevenDaysAgo = new Date() - sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7) - - const result = await this.sandboxRepository.delete({ - state: SandboxState.BUILD_FAILED, - desiredState: SandboxDesiredState.STARTED, - updatedAt: LessThan(sevenDaysAgo), - }) - - if (result.affected > 0) { - this.logger.debug(`Cleaned up ${result.affected} stale build failed sandboxes`) - } - } - - @Cron(CronExpression.EVERY_SECOND, { name: 'cleanup-stale-error-sandboxes' }) - @LogExecution('cleanup-stale-error-sandboxes') - @WithInstrumentation() - async cleanupStaleErrorSandboxes() { - const sevenDaysAgo = new Date() - sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7) - - const result = await this.sandboxRepository.delete({ - state: SandboxState.ERROR, - desiredState: SandboxDesiredState.DESTROYED, - updatedAt: LessThan(sevenDaysAgo), - }) - - if (result.affected > 0) { - this.logger.debug(`Cleaned up ${result.affected} stale error sandboxes`) - } - } - - async setAutostopInterval(sandboxIdOrName: string, interval: number, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - const updateData: Partial = { - autoStopInterval: this.resolveAutoStopInterval(interval), - } - - return await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - } - - async setAutoArchiveInterval(sandboxIdOrName: string, interval: number, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - const updateData: Partial = { - autoArchiveInterval: this.resolveAutoArchiveInterval(interval), - } - - return await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - } - - async setAutoDeleteInterval(sandboxIdOrName: string, interval: number, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - const updateData: Partial = { - autoDeleteInterval: interval, - } - - return await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - } - - async updateNetworkSettings( - sandboxIdOrName: string, - networkBlockAll?: boolean, - networkAllowList?: string, - organizationId?: string, - ): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - const updateData: Partial = {} - - if (networkBlockAll !== undefined) { - updateData.networkBlockAll = networkBlockAll - } - - if (networkAllowList !== undefined) { - updateData.networkAllowList = this.resolveNetworkAllowList(networkAllowList) - } - - const updatedSandbox = await this.sandboxRepository.update(sandbox.id, { updateData, entity: sandbox }) - - // Update network settings on the runner - if (sandbox.runnerId) { - const runner = await this.runnerService.findOne(sandbox.runnerId) - if (runner) { - const runnerAdapter = await this.runnerAdapterFactory.create(runner) - await runnerAdapter.updateNetworkSettings(sandbox.id, networkBlockAll, networkAllowList) - } - } - - return updatedSandbox - } - - // used by internal services to update the state of a sandbox to resolve domain and runner state mismatch - // notably, when a sandbox instance stops or errors on the runner, the domain state needs to be updated to reflect the actual state - async updateState( - sandboxId: string, - newState: SandboxState, - recoverable = false, - errorReason?: string, - ): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - if (sandbox.state === newState) { - this.logger.debug(`Sandbox ${sandboxId} is already in state ${newState}`) - return - } - - // only allow updating the state of started | stopped sandboxes - if (![SandboxState.STARTED, SandboxState.STOPPED].includes(sandbox.state)) { - throw new BadRequestError('Sandbox is not in a valid state to be updated') - } - - if (sandbox.desiredState == SandboxDesiredState.DESTROYED) { - this.logger.debug(`Sandbox ${sandboxId} is already DESTROYED, skipping state update`) - return - } - - const oldState = sandbox.state - const oldDesiredState = sandbox.desiredState - - const updateData: Partial = { - state: newState, - recoverable: false, - } - - if (errorReason !== undefined) { - updateData.errorReason = errorReason - if (newState === SandboxState.ERROR) { - updateData.recoverable = recoverable - } - } - - // we need to update the desired state to match the new state - const desiredState = this.getExpectedDesiredStateForState(newState) - if (desiredState) { - updateData.desiredState = desiredState - } - - await this.sandboxRepository.updateWhere(sandbox.id, { - updateData, - whereCondition: { pending: false, state: oldState, desiredState: oldDesiredState }, - }) - } - - @OnEvent(WarmPoolEvents.TOPUP_REQUESTED) - private async createWarmPoolSandbox(event: WarmPoolTopUpRequested) { - await this.createForWarmPool(event.warmPool) - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'handle-unschedulable-runners' }) - @LogExecution('handle-unschedulable-runners') - @WithInstrumentation() - private async handleUnschedulableRunners() { - const runners = await this.runnerRepository.find({ where: { unschedulable: true } }) - - if (runners.length === 0) { - return - } - - // find all sandboxes that are using the unschedulable runners and have organizationId = '00000000-0000-0000-0000-000000000000' - const sandboxes = await this.sandboxRepository.find({ - where: { - runnerId: In(runners.map((runner) => runner.id)), - organizationId: '00000000-0000-0000-0000-000000000000', - state: SandboxState.STARTED, - desiredState: Not(SandboxDesiredState.DESTROYED), - }, - }) - - if (sandboxes.length === 0) { - return - } - - const destroyPromises = sandboxes.map((sandbox) => this.destroy(sandbox.id)) - const results = await Promise.allSettled(destroyPromises) - - // Log any failed sandbox destructions - results.forEach((result, index) => { - if (result.status === 'rejected') { - this.logger.error(`Failed to destroy sandbox ${sandboxes[index].id}: ${result.reason}`) - } - }) - } - - async isSandboxPublic(sandboxId: string): Promise { - const sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, - }) - - if (!sandbox) { - throw new NotFoundException(`Sandbox with ID ${sandboxId} not found`) - } - - return sandbox.public - } - - @OnEvent(OrganizationEvents.SUSPENDED_SANDBOX_STOPPED) - async handleSuspendedSandboxStopped(event: OrganizationSuspendedSandboxStoppedEvent) { - await this.stop(event.sandboxId).catch((error) => { - // log the error for now, but don't throw it as it will be retried - this.logger.error(`Error stopping sandbox from suspended organization. SandboxId: ${event.sandboxId}: `, error) - }) - } - - private resolveAutoStopInterval(autoStopInterval: number): number { - if (autoStopInterval < 0) { - throw new BadRequestError('Auto-stop interval must be non-negative') - } - - return autoStopInterval - } - - private resolveAutoArchiveInterval(autoArchiveInterval: number): number { - if (autoArchiveInterval < 0) { - throw new BadRequestError('Auto-archive interval must be non-negative') - } - - const maxAutoArchiveInterval = this.configService.getOrThrow('maxAutoArchiveInterval') - - if (autoArchiveInterval === 0) { - return maxAutoArchiveInterval - } - - return Math.min(autoArchiveInterval, maxAutoArchiveInterval) - } - - private resolveNetworkAllowList(networkAllowList: string): string { - try { - validateNetworkAllowList(networkAllowList) - } catch (error) { - throw new BadRequestError(error instanceof Error ? error.message : 'Invalid network allow list') - } - - return networkAllowList - } - - private resolveVolumes(volumes: SandboxVolume[]): SandboxVolume[] { - try { - validateMountPaths(volumes) - } catch (error) { - throw new BadRequestError(error instanceof Error ? error.message : 'Invalid volume mount configuration') - } - - try { - validateSubpaths(volumes) - } catch (error) { - throw new BadRequestError(error instanceof Error ? error.message : 'Invalid volume subpath configuration') - } - - return volumes - } - - async createSshAccess( - sandboxIdOrName: string, - expiresInMinutes = 60, - organizationId?: string, - ): Promise { - // check if sandbox exists - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - // Revoke any existing SSH access for this sandbox - await this.revokeSshAccess(sandbox.id) - - const sshAccess = new SshAccess() - sshAccess.sandboxId = sandbox.id - // Generate a safe token that can't doesn't have _ or - to avoid CLI issues - sshAccess.token = customNanoid(urlAlphabet.replace('_', '').replace('-', ''))(32) - sshAccess.expiresAt = new Date(Date.now() + expiresInMinutes * 60 * 1000) - - await this.sshAccessRepository.save(sshAccess) - - const region = await this.regionService.findOne(sandbox.region, true) - if (region && region.sshGatewayUrl) { - return SshAccessDto.fromSshAccess(sshAccess, region.sshGatewayUrl) - } - - return SshAccessDto.fromSshAccess(sshAccess, this.configService.getOrThrow('sshGateway.url')) - } - - async revokeSshAccess(sandboxIdOrName: string, token?: string, organizationId?: string): Promise { - const sandbox = await this.findOneByIdOrName(sandboxIdOrName, organizationId) - - if (token) { - // Revoke specific SSH access by token - await this.sshAccessRepository.delete({ sandboxId: sandbox.id, token }) - } else { - // Revoke all SSH access for the sandbox - await this.sshAccessRepository.delete({ sandboxId: sandbox.id }) - } - - return sandbox - } - - async validateSshAccess(token: string): Promise { - const sshAccess = await this.sshAccessRepository.findOne({ - where: { - token, - }, - relations: ['sandbox'], - }) - - if (!sshAccess) { - return { valid: false, sandboxId: null } - } - - // Check if token is expired - const isExpired = sshAccess.expiresAt < new Date() - if (isExpired) { - return { valid: false, sandboxId: null } - } - - // Get runner information if sandbox exists - if (sshAccess.sandbox && sshAccess.sandbox.runnerId) { - const runner = await this.runnerService.findOne(sshAccess.sandbox.runnerId) - - if (runner) { - return { - valid: true, - sandboxId: sshAccess.sandbox.id, - } - } - } - - return { valid: true, sandboxId: sshAccess.sandbox.id } - } - - async updateSandboxBackupState( - sandboxId: string, - backupState: BackupState, - backupSnapshot?: string | null, - backupRegistryId?: string | null, - backupErrorReason?: string | null, - ): Promise { - const sandboxToUpdate = await this.sandboxRepository.findOneByOrFail({ - id: sandboxId, - }) - - const updateData = Sandbox.getBackupStateUpdate( - sandboxToUpdate, - backupState, - backupSnapshot, - backupRegistryId, - backupErrorReason, - ) - - await this.sandboxRepository.update(sandboxId, { updateData, entity: sandboxToUpdate }) - } -} diff --git a/apps/api/src/sandbox/services/snapshot.service.ts b/apps/api/src/sandbox/services/snapshot.service.ts deleted file mode 100644 index abd744299..000000000 --- a/apps/api/src/sandbox/services/snapshot.service.ts +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Injectable, - NotFoundException, - ConflictException, - ForbiddenException, - BadRequestException, - Logger, -} from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository, Not, In, Raw, ILike, IsNull, FindOptionsWhere, Like, LessThan } from 'typeorm' -import { v4 as uuidv4, validate as isUUID } from 'uuid' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotState } from '../enums/snapshot-state.enum' -import { CreateSnapshotDto } from '../dto/create-snapshot.dto' -import { BuildInfo } from '../entities/build-info.entity' -import { generateBuildInfoHash as generateBuildSnapshotRef } from '../entities/build-info.entity' -import { Cron, CronExpression } from '@nestjs/schedule' -import { EventEmitter2, OnEvent } from '@nestjs/event-emitter' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxCreatedEvent } from '../events/sandbox-create.event' -import { Organization } from '../../organization/entities/organization.entity' -import { OrganizationService } from '../../organization/services/organization.service' -import { SnapshotRunner } from '../entities/snapshot-runner.entity' -import { SandboxState } from '../enums/sandbox-state.enum' -import { OrganizationEvents } from '../../organization/constants/organization-events.constant' -import { OrganizationSuspendedSnapshotDeactivatedEvent } from '../../organization/events/organization-suspended-snapshot-deactivated.event' -import { SnapshotRunnerState } from '../enums/snapshot-runner-state.enum' -import { PaginatedList } from '../../common/interfaces/paginated-list.interface' -import { OrganizationUsageService } from '../../organization/services/organization-usage.service' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { SnapshotSortDirection, SnapshotSortField } from '../dto/list-snapshots-query.dto' -import { PER_SANDBOX_LIMIT_MESSAGE } from '../../common/constants/error-messages' -import { DockerRegistryService } from '../../docker-registry/services/docker-registry.service' -import { DefaultRegionRequiredException } from '../../organization/exceptions/DefaultRegionRequiredException' -import { Region } from '../../region/entities/region.entity' -import { RunnerState } from '../enums/runner-state.enum' -import { OnAsyncEvent } from '../../common/decorators/on-async-event.decorator' -import { RunnerEvents } from '../constants/runner-events' -import { RunnerDeletedEvent } from '../events/runner-deleted.event' -import { SnapshotRegion } from '../entities/snapshot-region.entity' -import { RegionType } from '../../region/enums/region-type.enum' -import { SnapshotEvents } from '../constants/snapshot-events' -import { SnapshotCreatedEvent } from '../events/snapshot-created.event' -import { RunnerService } from './runner.service' -import { RegionService } from '../../region/services/region.service' -import { TypedConfigService } from '../../config/typed-config.service' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { SnapshotActivatedEvent } from '../events/snapshot-activated.event' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' - -const IMAGE_NAME_REGEX = /^[a-zA-Z0-9_.\-:]+(\/[a-zA-Z0-9_.\-:]+)*(@sha256:[a-f0-9]{64})?$/ -@Injectable() -export class SnapshotService { - private readonly logger = new Logger(SnapshotService.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - @InjectRepository(Snapshot) - private readonly snapshotRepository: Repository, - @InjectRepository(BuildInfo) - private readonly buildInfoRepository: Repository, - @InjectRepository(SnapshotRunner) - private readonly snapshotRunnerRepository: Repository, - @InjectRepository(Region) - private readonly regionRepository: Repository, - @InjectRepository(SnapshotRegion) - private readonly snapshotRegionRepository: Repository, - private readonly organizationService: OrganizationService, - private readonly organizationUsageService: OrganizationUsageService, - private readonly redisLockProvider: RedisLockProvider, - private readonly runnerService: RunnerService, - private readonly regionService: RegionService, - private readonly dockerRegistryService: DockerRegistryService, - private readonly eventEmitter: EventEmitter2, - private readonly configService: TypedConfigService, - ) {} - - private validateImageName(name: string): string | null { - // Check for digest format (@sha256:hash) - if (name.includes('@sha256:')) { - const [imageName, digest] = name.split('@sha256:') - if (!imageName || !digest || !/^[a-f0-9]{64}$/.test(digest)) { - return 'Invalid digest format. Must be image@sha256:64_hex_characters' - } - return null - } - - // Handle tag format (image:tag) - if (!name.includes(':') || name.endsWith(':') || /:\s*$/.test(name)) { - return 'Image name must include a tag (e.g., ubuntu:22.04) or digest (@sha256:...)' - } - - if (name.endsWith(':latest')) { - return 'Images with tag ":latest" are not allowed' - } - - if (!IMAGE_NAME_REGEX.test(name)) { - return 'Invalid image name format. Must be lowercase, may contain digits, dots, dashes, and single slashes between components' - } - - return null - } - - private validateSnapshotName(name: string): string | null { - if (!IMAGE_NAME_REGEX.test(name)) { - return 'Invalid snapshot name format. May contain letters, digits, dots, colons, and dashes' - } - - return null - } - - private processEntrypoint(entrypoint?: string[]): string[] | undefined { - if (!entrypoint || entrypoint.length === 0) { - return undefined - } - - // Filter out empty strings from the array - const filteredEntrypoint = entrypoint.filter((cmd) => cmd && cmd.trim().length > 0) - - return filteredEntrypoint.length > 0 ? filteredEntrypoint : undefined - } - - private async readySnapshotRunnerExists(ref: string, regionId: string): Promise { - return await this.snapshotRunnerRepository - .createQueryBuilder('sr') - .innerJoin('runner', 'r', 'r.id::text = sr."runnerId"::text') - .where('sr."snapshotRef" = :ref', { ref }) - .andWhere('sr.state = :snapshotRunnerState', { snapshotRunnerState: SnapshotRunnerState.READY }) - .andWhere('r.region = :regionId', { regionId }) - .andWhere('r.state = :runnerState', { runnerState: RunnerState.READY }) - .andWhere('r.unschedulable = false') - .getExists() - } - - async createFromPull(organization: Organization, createSnapshotDto: CreateSnapshotDto, general = false) { - if (!organization.defaultRegionId) { - throw new DefaultRegionRequiredException() - } - - const regionId = await this.getValidatedOrDefaultRegionId(organization, createSnapshotDto.regionId) - - let pendingSnapshotCountIncrement: number | undefined - - if (!createSnapshotDto.imageName) { - throw new BadRequestException('Must specify an image name') - } - - try { - const entrypoint = createSnapshotDto.entrypoint - const ref: string | undefined = undefined - const state: SnapshotState = SnapshotState.PENDING - - const nameValidationError = this.validateSnapshotName(createSnapshotDto.name) - if (nameValidationError) { - throw new BadRequestException(nameValidationError) - } - - const imageValidationError = this.validateImageName(createSnapshotDto.imageName) - if (imageValidationError) { - throw new BadRequestException(imageValidationError) - } - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const newSnapshotCount = 1 - - const { pendingSnapshotCountIncremented } = await this.validateOrganizationQuotas( - organization, - newSnapshotCount, - createSnapshotDto.cpu, - createSnapshotDto.memory, - createSnapshotDto.disk, - ) - - if (pendingSnapshotCountIncremented) { - pendingSnapshotCountIncrement = newSnapshotCount - } - - try { - const snapshotId = uuidv4() - - const snapshot = this.snapshotRepository.create({ - id: snapshotId, - organizationId: organization.id, - ...createSnapshotDto, - entrypoint: this.processEntrypoint(entrypoint), - mem: createSnapshotDto.memory, // Map memory to mem - state, - ref, - general, - snapshotRegions: [{ snapshotId, regionId }], - }) - - const savedSnapshot = await this.snapshotRepository.save(snapshot) - - this.eventEmitter.emit(SnapshotEvents.CREATED, new SnapshotCreatedEvent(savedSnapshot)) - - return savedSnapshot - } catch (error) { - if (error.code === '23505') { - // PostgreSQL unique violation error code - throw new ConflictException( - `Snapshot with name "${createSnapshotDto.name}" already exists for this organization`, - ) - } - throw error - } - } catch (error) { - await this.rollbackPendingUsage(organization.id, pendingSnapshotCountIncrement) - throw error - } - } - - async createFromBuildInfo(organization: Organization, createSnapshotDto: CreateSnapshotDto, general = false) { - if (!organization.defaultRegionId) { - throw new DefaultRegionRequiredException() - } - - const regionId = await this.getValidatedOrDefaultRegionId(organization, createSnapshotDto.regionId) - - let pendingSnapshotCountIncrement: number | undefined - let entrypoint: string[] | undefined = undefined - - try { - const nameValidationError = this.validateSnapshotName(createSnapshotDto.name) - if (nameValidationError) { - throw new BadRequestException(nameValidationError) - } - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const newSnapshotCount = 1 - - const { pendingSnapshotCountIncremented } = await this.validateOrganizationQuotas( - organization, - newSnapshotCount, - createSnapshotDto.cpu, - createSnapshotDto.memory, - createSnapshotDto.disk, - ) - - if (pendingSnapshotCountIncremented) { - pendingSnapshotCountIncrement = newSnapshotCount - } - - entrypoint = this.getEntrypointFromDockerfile(createSnapshotDto.buildInfo.dockerfileContent) - - const snapshotId = uuidv4() - - const snapshot = this.snapshotRepository.create({ - id: snapshotId, - organizationId: organization.id, - ...createSnapshotDto, - entrypoint: this.processEntrypoint(entrypoint), - mem: createSnapshotDto.memory, // Map memory to mem - state: SnapshotState.PENDING, - general, - snapshotRegions: [{ snapshotId, regionId }], - }) - - const buildSnapshotRef = generateBuildSnapshotRef( - createSnapshotDto.buildInfo.dockerfileContent, - createSnapshotDto.buildInfo.contextHashes, - ) - - // Check if buildInfo with the same snapshotRef already exists - const existingBuildInfo = await this.buildInfoRepository.findOne({ - where: { snapshotRef: buildSnapshotRef }, - }) - - if (existingBuildInfo) { - snapshot.buildInfo = existingBuildInfo - // Update lastUsed once per minute at most - if (await this.redisLockProvider.lock(`build-info:${existingBuildInfo.snapshotRef}:update`, 60)) { - existingBuildInfo.lastUsedAt = new Date() - await this.buildInfoRepository.save(existingBuildInfo) - } - } else { - const buildInfoEntity = this.buildInfoRepository.create({ - ...createSnapshotDto.buildInfo, - }) - await this.buildInfoRepository.save(buildInfoEntity) - snapshot.buildInfo = buildInfoEntity - } - - const internalRegistry = await this.dockerRegistryService.getAvailableInternalRegistry(regionId) - if (!internalRegistry) { - throw new Error('No internal registry found for snapshot') - } - snapshot.ref = `${internalRegistry.url.replace(/^(https?:\/\/)/, '')}/${internalRegistry.project || 'boxlite'}/${buildSnapshotRef}` - - const exists = await this.readySnapshotRunnerExists(snapshot.ref, regionId) - - if (exists) { - const existingSnapshot = await this.snapshotRepository.findOne({ - where: { ref: snapshot.ref, size: Not(IsNull()) }, - select: ['id', 'size'], - }) - - if (existingSnapshot?.size != null) { - if (existingSnapshot.size > organization.maxSnapshotSize) { - throw new BadRequestException( - `Snapshot size (${existingSnapshot.size.toFixed(2)}GB) exceeds maximum allowed size of ${organization.maxSnapshotSize}GB`, - ) - } - snapshot.size = existingSnapshot.size - snapshot.state = SnapshotState.ACTIVE - snapshot.lastUsedAt = new Date() - } - } - - try { - const savedSnapshot = await this.snapshotRepository.save(snapshot) - - this.eventEmitter.emit(SnapshotEvents.CREATED, new SnapshotCreatedEvent(savedSnapshot)) - - return savedSnapshot - } catch (error) { - if (error.code === '23505') { - // PostgreSQL unique violation error code - throw new ConflictException( - `Snapshot with name "${createSnapshotDto.name}" already exists for this organization`, - ) - } - throw error - } - } catch (error) { - await this.rollbackPendingUsage(organization.id, pendingSnapshotCountIncrement) - throw error - } - } - - async removeSnapshot(snapshotId: string) { - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotId} not found`) - } - if (snapshot.general) { - throw new ForbiddenException('You cannot delete a general snapshot') - } - snapshot.state = SnapshotState.REMOVING - await this.snapshotRepository.save(snapshot) - } - - async getAllSnapshots( - organizationId: string, - page = 1, - limit = 10, - filters?: { name?: string }, - sort?: { field?: SnapshotSortField; direction?: SnapshotSortDirection }, - ): Promise> { - const pageNum = Number(page) - const limitNum = Number(limit) - - const { name } = filters || {} - const { field: sortField, direction: sortDirection } = sort || {} - - const baseFindOptions: FindOptionsWhere = { - ...(name ? { name: ILike(`%${name}%`) } : {}), - } - - // Retrieve all snapshots belonging to the organization as well as all general snapshots - const where: FindOptionsWhere[] = [ - { - ...baseFindOptions, - organizationId, - }, - { - ...baseFindOptions, - general: true, - hideFromUsers: false, - }, - ] - - const [items, total] = await this.snapshotRepository.findAndCount({ - where, - relations: ['snapshotRegions'], - order: { - general: 'ASC', // Sort general snapshots last - [sortField]: { - direction: sortDirection, - nulls: 'LAST', - }, - ...(sortField !== SnapshotSortField.CREATED_AT && { createdAt: 'DESC' }), - }, - skip: (pageNum - 1) * limitNum, - take: limitNum, - }) - - // Filter out snapshot regions that are not available to the organization - const availableRegions = await this.organizationService.listAvailableRegions(organizationId) - const availableRegionIds = new Set(availableRegions.map((r) => r.id)) - - for (const snapshot of items) { - if (snapshot.snapshotRegions) { - snapshot.snapshotRegions = snapshot.snapshotRegions.filter((sr) => availableRegionIds.has(sr.regionId)) - } - } - - return { - items, - total, - page: pageNum, - totalPages: Math.ceil(total / limit), - } - } - - async getSnapshot(snapshotId: string): Promise { - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotId} not found`) - } - - return snapshot - } - - async getSnapshotWithRegions(snapshotIdOrName: string, organizationId: string): Promise { - const where: FindOptionsWhere[] = [ - { name: snapshotIdOrName, organizationId }, - { name: snapshotIdOrName, general: true }, - ] - if (isUUID(snapshotIdOrName)) { - where.push({ id: snapshotIdOrName }) - } - - const snapshot = await this.snapshotRepository.findOne({ - where, - relations: ['snapshotRegions'], - order: { general: 'ASC' }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotIdOrName} not found`) - } - - const availableRegions = await this.organizationService.listAvailableRegions(organizationId) - const availableRegionIds = new Set(availableRegions.map((r) => r.id)) - if (snapshot.snapshotRegions) { - snapshot.snapshotRegions = snapshot.snapshotRegions.filter((sr) => availableRegionIds.has(sr.regionId)) - } - - return snapshot - } - - async getSnapshotByName(snapshotName: string, organizationId: string): Promise { - const snapshot = await this.snapshotRepository.findOne({ - where: { name: snapshotName, organizationId }, - }) - - if (!snapshot) { - // check if the snapshot is general - const generalSnapshot = await this.snapshotRepository.findOne({ - where: { name: snapshotName, general: true }, - }) - if (generalSnapshot) { - return generalSnapshot - } - - throw new NotFoundException(`Snapshot with name ${snapshotName} not found`) - } - - return snapshot - } - - async setSnapshotGeneralStatus(snapshotId: string, general: boolean) { - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotId} not found`) - } - - snapshot.general = general - return await this.snapshotRepository.save(snapshot) - } - - async getBuildLogsUrl(snapshot: Snapshot): Promise { - if (!snapshot.initialRunnerId) { - throw new NotFoundException(`Snapshot ${snapshot.id} has no initial runner`) - } - - const runner = await this.runnerService.findOneOrFail(snapshot.initialRunnerId) - const region = await this.regionService.findOne(runner.region, true) - - if (!region) { - throw new NotFoundException(`Region for initial runner for snapshot ${snapshot.id} not found`) - } - - if (!region.proxyUrl) { - return `${this.configService.getOrThrow('proxy.protocol')}://${this.configService.getOrThrow('proxy.domain')}/snapshots/${snapshot.id}/build-logs` - } - - return region.proxyUrl + '/snapshots/' + snapshot.id + '/build-logs' - } - - private async validateOrganizationQuotas( - organization: Organization, - addedSnapshotCount: number, - cpu?: number, - memory?: number, - disk?: number, - ): Promise<{ - pendingSnapshotCountIncremented: boolean - }> { - // validate per-sandbox quotas - if (cpu && cpu > organization.maxCpuPerSandbox) { - throw new ForbiddenException( - `CPU request ${cpu} exceeds maximum allowed per sandbox (${organization.maxCpuPerSandbox}).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (memory && memory > organization.maxMemoryPerSandbox) { - throw new ForbiddenException( - `Memory request ${memory}GB exceeds maximum allowed per sandbox (${organization.maxMemoryPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - if (disk && disk > organization.maxDiskPerSandbox) { - throw new ForbiddenException( - `Disk request ${disk}GB exceeds maximum allowed per sandbox (${organization.maxDiskPerSandbox}GB).\n${PER_SANDBOX_LIMIT_MESSAGE}`, - ) - } - - // validate usage quotas - await this.organizationUsageService.incrementPendingSnapshotUsage(organization.id, addedSnapshotCount) - - const usageOverview = await this.organizationUsageService.getSnapshotUsageOverview(organization.id) - - try { - if (usageOverview.currentSnapshotUsage + usageOverview.pendingSnapshotUsage > organization.snapshotQuota) { - throw new ForbiddenException(`Snapshot quota exceeded. Maximum allowed: ${organization.snapshotQuota}`) - } - } catch (error) { - await this.rollbackPendingUsage(organization.id, addedSnapshotCount) - throw error - } - - return { - pendingSnapshotCountIncremented: true, - } - } - - async rollbackPendingUsage(organizationId: string, pendingSnapshotCountIncrement?: number): Promise { - if (!pendingSnapshotCountIncrement) { - return - } - - try { - await this.organizationUsageService.decrementPendingSnapshotUsage(organizationId, pendingSnapshotCountIncrement) - } catch (error) { - this.logger.error(`Error rolling back pending snapshot usage: ${error}`) - } - } - - @OnEvent(SandboxEvents.CREATED) - private async handleSandboxCreatedEvent(event: SandboxCreatedEvent) { - if (!event.sandbox.snapshot) { - return - } - - // Update once per minute at most - if (!(await this.redisLockProvider.lock(`snapshot:${event.sandbox.snapshot}:update-last-used`, 60))) { - return - } - - const snapshot = await this.getSnapshotByName(event.sandbox.snapshot, event.sandbox.organizationId) - snapshot.lastUsedAt = event.sandbox.createdAt - await this.snapshotRepository.save(snapshot) - } - - async activateSnapshot(snapshotId: string, organization: Organization): Promise { - const lockKey = `snapshot:${snapshotId}:activate` - await this.redisLockProvider.waitForLock(lockKey, 60) - - let pendingSnapshotCountIncrement: number | undefined - - try { - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotId} not found`) - } - - if (snapshot.state === SnapshotState.ACTIVE) { - throw new BadRequestException(`Snapshot ${snapshotId} is already active`) - } - - if (snapshot.state !== SnapshotState.INACTIVE) { - throw new BadRequestException(`Snapshot ${snapshotId} cannot be activated - it is in ${snapshot.state} state`) - } - - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const activatedSnapshotCount = 1 - - const { pendingSnapshotCountIncremented } = await this.validateOrganizationQuotas( - organization, - activatedSnapshotCount, - snapshot.cpu, - snapshot.mem, - snapshot.disk, - ) - - if (pendingSnapshotCountIncremented) { - pendingSnapshotCountIncrement = activatedSnapshotCount - } - - snapshot.state = SnapshotState.PENDING - const savedSnapshot = await this.snapshotRepository.save(snapshot) - - this.eventEmitter.emit(SnapshotEvents.ACTIVATED, new SnapshotActivatedEvent(savedSnapshot)) - - return savedSnapshot - } catch (error) { - await this.rollbackPendingUsage(organization.id, pendingSnapshotCountIncrement) - throw error - } finally { - await this.redisLockProvider.unlock(lockKey) - } - } - - async canCleanupImage(imageName: string): Promise { - const snapshot = await this.snapshotRepository.findOne({ - where: { - state: Not(In([SnapshotState.ERROR, SnapshotState.BUILD_FAILED])), - ref: imageName, - }, - }) - - if (snapshot) { - return false - } - - const sandbox = await this.sandboxRepository.findOne({ - where: [ - { - existingBackupSnapshots: Raw((alias) => `${alias} @> '[{"snapshotName":"${imageName}"}]'::jsonb`), - }, - { - existingBackupSnapshots: Raw((alias) => `${alias} @> '[{"imageName":"${imageName}"}]'::jsonb`), - }, - { - backupSnapshot: imageName, - }, - ], - }) - - if (sandbox && sandbox.state !== SandboxState.DESTROYED) { - return false - } - - return true - } - - async deactivateSnapshot(snapshotId: string): Promise { - const snapshot = await this.snapshotRepository.findOne({ - where: { id: snapshotId }, - }) - - if (!snapshot) { - throw new NotFoundException(`Snapshot ${snapshotId} not found`) - } - - if (snapshot.state === SnapshotState.INACTIVE) { - return - } - - snapshot.state = SnapshotState.INACTIVE - await this.snapshotRepository.save(snapshot) - - try { - const countActiveSnapshots = await this.snapshotRepository.count({ - where: { - state: SnapshotState.ACTIVE, - ref: snapshot.ref, - }, - }) - - if (countActiveSnapshots === 0) { - // Set associated SnapshotRunner records to REMOVING state - const result = await this.snapshotRunnerRepository.update( - { snapshotRef: snapshot.ref }, - { state: SnapshotRunnerState.REMOVING }, - ) - this.logger.debug( - `Deactivated snapshot ${snapshot.id} and marked ${result.affected} SnapshotRunners for removal`, - ) - } - } catch (error) { - this.logger.error(`Deactivated snapshot ${snapshot.id}, but failed to mark snapshot runners for removal`, error) - } - } - - // TODO: revise/cleanup - getEntrypointFromDockerfile(dockerfileContent: string): string[] { - // Match ENTRYPOINT with either a string or JSON array - const matches = [...dockerfileContent.matchAll(/ENTRYPOINT\s+(.*)/g)] - const entrypointMatch = matches.length ? matches[matches.length - 1] : null - if (entrypointMatch) { - const rawEntrypoint = entrypointMatch[1].trim() - try { - // Try parsing as JSON array - const parsed = JSON.parse(rawEntrypoint) - if (Array.isArray(parsed)) { - return parsed - } - } catch { - // Fallback: it's probably a plain string - return [rawEntrypoint.replace(/["']/g, '')] - } - } - - return ['sleep', 'infinity'] - } - - /** - * Validates and returns a region ID for snapshot availability. - * - * @param organization - The organization which is creating the snapshot. - * @param regionId - The requested region ID. If omitted, the organization's default region is used. - * @returns The validated region ID - * @throws {NotFoundException} If the requested region is not available to the organization - */ - private async getValidatedOrDefaultRegionId(organization: Organization, regionId?: string): Promise { - if (!regionId) { - return organization.defaultRegionId - } - - const region = await this.regionRepository.findOne({ - where: { id: regionId }, - }) - - if (!region) { - throw new NotFoundException('Region not found') - } - - const availableRegions = await this.organizationService.listAvailableRegions(organization.id) - - if (!availableRegions.some((r) => r.id === regionId)) { - if (region.regionType === RegionType.SHARED) { - // region is public, but the organization does not have a quota for it - throw new ForbiddenException(`Region ${regionId} is not available to the organization`) - } else { - // region is not public, respond as if the region was not found - throw new NotFoundException('Region not found') - } - } - - return regionId - } - - /** - * @param snapshotId - * @returns The regions where the snapshot is configured to be propagated to. - */ - async getSnapshotRegions(snapshotId: string): Promise { - return await this.regionRepository - .createQueryBuilder('r') - .innerJoin('snapshot_region', 'sr', 'sr."regionId" = r.id') - .where('sr."snapshotId" = :snapshotId', { snapshotId }) - .getMany() - } - - /** - * @param snapshotId - The ID of the snapshot. - * @param regionId - The ID of the region. - * @returns true if the snapshot is available in the region, false otherwise. - */ - async isAvailableInRegion(snapshotId: string, regionId: string): Promise { - return await this.snapshotRegionRepository.exists({ - where: { - snapshotId, - regionId, - }, - }) - } - - @OnEvent(OrganizationEvents.SUSPENDED_SNAPSHOT_DEACTIVATED) - async handleSuspendedOrganizationSnapshotDeactivated(event: OrganizationSuspendedSnapshotDeactivatedEvent) { - await this.deactivateSnapshot(event.snapshotId).catch((error) => { - // log the error for now, but don't throw it as it will be retried - this.logger.error( - `Error deactivating snapshot from suspended organization. SnapshotId: ${event.snapshotId}: `, - error, - ) - }) - } - - @OnAsyncEvent({ - event: RunnerEvents.DELETED, - }) - async handleRunnerDeletedEvent(payload: RunnerDeletedEvent): Promise { - await payload.entityManager.update( - SnapshotRunner, - { runnerId: payload.runnerId }, - { state: SnapshotRunnerState.REMOVING }, - ) - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'cleanup-failed-snapshot-runners' }) - @LogExecution('cleanup-failed-snapshot-runners') - @WithInstrumentation() - async cleanupFailedSnapshotRunners() { - const retentionHours = this.configService.getOrThrow('failedSnapshotRunnerRetentionHours') - const cutoff = new Date() - cutoff.setHours(cutoff.getHours() - retentionHours) - - const result = await this.snapshotRunnerRepository.delete({ - snapshotRef: Like('boxlite-%'), - state: SnapshotRunnerState.ERROR, - updatedAt: LessThan(cutoff), - }) - - if (result.affected > 0) { - this.logger.debug(`Cleaned up ${result.affected} failed snapshot runners`) - } - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'cleanup-decommissioned-snapshot-runners' }) - @LogExecution('cleanup-decommissioned-snapshot-runners') - @WithInstrumentation() - async cleanupDecommissionedSnapshotRunners() { - const cutoff = new Date() - cutoff.setHours(cutoff.getHours() - 1) - - const snapshotRunners = await this.snapshotRunnerRepository - .createQueryBuilder('sr') - .innerJoin('runner', 'r', 'r.id::text = sr."runnerId"::text') - .where('r.state = :runnerState', { runnerState: RunnerState.DECOMMISSIONED }) - .andWhere('sr."updatedAt" < :cutoff', { cutoff }) - .select('sr.id') - .take(500) - .getMany() - - if (snapshotRunners.length === 0) { - return - } - - const ids = snapshotRunners.map((sr) => sr.id) - await this.snapshotRunnerRepository.delete(ids) - - this.logger.debug(`Cleaned up ${ids.length} snapshot runners from decommissioned runners`) - } -} diff --git a/apps/api/src/sandbox/services/toolbox.deprecated.service.ts b/apps/api/src/sandbox/services/toolbox.deprecated.service.ts deleted file mode 100644 index 9e2d55b7a..000000000 --- a/apps/api/src/sandbox/services/toolbox.deprecated.service.ts +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, NotFoundException, HttpException, BadRequestException, Logger } from '@nestjs/common' -import { Sandbox } from '../entities/sandbox.entity' -import { Runner } from '../entities/runner.entity' -import axios from 'axios' -import { SandboxState } from '../enums/sandbox-state.enum' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { SandboxService } from './sandbox.service' -import { RunnerService } from './runner.service' -import { SandboxRepository } from '../repositories/sandbox.repository' - -@Injectable() -export class ToolboxService { - private readonly logger = new Logger(ToolboxService.name) - - constructor( - private readonly sandboxRepository: SandboxRepository, - private readonly redisLockProvider: RedisLockProvider, - private readonly sandboxService: SandboxService, - private readonly runnerService: RunnerService, - ) {} - - async forwardRequestToRunner(sandboxId: string, method: string, path: string, data?: any): Promise { - const runner = await this.getRunner(sandboxId) - - if (!runner.proxyUrl) { - throw new NotFoundException(`Runner for sandbox ${sandboxId} has no proxy URL`) - } - - const maxRetries = 5 - let attempt = 1 - - while (attempt <= maxRetries) { - try { - const headers: any = { - Authorization: `Bearer ${runner.apiKey}`, - } - - // Only set Content-Type for requests with body data - if (data && typeof data === 'object' && Object.keys(data).length > 0) { - headers['Content-Type'] = 'application/json' - } - - const requestConfig: any = { - method, - url: `${runner.proxyUrl}/sandboxes/${sandboxId}${path}`, - headers, - maxBodyLength: 209715200, // 200MB in bytes - maxContentLength: 209715200, // 200MB in bytes - timeout: 360000, // 360 seconds - } - - // Only add data if it's not an empty string or undefined - if (data !== undefined && data !== '') { - requestConfig.data = data - } - - const response = await axios(requestConfig) - return response.data - } catch (error) { - if (error.message.includes('ECONNREFUSED')) { - if (attempt === maxRetries) { - throw new HttpException('Failed to connect to runner after multiple attempts', 500) - } - // Wait for attempt * 1000ms (1s, 2s, 3s) - await new Promise((resolve) => setTimeout(resolve, attempt * 1000)) - attempt++ - continue - } - // If it's an axios error with a response, throw a NestJS HttpException - if (error.response) { - throw new HttpException(error.response.data, error.response.status) - } - - // For other types of errors, throw a generic 500 error - throw new HttpException(`Error forwarding request to runner: ${error.message}`, 500) - } - } - } - - public async getRunner(sandboxId: string): Promise { - let sandbox: Sandbox | null = null - try { - sandbox = await this.sandboxRepository.findOne({ - where: { id: sandboxId }, - }) - - if (!sandbox) { - throw new NotFoundException('Sandbox not found') - } - - const runner = await this.runnerService.findOneOrFail(sandbox.runnerId) - - if (sandbox.state !== SandboxState.STARTED) { - throw new BadRequestException('Sandbox is not running') - } - - return runner - } finally { - const lockKey = `sandbox-last-activity-${sandboxId}` - const acquired = await this.redisLockProvider.lock(lockKey, 10) - - // redis for cooldown period - 10 seconds - // prevents database flooding when multiple requests are made at the same time - if (acquired) { - await this.sandboxService.updateLastActivityAt(sandboxId, new Date()) - } - } - } -} diff --git a/apps/api/src/sandbox/services/volume.service.ts b/apps/api/src/sandbox/services/volume.service.ts deleted file mode 100644 index e6c1b1af0..000000000 --- a/apps/api/src/sandbox/services/volume.service.ts +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - ConflictException, - ForbiddenException, - Injectable, - Logger, - NotFoundException, - ServiceUnavailableException, -} from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { Repository, Not, In } from 'typeorm' -import { Volume } from '../entities/volume.entity' -import { VolumeState } from '../enums/volume-state.enum' -import { CreateVolumeDto } from '../dto/create-volume.dto' -import { v4 as uuidv4 } from 'uuid' -import { BadRequestError } from '../../exceptions/bad-request.exception' -import { Organization } from '../../organization/entities/organization.entity' -import { OnEvent } from '@nestjs/event-emitter' -import { SandboxEvents } from '../constants/sandbox-events.constants' -import { SandboxCreatedEvent } from '../events/sandbox-create.event' -import { OrganizationService } from '../../organization/services/organization.service' -import { OrganizationUsageService } from '../../organization/services/organization-usage.service' -import { TypedConfigService } from '../../config/typed-config.service' -import { RedisLockProvider } from '../common/redis-lock.provider' -import { SandboxRepository } from '../repositories/sandbox.repository' -import { SandboxDesiredState } from '../enums/sandbox-desired-state.enum' - -@Injectable() -export class VolumeService { - private readonly logger = new Logger(VolumeService.name) - - constructor( - @InjectRepository(Volume) - private readonly volumeRepository: Repository, - private readonly sandboxRepository: SandboxRepository, - private readonly organizationService: OrganizationService, - private readonly organizationUsageService: OrganizationUsageService, - private readonly configService: TypedConfigService, - private readonly redisLockProvider: RedisLockProvider, - ) {} - - private async validateOrganizationQuotas( - organization: Organization, - addedVolumeCount: number, - ): Promise<{ - pendingVolumeCountIncremented: boolean - }> { - // validate usage quotas - await this.organizationUsageService.incrementPendingVolumeUsage(organization.id, addedVolumeCount) - - const usageOverview = await this.organizationUsageService.getVolumeUsageOverview(organization.id) - - try { - if (usageOverview.currentVolumeUsage + usageOverview.pendingVolumeUsage > organization.volumeQuota) { - throw new ForbiddenException(`Volume quota exceeded. Maximum allowed: ${organization.volumeQuota}`) - } - } catch (error) { - await this.rollbackPendingUsage(organization.id, addedVolumeCount) - throw error - } - - return { - pendingVolumeCountIncremented: true, - } - } - - async rollbackPendingUsage(organizationId: string, pendingVolumeCountIncrement?: number): Promise { - if (!pendingVolumeCountIncrement) { - return - } - - try { - await this.organizationUsageService.decrementPendingVolumeUsage(organizationId, pendingVolumeCountIncrement) - } catch (error) { - this.logger.error(`Error rolling back pending volume usage: ${error}`) - } - } - - async create(organization: Organization, createVolumeDto: CreateVolumeDto): Promise { - if (!this.configService.get('s3.endpoint')) { - throw new ServiceUnavailableException('Object storage is not configured') - } - - let pendingVolumeCountIncrement: number | undefined - - try { - this.organizationService.assertOrganizationIsNotSuspended(organization) - - const newVolumeCount = 1 - - const { pendingVolumeCountIncremented } = await this.validateOrganizationQuotas(organization, newVolumeCount) - - if (pendingVolumeCountIncremented) { - pendingVolumeCountIncrement = newVolumeCount - } - - const volume = new Volume() - - // Generate ID - volume.id = uuidv4() - - // Set name from DTO or use ID as default - volume.name = createVolumeDto.name || volume.id - - // Check if volume with same name already exists for organization - const existingVolume = await this.volumeRepository.findOne({ - where: { - organizationId: organization.id, - name: volume.name, - state: Not(VolumeState.DELETED), - }, - }) - - if (existingVolume) { - throw new BadRequestError(`Volume with name ${volume.name} already exists`) - } - - volume.organizationId = organization.id - volume.state = VolumeState.PENDING_CREATE - - const savedVolume = await this.volumeRepository.save(volume) - this.logger.debug(`Created volume ${savedVolume.id} for organization ${organization.id}`) - return savedVolume - } catch (error) { - await this.rollbackPendingUsage(organization.id, pendingVolumeCountIncrement) - throw error - } - } - - async delete(volumeId: string): Promise { - const volume = await this.volumeRepository.findOne({ - where: { - id: volumeId, - }, - }) - - if (!volume) { - throw new NotFoundException(`Volume with ID ${volumeId} not found`) - } - - if (volume.state !== VolumeState.READY && volume.state !== VolumeState.ERROR) { - throw new BadRequestError( - `Volume must be in '${VolumeState.READY}' or '${VolumeState.ERROR}' state in order to be deleted`, - ) - } - - // Check if any non-destroyed sandboxes are using this volume - const sandboxUsingVolume = await this.sandboxRepository - .createQueryBuilder('sandbox') - .where('sandbox.organizationId = :organizationId', { - organizationId: volume.organizationId, - }) - .andWhere('sandbox.volumes @> :volFilter::jsonb', { - volFilter: JSON.stringify([{ volumeId }]), - }) - .andWhere('sandbox.desiredState != :destroyed', { - destroyed: SandboxDesiredState.DESTROYED, - }) - .select(['sandbox.id', 'sandbox.name']) - .getOne() - - if (sandboxUsingVolume) { - throw new ConflictException( - `Volume cannot be deleted because it is in use by one or more sandboxes (e.g. ${sandboxUsingVolume.name})`, - ) - } - - // Update state to mark as deleting - volume.state = VolumeState.PENDING_DELETE - await this.volumeRepository.save(volume) - this.logger.debug(`Marked volume ${volumeId} for deletion`) - } - - async findOne(volumeId: string): Promise { - const volume = await this.volumeRepository.findOne({ - where: { id: volumeId }, - }) - - if (!volume) { - throw new NotFoundException(`Volume with ID ${volumeId} not found`) - } - - return volume - } - - async findAll(organizationId: string, includeDeleted = false): Promise { - return this.volumeRepository.find({ - where: { - organizationId, - ...(includeDeleted ? {} : { state: Not(VolumeState.DELETED) }), - }, - order: { - lastUsedAt: { - direction: 'DESC', - nulls: 'LAST', - }, - createdAt: 'DESC', - }, - }) - } - - async findByName(organizationId: string, name: string): Promise { - const volume = await this.volumeRepository.findOne({ - where: { - organizationId, - name, - state: Not(VolumeState.DELETED), - }, - }) - - if (!volume) { - throw new NotFoundException(`Volume with name ${name} not found`) - } - - return volume - } - - async validateVolumes(organizationId: string, volumeIdOrNames: string[]): Promise { - if (!volumeIdOrNames.length) { - return - } - - const volumes = await this.volumeRepository.find({ - where: [ - { id: In(volumeIdOrNames), organizationId, state: Not(VolumeState.DELETED) }, - { name: In(volumeIdOrNames), organizationId, state: Not(VolumeState.DELETED) }, - ], - }) - - // Check if all requested volumes were found and are in a READY state - const foundIds = new Set(volumes.map((v) => v.id)) - const foundNames = new Set(volumes.map((v) => v.name)) - - for (const idOrName of volumeIdOrNames) { - if (!foundIds.has(idOrName) && !foundNames.has(idOrName)) { - throw new NotFoundException(`Volume '${idOrName}' not found`) - } - } - - for (const volume of volumes) { - if (volume.state !== VolumeState.READY) { - throw new BadRequestError(`Volume '${volume.name}' is not in a ready state. Current state: ${volume.state}`) - } - } - } - - async getOrganizationId(params: { id: string } | { name: string; organizationId: string }): Promise { - if ('id' in params) { - const volume = await this.volumeRepository.findOneOrFail({ - where: { - id: params.id, - }, - select: ['organizationId'], - loadEagerRelations: false, - }) - return volume.organizationId - } - - const volume = await this.volumeRepository.findOneOrFail({ - where: { - name: params.name, - organizationId: params.organizationId, - }, - select: ['organizationId'], - loadEagerRelations: false, - }) - - return volume.organizationId - } - - @OnEvent(SandboxEvents.CREATED) - private async handleSandboxCreatedEvent(event: SandboxCreatedEvent) { - if (!event.sandbox.volumes.length) { - return - } - - try { - const volumeIds = event.sandbox.volumes.map((vol) => vol.volumeId) - const volumes = await this.volumeRepository.find({ where: { id: In(volumeIds) } }) - - const results = await Promise.allSettled( - volumes.map(async (volume) => { - // Update once per minute at most - if (!(await this.redisLockProvider.lock(`volume:${volume.id}:update-last-used`, 60))) { - return - } - volume.lastUsedAt = event.sandbox.createdAt - return this.volumeRepository.save(volume) - }), - ) - - results.forEach((result) => { - if (result.status === 'rejected') { - this.logger.error( - `Failed to update volume lastUsedAt timestamp for sandbox ${event.sandbox.id}: ${result.reason}`, - ) - } - }) - } catch (err) { - this.logger.error(err) - } - } -} diff --git a/apps/api/src/sandbox/subscribers/snapshot.subscriber.ts b/apps/api/src/sandbox/subscribers/snapshot.subscriber.ts deleted file mode 100644 index 6436c952d..000000000 --- a/apps/api/src/sandbox/subscribers/snapshot.subscriber.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Inject } from '@nestjs/common' -import { EventEmitter2 } from '@nestjs/event-emitter' -import { DataSource, EntitySubscriberInterface, EventSubscriber, RemoveEvent, UpdateEvent } from 'typeorm' -import { SnapshotEvents } from '../constants/snapshot-events' -import { Snapshot } from '../entities/snapshot.entity' -import { SnapshotStateUpdatedEvent } from '../events/snapshot-state-updated.event' -import { SnapshotRemovedEvent } from '../events/snapshot-removed.event' - -@EventSubscriber() -export class SnapshotSubscriber implements EntitySubscriberInterface { - @Inject(EventEmitter2) - private eventEmitter: EventEmitter2 - - constructor(dataSource: DataSource) { - dataSource.subscribers.push(this) - } - - listenTo() { - return Snapshot - } - - afterUpdate(event: UpdateEvent) { - const updatedColumns = event.updatedColumns.map((col) => col.propertyName) - - updatedColumns.forEach((column) => { - switch (column) { - case 'state': - this.eventEmitter.emit( - SnapshotEvents.STATE_UPDATED, - new SnapshotStateUpdatedEvent(event.entity as Snapshot, event.databaseEntity[column], event.entity[column]), - ) - break - default: - break - } - }) - } - - beforeRemove(event: RemoveEvent) { - this.eventEmitter.emit(SnapshotEvents.REMOVED, new SnapshotRemovedEvent(event.databaseEntity as Snapshot)) - } -} diff --git a/apps/api/src/sandbox/utils/network-validation.util.ts b/apps/api/src/sandbox/utils/network-validation.util.ts deleted file mode 100644 index 3359b4369..000000000 --- a/apps/api/src/sandbox/utils/network-validation.util.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ -import { isIPv4 } from 'net' - -/** - * Validates network allow list to ensure valid CIDR network addresses are allowed - * @param networkAllowList - Comma-separated string of network addresses - * @returns null if valid, error message string if invalid - */ -export function validateNetworkAllowList(networkAllowList: string): void { - const networks = networkAllowList.split(',').map((net: string) => net.trim()) - - for (const network of networks) { - if (!network) continue // Skip empty entries - - const [ipAddress, prefixLength] = network.split('/') - - if (!isIPv4(ipAddress)) { - throw new Error(`Invalid IP address: "${ipAddress}" in network "${network}". Must be a valid IPv4 address`) - } - - if (!prefixLength) { - throw new Error(`Invalid network format: "${network}". Missing CIDR prefix length (e.g., /24)`) - } - - // Validate CIDR prefix length (0-32 for IPv4) - const prefix = parseInt(prefixLength, 10) - if (prefix < 0 || prefix > 32) { - throw new Error(`Invalid CIDR prefix length: ${network}. Prefix must be between 0 and 32`) - } - } - - if (networks.length > 10) { - throw new Error(`Network allow list cannot contain more than 10 networks`) - } -} diff --git a/apps/api/src/sandbox/utils/sandbox-lookup-cache.util.ts b/apps/api/src/sandbox/utils/sandbox-lookup-cache.util.ts deleted file mode 100644 index 2c51d4cd2..000000000 --- a/apps/api/src/sandbox/utils/sandbox-lookup-cache.util.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -export const SANDBOX_LOOKUP_CACHE_TTL_MS = 10_000 -export const SANDBOX_BUILD_INFO_CACHE_TTL_MS = 60_000 -export const SANDBOX_ORG_ID_CACHE_TTL_MS = 60_000 -export const TOOLBOX_PROXY_URL_CACHE_TTL_S = 30 * 60 // 30 minutes - -type SandboxLookupCacheKeyArgs = { - organizationId?: string | null - returnDestroyed?: boolean -} - -export function sandboxLookupCacheKeyById(args: SandboxLookupCacheKeyArgs & { sandboxId: string }): string { - const organizationId = args.organizationId ?? 'none' - const returnDestroyed = args.returnDestroyed ? 1 : 0 - return `sandbox:lookup:by-id:org:${organizationId}:returnDestroyed:${returnDestroyed}:value:${args.sandboxId}` -} - -export function sandboxLookupCacheKeyByName(args: SandboxLookupCacheKeyArgs & { sandboxName: string }): string { - const organizationId = args.organizationId ?? 'none' - const returnDestroyed = args.returnDestroyed ? 1 : 0 - return `sandbox:lookup:by-name:org:${organizationId}:returnDestroyed:${returnDestroyed}:value:${args.sandboxName}` -} - -export function sandboxLookupCacheKeyByAuthToken(args: { authToken: string }): string { - return `sandbox:lookup:by-authToken:${args.authToken}` -} - -type SandboxOrgIdCacheKeyArgs = { - organizationId?: string -} - -export function sandboxOrgIdCacheKeyById(args: SandboxOrgIdCacheKeyArgs & { sandboxId: string }): string { - const organizationId = args.organizationId ?? 'none' - return `sandbox:orgId:by-id:org:${organizationId}:value:${args.sandboxId}` -} - -export function sandboxOrgIdCacheKeyByName(args: SandboxOrgIdCacheKeyArgs & { sandboxName: string }): string { - const organizationId = args.organizationId ?? 'none' - return `sandbox:orgId:by-name:org:${organizationId}:value:${args.sandboxName}` -} - -export function toolboxProxyUrlCacheKey(regionId: string): string { - return `toolbox-proxy-url:region:${regionId}` -} diff --git a/apps/api/src/sandbox/utils/snapshot-ref.util.spec.ts b/apps/api/src/sandbox/utils/snapshot-ref.util.spec.ts deleted file mode 100644 index d5abcd888..000000000 --- a/apps/api/src/sandbox/utils/snapshot-ref.util.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - createBoxLiteInternalSnapshotRef, - isBoxLiteInternalSnapshotRef, - normalizeSnapshotDigest, -} from './snapshot-ref.util' - -const DIGEST = 'a'.repeat(64) - -describe('snapshot-ref.util', () => { - describe('isBoxLiteInternalSnapshotRef', () => { - it('matches generated BoxLite internal refs', () => { - expect(isBoxLiteInternalSnapshotRef(`registry.local/project/boxlite-${DIGEST}:boxlite`)).toBe(true) - expect(isBoxLiteInternalSnapshotRef(`boxlite-${DIGEST}:boxlite`)).toBe(true) - }) - - it('does not match normal external image refs', () => { - expect(isBoxLiteInternalSnapshotRef('alpine:3.22.4')).toBe(false) - expect(isBoxLiteInternalSnapshotRef('ghcr.io/example/app:1.0.0')).toBe(false) - }) - }) - - describe('normalizeSnapshotDigest', () => { - it('accepts sha256-prefixed digests', () => { - expect(normalizeSnapshotDigest(`sha256:${DIGEST.toUpperCase()}`)).toBe(DIGEST) - }) - - it('rejects empty digests', () => { - expect(() => normalizeSnapshotDigest('')).toThrow('Snapshot digest is empty') - }) - }) - - describe('createBoxLiteInternalSnapshotRef', () => { - it('builds an internal ref from a valid digest', () => { - expect(createBoxLiteInternalSnapshotRef('https://registry.local', 'snapshots', DIGEST)).toBe( - `registry.local/snapshots/boxlite-${DIGEST}:boxlite`, - ) - }) - }) -}) diff --git a/apps/api/src/sandbox/utils/snapshot-ref.util.ts b/apps/api/src/sandbox/utils/snapshot-ref.util.ts deleted file mode 100644 index b24ae530e..000000000 --- a/apps/api/src/sandbox/utils/snapshot-ref.util.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -const BOXLITE_INTERNAL_REF_PATTERN = /(?:^|\/)boxlite-[a-f0-9]{64}:boxlite$/i - -export function isBoxLiteInternalSnapshotRef(ref?: string): boolean { - return !!ref && BOXLITE_INTERNAL_REF_PATTERN.test(ref) -} - -export function normalizeSnapshotDigest(hash: string): string { - const digest = hash?.trim().replace(/^sha256:/, '') - - if (!digest) { - throw new Error('Snapshot digest is empty') - } - - if (!/^[a-f0-9]{64}$/i.test(digest)) { - throw new Error(`Invalid snapshot digest: ${hash}`) - } - - return digest.toLowerCase() -} - -export function createBoxLiteInternalSnapshotRef( - registryUrl: string, - project: string | undefined, - hash: string, -): string { - const sanitizedUrl = registryUrl.replace(/^https?:\/\//, '') - const digest = normalizeSnapshotDigest(hash) - - return `${sanitizedUrl}/${project || 'boxlite'}/boxlite-${digest}:boxlite` -} diff --git a/apps/api/src/serve-static-cache.spec.ts b/apps/api/src/serve-static-cache.spec.ts new file mode 100644 index 000000000..137fbd090 --- /dev/null +++ b/apps/api/src/serve-static-cache.spec.ts @@ -0,0 +1,31 @@ +import { dashboardStaticCacheControl, setDashboardStaticHeaders } from './serve-static-cache' + +describe('dashboardStaticCacheControl', () => { + it('caches content-hashed assets forever (immutable)', () => { + expect(dashboardStaticCacheControl('/srv/dashboard/assets/index-C8CfaZCN.js')).toBe( + 'public, max-age=31536000, immutable', + ) + expect(dashboardStaticCacheControl('/srv/dashboard/assets/index-Dt9Taow4.css')).toBe( + 'public, max-age=31536000, immutable', + ) + }) + + it('never long-caches the HTML shell (must point at the current bundle)', () => { + expect(dashboardStaticCacheControl('/srv/dashboard/index.html')).toBe('no-cache') + }) + + it('revalidates other top-level static files', () => { + expect(dashboardStaticCacheControl('/srv/dashboard/favicon.ico')).toBe('public, max-age=0, must-revalidate') + }) +}) + +describe('setDashboardStaticHeaders', () => { + it('writes the resolved Cache-Control onto the response', () => { + const headers: Record = {} + const res = { setHeader: (name: string, value: string) => (headers[name] = value) } + + setDashboardStaticHeaders(res, '/srv/dashboard/assets/index-C8CfaZCN.js') + + expect(headers['Cache-Control']).toBe('public, max-age=31536000, immutable') + }) +}) diff --git a/apps/api/src/serve-static-cache.ts b/apps/api/src/serve-static-cache.ts new file mode 100644 index 000000000..944d584be --- /dev/null +++ b/apps/api/src/serve-static-cache.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +/** + * Cache-Control policy for the dashboard SPA static files. + * + * Vite emits content-hashed build assets (e.g. /assets/index-C8CfaZCN.js). The + * filename changes on every build, so those files are immutable and safe to + * cache forever — this is what stops the browser (and CloudFront) from + * re-downloading the ~600KB bundle on every Auth0 callback / repeat visit. + * + * The HTML shell must NOT be cached long-term: it is the only file that points + * at the current hashed bundle, so caching it would pin a client to a stale + * deploy. Everything else falls back to revalidate-always. + */ +export function dashboardStaticCacheControl(filePath: string): string { + if (/\.html?$/i.test(filePath)) { + return 'no-cache' + } + if (filePath.includes('/assets/')) { + return 'public, max-age=31536000, immutable' + } + return 'public, max-age=0, must-revalidate' +} + +/** + * `setHeaders` hook for @nestjs/serve-static (express.static). Applies the + * content-addressed cache policy above per served file. + */ +export function setDashboardStaticHeaders( + res: { setHeader(name: string, value: string): void }, + filePath: string, +): void { + res.setHeader('Cache-Control', dashboardStaticCacheControl(filePath)) +} diff --git a/apps/api/src/tracing.ts b/apps/api/src/tracing.ts index 84d4e59ab..956531484 100644 --- a/apps/api/src/tracing.ts +++ b/apps/api/src/tracing.ts @@ -10,7 +10,6 @@ import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express' import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core' import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base' import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http' -import { CompressionAlgorithm, OTLPExporterNodeConfigBase } from '@opentelemetry/otlp-exporter-base' import { resourceFromAttributes } from '@opentelemetry/resources' import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions' import { @@ -36,8 +35,7 @@ diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.WARN) const appMode = getAppMode() const serviceNameSuffix = appMode === 'api' ? 'api' : appMode === 'worker' ? 'worker' : 'api' -const otlpExporterConfig: OTLPExporterNodeConfigBase = { - compression: CompressionAlgorithm.GZIP, +const otlpExporterConfig = { keepAlive: true, } diff --git a/apps/api/src/usage/entities/sandbox-usage-period-archive.entity.ts b/apps/api/src/usage/entities/sandbox-usage-period-archive.entity.ts deleted file mode 100644 index 7633f6755..000000000 --- a/apps/api/src/usage/entities/sandbox-usage-period-archive.entity.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' -import { SandboxUsagePeriod } from './sandbox-usage-period.entity' - -// Duplicate of SandboxUsagePeriod -// Used to archive usage periods and keep the original table lightweight -// Will only contain closed usage periods -@Entity('sandbox_usage_periods_archive') -export class SandboxUsagePeriodArchive { - @PrimaryGeneratedColumn('uuid') - id: string - - @Column() - sandboxId: string - - @Column() - // Redundant property to optimize billing queries - organizationId: string - - @Column({ type: 'timestamp with time zone' }) - startAt: Date - - @Column({ type: 'timestamp with time zone' }) - endAt: Date - - @Column({ type: 'float' }) - cpu: number - - @Column({ type: 'float' }) - gpu: number - - @Column({ type: 'float' }) - mem: number - - @Column({ type: 'float' }) - disk: number - - @Column() - region: string - - public static fromUsagePeriod(usagePeriod: SandboxUsagePeriod) { - const usagePeriodEntity = new SandboxUsagePeriodArchive() - usagePeriodEntity.sandboxId = usagePeriod.sandboxId - usagePeriodEntity.organizationId = usagePeriod.organizationId - usagePeriodEntity.startAt = usagePeriod.startAt - usagePeriodEntity.endAt = usagePeriod.endAt - usagePeriodEntity.cpu = usagePeriod.cpu - usagePeriodEntity.gpu = usagePeriod.gpu - usagePeriodEntity.mem = usagePeriod.mem - usagePeriodEntity.disk = usagePeriod.disk - usagePeriodEntity.region = usagePeriod.region - return usagePeriodEntity - } -} diff --git a/apps/api/src/usage/entities/sandbox-usage-period.entity.ts b/apps/api/src/usage/entities/sandbox-usage-period.entity.ts deleted file mode 100644 index 333a09ef9..000000000 --- a/apps/api/src/usage/entities/sandbox-usage-period.entity.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm' - -@Entity('sandbox_usage_periods') -@Index('idx_sandbox_usage_periods_sandbox_end', ['sandboxId', 'endAt']) -export class SandboxUsagePeriod { - @PrimaryGeneratedColumn('uuid') - id: string - - @Column() - sandboxId: string - - @Column() - // Redundant property to optimize billing queries - organizationId: string - - @Column({ type: 'timestamp with time zone' }) - startAt: Date - - @Column({ type: 'timestamp with time zone', nullable: true }) - endAt: Date | null - - @Column({ type: 'float' }) - cpu: number - - @Column({ type: 'float' }) - gpu: number - - @Column({ type: 'float' }) - mem: number - - @Column({ type: 'float' }) - disk: number - - @Column() - region: string - - public static fromUsagePeriod(usagePeriod: SandboxUsagePeriod) { - const usagePeriodEntity = new SandboxUsagePeriod() - usagePeriodEntity.sandboxId = usagePeriod.sandboxId - usagePeriodEntity.organizationId = usagePeriod.organizationId - usagePeriodEntity.startAt = usagePeriod.startAt - usagePeriodEntity.endAt = usagePeriod.endAt - usagePeriodEntity.cpu = usagePeriod.cpu - usagePeriodEntity.gpu = usagePeriod.gpu - usagePeriodEntity.mem = usagePeriod.mem - usagePeriodEntity.disk = usagePeriod.disk - usagePeriodEntity.region = usagePeriod.region - return usagePeriodEntity - } -} diff --git a/apps/api/src/usage/services/usage.service.ts b/apps/api/src/usage/services/usage.service.ts deleted file mode 100644 index 1e7112f0a..000000000 --- a/apps/api/src/usage/services/usage.service.ts +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common' -import { InjectRepository } from '@nestjs/typeorm' -import { IsNull, LessThan, Not, Repository } from 'typeorm' -import { SandboxUsagePeriod } from '../entities/sandbox-usage-period.entity' -import { OnEvent } from '@nestjs/event-emitter' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SandboxEvents } from './../../sandbox/constants/sandbox-events.constants' -import { Cron, CronExpression } from '@nestjs/schedule' -import { RedisLockProvider } from '../../sandbox/common/redis-lock.provider' -import { SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION } from '../../sandbox/constants/sandbox.constants' -import { SandboxUsagePeriodArchive } from '../entities/sandbox-usage-period-archive.entity' -import { TrackableJobExecutions } from '../../common/interfaces/trackable-job-executions' -import { TrackJobExecution } from '../../common/decorators/track-job-execution.decorator' -import { setTimeout as sleep } from 'timers/promises' -import { LogExecution } from '../../common/decorators/log-execution.decorator' -import { WithInstrumentation } from '../../common/decorators/otel.decorator' -import { SandboxRepository } from '../../sandbox/repositories/sandbox.repository' - -@Injectable() -export class UsageService implements TrackableJobExecutions, OnApplicationShutdown { - activeJobs = new Set() - private readonly logger = new Logger(UsageService.name) - - constructor( - @InjectRepository(SandboxUsagePeriod) - private sandboxUsagePeriodRepository: Repository, - private readonly redisLockProvider: RedisLockProvider, - private readonly sandboxRepository: SandboxRepository, - ) {} - - async onApplicationShutdown() { - // wait for all active jobs to finish - while (this.activeJobs.size > 0) { - this.logger.log(`Waiting for ${this.activeJobs.size} active jobs to finish`) - await sleep(1000) - } - } - - @OnEvent(SandboxEvents.STATE_UPDATED) - @TrackJobExecution() - async handleSandboxStateUpdate(event: SandboxStateUpdatedEvent) { - await this.waitForLock(event.sandbox.id) - - try { - switch (event.newState) { - case SandboxState.STARTED: { - await this.closeUsagePeriod(event.sandbox.id) - await this.createUsagePeriod(event) - break - } - case SandboxState.STOPPING: - await this.closeUsagePeriod(event.sandbox.id) - await this.createUsagePeriod(event, true) - break - case SandboxState.ERROR: - case SandboxState.BUILD_FAILED: - case SandboxState.ARCHIVED: - case SandboxState.DESTROYED: { - await this.closeUsagePeriod(event.sandbox.id) - break - } - } - } finally { - this.releaseLock(event.sandbox.id).catch((error) => { - this.logger.error(`Error releasing lock for sandbox ${event.sandbox.id}`, error) - }) - } - } - - private async createUsagePeriod(event: SandboxStateUpdatedEvent, diskOnly = false) { - const usagePeriod = new SandboxUsagePeriod() - usagePeriod.sandboxId = event.sandbox.id - usagePeriod.startAt = new Date() - usagePeriod.endAt = null - if (!diskOnly) { - usagePeriod.cpu = event.sandbox.cpu - usagePeriod.gpu = event.sandbox.gpu - usagePeriod.mem = event.sandbox.mem - } else { - usagePeriod.cpu = 0 - usagePeriod.gpu = 0 - usagePeriod.mem = 0 - } - usagePeriod.disk = event.sandbox.disk - usagePeriod.organizationId = event.sandbox.organizationId - usagePeriod.region = event.sandbox.region - - await this.sandboxUsagePeriodRepository.save(usagePeriod) - } - - private async closeUsagePeriod(sandboxId: string) { - const lastUsagePeriod = await this.sandboxUsagePeriodRepository.findOne({ - where: { - sandboxId, - endAt: IsNull(), - }, - }) - - if (lastUsagePeriod) { - lastUsagePeriod.endAt = new Date() - await this.sandboxUsagePeriodRepository.save(lastUsagePeriod) - } - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'close-and-reopen-usage-periods' }) - @TrackJobExecution() - @LogExecution('close-and-reopen-usage-periods') - @WithInstrumentation() - async closeAndReopenUsagePeriods() { - if (!(await this.redisLockProvider.lock('close-and-reopen-usage-periods', 60))) { - return - } - - const usagePeriods = await this.sandboxUsagePeriodRepository.find({ - where: { - endAt: IsNull(), - // 1 day ago - startAt: LessThan(new Date(Date.now() - 1000 * 60 * 60 * 24)), - organizationId: Not(SANDBOX_WARM_POOL_UNASSIGNED_ORGANIZATION), - }, - order: { - startAt: 'ASC', - }, - take: 100, - }) - - for (const usagePeriod of usagePeriods) { - if (!(await this.aquireLock(usagePeriod.sandboxId))) { - continue - } - - // validate that the usage period should remain active just in case - try { - const sandbox = await this.sandboxRepository.findOne({ - where: { - id: usagePeriod.sandboxId, - }, - }) - - await this.sandboxUsagePeriodRepository.manager.transaction(async (transactionalEntityManager) => { - // Close usage period - const closeTime = new Date() - usagePeriod.endAt = closeTime - await transactionalEntityManager.save(usagePeriod) - - if ( - sandbox && - (sandbox.state === SandboxState.STARTED || - sandbox.state === SandboxState.STOPPED || - sandbox.state === SandboxState.STOPPING) - ) { - // Create new usage period - const newUsagePeriod = SandboxUsagePeriod.fromUsagePeriod(usagePeriod) - newUsagePeriod.startAt = closeTime - newUsagePeriod.endAt = null - await transactionalEntityManager.save(newUsagePeriod) - } - }) - } catch (error) { - this.logger.error(`Error closing and reopening usage period ${usagePeriod.sandboxId}`, error) - } finally { - await this.releaseLock(usagePeriod.sandboxId) - } - } - - await this.redisLockProvider.unlock('close-and-reopen-usage-periods') - } - - @Cron(CronExpression.EVERY_MINUTE, { name: 'archive-usage-periods' }) - @TrackJobExecution() - @LogExecution('archive-usage-periods') - @WithInstrumentation() - async archiveUsagePeriods() { - const lockKey = 'archive-usage-periods' - if (!(await this.redisLockProvider.lock(lockKey, 60))) { - return - } - - await this.sandboxUsagePeriodRepository.manager.transaction(async (transactionalEntityManager) => { - const usagePeriods = await transactionalEntityManager.find(SandboxUsagePeriod, { - where: { - endAt: Not(IsNull()), - }, - order: { - startAt: 'ASC', - }, - take: 1000, - }) - - if (usagePeriods.length === 0) { - return - } - - this.logger.debug(`Found ${usagePeriods.length} usage periods to archive`) - - await transactionalEntityManager.delete( - SandboxUsagePeriod, - usagePeriods.map((usagePeriod) => usagePeriod.id), - ) - await transactionalEntityManager.save(usagePeriods.map(SandboxUsagePeriodArchive.fromUsagePeriod)) - }) - - await this.redisLockProvider.unlock(lockKey) - } - - private async waitForLock(sandboxId: string) { - while (!(await this.aquireLock(sandboxId))) { - await new Promise((resolve) => setTimeout(resolve, 500)) - } - } - - private async aquireLock(sandboxId: string): Promise { - return await this.redisLockProvider.lock(`usage-period-${sandboxId}`, 60) - } - - private async releaseLock(sandboxId: string) { - await this.redisLockProvider.unlock(`usage-period-${sandboxId}`) - } -} diff --git a/apps/api/src/usage/usage.module.ts b/apps/api/src/usage/usage.module.ts deleted file mode 100644 index 229c6977b..000000000 --- a/apps/api/src/usage/usage.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Module } from '@nestjs/common' -import { TypeOrmModule } from '@nestjs/typeorm' -import { DataSource } from 'typeorm' -import { EventEmitter2 } from '@nestjs/event-emitter' -import { SandboxUsagePeriod } from './entities/sandbox-usage-period.entity' -import { UsageService } from './services/usage.service' -import { RedisLockProvider } from '../sandbox/common/redis-lock.provider' -import { SandboxUsagePeriodArchive } from './entities/sandbox-usage-period-archive.entity' -import { SandboxRepository } from '../sandbox/repositories/sandbox.repository' -import { SandboxLookupCacheInvalidationService } from '../sandbox/services/sandbox-lookup-cache-invalidation.service' -import { Sandbox } from '../sandbox/entities/sandbox.entity' - -@Module({ - imports: [TypeOrmModule.forFeature([SandboxUsagePeriod, Sandbox, SandboxUsagePeriodArchive])], - providers: [ - UsageService, - RedisLockProvider, - SandboxLookupCacheInvalidationService, - { - provide: SandboxRepository, - inject: [DataSource, EventEmitter2, SandboxLookupCacheInvalidationService], - useFactory: ( - dataSource: DataSource, - eventEmitter: EventEmitter2, - sandboxLookupCacheInvalidationService: SandboxLookupCacheInvalidationService, - ) => new SandboxRepository(dataSource, eventEmitter, sandboxLookupCacheInvalidationService), - }, - ], - exports: [UsageService], -}) -export class UsageModule {} diff --git a/apps/api/src/user/dto/create-user.dto.ts b/apps/api/src/user/dto/create-user.dto.ts index 46c114951..6d6284f2c 100644 --- a/apps/api/src/user/dto/create-user.dto.ts +++ b/apps/api/src/user/dto/create-user.dto.ts @@ -7,7 +7,6 @@ import { ApiProperty, ApiPropertyOptional, ApiSchema } from '@nestjs/swagger' import { IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator' import { SystemRole } from '../enums/system-role.enum' -import { CreateOrganizationQuotaDto } from '../../organization/dto/create-organization-quota.dto' @ApiSchema({ name: 'CreateUser' }) export class CreateUserDto { @@ -25,10 +24,14 @@ export class CreateUserDto { email?: string @ApiPropertyOptional() + @IsString() @IsOptional() - personalOrganizationQuota?: CreateOrganizationQuotaDto + defaultOrganizationDefaultRegionId?: string - @ApiPropertyOptional() + @ApiPropertyOptional({ + description: 'Deprecated alias for defaultOrganizationDefaultRegionId.', + deprecated: true, + }) @IsString() @IsOptional() personalOrganizationDefaultRegionId?: string diff --git a/apps/api/src/user/events/user-created.event.ts b/apps/api/src/user/events/user-created.event.ts index ca0625254..3abdccc2a 100644 --- a/apps/api/src/user/events/user-created.event.ts +++ b/apps/api/src/user/events/user-created.event.ts @@ -6,13 +6,11 @@ import { EntityManager } from 'typeorm' import { User } from '../user.entity' -import { CreateOrganizationQuotaDto } from '../../organization/dto/create-organization-quota.dto' export class UserCreatedEvent { constructor( public readonly entityManager: EntityManager, public readonly user: User, - public readonly personalOrganizationQuota?: CreateOrganizationQuotaDto, - public readonly personalOrganizationDefaultRegionId?: string, + public readonly defaultOrganizationDefaultRegionId?: string, ) {} } diff --git a/apps/api/src/user/user.controller.ts b/apps/api/src/user/user.controller.ts index afe80caa0..5333b72bc 100644 --- a/apps/api/src/user/user.controller.ts +++ b/apps/api/src/user/user.controller.ts @@ -87,7 +87,8 @@ export class UserController { id: req.body?.id, name: req.body?.name, email: req.body?.email, - personalOrganizationQuota: req.body?.personalOrganizationQuota, + defaultOrganizationDefaultRegionId: req.body?.defaultOrganizationDefaultRegionId, + personalOrganizationDefaultRegionId: req.body?.personalOrganizationDefaultRegionId, role: req.body?.role, emailVerified: req.body?.emailVerified, }), diff --git a/apps/api/src/user/user.service.default-organization-compat.spec.ts b/apps/api/src/user/user.service.default-organization-compat.spec.ts new file mode 100644 index 000000000..c997fb09b --- /dev/null +++ b/apps/api/src/user/user.service.default-organization-compat.spec.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { UserService } from './user.service' +import { UserCreatedEvent } from './events/user-created.event' + +describe('UserService default organization compatibility', () => { + it('accepts deprecated personal organization create-user fields as aliases for default organization fields', async () => { + const eventEmitter = { + emitAsync: jest.fn().mockResolvedValue(undefined), + } + const entityManager = { + save: jest.fn(async (entity) => entity), + } + const dataSource = { + transaction: jest.fn(async (callback) => callback(entityManager)), + } + const service = new UserService({} as never, eventEmitter as never, dataSource as never) + jest.spyOn(service as never, 'generatePrivateKey').mockResolvedValue({ + privateKey: 'private-key', + publicKey: 'public-key', + } as never) + + await service.create({ + id: 'user-1', + name: 'User One', + personalOrganizationDefaultRegionId: 'region-1', + } as never) + + const event = eventEmitter.emitAsync.mock.calls[0][1] as UserCreatedEvent + expect(event.defaultOrganizationDefaultRegionId).toBe('region-1') + }) +}) diff --git a/apps/api/src/user/user.service.ts b/apps/api/src/user/user.service.ts index 4d5b046fe..4e03b5a88 100644 --- a/apps/api/src/user/user.service.ts +++ b/apps/api/src/user/user.service.ts @@ -10,7 +10,6 @@ import { User, UserSSHKeyPair } from './user.entity' import { DataSource, ILike, In, Repository } from 'typeorm' import { CreateUserDto } from './dto/create-user.dto' import * as crypto from 'crypto' -import * as forge from 'node-forge' import { EventEmitter2 } from '@nestjs/event-emitter' import { UserEvents } from './constants/user-events.constant' import { UpdateUserDto } from './dto/update-user.dto' @@ -28,6 +27,8 @@ export class UserService { ) {} async create(createUserDto: CreateUserDto): Promise { + const defaultOrganizationDefaultRegionId = + createUserDto.defaultOrganizationDefaultRegionId ?? createUserDto.personalOrganizationDefaultRegionId let user = new User() user.id = createUserDto.id user.name = createUserDto.name @@ -48,12 +49,7 @@ export class UserService { user = await em.save(user) await this.eventEmitter.emitAsync( UserEvents.CREATED, - new UserCreatedEvent( - em, - user, - createUserDto.personalOrganizationQuota, - createUserDto.personalOrganizationDefaultRegionId, - ), + new UserCreatedEvent(em, user, defaultOrganizationDefaultRegionId), ) }) @@ -127,13 +123,9 @@ export class UserService { if (error) { reject(error) } else { - const publicKeySShEncoded = forge.ssh.publicKeyToOpenSSH(forge.pki.publicKeyFromPem(publicKey), comment) - - const privateKeySShEncoded = forge.ssh.privateKeyToOpenSSH(forge.pki.privateKeyFromPem(privateKey)) - resolve({ - publicKey: publicKeySShEncoded, - privateKey: privateKeySShEncoded, + publicKey: this.encodeOpenSshRsaPublicKey(publicKey, comment), + privateKey, }) } }, @@ -141,6 +133,40 @@ export class UserService { }) } + private encodeOpenSshRsaPublicKey(publicKeyPem: string, comment: string): string { + const publicKey = crypto.createPublicKey(publicKeyPem) + const jwk = publicKey.export({ format: 'jwk' }) as { e?: string; n?: string } + + if (!jwk.e || !jwk.n) { + throw new Error('Failed to export RSA public key as JWK') + } + + const wireKey = Buffer.concat([ + this.encodeSshString(Buffer.from('ssh-rsa')), + this.encodeSshMpint(this.base64UrlToBuffer(jwk.e)), + this.encodeSshMpint(this.base64UrlToBuffer(jwk.n)), + ]) + + return `ssh-rsa ${wireKey.toString('base64')} ${comment}` + } + + private encodeSshString(value: Buffer): Buffer { + const length = Buffer.alloc(4) + length.writeUInt32BE(value.length, 0) + return Buffer.concat([length, value]) + } + + private encodeSshMpint(value: Buffer): Buffer { + const needsSignPadding = value.length > 0 && (value[0] & 0x80) !== 0 + const normalized = needsSignPadding ? Buffer.concat([Buffer.from([0]), value]) : value + return this.encodeSshString(normalized) + } + + private base64UrlToBuffer(value: string): Buffer { + const base64 = value.replace(/-/g, '+').replace(/_/g, '/') + return Buffer.from(base64.padEnd(Math.ceil(base64.length / 4) * 4, '='), 'base64') + } + // TODO: discuss if we need separate methods for updating specific fields async update(userId: string, updateUserDto: UpdateUserDto): Promise { const user = await this.userRepository.findOne({ diff --git a/apps/api/src/webhook/README.md b/apps/api/src/webhook/README.md index 63f1d932c..36bc5d5f5 100644 --- a/apps/api/src/webhook/README.md +++ b/apps/api/src/webhook/README.md @@ -94,10 +94,10 @@ Returns the current status of the webhook service, indicating whether it's prope The service automatically sends webhooks for the following events: -### Sandbox Events +### Box Events -- `sandbox.created` - When a sandbox is created -- `sandbox.state.updated` - When sandbox state changes +- `box.created` - When a box is created +- `box.state.updated` - When box state changes ### Snapshot Events @@ -114,11 +114,11 @@ The service automatically sends webhooks for the following events: All webhooks include event-specific data relevant to the resource being updated. -### Example Sandbox Created Payload +### Example Box Created Payload ```json { - "id": "sandbox-uuid", + "id": "box-uuid", "organizationId": "org-uuid", "state": "STARTED", "class": "SMALL", @@ -126,11 +126,11 @@ All webhooks include event-specific data relevant to the resource being updated. } ``` -### Example Sandbox State Updated Payload +### Example Box State Updated Payload ```json { - "id": "sandbox-uuid", + "id": "box-uuid", "organizationId": "org-uuid", "oldState": "STOPPED", "newState": "STARTED", @@ -201,7 +201,7 @@ For local development without Svix: ### Event Flow -1. System event occurs (e.g., sandbox created) +1. System event occurs (e.g., box created) 2. Event emitter publishes the event 3. Webhook event handler catches the event 4. Handler calls webhook service to send webhook diff --git a/apps/api/src/webhook/constants/webhook-events.constants.ts b/apps/api/src/webhook/constants/webhook-events.constants.ts index 6b32cd457..f982d28c7 100644 --- a/apps/api/src/webhook/constants/webhook-events.constants.ts +++ b/apps/api/src/webhook/constants/webhook-events.constants.ts @@ -5,11 +5,11 @@ */ export enum WebhookEvent { - SANDBOX_CREATED = 'sandbox.created', - SANDBOX_STATE_UPDATED = 'sandbox.state.updated', - SNAPSHOT_CREATED = 'snapshot.created', - SNAPSHOT_STATE_UPDATED = 'snapshot.state.updated', - SNAPSHOT_REMOVED = 'snapshot.removed', + BOX_CREATED = 'box.created', + BOX_STATE_UPDATED = 'box.state.updated', + TEMPLATE_CREATED = 'template.created', + TEMPLATE_STATE_UPDATED = 'template.state.updated', + TEMPLATE_REMOVED = 'template.removed', VOLUME_CREATED = 'volume.created', VOLUME_STATE_UPDATED = 'volume.state.updated', } diff --git a/apps/api/src/webhook/dto/send-webhook.dto.ts b/apps/api/src/webhook/dto/send-webhook.dto.ts index 5b8cb482c..1799151eb 100644 --- a/apps/api/src/webhook/dto/send-webhook.dto.ts +++ b/apps/api/src/webhook/dto/send-webhook.dto.ts @@ -13,14 +13,14 @@ export class SendWebhookDto { description: 'The type of event being sent', enum: WebhookEvent, enumName: 'WebhookEvent', - example: 'sandbox.created', + example: 'box.created', }) @IsEnum(WebhookEvent) eventType: WebhookEvent @ApiProperty({ description: 'The payload data to send', - example: { id: 'sandbox-123', name: 'My Sandbox' }, + example: { id: 'box-123', name: 'My Box' }, }) @IsObject() payload: Record diff --git a/apps/api/src/webhook/dto/webhook-event-payloads.dto.ts b/apps/api/src/webhook/dto/webhook-event-payloads.dto.ts index 353a23492..5268ec09d 100644 --- a/apps/api/src/webhook/dto/webhook-event-payloads.dto.ts +++ b/apps/api/src/webhook/dto/webhook-event-payloads.dto.ts @@ -6,24 +6,20 @@ import { ApiProperty, ApiSchema } from '@nestjs/swagger' import { WebhookEvent } from '../constants/webhook-events.constants' -import { SandboxState } from '../../sandbox/enums/sandbox-state.enum' -import { SandboxClass } from '../../sandbox/enums/sandbox-class.enum' -import { SnapshotState } from '../../sandbox/enums/snapshot-state.enum' -import { VolumeState } from '../../sandbox/enums/volume-state.enum' -import { SandboxCreatedEvent } from '../../sandbox/events/sandbox-create.event' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SnapshotCreatedEvent } from '../../sandbox/events/snapshot-created.event' -import { SnapshotStateUpdatedEvent } from '../../sandbox/events/snapshot-state-updated.event' -import { SnapshotRemovedEvent } from '../../sandbox/events/snapshot-removed.event' -import { VolumeCreatedEvent } from '../../sandbox/events/volume-created.event' -import { VolumeStateUpdatedEvent } from '../../sandbox/events/volume-state-updated.event' +import { BoxState } from '../../box/enums/box-state.enum' +import { BoxClass } from '../../box/enums/box-class.enum' +import { VolumeState } from '../../box/enums/volume-state.enum' +import { BoxCreatedEvent } from '../../box/events/box-create.event' +import { BoxStateUpdatedEvent } from '../../box/events/box-state-updated.event' +import { VolumeCreatedEvent } from '../../box/events/volume-created.event' +import { VolumeStateUpdatedEvent } from '../../box/events/volume-state-updated.event' export abstract class BaseWebhookEventDto { @ApiProperty({ description: 'Event type identifier', enum: WebhookEvent, enumName: 'WebhookEvent', - example: 'sandbox.created', + example: 'box.created', }) event: string @@ -35,11 +31,11 @@ export abstract class BaseWebhookEventDto { timestamp: string } -@ApiSchema({ name: 'SandboxCreatedWebhook' }) -export class SandboxCreatedWebhookDto extends BaseWebhookEventDto { +@ApiSchema({ name: 'BoxCreatedWebhook' }) +export class BoxCreatedWebhookDto extends BaseWebhookEventDto { @ApiProperty({ - description: 'Sandbox ID', - example: 'sandbox123', + description: 'Box ID', + example: 'box123', }) id: string @@ -50,44 +46,44 @@ export class SandboxCreatedWebhookDto extends BaseWebhookEventDto { organizationId: string @ApiProperty({ - description: 'Sandbox state', - enum: SandboxState, - enumName: 'SandboxState', + description: 'Box state', + enum: BoxState, + enumName: 'BoxState', }) - state: SandboxState + state: BoxState @ApiProperty({ - description: 'Sandbox class', - enum: SandboxClass, - enumName: 'SandboxClass', + description: 'Box class', + enum: BoxClass, + enumName: 'BoxClass', }) - class: SandboxClass + class: BoxClass @ApiProperty({ - description: 'When the sandbox was created', + description: 'When the box was created', example: '2025-12-19T10:30:00.000Z', format: 'date-time', }) createdAt: string - static fromEvent(event: SandboxCreatedEvent, eventType: string): SandboxCreatedWebhookDto { + static fromEvent(event: BoxCreatedEvent, eventType: string): BoxCreatedWebhookDto { return { event: eventType, timestamp: new Date().toISOString(), - id: event.sandbox.id, - organizationId: event.sandbox.organizationId, - state: event.sandbox.state, - class: event.sandbox.class, - createdAt: event.sandbox.createdAt.toISOString(), + id: event.box.id, + organizationId: event.box.organizationId, + state: event.box.state, + class: event.box.class, + createdAt: event.box.createdAt.toISOString(), } } } -@ApiSchema({ name: 'SandboxStateUpdatedWebhook' }) -export class SandboxStateUpdatedWebhookDto extends BaseWebhookEventDto { +@ApiSchema({ name: 'BoxStateUpdatedWebhook' }) +export class BoxStateUpdatedWebhookDto extends BaseWebhookEventDto { @ApiProperty({ - description: 'Sandbox ID', - example: 'sandbox123', + description: 'Box ID', + example: 'box123', }) id: string @@ -99,178 +95,40 @@ export class SandboxStateUpdatedWebhookDto extends BaseWebhookEventDto { @ApiProperty({ description: 'Previous state', - enum: SandboxState, - enumName: 'SandboxState', + enum: BoxState, + enumName: 'BoxState', }) - oldState: SandboxState + oldState: BoxState @ApiProperty({ description: 'New state', - enum: SandboxState, - enumName: 'SandboxState', + enum: BoxState, + enumName: 'BoxState', }) - newState: SandboxState + newState: BoxState @ApiProperty({ - description: 'When the sandbox was last updated', + description: 'When the box was last updated', example: '2025-12-19T10:30:00.000Z', format: 'date-time', }) updatedAt: string - static fromEvent(event: SandboxStateUpdatedEvent, eventType: string): SandboxStateUpdatedWebhookDto { + static fromEvent(event: BoxStateUpdatedEvent, eventType: string): BoxStateUpdatedWebhookDto { return { event: eventType, timestamp: new Date().toISOString(), - id: event.sandbox.id, - organizationId: event.sandbox.organizationId, + id: event.box.id, + organizationId: event.box.organizationId, oldState: event.oldState, newState: event.newState, - updatedAt: event.sandbox.updatedAt.toISOString(), + updatedAt: event.box.updatedAt.toISOString(), } } } -@ApiSchema({ name: 'SnapshotCreatedWebhook' }) -export class SnapshotCreatedWebhookDto extends BaseWebhookEventDto { - @ApiProperty({ - description: 'Snapshot ID', - example: 'snapshot123', - }) - id: string - - @ApiProperty({ - description: 'Snapshot name', - example: 'my-snapshot', - }) - name: string - - @ApiProperty({ - description: 'Organization ID', - example: 'org123', - }) - organizationId: string - - @ApiProperty({ - description: 'Snapshot state', - enum: SnapshotState, - enumName: 'SnapshotState', - }) - state: SnapshotState - - @ApiProperty({ - description: 'When the snapshot was created', - example: '2025-12-19T10:30:00.000Z', - format: 'date-time', - }) - createdAt: string - - static fromEvent(event: SnapshotCreatedEvent, eventType: string): SnapshotCreatedWebhookDto { - return { - event: eventType, - timestamp: new Date().toISOString(), - id: event.snapshot.id, - name: event.snapshot.name, - organizationId: event.snapshot.organizationId, - state: event.snapshot.state, - createdAt: event.snapshot.createdAt.toISOString(), - } - } -} - -@ApiSchema({ name: 'SnapshotStateUpdatedWebhook' }) -export class SnapshotStateUpdatedWebhookDto extends BaseWebhookEventDto { - @ApiProperty({ - description: 'Snapshot ID', - example: 'snapshot123', - }) - id: string - - @ApiProperty({ - description: 'Snapshot name', - example: 'my-snapshot', - }) - name: string - - @ApiProperty({ - description: 'Organization ID', - example: 'org123', - }) - organizationId: string - - @ApiProperty({ - description: 'Previous state', - enum: SnapshotState, - enumName: 'SnapshotState', - }) - oldState: SnapshotState - - @ApiProperty({ - description: 'New state', - enum: SnapshotState, - enumName: 'SnapshotState', - }) - newState: SnapshotState - - @ApiProperty({ - description: 'When the snapshot was last updated', - example: '2025-12-19T10:30:00.000Z', - format: 'date-time', - }) - updatedAt: string - - static fromEvent(event: SnapshotStateUpdatedEvent, eventType: string): SnapshotStateUpdatedWebhookDto { - return { - event: eventType, - timestamp: new Date().toISOString(), - id: event.snapshot.id, - name: event.snapshot.name, - organizationId: event.snapshot.organizationId, - oldState: event.oldState, - newState: event.newState, - updatedAt: event.snapshot.updatedAt.toISOString(), - } - } -} - -@ApiSchema({ name: 'SnapshotRemovedWebhook' }) -export class SnapshotRemovedWebhookDto extends BaseWebhookEventDto { - @ApiProperty({ - description: 'Snapshot ID', - example: 'snapshot123', - }) - id: string - - @ApiProperty({ - description: 'Snapshot name', - example: 'my-snapshot', - }) - name: string - - @ApiProperty({ - description: 'Organization ID', - example: 'org123', - }) - organizationId: string - - @ApiProperty({ - description: 'When the snapshot was removed', - example: '2025-12-19T10:30:00.000Z', - format: 'date-time', - }) - removedAt: string - - static fromEvent(event: SnapshotRemovedEvent, eventType: string): SnapshotRemovedWebhookDto { - return { - event: eventType, - timestamp: new Date().toISOString(), - id: event.snapshot.id, - name: event.snapshot.name, - organizationId: event.snapshot.organizationId, - removedAt: new Date().toISOString(), - } - } -} +// TODO(image-rewrite): template webhook payloads removed with the image/template subsystem; +// rebuild template/image webhook payloads here. @ApiSchema({ name: 'VolumeCreatedWebhook' }) export class VolumeCreatedWebhookDto extends BaseWebhookEventDto { diff --git a/apps/api/src/webhook/services/webhook-event-handler.service.ts b/apps/api/src/webhook/services/webhook-event-handler.service.ts index 6f4b2b346..168fe1f95 100644 --- a/apps/api/src/webhook/services/webhook-event-handler.service.ts +++ b/apps/api/src/webhook/services/webhook-event-handler.service.ts @@ -7,23 +7,16 @@ import { Injectable, Logger } from '@nestjs/common' import { OnEvent } from '@nestjs/event-emitter' import { WebhookService } from './webhook.service' -import { SandboxEvents } from '../../sandbox/constants/sandbox-events.constants' -import { SnapshotEvents } from '../../sandbox/constants/snapshot-events' -import { VolumeEvents } from '../../sandbox/constants/volume-events' -import { SandboxCreatedEvent } from '../../sandbox/events/sandbox-create.event' -import { SandboxStateUpdatedEvent } from '../../sandbox/events/sandbox-state-updated.event' -import { SnapshotCreatedEvent } from '../../sandbox/events/snapshot-created.event' -import { SnapshotStateUpdatedEvent } from '../../sandbox/events/snapshot-state-updated.event' -import { SnapshotRemovedEvent } from '../../sandbox/events/snapshot-removed.event' -import { VolumeCreatedEvent } from '../../sandbox/events/volume-created.event' -import { VolumeStateUpdatedEvent } from '../../sandbox/events/volume-state-updated.event' +import { BoxEvents } from '../../box/constants/box-events.constants' +import { VolumeEvents } from '../../box/constants/volume-events' +import { BoxCreatedEvent } from '../../box/events/box-create.event' +import { BoxStateUpdatedEvent } from '../../box/events/box-state-updated.event' +import { VolumeCreatedEvent } from '../../box/events/volume-created.event' +import { VolumeStateUpdatedEvent } from '../../box/events/volume-state-updated.event' import { WebhookEvent } from '../constants/webhook-events.constants' import { - SandboxCreatedWebhookDto, - SandboxStateUpdatedWebhookDto, - SnapshotCreatedWebhookDto, - SnapshotStateUpdatedWebhookDto, - SnapshotRemovedWebhookDto, + BoxCreatedWebhookDto, + BoxStateUpdatedWebhookDto, VolumeCreatedWebhookDto, VolumeStateUpdatedWebhookDto, } from '../dto/webhook-event-payloads.dto' @@ -34,75 +27,35 @@ export class WebhookEventHandlerService { constructor(private readonly webhookService: WebhookService) {} - @OnEvent(SandboxEvents.CREATED) - async handleSandboxCreated(event: SandboxCreatedEvent) { + @OnEvent(BoxEvents.CREATED) + async handleBoxCreated(event: BoxCreatedEvent) { if (!this.webhookService.isEnabled()) { return } try { - const payload = SandboxCreatedWebhookDto.fromEvent(event, WebhookEvent.SANDBOX_CREATED) - await this.webhookService.sendWebhook(event.sandbox.organizationId, WebhookEvent.SANDBOX_CREATED, payload) + const payload = BoxCreatedWebhookDto.fromEvent(event, WebhookEvent.BOX_CREATED) + await this.webhookService.sendWebhook(event.box.organizationId, WebhookEvent.BOX_CREATED, payload) } catch (error) { - this.logger.error(`Failed to send webhook for sandbox created: ${error.message}`) + this.logger.error(`Failed to send webhook for box created: ${error.message}`) } } - @OnEvent(SandboxEvents.STATE_UPDATED) - async handleSandboxStateUpdated(event: SandboxStateUpdatedEvent) { + @OnEvent(BoxEvents.STATE_UPDATED) + async handleBoxStateUpdated(event: BoxStateUpdatedEvent) { if (!this.webhookService.isEnabled()) { return } try { - const payload = SandboxStateUpdatedWebhookDto.fromEvent(event, WebhookEvent.SANDBOX_STATE_UPDATED) - await this.webhookService.sendWebhook(event.sandbox.organizationId, WebhookEvent.SANDBOX_STATE_UPDATED, payload) + const payload = BoxStateUpdatedWebhookDto.fromEvent(event, WebhookEvent.BOX_STATE_UPDATED) + await this.webhookService.sendWebhook(event.box.organizationId, WebhookEvent.BOX_STATE_UPDATED, payload) } catch (error) { - this.logger.error(`Failed to send webhook for sandbox state updated: ${error.message}`) + this.logger.error(`Failed to send webhook for box state updated: ${error.message}`) } } - @OnEvent(SnapshotEvents.CREATED) - async handleSnapshotCreated(event: SnapshotCreatedEvent) { - if (!this.webhookService.isEnabled()) { - return - } - - try { - const payload = SnapshotCreatedWebhookDto.fromEvent(event, WebhookEvent.SNAPSHOT_CREATED) - await this.webhookService.sendWebhook(event.snapshot.organizationId, WebhookEvent.SNAPSHOT_CREATED, payload) - } catch (error) { - this.logger.error(`Failed to send webhook for snapshot created: ${error.message}`) - } - } - - @OnEvent(SnapshotEvents.STATE_UPDATED) - async handleSnapshotStateUpdated(event: SnapshotStateUpdatedEvent) { - if (!this.webhookService.isEnabled()) { - return - } - - try { - const payload = SnapshotStateUpdatedWebhookDto.fromEvent(event, WebhookEvent.SNAPSHOT_STATE_UPDATED) - await this.webhookService.sendWebhook(event.snapshot.organizationId, WebhookEvent.SNAPSHOT_STATE_UPDATED, payload) - } catch (error) { - this.logger.error(`Failed to send webhook for snapshot state updated: ${error.message}`) - } - } - - @OnEvent(SnapshotEvents.REMOVED) - async handleSnapshotRemoved(event: SnapshotRemovedEvent) { - if (!this.webhookService.isEnabled()) { - return - } - - try { - const payload = SnapshotRemovedWebhookDto.fromEvent(event, WebhookEvent.SNAPSHOT_REMOVED) - await this.webhookService.sendWebhook(event.snapshot.organizationId, WebhookEvent.SNAPSHOT_REMOVED, payload) - } catch (error) { - this.logger.error(`Failed to send webhook for snapshot removed: ${error.message}`) - } - } + // TODO(image-rewrite): box_template webhook handlers removed with box_template; rebuild here. @OnEvent(VolumeEvents.CREATED) async handleVolumeCreated(event: VolumeCreatedEvent) { diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index c1e2dd4e8..3773c2597 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../tsconfig.base.json", + "extends": "../tsconfig.base.json", "files": [], "include": [], "references": [ diff --git a/apps/api/webpack.config.js b/apps/api/webpack.config.js index 62929cc1e..38001cc61 100644 --- a/apps/api/webpack.config.js +++ b/apps/api/webpack.config.js @@ -7,7 +7,7 @@ const { composePlugins, withNx } = require('@nx/webpack') const path = require('path') const glob = require('glob') -const migrationFiles = glob.sync('apps/api/src/migrations/**/*-migration.{ts,js}') +const migrationFiles = glob.sync(path.join(__dirname, 'src/migrations/**/*-migration.{ts,js}')) const migrationEntries = migrationFiles.reduce((acc, migrationFile) => { const entryName = migrationFile.substring(migrationFile.lastIndexOf('/') + 1, migrationFile.lastIndexOf('.')) acc[entryName] = migrationFile diff --git a/apps/cli/LICENSE b/apps/cli/LICENSE deleted file mode 100644 index 4123d6696..000000000 --- a/apps/cli/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -This app contains Daytona-derived source licensed under the GNU Affero General -Public License version 3. - -See ../LICENSES/AGPL-3.0.txt for the full license text. diff --git a/apps/cli/apiclient/api_client.go b/apps/cli/apiclient/api_client.go deleted file mode 100644 index 894ca1378..000000000 --- a/apps/cli/apiclient/api_client.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package apiclient - -import ( - "context" - "fmt" - "net/http" - "strings" - "sync" - - "github.com/boxlite-ai/boxlite/cli/auth" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/internal" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - - log "github.com/sirupsen/logrus" -) - -type versionCheckTransport struct { - transport http.RoundTripper -} - -var versionMismatchWarningOnce sync.Once - -func (t *versionCheckTransport) RoundTrip(req *http.Request) (*http.Response, error) { - resp, err := t.transport.RoundTrip(req) - if resp != nil { - // Check version mismatch on all responses, not just errors - checkVersionsMismatch(resp) - } - return resp, err -} - -var apiClient *apiclient.APIClient - -const BoxliteSourceHeader = "X-BoxLite-Source" -const API_VERSION_HEADER = "X-BoxLite-Api-Version" - -func checkVersionsMismatch(res *http.Response) { - // If the CLI is running in a structured output mode (e.g. json/yaml), - // avoid printing human-readable warnings that could break consumers. - if internal.SuppressVersionMismatchWarning { - return - } - - serverVersion := res.Header.Get(API_VERSION_HEADER) - if serverVersion == "" { - return - } - - // Trim "v" prefix from both versions for comparison - cliVersion := strings.TrimPrefix(internal.Version, "v") - apiVersion := strings.TrimPrefix(serverVersion, "v") - - if cliVersion == "0.0.0-dev" || cliVersion == apiVersion { - return - } - - if compareVersions(cliVersion, apiVersion) >= 0 { - return - } - - versionMismatchWarningOnce.Do(func() { - log.Warn(fmt.Sprintf("Version mismatch: BoxLite CLI is on v%s and API is on v%s.\nMake sure the versions are aligned using 'brew upgrade boxlite-ai/cli/boxlite' or by downloading the latest version from https://github.com/boxlite-ai/boxlite/releases.", cliVersion, apiVersion)) - }) -} - -// compareVersions compares two semver strings -// Returns: -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2 -func compareVersions(v1, v2 string) int { - parts1 := strings.Split(v1, ".") - parts2 := strings.Split(v2, ".") - - maxLen := len(parts1) - if len(parts2) > maxLen { - maxLen = len(parts2) - } - - for i := 0; i < maxLen; i++ { - var n1, n2 int - if i < len(parts1) { - _, _ = fmt.Sscanf(parts1[i], "%d", &n1) - } - if i < len(parts2) { - _, _ = fmt.Sscanf(parts2[i], "%d", &n2) - } - - if n1 < n2 { - return -1 - } - if n1 > n2 { - return 1 - } - } - - return 0 -} - -func GetApiClient(profile *config.Profile, defaultHeaders map[string]string) (*apiclient.APIClient, error) { - c, err := config.GetConfig() - if err != nil { - return nil, err - } - - var activeProfile config.Profile - if profile == nil { - var err error - activeProfile, err = c.GetActiveProfile() - if err != nil { - return nil, err - } - } else { - activeProfile = *profile - } - - if apiClient != nil && activeProfile.Api.Key == nil { - err := auth.RefreshTokenIfNeeded(context.Background()) - if err != nil { - return nil, err - } - - return apiClient, nil - } - - var newApiClient *apiclient.APIClient - - serverUrl := activeProfile.Api.Url - - clientConfig := apiclient.NewConfiguration() - clientConfig.Servers = apiclient.ServerConfigurations{ - { - URL: serverUrl, - }, - } - - if activeProfile.Api.Key != nil { - clientConfig.AddDefaultHeader("Authorization", "Bearer "+*activeProfile.Api.Key) - } else if activeProfile.Api.Token != nil { - clientConfig.AddDefaultHeader("Authorization", "Bearer "+activeProfile.Api.Token.AccessToken) - - if activeProfile.ActiveOrganizationId != nil { - clientConfig.AddDefaultHeader("X-BoxLite-Organization-ID", *activeProfile.ActiveOrganizationId) - } - } - - clientConfig.AddDefaultHeader(BoxliteSourceHeader, "cli") - - for headerKey, headerValue := range defaultHeaders { - clientConfig.AddDefaultHeader(headerKey, headerValue) - } - - newApiClient = apiclient.NewAPIClient(clientConfig) - - newApiClient.GetConfig().HTTPClient = &http.Client{ - Transport: &versionCheckTransport{ - transport: http.DefaultTransport, - }, - } - - if apiClient != nil && activeProfile.Api.Key == nil { - err = auth.RefreshTokenIfNeeded(context.Background()) - if err != nil { - return nil, err - } - } - - apiClient = newApiClient - return apiClient, nil -} diff --git a/apps/cli/apiclient/error_handler.go b/apps/cli/apiclient/error_handler.go deleted file mode 100644 index b03a493ff..000000000 --- a/apps/cli/apiclient/error_handler.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package apiclient - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net/http" -) - -type ApiErrorResponse struct { - Error string `json:"error"` - Message any `json:"message,omitempty"` -} - -func HandleErrorResponse(res *http.Response, requestErr error) error { - if res == nil { - return requestErr - } - - defer res.Body.Close() - - body, err := io.ReadAll(res.Body) - if err != nil { - return err - } - - var errResponse ApiErrorResponse - err = json.Unmarshal(body, &errResponse) - if err != nil { - return err - } - - errMessage := string(errResponse.Error) - if errMessage == "" { - // Fall back to raw body if error field is empty - errMessage = string(body) - } else { - if errResponse.Message != nil { - // Message field could be a string or an array - switch msg := errResponse.Message.(type) { - case string: - errMessage += ": " + msg - case []any: - if len(msg) > 0 { - msgStr := fmt.Sprintf("%v", msg) - errMessage += ": " + msgStr - } - } - } - } - - if res.StatusCode == http.StatusUnauthorized { - errMessage += " - run 'boxlite login' to reauthenticate" - } - - return errors.New(errMessage) -} diff --git a/apps/cli/auth/auth.go b/apps/cli/auth/auth.go deleted file mode 100644 index ae33ef7d8..000000000 --- a/apps/cli/auth/auth.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package auth - -import ( - "context" - "crypto/rand" - _ "embed" - "encoding/base64" - "fmt" - "net/http" - "sync" - "time" - - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/coreos/go-oidc/v3/oidc" - log "github.com/sirupsen/logrus" - "golang.org/x/oauth2" -) - -//go:embed auth_success.html -var successHTML []byte - -func StartCallbackServer(expectedState string) (string, error) { - var code string - var err error - var wg sync.WaitGroup - wg.Add(1) - - server := &http.Server{Addr: fmt.Sprintf(":%s", config.GetAuth0CallbackPort())} - - http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { - if r.URL.Query().Get("state") != expectedState { - err = fmt.Errorf("invalid state parameter") - http.Error(w, "State invalid", http.StatusBadRequest) - wg.Done() - return - } - - code = r.URL.Query().Get("code") - if code == "" { - err = fmt.Errorf("no code in callback") - http.Error(w, "No code", http.StatusBadRequest) - wg.Done() - return - } - - w.Header().Set("Content-Type", "text/html") - _, _ = w.Write(successHTML) - - // Delay server close to ensure browser receives the success page - go func() { - time.Sleep(500 * time.Millisecond) - wg.Done() - server.Close() - }() - }) - - go func() { - if err := server.ListenAndServe(); err != http.ErrServerClosed { - log.Errorf("HTTP server error: %v", err) - } - }() - wg.Wait() - - if err != nil { - return "", err - } - return code, nil -} - -func GenerateRandomState() (string, error) { - b := make([]byte, 32) - _, err := rand.Read(b) - if err != nil { - return "", err - } - - return base64.URLEncoding.EncodeToString(b), nil -} - -func RefreshTokenIfNeeded(ctx context.Context) error { - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - if activeProfile.Api.Key != nil { - return nil - } - - if activeProfile.Api.Token == nil { - return fmt.Errorf("no valid token found, use 'boxlite login' to reauthenticate") - } - - // Check if token is about to expire (within 5 minutes) - if time.Until(activeProfile.Api.Token.ExpiresAt) > 5*time.Minute { - return nil - } - - provider, err := oidc.NewProvider(ctx, config.GetAuth0Domain()) - if err != nil { - return fmt.Errorf("failed to initialize OIDC provider: %w", err) - } - - oauth2Config := oauth2.Config{ - ClientID: config.GetAuth0ClientId(), - ClientSecret: config.GetAuth0ClientSecret(), - RedirectURL: fmt.Sprintf("http://localhost:%s/callback", config.GetAuth0CallbackPort()), - Endpoint: provider.Endpoint(), - Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile"}, - } - - token := &oauth2.Token{ - RefreshToken: activeProfile.Api.Token.RefreshToken, - } - - newToken, err := oauth2Config.TokenSource(ctx, token).Token() - if err != nil { - return fmt.Errorf("use 'boxlite login' to reauthenticate: %w", err) - } - - activeProfile.Api.Token = &config.Token{ - AccessToken: newToken.AccessToken, - RefreshToken: newToken.RefreshToken, - ExpiresAt: newToken.Expiry, - } - - return c.EditProfile(activeProfile) -} diff --git a/apps/cli/auth/auth_success.html b/apps/cli/auth/auth_success.html deleted file mode 100644 index 828689a9e..000000000 --- a/apps/cli/auth/auth_success.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - BoxLite - - - - - -
- -

Authentication Successful

-

You can now close this window and return to the CLI.

-
- - - - diff --git a/apps/cli/cmd/auth/login.go b/apps/cli/cmd/auth/login.go deleted file mode 100644 index 2680ad7a9..000000000 --- a/apps/cli/cmd/auth/login.go +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package auth - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/auth" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/internal" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/coreos/go-oidc/v3/oidc" - "github.com/pkg/browser" - "github.com/spf13/cobra" - "golang.org/x/oauth2" -) - -var LoginCmd = &cobra.Command{ - Use: "login", - Short: "Log in to BoxLite", - Args: cobra.NoArgs, - GroupID: internal.USER_GROUP, - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - if apiKeyFlag != "" { - return updateProfileWithLogin(nil, &apiKeyFlag) - } - - items := []view_common.SelectItem{ - {Title: "Login with Browser", Desc: "Authenticate using OAuth in your browser"}, - {Title: "Set BoxLite API Key", Desc: "Authenticate using BoxLite API key"}, - } - - choice, err := view_common.Select("Select Authentication Method", items) - if err != nil { - return fmt.Errorf("error running selection prompt: %w", err) - } - - if choice == "" { - return nil - } - - var tokenConfig *config.Token - setApiKey := choice == "Set BoxLite API Key" - - if setApiKey { - // Prompt for API key - apiKey, err := view_common.PromptForInput("", "Enter your BoxLite API key", "You can find it in the BoxLite dashboard - https://app.boxlite.ai/dashboard") - if err != nil { - return err - } - return updateProfileWithLogin(nil, &apiKey) - } - - token, err := login(ctx) - if err != nil { - return err - } - - tokenConfig = &config.Token{ - AccessToken: token.AccessToken, - RefreshToken: token.RefreshToken, - ExpiresAt: token.Expiry, - } - - return updateProfileWithLogin(tokenConfig, nil) - }, -} - -var ( - apiKeyFlag string -) - -func init() { - LoginCmd.Flags().StringVar(&apiKeyFlag, "api-key", "", "API key to use for authentication") -} - -func updateProfileWithLogin(tokenConfig *config.Token, apiKey *string) error { - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - if err == config.ErrNoProfilesFound { - activeProfile, err = createInitialProfile(c) - if err != nil { - return err - } - } else { - return err - } - } - - if apiKey != nil { - activeProfile.Api.Token = nil - activeProfile.Api.Key = apiKey - - view_common.RenderInfoMessageBold("Successfully set BoxLite API key!") - } - - if tokenConfig != nil { - activeProfile.Api.Key = nil - activeProfile.Api.Token = tokenConfig - - err = c.EditProfile(activeProfile) - if err != nil { - return err - } - - if activeProfile.Api.Key == nil { - personalOrganizationId, err := common.GetPersonalOrganizationId(activeProfile) - if err != nil { - return err - } - - activeProfile.ActiveOrganizationId = &personalOrganizationId - } - } - - return c.EditProfile(activeProfile) -} - -func createInitialProfile(c *config.Config) (config.Profile, error) { - profile := config.Profile{ - Id: "initial", - Name: "initial", - Api: config.ServerApi{ - Url: config.GetBoxliteApiUrl(), - }, - } - - if internal.Version == "v0.0.0-dev" { - profile.Api.Url = "http://localhost:3001/api" - } - - return profile, c.AddProfile(profile) -} - -func login(ctx context.Context) (*oauth2.Token, error) { - provider, err := oidc.NewProvider(ctx, config.GetAuth0Domain()) - if err != nil { - return nil, fmt.Errorf("failed to initialize OIDC provider: %w", err) - } - - verifier := provider.Verifier(&oidc.Config{ClientID: config.GetAuth0ClientId()}) - - oauth2Config := oauth2.Config{ - ClientID: config.GetAuth0ClientId(), - ClientSecret: config.GetAuth0ClientSecret(), - RedirectURL: fmt.Sprintf("http://localhost:%s/callback", config.GetAuth0CallbackPort()), - Endpoint: provider.Endpoint(), - Scopes: []string{oidc.ScopeOpenID, oidc.ScopeOfflineAccess, "profile"}, - } - - state, err := auth.GenerateRandomState() - if err != nil { - return nil, fmt.Errorf("failed to generate random state: %w", err) - } - - authURL := oauth2Config.AuthCodeURL( - state, - oauth2.SetAuthURLParam("audience", config.GetAuth0Audience()), - ) - - view_common.RenderInfoMessageBold("Opening the browser for authentication ...") - - view_common.RenderInfoMessage("If opening fails, visit:\n") - - fmt.Println(authURL) - - _ = browser.OpenURL(authURL) - - code, err := auth.StartCallbackServer(state) - if err != nil { - return nil, fmt.Errorf("authentication failed: %w", err) - } - - token, err := oauth2Config.Exchange(ctx, code) - if err != nil { - return nil, fmt.Errorf("failed to exchange token: %w", err) - } - - rawIDToken, ok := token.Extra("id_token").(string) - if !ok { - return nil, fmt.Errorf("no id_token in token response") - } - - _, err = verifier.Verify(ctx, rawIDToken) - if err != nil { - return nil, fmt.Errorf("failed to verify ID token: %w", err) - } - - view_common.RenderInfoMessageBold("Successfully logged in!") - - return token, nil -} diff --git a/apps/cli/cmd/auth/logout.go b/apps/cli/cmd/auth/logout.go deleted file mode 100644 index 039e4d531..000000000 --- a/apps/cli/cmd/auth/logout.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package auth - -import ( - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/spf13/cobra" -) - -var LogoutCmd = &cobra.Command{ - Use: "logout", - Short: "Logout from BoxLite", - Args: cobra.NoArgs, - GroupID: internal.USER_GROUP, - RunE: func(cmd *cobra.Command, args []string) error { - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - // For now, this just clears the local auth token/api key entries - activeProfile.Api.Token = nil - activeProfile.Api.Key = nil - - err = c.EditProfile(activeProfile) - if err != nil { - return err - } - - common.RenderInfoMessageBold("Successfully logged out") - return nil - }, -} diff --git a/apps/cli/cmd/autocomplete.go b/apps/cli/cmd/autocomplete.go deleted file mode 100644 index e4b0c6c3b..000000000 --- a/apps/cli/cmd/autocomplete.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package cmd - -import ( - "errors" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/spf13/cobra" -) - -var supportedShells = []string{"bash", "zsh", "fish", "powershell"} - -var AutoCompleteCmd = &cobra.Command{ - Use: fmt.Sprintf("autocomplete [%s]", strings.Join(supportedShells, "|")), - Short: "Adds a completion script for your shell environment", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - shell := args[0] - - profilePath, err := SetupAutocompletionForShell(cmd.Root(), shell) - if err != nil { - return err - } - - fmt.Println("Autocomplete script generated and injected successfully.") - fmt.Printf("Please source your %s profile to apply the changes or restart your terminal.\n", shell) - fmt.Printf("For manual sourcing, use: source %s\n", profilePath) - if shell == "bash" { - fmt.Println("Please make sure that you have bash-completion installed in order to get full autocompletion functionality.") - fmt.Println("On how to install bash-completion, please refer to the following link: https://www.boxlite.ai/docs/tools/cli/#boxlite-autocomplete") - } - - return nil - }, -} - -func DetectShellAndSetupAutocompletion(rootCmd *cobra.Command) error { - shell := os.Getenv("SHELL") - if shell == "" { - return fmt.Errorf("unable to detect the shell, please use a supported one: %s", strings.Join(supportedShells, ", ")) - } - - for _, supportedShell := range supportedShells { - if strings.Contains(shell, supportedShell) { - shell = supportedShell - break - } - } - - _, err := SetupAutocompletionForShell(rootCmd, shell) - if err != nil { - return err - } - - return nil -} - -func SetupAutocompletionForShell(rootCmd *cobra.Command, shell string) (string, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return "", fmt.Errorf("error finding user home directory: %s", err) - } - - var filePath, profilePath string - switch shell { - case "bash": - filePath = filepath.Join(homeDir, ".boxlite.completion_script.bash") - profilePath = filepath.Join(homeDir, ".bashrc") - case "zsh": - filePath = filepath.Join(homeDir, ".boxlite.completion_script.zsh") - profilePath = filepath.Join(homeDir, ".zshrc") - case "fish": - filePath = filepath.Join(homeDir, ".config", "fish", "boxlite.completion_script.fish") - profilePath = filepath.Join(homeDir, ".config", "fish", "config.fish") - case "powershell": - filePath = filepath.Join(homeDir, "boxlite.completion_script.ps1") - profilePath = filepath.Join(homeDir, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1") - default: - return "", errors.New("unsupported shell type. Please use bash, zsh, fish, or powershell") - } - - file, err := os.Create(filePath) - if err != nil { - return "", fmt.Errorf("error creating completion script file: %s", err) - } - defer file.Close() - - switch shell { - case "bash": - err = rootCmd.GenBashCompletion(file) - case "zsh": - err = rootCmd.GenZshCompletion(file) - case "fish": - err = rootCmd.GenFishCompletion(file, true) - case "powershell": - err = rootCmd.GenPowerShellCompletionWithDesc(file) - } - - if err != nil { - return "", fmt.Errorf("error generating completion script: %s", err) - } - - sourceCommand := fmt.Sprintf("\nsource %s\n", filePath) - if shell == "powershell" { - sourceCommand = fmt.Sprintf(". %s\n", filePath) - } - - alreadyPresent := false - // Read existing content from the file - profile, err := os.ReadFile(profilePath) - - if err != nil && !os.IsNotExist(err) { - return "", fmt.Errorf("error while reading profile (%s): %s", profilePath, err) - } - - if strings.Contains(string(profile), strings.TrimSpace(sourceCommand)) { - alreadyPresent = true - } - - if !alreadyPresent { - // Append the source command to the shell's profile file if not present - profile, err := os.OpenFile(profilePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - return "", fmt.Errorf("error opening profile file (%s): %s", profilePath, err) - } - defer profile.Close() - - if _, err := profile.WriteString(sourceCommand); err != nil { - return "", fmt.Errorf("error writing to profile file (%s): %s", profilePath, err) - } - } - - return profilePath, nil -} diff --git a/apps/cli/cmd/common/aliases.go b/apps/cli/cmd/common/aliases.go deleted file mode 100644 index 914db90a7..000000000 --- a/apps/cli/cmd/common/aliases.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -var commandAliases = map[string][]string{ - "create": {"add", "new"}, - "delete": {"remove", "rm"}, - "update": {"set"}, - "install": {"i"}, - "uninstall": {"u"}, - "info": {"view", "inspect"}, - "code": {"open"}, - "logs": {"log"}, - "forward": {"fwd"}, - "list": {"ls"}, -} - -func GetAliases(cmd string) []string { - if aliases, exists := commandAliases[cmd]; exists { - return aliases - } - return nil -} diff --git a/apps/cli/cmd/common/build.go b/apps/cli/cmd/common/build.go deleted file mode 100644 index 5b7c2fc8d..000000000 --- a/apps/cli/cmd/common/build.go +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "context" - "fmt" - "net/url" - "os" - "path/filepath" - "regexp" - "strings" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/pkg/minio" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -// Create MinIO client from access parameters -func CreateMinioClient(accessParams *apiclient.StorageAccessDto) (*minio.Client, error) { - storageURL, err := url.Parse(accessParams.StorageUrl) - if err != nil { - return nil, fmt.Errorf("invalid storage URL: %w", err) - } - - minioClient, err := minio.NewClient( - storageURL.Host, - accessParams.AccessKey, - accessParams.Secret, - accessParams.Bucket, - storageURL.Scheme == "https", - accessParams.SessionToken, - ) - if err != nil { - return nil, fmt.Errorf("failed to create storage client: %w", err) - } - - return minioClient, nil -} - -// List existing objects in MinIO -func ListExistingObjects(ctx context.Context, minioClient *minio.Client, orgID string) (map[string]bool, error) { - objects, err := minioClient.ListObjects(ctx, orgID) - if err != nil { - return nil, fmt.Errorf("failed to list objects: %w", err) - } - - existingObjects := make(map[string]bool) - for _, obj := range objects { - existingObjects[obj] = true - } - return existingObjects, nil -} - -// getContextHashes processes context paths and returns their hashes -func getContextHashes(ctx context.Context, apiClient *apiclient.APIClient, contextPaths []string) ([]string, error) { - contextHashes := []string{} - if len(contextPaths) == 0 { - return contextHashes, nil - } - - // Get storage access parameters - accessParams, res, err := apiClient.ObjectStorageAPI.GetPushAccess(ctx).Execute() - if err != nil { - return nil, apiclient_cli.HandleErrorResponse(res, err) - } - - // Create MinIO client - minioClient, err := CreateMinioClient(accessParams) - if err != nil { - return nil, fmt.Errorf("failed to create storage client: %w", err) - } - - // List existing objects to avoid re-uploading - existingObjects, err := ListExistingObjects(ctx, minioClient, accessParams.OrganizationId) - if err != nil { - return nil, fmt.Errorf("failed to list existing objects: %w", err) - } - - // Process each context path - for _, contextPath := range contextPaths { - absPath, err := filepath.Abs(contextPath) - if err != nil { - return nil, fmt.Errorf("invalid context path %s: %w", contextPath, err) - } - - fileInfo, err := os.Stat(absPath) - if err != nil { - return nil, fmt.Errorf("failed to access context path %s: %w", contextPath, err) - } - - if fileInfo.IsDir() { - // Process directory - dirHashes, err := minioClient.ProcessDirectory(ctx, absPath, accessParams.OrganizationId, existingObjects) - if err != nil { - return nil, fmt.Errorf("failed to process directory %s: %w", absPath, err) - } - contextHashes = append(contextHashes, dirHashes...) - } else { - // Process single file - hash, err := minioClient.ProcessFile(ctx, absPath, accessParams.OrganizationId, existingObjects) - if err != nil { - return nil, fmt.Errorf("failed to process file %s: %w", absPath, err) - } - contextHashes = append(contextHashes, hash) - } - } - - return contextHashes, nil -} - -func parseDockerfileForSources(dockerfileContent string, dockerfileDir string) ([]string, error) { - var sources []string - lines := strings.Split(dockerfileContent, "\n") - - copyRegex := regexp.MustCompile(`^\s*COPY\s+(.+)`) - addRegex := regexp.MustCompile(`^\s*ADD\s+(.+)`) - - for _, line := range lines { - line = strings.TrimSpace(line) - - // Skip empty lines and comments - if line == "" || strings.HasPrefix(line, "#") { - continue - } - - var matches []string - if copyRegex.MatchString(line) { - // Skip COPY commands with --from= flag (multi-stage builds) - if !strings.Contains(line, "--from=") { - matches = copyRegex.FindStringSubmatch(line) - } - } else if addRegex.MatchString(line) { - matches = addRegex.FindStringSubmatch(line) - } - - if len(matches) > 1 { - sourcePaths := parseCopyAddCommand(matches[1]) - for _, srcPath := range sourcePaths { - // Skip if it's a URL (ADD command can use URLs) - if strings.HasPrefix(srcPath, "http://") || strings.HasPrefix(srcPath, "https://") { - continue - } - - // Convert relative paths to absolute paths relative to Dockerfile directory - if !filepath.IsAbs(srcPath) { - srcPath = filepath.Join(dockerfileDir, srcPath) - } - - srcPath = filepath.Clean(srcPath) - - // Check if path exists and add to sources - if _, err := os.Stat(srcPath); err == nil { - sources = append(sources, srcPath) - } else { - // If exact path doesn't exist, try to match glob patterns - matches, err := filepath.Glob(srcPath) - if err == nil && len(matches) > 0 { - sources = append(sources, matches...) - } - } - } - } - } - - // Remove duplicates and optimize paths - sourceMap := make(map[string]bool) - var uniqueSources []string - - // Check if we have the current directory (.) in our sources - hasCurrentDir := false - currentDirPath := dockerfileDir - - for _, src := range sources { - if src == currentDirPath { - hasCurrentDir = true - break - } - } - - // If we have the current directory, we only need that (it includes everything) - if hasCurrentDir { - return []string{currentDirPath}, nil - } - - // Otherwise, remove duplicates normally - for _, src := range sources { - if !sourceMap[src] { - sourceMap[src] = true - uniqueSources = append(uniqueSources, src) - } - } - - return uniqueSources, nil -} - -func parseCopyAddCommand(args string) []string { - args = strings.TrimSpace(args) - var sources []string - - // Handle JSON array format: ["src1", "src2", "dest"] - if strings.HasPrefix(args, "[") && strings.HasSuffix(args, "]") { - // Remove brackets and parse as space-separated values with quotes - content := strings.Trim(args, "[]") - parts := parseQuotedArguments(content) - if len(parts) >= 2 { - // All but the last argument are sources - sources = parts[:len(parts)-1] - } - return sources - } - - // Handle regular format with possible flags - parts := parseQuotedArguments(args) - - // Skip flags like --chown, --chmod, --from - sourcesStartIdx := 0 - for i := 0; i < len(parts); i++ { - part := parts[i] - if strings.HasPrefix(part, "--") { - // Skip the flag and its value if it has one - if !strings.Contains(part, "=") && i+1 < len(parts) && !strings.HasPrefix(parts[i+1], "--") { - sourcesStartIdx = i + 2 - } else { - sourcesStartIdx = i + 1 - } - } else { - break - } - } - - // After skipping flags, we need at least one source and one destination - if len(parts)-sourcesStartIdx >= 2 { - sources = parts[sourcesStartIdx : len(parts)-1] - } - - return sources -} - -func parseQuotedArguments(input string) []string { - var args []string - var current strings.Builder - inQuotes := false - quoteChar := byte(0) - - input = strings.TrimSpace(input) - - for i := 0; i < len(input); i++ { - char := input[i] - - if !inQuotes && (char == '"' || char == '\'') { - inQuotes = true - quoteChar = char - } else if inQuotes && char == quoteChar { - inQuotes = false - quoteChar = 0 - } else if !inQuotes && (char == ' ' || char == '\t') { - if current.Len() > 0 { - args = append(args, current.String()) - current.Reset() - } - // Skip consecutive whitespace - for i+1 < len(input) && (input[i+1] == ' ' || input[i+1] == '\t') { - i++ - } - } else { - current.WriteByte(char) - } - } - - if current.Len() > 0 { - args = append(args, current.String()) - } - - return args -} - -func GetCreateBuildInfoDto(ctx context.Context, dockerfilePath string, contextPaths []string) (*apiclient.CreateBuildInfo, error) { - dockerfileAbsPath, err := filepath.Abs(dockerfilePath) - if err != nil { - return nil, fmt.Errorf("invalid dockerfile path: %w", err) - } - - if _, err := os.Stat(dockerfileAbsPath); os.IsNotExist(err) { - return nil, fmt.Errorf("dockerfile does not exist: %s", dockerfileAbsPath) - } - - dockerfileContent, err := os.ReadFile(dockerfileAbsPath) - if err != nil { - return nil, fmt.Errorf("failed to read dockerfile: %w", err) - } - - dockerfileDir := filepath.Dir(dockerfileAbsPath) - - // If no context paths are provided, automatically parse the Dockerfile to find them - if len(contextPaths) == 0 { - autoContextPaths, err := parseDockerfileForSources(string(dockerfileContent), dockerfileDir) - if err != nil { - return nil, fmt.Errorf("failed to parse dockerfile for context: %w", err) - } - contextPaths = autoContextPaths - } else { - var resolvedContextPaths []string - for _, contextPath := range contextPaths { - var absPath string - if filepath.IsAbs(contextPath) { - absPath = contextPath - } else { - // When context paths are provided manually (via --context flag), - // resolve them relative to the current working directory - absPath, err = filepath.Abs(contextPath) - if err != nil { - return nil, fmt.Errorf("failed to resolve context path %s: %w", contextPath, err) - } - } - resolvedContextPaths = append(resolvedContextPaths, absPath) - } - contextPaths = resolvedContextPaths - } - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return nil, err - } - - contextHashes, err := getContextHashes(ctx, apiClient, contextPaths) - if err != nil { - return nil, err - } - - return &apiclient.CreateBuildInfo{ - DockerfileContent: string(dockerfileContent), - ContextHashes: contextHashes, - }, nil -} diff --git a/apps/cli/cmd/common/format.go b/apps/cli/cmd/common/format.go deleted file mode 100644 index b9dfd89e3..000000000 --- a/apps/cli/cmd/common/format.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" -) - -const ( - formatFlagDescription = `Output format. Must be one of (yaml, json)` - formatFlagName = "format" - formatFlagShortHand = "f" -) - -var ( - FormatFlag string - standardOut *os.File -) - -type outputFormatter struct { - data interface{} - formatter Formatter -} - -func NewFormatter(data interface{}) *outputFormatter { - var formatter Formatter - switch FormatFlag { - case "json": - formatter = JSONFormatter{} - case "yaml": - formatter = YAMLFormatter{} - case "": - formatter = nil - default: - formatter = JSONFormatter{} // Default to JSON - } - - return &outputFormatter{ - data: data, - formatter: formatter, - } - -} - -type Formatter interface { - Format(data interface{}) (string, error) -} - -type JSONFormatter struct{} - -func (f JSONFormatter) Format(data interface{}) (string, error) { - jsonData, err := json.MarshalIndent(data, "", " ") // Indent with two spaces - if err != nil { - return "", err - } - return string(jsonData), nil -} - -type YAMLFormatter struct{} - -func (f YAMLFormatter) Format(data interface{}) (string, error) { - yamlData, err := yaml.Marshal(data) - if err != nil { - return "", err - } - return string(yamlData), nil -} - -func (f *outputFormatter) Print() { - - formattedOutput, err := f.formatter.Format(f.data) - if err != nil { - fmt.Printf("Error formatting output: %v\n", err) - os.Exit(1) - } - - UnblockStdOut() - fmt.Println(formattedOutput) - BlockStdOut() -} - -func BlockStdOut() { - if os.Stdout != nil { - standardOut = os.Stdout - os.Stdout = nil - } -} - -func UnblockStdOut() { - if os.Stdout == nil { - os.Stdout = standardOut - standardOut = nil - } -} - -func RegisterFormatFlag(cmd *cobra.Command) { - cmd.Flags().StringVarP(&FormatFlag, formatFlagName, formatFlagShortHand, FormatFlag, formatFlagDescription) - cmd.PreRun = func(cmd *cobra.Command, args []string) { - if FormatFlag != "" { - BlockStdOut() - // When a structured output format is requested, suppress - // noisy warnings such as version mismatch so scripts - // consuming json/yaml aren't broken. - internal.SuppressVersionMismatchWarning = true - } else { - internal.SuppressVersionMismatchWarning = false - } - } -} diff --git a/apps/cli/cmd/common/logs.go b/apps/cli/cmd/common/logs.go deleted file mode 100644 index 6f7b27fb0..000000000 --- a/apps/cli/cmd/common/logs.go +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -package common - -import ( - "bufio" - "context" - "fmt" - "io" - "net/http" - "time" - - "github.com/boxlite-ai/boxlite/cli/config" - log "github.com/sirupsen/logrus" -) - -type ReadLogParams struct { - Id string - ServerUrl string - ServerApi config.ServerApi - ActiveOrganizationId *string - Follow *bool - ResourceType ResourceType -} - -type ResourceType string - -const ( - ResourceTypeSandbox ResourceType = "sandbox" - ResourceTypeSnapshot ResourceType = "snapshots" -) - -func ReadBuildLogs(ctx context.Context, params ReadLogParams) { - url := fmt.Sprintf("%s/%s/%s/build-logs", params.ServerUrl, params.ResourceType, params.Id) - if params.Follow != nil && *params.Follow { - url = fmt.Sprintf("%s?follow=true", url) - } - - req, err := http.NewRequestWithContext(ctx, "GET", url, nil) - if err != nil { - log.Errorf("Failed to create request: %v", err) - return - } - - if params.ServerApi.Key != nil { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", *params.ServerApi.Key)) - } else if params.ServerApi.Token != nil { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", params.ServerApi.Token.AccessToken)) - - if params.ActiveOrganizationId != nil { - req.Header.Add("X-BoxLite-Organization-ID", *params.ActiveOrganizationId) - } - } - - req.Header.Add("Accept", "application/octet-stream") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - log.Errorf("Failed to connect to server: %v", err) - return - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - log.Errorf("Server returned a non-OK status while retrieving logs: %d", resp.StatusCode) - return - } - - reader := bufio.NewReader(resp.Body) - buffer := make([]byte, 4096) - - for { - select { - case <-ctx.Done(): - return - default: - n, err := reader.Read(buffer) - if n > 0 { - fmt.Print(string(buffer[:n])) - } - - if err != nil { - if err == io.EOF { - if params.Follow != nil && *params.Follow { - time.Sleep(500 * time.Millisecond) - continue - } - return - } - // Don't log context.Canceled as it's an expected case when streaming is stopped - if err != context.Canceled { - log.Errorf("Error reading from stream: %v", err) - } - return - } - } - } -} diff --git a/apps/cli/cmd/common/organization.go b/apps/cli/cmd/common/organization.go deleted file mode 100644 index 4b8182c5e..000000000 --- a/apps/cli/cmd/common/organization.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "context" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/config" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -func GetPersonalOrganizationId(profile config.Profile) (string, error) { - apiClient, err := apiclient_cli.GetApiClient(&profile, nil) - if err != nil { - return "", err - } - - organizationList, res, err := apiClient.OrganizationsAPI.ListOrganizations(context.Background()).Execute() - if err != nil { - return "", apiclient_cli.HandleErrorResponse(res, err) - } - - for _, organization := range organizationList { - if organization.Personal { - return organization.Id, nil - } - } - - return "", nil -} - -func GetActiveOrganizationName(apiClient *apiclient.APIClient, ctx context.Context) (string, error) { - activeOrganizationId, err := config.GetActiveOrganizationId() - if err != nil { - return "", err - } - - if activeOrganizationId == "" { - return "", config.ErrNoActiveOrganization - } - - activeOrganization, res, err := apiClient.OrganizationsAPI.GetOrganization(ctx, activeOrganizationId).Execute() - if err != nil { - return "", apiclient_cli.HandleErrorResponse(res, err) - } - - return activeOrganization.Name, nil -} diff --git a/apps/cli/cmd/common/sandbox.go b/apps/cli/cmd/common/sandbox.go deleted file mode 100644 index 643d71858..000000000 --- a/apps/cli/cmd/common/sandbox.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -func RequireStartedState(sandbox *apiclient.Sandbox) error { - if sandbox.State == nil { - return fmt.Errorf("sandbox state is unknown") - } - - state := *sandbox.State - if state == apiclient.SANDBOXSTATE_STARTED { - return nil - } - - sandboxRef := sandbox.Id - if sandbox.Name != "" { - sandboxRef = sandbox.Name - } - - switch state { - case apiclient.SANDBOXSTATE_STOPPED: - return fmt.Errorf("sandbox is stopped. Start it with: boxlite sandbox start %s", sandboxRef) - case apiclient.SANDBOXSTATE_ARCHIVED: - return fmt.Errorf("sandbox is archived. Start it with: boxlite sandbox start %s", sandboxRef) - case apiclient.SANDBOXSTATE_ARCHIVING: - return fmt.Errorf("sandbox is archiving. Start it with: boxlite sandbox start %s", sandboxRef) - case apiclient.SANDBOXSTATE_STARTING: - return fmt.Errorf("sandbox is starting. Please wait for it to be ready") - case apiclient.SANDBOXSTATE_STOPPING: - return fmt.Errorf("sandbox is stopping. Please wait for it to complete") - case apiclient.SANDBOXSTATE_CREATING: - return fmt.Errorf("sandbox is being created. Please wait for it to be ready") - case apiclient.SANDBOXSTATE_DESTROYING: - return fmt.Errorf("sandbox is being destroyed") - case apiclient.SANDBOXSTATE_DESTROYED: - return fmt.Errorf("sandbox has been destroyed") - case apiclient.SANDBOXSTATE_ERROR: - return fmt.Errorf("sandbox is in an error state") - case apiclient.SANDBOXSTATE_BUILD_FAILED: - return fmt.Errorf("sandbox build failed") - default: - return fmt.Errorf("sandbox is not running (state: %s)", state) - } -} diff --git a/apps/cli/cmd/common/ssh.go b/apps/cli/cmd/common/ssh.go deleted file mode 100644 index 0c893796f..000000000 --- a/apps/cli/cmd/common/ssh.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - "strings" -) - -// ParseSSHCommand parses the SSH command string returned by the API -// Expected formats: -// - "ssh token@host" (port 22) -// - "ssh -p port token@host" -func ParseSSHCommand(sshCommand string) ([]string, error) { - parts := strings.Fields(sshCommand) - if len(parts) < 2 { - return nil, fmt.Errorf("invalid SSH command format: %s", sshCommand) - } - - // Skip the "ssh" part - args := parts[1:] - - return args, nil -} diff --git a/apps/cli/cmd/common/ssh_unix.go b/apps/cli/cmd/common/ssh_unix.go deleted file mode 100644 index 4743966be..000000000 --- a/apps/cli/cmd/common/ssh_unix.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -//go:build unix - -package common - -import ( - "fmt" - "os" - "os/exec" - "os/signal" - "syscall" -) - -// ExecuteSSH runs the SSH command with proper terminal handling -func ExecuteSSH(sshArgs []string) error { - sshPath, err := exec.LookPath("ssh") - if err != nil { - return fmt.Errorf("ssh not found in PATH: %w", err) - } - - // Create the command - sshCmd := exec.Command(sshPath, sshArgs...) - sshCmd.Stdin = os.Stdin - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - - // Handle signals - forward them to the SSH process - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGWINCH) - - // Start the SSH process - if err := sshCmd.Start(); err != nil { - return fmt.Errorf("failed to start SSH: %w", err) - } - - // Forward signals to the SSH process - go func() { - for sig := range sigChan { - if sshCmd.Process != nil { - _ = sshCmd.Process.Signal(sig) - } - } - }() - - // Wait for SSH to complete - err = sshCmd.Wait() - - // Stop signal handling - signal.Stop(sigChan) - close(sigChan) - - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - os.Exit(exitErr.ExitCode()) - } - return err - } - - return nil -} diff --git a/apps/cli/cmd/common/ssh_windows.go b/apps/cli/cmd/common/ssh_windows.go deleted file mode 100644 index 0714e0aa7..000000000 --- a/apps/cli/cmd/common/ssh_windows.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -//go:build windows - -package common - -import ( - "fmt" - "os" - "os/exec" - "os/signal" - "syscall" -) - -// ExecuteSSH runs the SSH command with proper terminal handling -func ExecuteSSH(sshArgs []string) error { - sshPath, err := exec.LookPath("ssh") - if err != nil { - return fmt.Errorf("ssh not found in PATH: %w", err) - } - - // Create the command - sshCmd := exec.Command(sshPath, sshArgs...) - sshCmd.Stdin = os.Stdin - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - - // Handle signals - forward them to the SSH process - // Note: SIGWINCH is not available on Windows - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) - - // Start the SSH process - if err := sshCmd.Start(); err != nil { - return fmt.Errorf("failed to start SSH: %w", err) - } - - // Forward signals to the SSH process - go func() { - for sig := range sigChan { - if sshCmd.Process != nil { - _ = sshCmd.Process.Signal(sig) - } - } - }() - - // Wait for SSH to complete - err = sshCmd.Wait() - - // Stop signal handling - signal.Stop(sigChan) - close(sigChan) - - if err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - os.Exit(exitErr.ExitCode()) - } - return err - } - - return nil -} diff --git a/apps/cli/cmd/common/state.go b/apps/cli/cmd/common/state.go deleted file mode 100644 index b1f71185e..000000000 --- a/apps/cli/cmd/common/state.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "context" - "fmt" - "time" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -func AwaitSnapshotState(ctx context.Context, apiClient *apiclient.APIClient, name string, state apiclient.SnapshotState) error { - for { - snapshot, res, err := apiClient.SnapshotsAPI.GetSnapshot(ctx, name).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - switch snapshot.State { - case state: - return nil - case apiclient.SNAPSHOTSTATE_ERROR, apiclient.SNAPSHOTSTATE_BUILD_FAILED: - if !snapshot.ErrorReason.IsSet() { - return fmt.Errorf("snapshot processing failed") - } - return fmt.Errorf("snapshot processing failed: %s", *snapshot.ErrorReason.Get()) - } - - time.Sleep(time.Second) - } -} - -func AwaitSandboxState(ctx context.Context, apiClient *apiclient.APIClient, targetSandbox string, state apiclient.SandboxState) error { - for { - sandbox, res, err := apiClient.SandboxAPI.GetSandbox(ctx, targetSandbox).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - if sandbox.State != nil && *sandbox.State == state { - return nil - } else if sandbox.State != nil && (*sandbox.State == apiclient.SANDBOXSTATE_ERROR || *sandbox.State == apiclient.SANDBOXSTATE_BUILD_FAILED) { - if sandbox.ErrorReason == nil { - return fmt.Errorf("sandbox processing failed") - } - return fmt.Errorf("sandbox processing failed: %s", *sandbox.ErrorReason) - } - - time.Sleep(time.Second) - } -} diff --git a/apps/cli/cmd/common/validate.go b/apps/cli/cmd/common/validate.go deleted file mode 100644 index ac5d3a54a..000000000 --- a/apps/cli/cmd/common/validate.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - "strings" -) - -func ValidateImageName(imageName string) error { - parts := strings.Split(imageName, ":") - if len(parts) != 2 { - return fmt.Errorf("invalid image format: must contain exactly one colon (e.g., 'ubuntu:22.04')") - } - if parts[1] == "latest" { - return fmt.Errorf("tag 'latest' not allowed, please use a specific version tag") - } - - return nil -} diff --git a/apps/cli/cmd/docs.go b/apps/cli/cmd/docs.go deleted file mode 100644 index ecfb3b4f8..000000000 --- a/apps/cli/cmd/docs.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package cmd - -import ( - "fmt" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/pkg/browser" - "github.com/spf13/cobra" -) - -var docsURL string = "https://www.boxlite.ai/docs/" - -var DocsCmd = &cobra.Command{ - Use: "docs", - Short: "Opens the BoxLite documentation in your default browser.", - Args: cobra.NoArgs, - Aliases: []string{"documentation", "doc"}, - RunE: func(cmd *cobra.Command, args []string) error { - common.RenderInfoMessageBold(fmt.Sprintf("Opening the BoxLite documentation in your default browser. If opening fails, you can go to %s manually.", common.LinkStyle.Render(docsURL))) - return browser.OpenURL(docsURL) - }, -} diff --git a/apps/cli/cmd/generatedocs.go b/apps/cli/cmd/generatedocs.go deleted file mode 100644 index 05b2cc6b9..000000000 --- a/apps/cli/cmd/generatedocs.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package cmd - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" -) - -var yamlDirectory = "hack" -var defaultDirectory = "docs" - -var GenerateDocsCmd = &cobra.Command{ - Use: "generate-docs", - Short: "Generate documentation for the BoxLite CLI", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - directory, err := cmd.Flags().GetString("directory") - if err != nil { - return err - } - - if directory == "" { - directory = defaultDirectory - } - - err = os.MkdirAll(directory, os.ModePerm) - if err != nil { - return err - } - - err = os.MkdirAll(filepath.Join(yamlDirectory, directory), os.ModePerm) - if err != nil { - return err - } - - err = doc.GenMarkdownTree(cmd.Root(), directory) - if err != nil { - return err - } - - err = doc.GenYamlTree(cmd.Root(), filepath.Join(yamlDirectory, directory)) - if err != nil { - return err - } - - fmt.Printf("Documentation generated at %s\n", directory) - return nil - }, - Hidden: true, -} - -func init() { - GenerateDocsCmd.Flags().String("directory", "", "Directory to generate documentation into") -} diff --git a/apps/cli/cmd/mcp/agents/claude.go b/apps/cli/cmd/mcp/agents/claude.go deleted file mode 100644 index f156dc559..000000000 --- a/apps/cli/cmd/mcp/agents/claude.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package agents - -import ( - "errors" - "os" - "path/filepath" - "runtime" -) - -func InitClaude(homeDir string) (string, string, error) { - var agentConfigFilePath string - var mcpLogFilePath string - - switch runtime.GOOS { - case "darwin": - agentConfigFilePath = filepath.Join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json") - mcpLogFilePath = filepath.Join(homeDir, "Library", "Logs", "Claude", mcpLogFileName) - - case "windows": - // Resolve %APPDATA% environment variable - appData := os.Getenv("APPDATA") - if appData == "" { - return "", "", errors.New("could not resolve APPDATA environment variable") - } - - agentConfigFilePath = filepath.Join(appData, "Claude", "claude_desktop_config.json") - mcpLogFilePath = filepath.Join(appData, "Claude", "Logs", mcpLogFileName) - - case "linux": - agentConfigFilePath = filepath.Join(homeDir, ".config", "Claude", "claude_desktop_config.json") - mcpLogFilePath = filepath.Join("var", "log", "Claude", mcpLogFileName) - default: - return "", "", errors.New("operating system is not supported") - } - - return agentConfigFilePath, mcpLogFilePath, nil -} diff --git a/apps/cli/cmd/mcp/agents/common.go b/apps/cli/cmd/mcp/agents/common.go deleted file mode 100644 index d78ef3201..000000000 --- a/apps/cli/cmd/mcp/agents/common.go +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package agents - -var mcpLogFileName string = "boxlite-mcp-server.log" diff --git a/apps/cli/cmd/mcp/agents/cursor.go b/apps/cli/cmd/mcp/agents/cursor.go deleted file mode 100644 index 89ac36b8c..000000000 --- a/apps/cli/cmd/mcp/agents/cursor.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package agents - -import ( - "errors" - "os" - "path/filepath" - "runtime" -) - -func InitCursor(homeDir string) (string, string, error) { - var agentConfigFilePath string - var mcpLogFilePath string - - switch runtime.GOOS { - case "darwin": - agentConfigFilePath = filepath.Join(homeDir, ".cursor", "mcp.json") - mcpLogFilePath = filepath.Join(homeDir, "Library", "Logs", "Cursor", mcpLogFileName) - - case "windows": - // Resolve %APPDATA% environment variable - appData := os.Getenv("APPDATA") - if appData == "" { - return "", "", errors.New("could not resolve APPDATA environment variable") - } - - agentConfigFilePath = filepath.Join(appData, ".cursor", "mcp.json") - mcpLogFilePath = filepath.Join(appData, "Cursor", "Logs", mcpLogFileName) - - case "linux": - agentConfigFilePath = filepath.Join(homeDir, ".cursor", "mcp.json") - mcpLogFilePath = filepath.Join("var", "log", "Cursor", mcpLogFileName) - default: - return "", "", errors.New("operating system is not supported") - } - - return agentConfigFilePath, mcpLogFilePath, nil -} diff --git a/apps/cli/cmd/mcp/agents/windsurf.go b/apps/cli/cmd/mcp/agents/windsurf.go deleted file mode 100644 index 885a05d76..000000000 --- a/apps/cli/cmd/mcp/agents/windsurf.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package agents - -import ( - "errors" - "os" - "path/filepath" - "runtime" -) - -func InitWindsurf(homeDir string) (string, string, error) { - var agentConfigFilePath string - var mcpLogFilePath string - - switch runtime.GOOS { - case "darwin": - agentConfigFilePath = filepath.Join(homeDir, ".codeium", "windsurf", "mcp_config.json") - mcpLogFilePath = filepath.Join(homeDir, "Library", "Logs", "Windsurf", mcpLogFileName) - - case "windows": - // Resolve %APPDATA% environment variable - appData := os.Getenv("APPDATA") - if appData == "" { - return "", "", errors.New("could not resolve APPDATA environment variable") - } - - agentConfigFilePath = filepath.Join(appData, ".codeium", "windsurf", "mcp_config.json") - mcpLogFilePath = filepath.Join(appData, "Windsurf", "Logs", mcpLogFileName) - - case "linux": - agentConfigFilePath = filepath.Join(homeDir, ".codeium", "windsurf", "mcp_config.json") - mcpLogFilePath = filepath.Join("var", "log", "Windsurf", mcpLogFileName) - default: - return "", "", errors.New("operating system is not supported") - } - - return agentConfigFilePath, mcpLogFilePath, nil -} diff --git a/apps/cli/cmd/mcp/config.go b/apps/cli/cmd/mcp/config.go deleted file mode 100644 index c2d3120ee..000000000 --- a/apps/cli/cmd/mcp/config.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package mcp - -import ( - "encoding/json" - "fmt" - "os" - "runtime" - - "github.com/spf13/cobra" -) - -var ConfigCmd = &cobra.Command{ - Use: "config [AGENT_NAME]", - Short: "Outputs JSON configuration for BoxLite MCP Server", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - homeDir, err := os.UserHomeDir() - if err != nil { - return err - } - - var mcpLogFilePath string - - switch runtime.GOOS { - case "darwin": - mcpLogFilePath = homeDir + "/.boxlite/boxlite-mcp.log" - case "windows": - mcpLogFilePath = os.Getenv("APPDATA") + "\\.boxlite\\boxlite-mcp.log" - case "linux": - mcpLogFilePath = homeDir + "/.boxlite/boxlite-mcp.log" - default: - return fmt.Errorf("unsupported OS: %s", runtime.GOOS) - } - - boxliteMcpConfig, err := getDayonaMcpConfig(mcpLogFilePath) - if err != nil { - return err - } - - mcpConfig := map[string]interface{}{ - "boxlite-mcp": boxliteMcpConfig, - } - - jsonBytes, err := json.MarshalIndent(mcpConfig, "", " ") - if err != nil { - return err - } - - fmt.Println(string(jsonBytes)) - - return nil - }, -} - -func getDayonaMcpConfig(mcpLogFilePath string) (map[string]interface{}, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, err - } - - // Create boxlite-mcp config - boxliteMcpConfig := map[string]interface{}{ - "command": "boxlite", - "args": []string{"mcp", "start"}, - "env": map[string]string{ - "PATH": homeDir + ":/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin", - "HOME": homeDir, - }, - "logFile": mcpLogFilePath, - } - - if runtime.GOOS == "windows" { - boxliteMcpConfig["env"].(map[string]string)["APPDATA"] = os.Getenv("APPDATA") - } - - return boxliteMcpConfig, nil -} diff --git a/apps/cli/cmd/mcp/init.go b/apps/cli/cmd/mcp/init.go deleted file mode 100644 index 9616b78ae..000000000 --- a/apps/cli/cmd/mcp/init.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package mcp - -import ( - "encoding/json" - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/cmd/mcp/agents" - "github.com/spf13/cobra" -) - -var InitCmd = &cobra.Command{ - Use: "init [AGENT_NAME]", - Short: "Initialize BoxLite MCP Server with an agent (currently supported: claude, windsurf, cursor)", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return fmt.Errorf("agent name is required") - } - - homeDir, err := os.UserHomeDir() - if err != nil { - return err - } - - var agentConfigFilePath, mcpLogFilePath string - - switch args[0] { - case "claude": - agentConfigFilePath, mcpLogFilePath, err = agents.InitClaude(homeDir) - if err != nil { - return err - } - case "cursor": - agentConfigFilePath, mcpLogFilePath, err = agents.InitCursor(homeDir) - if err != nil { - return err - } - case "windsurf": - agentConfigFilePath, mcpLogFilePath, err = agents.InitWindsurf(homeDir) - if err != nil { - return err - } - default: - return fmt.Errorf("agent name %s is not supported", args[0]) - } - - return injectConfig(agentConfigFilePath, mcpLogFilePath) - }, -} - -func injectConfig(agentConfigFilePath, mcpLogFilePath string) error { - boxliteMcpConfig, err := getDayonaMcpConfig(mcpLogFilePath) - if err != nil { - return err - } - - // Read existing model config or create new one - var agentConfig map[string]interface{} - if agentConfigData, err := os.ReadFile(agentConfigFilePath); err == nil { - if err := json.Unmarshal(agentConfigData, &agentConfig); err != nil { - return err - } - } else if !os.IsNotExist(err) { - return err - } else { - agentConfig = make(map[string]interface{}) - } - - // Initialize or update mcpServers field - mcpServers, ok := agentConfig["mcpServers"].(map[string]interface{}) - if !ok { - mcpServers = make(map[string]interface{}) - } - - // Add or update boxlite-mcp configuration - mcpServers["boxlite-mcp"] = boxliteMcpConfig - agentConfig["mcpServers"] = mcpServers - - // Write back the updated config with indentation - updatedJSON, err := json.MarshalIndent(agentConfig, "", " ") - if err != nil { - return err - } - - return os.WriteFile(agentConfigFilePath, updatedJSON, 0644) -} diff --git a/apps/cli/cmd/mcp/mcp.go b/apps/cli/cmd/mcp/mcp.go deleted file mode 100644 index 8e1177169..000000000 --- a/apps/cli/cmd/mcp/mcp.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package mcp - -import ( - "github.com/spf13/cobra" -) - -var MCPCmd = &cobra.Command{ - Use: "mcp", - Short: "Manage BoxLite MCP Server", - Long: "Commands for managing BoxLite MCP Server", -} - -func init() { - MCPCmd.AddCommand(InitCmd) - MCPCmd.AddCommand(StartCmd) - MCPCmd.AddCommand(ConfigCmd) -} diff --git a/apps/cli/cmd/mcp/start.go b/apps/cli/cmd/mcp/start.go deleted file mode 100644 index fcfb10149..000000000 --- a/apps/cli/cmd/mcp/start.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package mcp - -import ( - "os" - "os/signal" - - "github.com/boxlite-ai/boxlite/cli/mcp" - "github.com/spf13/cobra" -) - -var StartCmd = &cobra.Command{ - Use: "start", - Short: "Start BoxLite MCP Server", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, args []string) error { - server := mcp.NewBoxliteMCPServer() - - interruptChan := make(chan os.Signal, 1) - signal.Notify(interruptChan, os.Interrupt) - - errChan := make(chan error) - - go func() { - errChan <- server.Start() - }() - - select { - case err := <-errChan: - return err - case <-interruptChan: - return nil - } - }, -} diff --git a/apps/cli/cmd/organization/create.go b/apps/cli/cmd/organization/create.go deleted file mode 100644 index 83c93a89e..000000000 --- a/apps/cli/cmd/organization/create.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "context" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/organization" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var CreateCmd = &cobra.Command{ - Use: "create [ORGANIZATION_NAME]", - Short: "Create a new organization and set it as active", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - createOrganizationDto := apiclient.CreateOrganization{ - Name: args[0], - } - - org, res, err := apiClient.OrganizationsAPI.CreateOrganization(ctx).CreateOrganization(createOrganizationDto).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - activeProfile.ActiveOrganizationId = &org.Id - err = c.EditProfile(activeProfile) - if err != nil { - return err - } - - organization.RenderInfo(org, false) - - common.RenderInfoMessageBold("Your organization has been created and its approval is pending\nOur team has been notified and will set up your resource quotas shortly") - return nil - }, -} diff --git a/apps/cli/cmd/organization/delete.go b/apps/cli/cmd/organization/delete.go deleted file mode 100644 index 8486dcdca..000000000 --- a/apps/cli/cmd/organization/delete.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/organization" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var DeleteCmd = &cobra.Command{ - Use: "delete [ORGANIZATION]", - Short: "Delete an organization", - Args: cobra.MaximumNArgs(1), - Aliases: common.GetAliases("delete"), - RunE: func(cmd *cobra.Command, args []string) error { - var chosenOrganization *apiclient.Organization - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - orgList, res, err := apiClient.OrganizationsAPI.ListOrganizations(ctx).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - if len(orgList) == 0 { - util.NotifyEmptyOrganizationList(true) - return nil - } - - if len(args) == 0 { - chosenOrganization, err = organization.GetOrganizationIdFromPrompt(orgList) - if err != nil { - return err - } - } else { - for _, org := range orgList { - if org.Id == args[0] || org.Name == args[0] { - chosenOrganization = &org - break - } - } - - if chosenOrganization == nil { - return fmt.Errorf("organization %s not found", args[0]) - } - } - - if chosenOrganization.Name == "Personal" { - return fmt.Errorf("cannot delete personal organization") - } - - res, err = apiClient.OrganizationsAPI.DeleteOrganization(ctx, chosenOrganization.Id).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Organization %s has been deleted", chosenOrganization.Name)) - - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - if activeProfile.ActiveOrganizationId == nil || *activeProfile.ActiveOrganizationId != chosenOrganization.Id { - return nil - } - - personalOrganizationId, err := common.GetPersonalOrganizationId(activeProfile) - if err != nil { - return err - } - - activeProfile.ActiveOrganizationId = &personalOrganizationId - return c.EditProfile(activeProfile) - }, -} diff --git a/apps/cli/cmd/organization/list.go b/apps/cli/cmd/organization/list.go deleted file mode 100644 index d507f0106..000000000 --- a/apps/cli/cmd/organization/list.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "context" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/organization" - "github.com/spf13/cobra" -) - -var ListCmd = &cobra.Command{ - Use: "list", - Short: "List all organizations", - Args: cobra.NoArgs, - Aliases: common.GetAliases("list"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - organizationList, res, err := apiClient.OrganizationsAPI.ListOrganizations(ctx).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(organizationList) - formattedData.Print() - return nil - } - - activeOrganizationId, err := config.GetActiveOrganizationId() - if err != nil { - return err - } - - organization.ListOrganizations(organizationList, &activeOrganizationId) - return nil - }, -} - -func init() { - common.RegisterFormatFlag(ListCmd) -} diff --git a/apps/cli/cmd/organization/organization.go b/apps/cli/cmd/organization/organization.go deleted file mode 100644 index 26c0e500d..000000000 --- a/apps/cli/cmd/organization/organization.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "errors" - - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" -) - -var OrganizationCmd = &cobra.Command{ - Use: "organization", - Short: "Manage BoxLite organizations", - Long: "Commands for managing BoxLite organizations", - Aliases: []string{"organizations", "org", "orgs"}, - GroupID: internal.USER_GROUP, - PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - if config.IsApiKeyAuth() { - return errors.New("organization commands are not available when using API key authentication - run `boxlite login` to reauthenticate with browser") - } - - return nil - }, -} - -func init() { - OrganizationCmd.AddCommand(ListCmd) - OrganizationCmd.AddCommand(CreateCmd) - OrganizationCmd.AddCommand(UseCmd) - OrganizationCmd.AddCommand(DeleteCmd) -} diff --git a/apps/cli/cmd/organization/use.go b/apps/cli/cmd/organization/use.go deleted file mode 100644 index 1fa073687..000000000 --- a/apps/cli/cmd/organization/use.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/organization" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var UseCmd = &cobra.Command{ - Use: "use [ORGANIZATION]", - Short: "Set active organization", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - var chosenOrganization *apiclient.Organization - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - orgList, res, err := apiClient.OrganizationsAPI.ListOrganizations(ctx).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - if len(orgList) == 0 { - util.NotifyEmptyOrganizationList(true) - return nil - } - - if len(args) == 0 { - chosenOrganization, err = organization.GetOrganizationIdFromPrompt(orgList) - if err != nil { - return err - } - } else { - for _, org := range orgList { - if org.Id == args[0] || org.Name == args[0] { - chosenOrganization = &org - break - } - } - - if chosenOrganization == nil { - return fmt.Errorf("organization %s not found", args[0]) - } - } - - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - activeProfile.ActiveOrganizationId = &chosenOrganization.Id - err = c.EditProfile(activeProfile) - if err != nil { - return err - } - - common.RenderInfoMessageBold(fmt.Sprintf("Organization %s is now active", chosenOrganization.Name)) - return nil - }, -} diff --git a/apps/cli/cmd/sandbox/archive.go b/apps/cli/cmd/sandbox/archive.go deleted file mode 100644 index 0e7dddff6..000000000 --- a/apps/cli/cmd/sandbox/archive.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/spf13/cobra" -) - -var ArchiveCmd = &cobra.Command{ - Use: "archive [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "Archive a sandbox", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrNameArg := args[0] - - _, res, err := apiClient.SandboxAPI.ArchiveSandbox(ctx, sandboxIdOrNameArg).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Sandbox %s marked for archival", sandboxIdOrNameArg)) - return nil - }, -} - -func init() { -} diff --git a/apps/cli/cmd/sandbox/create.go b/apps/cli/cmd/sandbox/create.go deleted file mode 100644 index 053c9a547..000000000 --- a/apps/cli/cmd/sandbox/create.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - "strings" - "time" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/util" - views_common "github.com/boxlite-ai/boxlite/cli/views/common" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/lipgloss" - "github.com/spf13/cobra" -) - -const SANDBOX_TERMINAL_PORT = 22222 - -var CreateCmd = &cobra.Command{ - Use: "create [flags]", - Short: "Create a new sandbox", - Args: cobra.NoArgs, - Aliases: common.GetAliases("create"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - createSandbox := apiclient.NewCreateSandbox() - - // Add non-zero values to the request - if snapshotFlag != "" { - createSandbox.SetSnapshot(snapshotFlag) - } - if nameFlag != "" { - createSandbox.SetName(nameFlag) - } - if userFlag != "" { - createSandbox.SetUser(userFlag) - } - if len(envFlag) > 0 { - env := make(map[string]string) - for _, e := range envFlag { - parts := strings.SplitN(e, "=", 2) - if len(parts) == 2 { - env[parts[0]] = parts[1] - } - } - createSandbox.SetEnv(env) - } - if len(labelsFlag) > 0 { - labels := make(map[string]string) - for _, l := range labelsFlag { - parts := strings.SplitN(l, "=", 2) - if len(parts) == 2 { - labels[parts[0]] = parts[1] - } - } - createSandbox.SetLabels(labels) - } - if publicFlag { - createSandbox.SetPublic(true) - } - if classFlag != "" { - createSandbox.SetClass(classFlag) - } - if targetFlag != "" { - createSandbox.SetTarget(targetFlag) - } - if cpuFlag > 0 { - createSandbox.SetCpu(cpuFlag) - } - if gpuFlag > 0 { - createSandbox.SetGpu(gpuFlag) - } - if memoryFlag > 0 { - createSandbox.SetMemory(memoryFlag) - } - if diskFlag > 0 { - createSandbox.SetDisk(diskFlag) - } - if autoStopFlag >= 0 { - createSandbox.SetAutoStopInterval(autoStopFlag) - } - if autoArchiveFlag >= 0 { - createSandbox.SetAutoArchiveInterval(autoArchiveFlag) - } - createSandbox.SetAutoDeleteInterval(autoDeleteFlag) - - createSandbox.SetNetworkBlockAll(networkBlockAllFlag) - if networkAllowListFlag != "" { - createSandbox.SetNetworkAllowList(networkAllowListFlag) - } - - if dockerfileFlag != "" { - createBuildInfoDto, err := common.GetCreateBuildInfoDto(ctx, dockerfileFlag, contextFlag) - if err != nil { - return err - } - createSandbox.SetBuildInfo(*createBuildInfoDto) - } - - if len(volumesFlag) > 0 { - volumes := make([]apiclient.SandboxVolume, 0, len(volumesFlag)) - for _, v := range volumesFlag { - parts := strings.SplitN(v, ":", 2) - if len(parts) == 2 { - volumeId := parts[0] - mountPath := parts[1] - volume := apiclient.SandboxVolume{ - VolumeId: volumeId, - MountPath: mountPath, - } - volumes = append(volumes, volume) - } - } - if len(volumes) > 0 { - createSandbox.SetVolumes(volumes) - } - } - - var sandbox *apiclient.Sandbox - - sandbox, res, err := apiClient.SandboxAPI.CreateSandbox(ctx).CreateSandbox(*createSandbox).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - if sandbox.State != nil && *sandbox.State == apiclient.SANDBOXSTATE_PENDING_BUILD { - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, apiclient.SANDBOXSTATE_BUILDING_SNAPSHOT) - if err != nil { - return err - } - - logsContext, stopLogs := context.WithCancel(context.Background()) - defer stopLogs() - - go common.ReadBuildLogs(logsContext, common.ReadLogParams{ - Id: sandbox.Id, - ServerUrl: activeProfile.Api.Url, - ServerApi: activeProfile.Api, - ActiveOrganizationId: activeProfile.ActiveOrganizationId, - Follow: util.Pointer(true), - ResourceType: common.ResourceTypeSandbox, - }) - - err = common.AwaitSandboxState(ctx, apiClient, sandbox.Id, apiclient.SANDBOXSTATE_STARTED) - if err != nil { - return err - } - - // Wait for the last logs to be read - time.Sleep(250 * time.Millisecond) - stopLogs() - } - - previewUrl, res, err := apiClient.SandboxAPI.GetPortPreviewUrl(ctx, sandbox.Id, SANDBOX_TERMINAL_PORT).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - boldStyle := lipgloss.NewStyle().Bold(true) - - views_common.RenderInfoMessageBold(fmt.Sprintf("Sandbox '%s' created successfully", sandbox.Name)) - views_common.RenderInfoMessage(fmt.Sprintf("Connect via SSH: %s", boldStyle.Render(fmt.Sprintf("boxlite ssh %s", sandbox.Name)))) - views_common.RenderInfoMessage(fmt.Sprintf("Open the Web Terminal: %s\n", views_common.LinkStyle.Render(previewUrl.Url))) - return nil - }, -} - -var ( - snapshotFlag string - nameFlag string - userFlag string - envFlag []string - labelsFlag []string - publicFlag bool - classFlag string - targetFlag string - cpuFlag int32 - gpuFlag int32 - memoryFlag int32 - diskFlag int32 - autoStopFlag int32 - autoArchiveFlag int32 - autoDeleteFlag int32 - volumesFlag []string - dockerfileFlag string - contextFlag []string - networkBlockAllFlag bool - networkAllowListFlag string -) - -func init() { - CreateCmd.Flags().StringVar(&snapshotFlag, "snapshot", "", "Snapshot to use for the sandbox") - CreateCmd.Flags().StringVar(&nameFlag, "name", "", "Name of the sandbox") - CreateCmd.Flags().StringVar(&userFlag, "user", "", "User associated with the sandbox") - CreateCmd.Flags().StringArrayVarP(&envFlag, "env", "e", []string{}, "Environment variables (format: KEY=VALUE)") - CreateCmd.Flags().StringArrayVarP(&labelsFlag, "label", "l", []string{}, "Labels (format: KEY=VALUE)") - CreateCmd.Flags().BoolVar(&publicFlag, "public", false, "Make sandbox publicly accessible") - CreateCmd.Flags().StringVar(&classFlag, "class", "", "Sandbox class type (small, medium, large)") - CreateCmd.Flags().StringVar(&targetFlag, "target", "", "Target region (eu, us)") - CreateCmd.Flags().Int32Var(&cpuFlag, "cpu", 0, "CPU cores allocated to the sandbox") - CreateCmd.Flags().Int32Var(&gpuFlag, "gpu", 0, "GPU units allocated to the sandbox") - CreateCmd.Flags().Int32Var(&memoryFlag, "memory", 0, "Memory allocated to the sandbox in MB") - CreateCmd.Flags().Int32Var(&diskFlag, "disk", 0, "Disk space allocated to the sandbox in GB") - CreateCmd.Flags().Int32Var(&autoStopFlag, "auto-stop", 15, "Auto-stop interval in minutes (0 means disabled)") - CreateCmd.Flags().Int32Var(&autoArchiveFlag, "auto-archive", 10080, "Auto-archive interval in minutes (0 means the maximum interval will be used)") - CreateCmd.Flags().Int32Var(&autoDeleteFlag, "auto-delete", -1, "Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping)") - CreateCmd.Flags().StringArrayVarP(&volumesFlag, "volume", "v", []string{}, "Volumes to mount (format: VOLUME_NAME:MOUNT_PATH)") - CreateCmd.Flags().StringVarP(&dockerfileFlag, "dockerfile", "f", "", "Path to Dockerfile for Sandbox snapshot") - CreateCmd.Flags().StringArrayVarP(&contextFlag, "context", "c", []string{}, "Files or directories to include in the build context (can be specified multiple times)") - CreateCmd.Flags().BoolVar(&networkBlockAllFlag, "network-block-all", false, "Whether to block all network access for the sandbox") - CreateCmd.Flags().StringVar(&networkAllowListFlag, "network-allow-list", "", "Comma-separated list of allowed CIDR network addresses for the sandbox") - - CreateCmd.MarkFlagsMutuallyExclusive("snapshot", "dockerfile") - CreateCmd.MarkFlagsMutuallyExclusive("snapshot", "context") -} diff --git a/apps/cli/cmd/sandbox/delete.go b/apps/cli/cmd/sandbox/delete.go deleted file mode 100644 index ddea537f0..000000000 --- a/apps/cli/cmd/sandbox/delete.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - "sync" - "sync/atomic" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - views_util "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -const spinnerThreshold = 10 - -var DeleteCmd = &cobra.Command{ - Use: "delete [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "Delete a sandbox", - Args: cobra.MaximumNArgs(1), - Aliases: common.GetAliases("delete"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - // Handle case when no sandbox ID is provided and allFlag is true - if len(args) == 0 { - if allFlag { - page := float32(1.0) - limit := float32(200.0) // 200 is the maximum limit for the API - var allSandboxes []apiclient.Sandbox - - for { - sandboxBatch, res, err := apiClient.SandboxAPI.ListSandboxesPaginated(ctx).Page(page).Limit(limit).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - allSandboxes = append(allSandboxes, sandboxBatch.Items...) - - if len(sandboxBatch.Items) < int(limit) || page >= float32(sandboxBatch.TotalPages) { - break - } - page++ - } - - if len(allSandboxes) == 0 { - view_common.RenderInfoMessageBold("No sandboxes to delete") - return nil - } - - var deletedCount int64 - - deleteFn := func() error { - var wg sync.WaitGroup - sem := make(chan struct{}, 10) // limit to 10 concurrent deletes - - for _, sb := range allSandboxes { - wg.Add(1) - go func(sb apiclient.Sandbox) { - defer wg.Done() - sem <- struct{}{} - defer func() { <-sem }() - - _, res, err := apiClient.SandboxAPI.DeleteSandbox(ctx, sb.Id).Execute() - if err != nil { - fmt.Printf("Failed to delete sandbox %s: %s\n", sb.Id, apiclient_cli.HandleErrorResponse(res, err)) - } else { - atomic.AddInt64(&deletedCount, 1) - } - }(sb) - } - wg.Wait() - return nil - } - - if len(allSandboxes) > spinnerThreshold { - err = views_util.WithInlineSpinner("Deleting all sandboxes", deleteFn) - } else { - err = deleteFn() - } - if err != nil { - return err - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Deleted %d sandboxes", atomic.LoadInt64(&deletedCount))) - return nil - } - return cmd.Help() - } - - // Handle case when a sandbox ID is provided - sandboxIdOrNameArg := args[0] - - _, res, err := apiClient.SandboxAPI.DeleteSandbox(ctx, sandboxIdOrNameArg).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Sandbox %s deleted", sandboxIdOrNameArg)) - - return nil - }, -} - -var allFlag bool - -func init() { - DeleteCmd.Flags().BoolVarP(&allFlag, "all", "a", false, "Delete all sandboxes") -} diff --git a/apps/cli/cmd/sandbox/exec.go b/apps/cli/cmd/sandbox/exec.go deleted file mode 100644 index 420e1f99b..000000000 --- a/apps/cli/cmd/sandbox/exec.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/toolbox" - "github.com/spf13/cobra" -) - -var ExecCmd = &cobra.Command{ - Use: "exec [SANDBOX_ID | SANDBOX_NAME] -- [COMMAND] [ARGS...]", - Short: "Execute a command in a sandbox", - Long: "Execute a command in a running sandbox", - Args: cobra.MinimumNArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrName := args[0] - - // Find the command args after "--" - commandArgs := args[1:] - if len(commandArgs) == 0 { - return fmt.Errorf("no command specified") - } - - // First, get the sandbox to get its ID and region (in case name was provided) - sandbox, res, err := apiClient.SandboxAPI.GetSandbox(ctx, sandboxIdOrName).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if err := common.RequireStartedState(sandbox); err != nil { - return err - } - - toolboxClient := toolbox.NewClient(apiClient) - - command := strings.Join(commandArgs, " ") - - executeRequest := toolbox.ExecuteRequest{ - Command: command, - } - if execCwd != "" { - executeRequest.Cwd = &execCwd - } - if execTimeout > 0 { - timeout := float32(execTimeout) - executeRequest.Timeout = &timeout - } - - // Execute the command via toolbox - response, err := toolboxClient.ExecuteCommand(ctx, sandbox, executeRequest) - if err != nil { - return err - } - - // Print the output (stdout + stderr combined) - if response.Result != "" { - fmt.Print(response.Result) - } - - // Exit with the command's exit code - exitCode := int(response.ExitCode) - if exitCode != 0 { - if response.Result == "" { - fmt.Fprintf(os.Stderr, "Command failed with exit code %d\n", exitCode) - } - os.Exit(exitCode) - } - - return nil - }, -} - -var ( - execCwd string - execTimeout int -) - -func init() { - ExecCmd.Flags().StringVar(&execCwd, "cwd", "", "Working directory for command execution") - ExecCmd.Flags().IntVar(&execTimeout, "timeout", 0, "Command timeout in seconds (0 for no timeout)") -} diff --git a/apps/cli/cmd/sandbox/info.go b/apps/cli/cmd/sandbox/info.go deleted file mode 100644 index f7f9c508c..000000000 --- a/apps/cli/cmd/sandbox/info.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/views/sandbox" - "github.com/spf13/cobra" -) - -var InfoCmd = &cobra.Command{ - Use: "info [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "Get sandbox info", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("info"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrNameArg := args[0] - - sb, res, err := apiClient.SandboxAPI.GetSandbox(ctx, sandboxIdOrNameArg).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(sb) - formattedData.Print() - return nil - } - - sandbox.RenderInfo(sb, false) - - return nil - }, -} - -func init() { - common.RegisterFormatFlag(InfoCmd) -} diff --git a/apps/cli/cmd/sandbox/list.go b/apps/cli/cmd/sandbox/list.go deleted file mode 100644 index c1142b2ec..000000000 --- a/apps/cli/cmd/sandbox/list.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/sandbox" - "github.com/spf13/cobra" -) - -var ( - pageFlag int - limitFlag int -) - -var ListCmd = &cobra.Command{ - Use: "list", - Short: "List sandboxes", - Args: cobra.NoArgs, - Aliases: common.GetAliases("list"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - page := float32(1.0) - limit := float32(100.0) - - if cmd.Flags().Changed("page") { - page = float32(pageFlag) - } - - if cmd.Flags().Changed("limit") { - limit = float32(limitFlag) - } - - sandboxList, res, err := apiClient.SandboxAPI.ListSandboxesPaginated(ctx).Page(page).Limit(limit).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - sandbox.SortSandboxes(&sandboxList.Items) - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(sandboxList) - formattedData.Print() - return nil - } - - var activeOrganizationName *string - - if !config.IsApiKeyAuth() { - name, err := common.GetActiveOrganizationName(apiClient, ctx) - if err != nil { - return err - } - activeOrganizationName = &name - } - - sandbox.ListSandboxes(sandboxList.Items, activeOrganizationName) - return nil - }, -} - -func init() { - ListCmd.Flags().IntVarP(&pageFlag, "page", "p", 1, "Page number for pagination (starting from 1)") - ListCmd.Flags().IntVarP(&limitFlag, "limit", "l", 100, "Maximum number of items per page") - common.RegisterFormatFlag(ListCmd) -} diff --git a/apps/cli/cmd/sandbox/preview_url.go b/apps/cli/cmd/sandbox/preview_url.go deleted file mode 100644 index e04b75d7b..000000000 --- a/apps/cli/cmd/sandbox/preview_url.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/spf13/cobra" -) - -var PreviewUrlCmd = &cobra.Command{ - Use: "preview-url [SANDBOX_ID | SANDBOX_NAME]", - Short: "Get signed preview URL for a sandbox port", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("preview-url"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrName := args[0] - - if previewUrlPort == 0 { - return fmt.Errorf("port flag is required") - } - - req := apiClient.SandboxAPI.GetSignedPortPreviewUrl(ctx, sandboxIdOrName, previewUrlPort). - ExpiresInSeconds(previewUrlExpires) - - previewUrl, res, err := req.Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - fmt.Println(previewUrl.Url) - - return nil - }, -} - -var ( - previewUrlPort int32 - previewUrlExpires int32 -) - -func init() { - PreviewUrlCmd.Flags().Int32VarP(&previewUrlPort, "port", "p", 0, "Port number to get preview URL for (required)") - PreviewUrlCmd.Flags().Int32Var(&previewUrlExpires, "expires", 3600, "URL expiration time in seconds") - - _ = PreviewUrlCmd.MarkFlagRequired("port") -} diff --git a/apps/cli/cmd/sandbox/sandbox.go b/apps/cli/cmd/sandbox/sandbox.go deleted file mode 100644 index 447516d2a..000000000 --- a/apps/cli/cmd/sandbox/sandbox.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" -) - -var SandboxCmd = &cobra.Command{ - Use: "sandbox", - Short: "Manage BoxLite sandboxes", - Long: "Commands for managing BoxLite sandboxes", - Aliases: []string{"sandboxes"}, - GroupID: internal.SANDBOX_GROUP, - Hidden: true, // Deprecated: use top-level commands instead (e.g., "boxlite start" instead of "boxlite sandbox start") -} - -func init() { - SandboxCmd.AddCommand(ListCmd) - SandboxCmd.AddCommand(CreateCmd) - SandboxCmd.AddCommand(InfoCmd) - SandboxCmd.AddCommand(DeleteCmd) - SandboxCmd.AddCommand(StartCmd) - SandboxCmd.AddCommand(StopCmd) - SandboxCmd.AddCommand(ArchiveCmd) - SandboxCmd.AddCommand(SSHCmd) - SandboxCmd.AddCommand(ExecCmd) - SandboxCmd.AddCommand(PreviewUrlCmd) -} diff --git a/apps/cli/cmd/sandbox/ssh.go b/apps/cli/cmd/sandbox/ssh.go deleted file mode 100644 index 1df257050..000000000 --- a/apps/cli/cmd/sandbox/ssh.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/spf13/cobra" -) - -var SSHCmd = &cobra.Command{ - Use: "ssh [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "SSH into a sandbox", - Long: "Establish an SSH connection to a running sandbox", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrName := args[0] - - // Get sandbox to check state - sandbox, res, err := apiClient.SandboxAPI.GetSandbox(ctx, sandboxIdOrName).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if err := common.RequireStartedState(sandbox); err != nil { - return err - } - - // Create SSH access token - sshAccessRequest := apiClient.SandboxAPI.CreateSshAccess(ctx, sandbox.Id) - if sshExpiresInMinutes > 0 { - sshAccessRequest = sshAccessRequest.ExpiresInMinutes(float32(sshExpiresInMinutes)) - } - - sshAccess, res, err := sshAccessRequest.Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - // Parse the SSH command from the response - sshArgs, err := common.ParseSSHCommand(sshAccess.SshCommand) - if err != nil { - return fmt.Errorf("failed to parse SSH command: %w", err) - } - - // Execute SSH - return common.ExecuteSSH(sshArgs) - }, -} - -var sshExpiresInMinutes int - -func init() { - SSHCmd.Flags().IntVar(&sshExpiresInMinutes, "expires", 1440, "SSH access token expiration time in minutes (defaults to 24 hours)") -} diff --git a/apps/cli/cmd/sandbox/start.go b/apps/cli/cmd/sandbox/start.go deleted file mode 100644 index b11f4f707..000000000 --- a/apps/cli/cmd/sandbox/start.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/spf13/cobra" -) - -var StartCmd = &cobra.Command{ - Use: "start [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "Start a sandbox", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrNameArg := args[0] - - _, res, err := apiClient.SandboxAPI.StartSandbox(ctx, sandboxIdOrNameArg).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Sandbox %s started", sandboxIdOrNameArg)) - - return nil - }, -} - -func init() { -} diff --git a/apps/cli/cmd/sandbox/stop.go b/apps/cli/cmd/sandbox/stop.go deleted file mode 100644 index 4b0ed4459..000000000 --- a/apps/cli/cmd/sandbox/stop.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/spf13/cobra" -) - -var forceFlag bool - -var StopCmd = &cobra.Command{ - Use: "stop [SANDBOX_ID] | [SANDBOX_NAME]", - Short: "Stop a sandbox", - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - sandboxIdOrNameArg := args[0] - - req := apiClient.SandboxAPI.StopSandbox(ctx, sandboxIdOrNameArg) - if forceFlag { - req = req.Force(forceFlag) - } - _, res, err := req.Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Sandbox %s stopped", sandboxIdOrNameArg)) - return nil - }, -} - -func init() { - StopCmd.Flags().BoolVarP(&forceFlag, "force", "f", false, "Force stop the sandbox using SIGKILL") -} diff --git a/apps/cli/cmd/snapshot/create.go b/apps/cli/cmd/snapshot/create.go deleted file mode 100644 index 55ec19f54..000000000 --- a/apps/cli/cmd/snapshot/create.go +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "context" - "fmt" - "strings" - "time" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/util" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - views_util "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var CreateCmd = &cobra.Command{ - Use: "create [SNAPSHOT]", - Short: "Create a snapshot", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("create"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - snapshotName := args[0] - - usingDockerfile := dockerfilePathFlag != "" - usingImage := imageNameFlag != "" - - if !usingDockerfile && !usingImage { - return fmt.Errorf("must specify either --dockerfile or --image") - } - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - createSnapshot := apiclient.NewCreateSnapshot(snapshotName) - - if cpuFlag != 0 { - createSnapshot.SetCpu(cpuFlag) - } - if memoryFlag != 0 { - createSnapshot.SetMemory(memoryFlag) - } - if diskFlag != 0 { - createSnapshot.SetDisk(diskFlag) - } - if regionIdFlag != "" { - createSnapshot.SetRegionId(regionIdFlag) - } - - if usingDockerfile { - createBuildInfoDto, err := common.GetCreateBuildInfoDto(ctx, dockerfilePathFlag, contextFlag) - if err != nil { - return err - } - createSnapshot.SetBuildInfo(*createBuildInfoDto) - } else if usingImage { - err := common.ValidateImageName(imageNameFlag) - if err != nil { - return err - } - createSnapshot.SetImageName(imageNameFlag) - if entrypointFlag != "" { - createSnapshot.SetEntrypoint(strings.Split(entrypointFlag, " ")) - } - } else if entrypointFlag != "" { - createSnapshot.SetEntrypoint(strings.Split(entrypointFlag, " ")) - } - - // Send create request - snapshot, res, err := apiClient.SnapshotsAPI.CreateSnapshot(ctx).CreateSnapshot(*createSnapshot).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - // If we're building from a Dockerfile, show build logs - if usingDockerfile { - c, err := config.GetConfig() - if err != nil { - return err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return err - } - - logsContext, stopLogs := context.WithCancel(context.Background()) - defer stopLogs() - - go common.ReadBuildLogs(logsContext, common.ReadLogParams{ - Id: snapshot.Id, - ServerUrl: activeProfile.Api.Url, - ServerApi: activeProfile.Api, - ActiveOrganizationId: activeProfile.ActiveOrganizationId, - Follow: util.Pointer(true), - ResourceType: common.ResourceTypeSnapshot, - }) - - err = common.AwaitSnapshotState(ctx, apiClient, snapshotName, apiclient.SNAPSHOTSTATE_PENDING) - if err != nil { - return err - } - - // Wait for the last logs to be read - time.Sleep(250 * time.Millisecond) - stopLogs() - } - - err = views_util.WithInlineSpinner("Waiting for the snapshot to be validated", func() error { - return common.AwaitSnapshotState(ctx, apiClient, snapshotName, apiclient.SNAPSHOTSTATE_ACTIVE) - }) - if err != nil { - return err - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Snapshot %s successfully created", snapshotName)) - view_common.RenderInfoMessage(fmt.Sprintf("%s Run 'boxlite sandbox create --snapshot %s' to create a new sandbox using this snapshot", view_common.Checkmark, snapshotName)) - return nil - }, -} - -var ( - entrypointFlag string - imageNameFlag string - dockerfilePathFlag string - contextFlag []string - cpuFlag int32 - memoryFlag int32 - diskFlag int32 - regionIdFlag string -) - -func init() { - CreateCmd.Flags().StringVarP(&entrypointFlag, "entrypoint", "e", "", "The entrypoint command for the snapshot") - CreateCmd.Flags().StringVarP(&imageNameFlag, "image", "i", "", "The image name for the snapshot") - CreateCmd.Flags().StringVarP(&dockerfilePathFlag, "dockerfile", "f", "", "Path to Dockerfile to build") - CreateCmd.Flags().StringArrayVarP(&contextFlag, "context", "c", []string{}, "Files or directories to include in the build context (can be specified multiple times). If not provided, context will be automatically determined from COPY/ADD commands in the Dockerfile") - CreateCmd.Flags().Int32Var(&cpuFlag, "cpu", 0, "CPU cores that will be allocated to the underlying sandboxes (default: 1)") - CreateCmd.Flags().Int32Var(&memoryFlag, "memory", 0, "Memory that will be allocated to the underlying sandboxes in GB (default: 1)") - CreateCmd.Flags().Int32Var(&diskFlag, "disk", 0, "Disk space that will be allocated to the underlying sandboxes in GB (default: 3)") - CreateCmd.Flags().StringVar(®ionIdFlag, "region", "", "ID of the region where the snapshot will be available (defaults to organization default region)") - - CreateCmd.MarkFlagsMutuallyExclusive("image", "dockerfile") - CreateCmd.MarkFlagsMutuallyExclusive("image", "context") - CreateCmd.MarkFlagsMutuallyExclusive("entrypoint", "dockerfile") - CreateCmd.MarkFlagsMutuallyExclusive("entrypoint", "context") -} diff --git a/apps/cli/cmd/snapshot/delete.go b/apps/cli/cmd/snapshot/delete.go deleted file mode 100644 index 855d000a7..000000000 --- a/apps/cli/cmd/snapshot/delete.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var DeleteCmd = &cobra.Command{ - Use: "delete [SNAPSHOT_ID | SNAPSHOT_NAME]", - Short: "Delete a snapshot", - Args: cobra.MaximumNArgs(1), - Aliases: common.GetAliases("delete"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - // Handle case when no snapshot ID is provided and allFlag is true - if len(args) == 0 { - if allFlag { - page := float32(1.0) - limit := float32(200.0) // 200 is the maximum limit for the API - var allSnapshots []apiclient.SnapshotDto - - for { - snapshotBatch, res, err := apiClient.SnapshotsAPI.GetAllSnapshots(ctx).Page(page).Limit(limit).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - allSnapshots = append(allSnapshots, snapshotBatch.Items...) - - if len(snapshotBatch.Items) < int(limit) || page >= snapshotBatch.TotalPages { - break - } - page++ - } - - if len(allSnapshots) == 0 { - view_common.RenderInfoMessageBold("No snapshots to delete") - return nil - } - - var deletedCount int - - for _, snapshot := range allSnapshots { - res, err := apiClient.SnapshotsAPI.RemoveSnapshot(ctx, snapshot.Id).Execute() - if err != nil { - fmt.Printf("Failed to delete snapshot %s: %s\n", snapshot.Id, apiclient_cli.HandleErrorResponse(res, err)) - } else { - deletedCount++ - } - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Deleted %d snapshots", deletedCount)) - return nil - } - return cmd.Help() - } - - snapshotIdOrName := args[0] - - snapshot, res, err := apiClient.SnapshotsAPI.GetSnapshot(ctx, snapshotIdOrName).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - res, err = apiClient.SnapshotsAPI.RemoveSnapshot(ctx, snapshot.Id).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Snapshot %s deleted", snapshotIdOrName)) - - return nil - }, -} - -var allFlag bool - -func init() { - DeleteCmd.Flags().BoolVarP(&allFlag, "all", "a", false, "Delete all snapshots") -} diff --git a/apps/cli/cmd/snapshot/list.go b/apps/cli/cmd/snapshot/list.go deleted file mode 100644 index 8a8674005..000000000 --- a/apps/cli/cmd/snapshot/list.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/snapshot" - "github.com/spf13/cobra" -) - -var ( - pageFlag int - limitFlag int -) - -var ListCmd = &cobra.Command{ - Use: "list", - Short: "List all snapshots", - Long: "List all available BoxLite snapshots", - Aliases: common.GetAliases("list"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - page := float32(1.0) - limit := float32(100.0) - - if cmd.Flags().Changed("page") { - page = float32(pageFlag) - } - - if cmd.Flags().Changed("limit") { - limit = float32(limitFlag) - } - - snapshots, res, err := apiClient.SnapshotsAPI.GetAllSnapshots(ctx).Page(page).Limit(limit).Execute() - if err != nil { - fmt.Printf("Error: %v\n", err) - return apiclient.HandleErrorResponse(res, err) - } - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(snapshots.Items) - formattedData.Print() - return nil - } - - var activeOrganizationName *string - - if !config.IsApiKeyAuth() { - name, err := common.GetActiveOrganizationName(apiClient, ctx) - if err != nil { - return err - } - activeOrganizationName = &name - } - - snapshot.ListSnapshots(snapshots.Items, activeOrganizationName) - return nil - }, -} - -func init() { - common.RegisterFormatFlag(ListCmd) - ListCmd.Flags().IntVarP(&pageFlag, "page", "p", 1, "Page number for pagination (starting from 1)") - ListCmd.Flags().IntVarP(&limitFlag, "limit", "l", 100, "Maximum number of items per page") -} diff --git a/apps/cli/cmd/snapshot/push.go b/apps/cli/cmd/snapshot/push.go deleted file mode 100644 index 98bb1fb65..000000000 --- a/apps/cli/cmd/snapshot/push.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "os" - "strings" - "time" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/docker" - views_common "github.com/boxlite-ai/boxlite/cli/views/common" - views_util "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/registry" - "github.com/docker/docker/client" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/spf13/cobra" -) - -var PushCmd = &cobra.Command{ - Use: "push [SNAPSHOT]", - Short: "Push local snapshot", - Long: "Push a local Docker image to BoxLite. To securely build it on our infrastructure, use 'boxlite snapshot build'", - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - sourceImage := args[0] - - err := common.ValidateImageName(sourceImage) - if err != nil { - return err - } - - dockerClient, err := client.NewClientWithOpts( - client.FromEnv, - client.WithAPIVersionNegotiation(), - ) - if err != nil { - return fmt.Errorf("failed to create Docker client: %w", err) - } - defer dockerClient.Close() - - // Check if the image exists locally when not building - if exists, err := docker.ImageExistsLocally(ctx, dockerClient, sourceImage); err != nil { - return err - } else if !exists { - return fmt.Errorf("image '%s' not found locally. Please ensure the image exists and try again", sourceImage) - } - - // Validate image architecture - isArchAmd, err := docker.CheckAmdArchitecture(ctx, dockerClient, sourceImage) - if err != nil { - return fmt.Errorf("failed to check image architecture: %w", err) - } - if !isArchAmd { - return fmt.Errorf("image '%s' is not compatible with AMD architecture", sourceImage) - } - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - pushAccessRequest := apiClient.DockerRegistryAPI.GetTransientPushAccess(ctx) - if regionIdFlag != "" { - pushAccessRequest = pushAccessRequest.RegionId(regionIdFlag) - } - tokenResponse, res, err := pushAccessRequest.Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - encodedAuthConfig, err := json.Marshal(registry.AuthConfig{ - Username: tokenResponse.Username, - Password: tokenResponse.Secret, - ServerAddress: tokenResponse.RegistryUrl, - }) - if err != nil { - return fmt.Errorf("failed to marshal auth config: %w", err) - } - - // Extract image name without tag and create timestamp-based tag - imageName := sourceImage - if colonIndex := strings.LastIndex(sourceImage, ":"); colonIndex != -1 { - imageName = sourceImage[:colonIndex] - } - - // Generate timestamp-based tag to avoid inconsistencies - // 20060102150405 is the format of the timestamp (year, month, day, hour, minute, second) - timestamp := time.Now().Format("20060102150405") - targetImage := fmt.Sprintf("%s/%s/%s:%s", tokenResponse.RegistryUrl, tokenResponse.Project, imageName, timestamp) - err = dockerClient.ImageTag(ctx, sourceImage, targetImage) - if err != nil { - return fmt.Errorf("failed to tag image: %w", err) - } - - // Push image to transient registry - pushReader, err := dockerClient.ImagePush(ctx, targetImage, image.PushOptions{ - RegistryAuth: base64.URLEncoding.EncodeToString(encodedAuthConfig), - }) - if err != nil { - return fmt.Errorf("failed to push image: %w", err) - } - defer pushReader.Close() - - err = jsonmessage.DisplayJSONMessagesStream(pushReader, os.Stdout, 0, true, nil) - if err != nil { - return err - } - - createSnapshot := apiclient.NewCreateSnapshot(nameFlag) - - createSnapshot.SetImageName(targetImage) - - if entrypointFlag != "" { - createSnapshot.SetEntrypoint(strings.Split(entrypointFlag, " ")) - } - - // Poll until the image is really available on the registry - // This is a workaround for harbor's delay in making newly created images available - for { - _, err := dockerClient.DistributionInspect(ctx, targetImage, - base64.URLEncoding.EncodeToString(encodedAuthConfig)) - if err == nil { - break - } - time.Sleep(time.Second) - } - - if cpuFlag != 0 { - createSnapshot.SetCpu(cpuFlag) - } - if memoryFlag != 0 { - createSnapshot.SetMemory(memoryFlag) - } - if diskFlag != 0 { - createSnapshot.SetDisk(diskFlag) - } - if regionIdFlag != "" { - createSnapshot.SetRegionId(regionIdFlag) - } - - _, res, err = apiClient.SnapshotsAPI.CreateSnapshot(ctx).CreateSnapshot(*createSnapshot).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - views_common.RenderInfoMessageBold(fmt.Sprintf("Successfully pushed %s to BoxLite", sourceImage)) - - err = views_util.WithInlineSpinner("Waiting for the snapshot to be validated", func() error { - return common.AwaitSnapshotState(ctx, apiClient, nameFlag, apiclient.SNAPSHOTSTATE_ACTIVE) - }) - if err != nil { - return err - } - - views_common.RenderInfoMessage(fmt.Sprintf("%s Use '%s' to create a new sandbox using this snapshot", views_common.Checkmark, nameFlag)) - return nil - }, -} - -var ( - nameFlag string -) - -func init() { - PushCmd.Flags().StringVarP(&entrypointFlag, "entrypoint", "e", "", "The entrypoint command for the image") - PushCmd.Flags().StringVarP(&nameFlag, "name", "n", "", "Specify the Snapshot name") - PushCmd.Flags().Int32Var(&cpuFlag, "cpu", 0, "CPU cores that will be allocated to the underlying sandboxes (default: 1)") - PushCmd.Flags().Int32Var(&memoryFlag, "memory", 0, "Memory that will be allocated to the underlying sandboxes in GB (default: 1)") - PushCmd.Flags().Int32Var(&diskFlag, "disk", 0, "Disk space that will be allocated to the underlying sandboxes in GB (default: 3)") - PushCmd.Flags().StringVar(®ionIdFlag, "region", "", "ID of the region where the snapshot will be available (defaults to organization default region)") - - _ = PushCmd.MarkFlagRequired("name") -} diff --git a/apps/cli/cmd/snapshot/snapshot.go b/apps/cli/cmd/snapshot/snapshot.go deleted file mode 100644 index 85807e884..000000000 --- a/apps/cli/cmd/snapshot/snapshot.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" -) - -var SnapshotsCmd = &cobra.Command{ - Use: "snapshot", - Short: "Manage BoxLite snapshots", - Long: "Commands for managing BoxLite snapshots", - Aliases: []string{"snapshots"}, - GroupID: internal.SANDBOX_GROUP, -} - -func init() { - SnapshotsCmd.AddCommand(ListCmd) - SnapshotsCmd.AddCommand(CreateCmd) - SnapshotsCmd.AddCommand(PushCmd) - SnapshotsCmd.AddCommand(DeleteCmd) -} diff --git a/apps/cli/cmd/version.go b/apps/cli/cmd/version.go deleted file mode 100644 index 90305b275..000000000 --- a/apps/cli/cmd/version.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package cmd - -import ( - "fmt" - - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" -) - -var VersionCmd = &cobra.Command{ - Use: "version", - Short: "Print the version number", - RunE: func(cmd *cobra.Command, args []string) error { - fmt.Println("BoxLite CLI version", internal.Version) - return nil - }, -} diff --git a/apps/cli/cmd/volume/create.go b/apps/cli/cmd/volume/create.go deleted file mode 100644 index 1666f06cb..000000000 --- a/apps/cli/cmd/volume/create.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/spf13/cobra" -) - -var CreateCmd = &cobra.Command{ - Use: "create [NAME]", - Short: "Create a volume", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("create"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient_cli.GetApiClient(nil, nil) - if err != nil { - return err - } - - volume, res, err := apiClient.VolumesAPI.CreateVolume(ctx).CreateVolume(apiclient.CreateVolume{ - Name: args[0], - }).Execute() - if err != nil { - return apiclient_cli.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Volume %s successfully created", volume.Name)) - return nil - }, -} - -var sizeFlag int32 - -func init() { - CreateCmd.Flags().Int32VarP(&sizeFlag, "size", "s", 10, "Size of the volume in GB") -} diff --git a/apps/cli/cmd/volume/delete.go b/apps/cli/cmd/volume/delete.go deleted file mode 100644 index c36c8fbae..000000000 --- a/apps/cli/cmd/volume/delete.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - view_common "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/spf13/cobra" -) - -var DeleteCmd = &cobra.Command{ - Use: "delete [VOLUME_ID]", - Short: "Delete a volume", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("delete"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - res, err := apiClient.VolumesAPI.DeleteVolume(ctx, args[0]).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - view_common.RenderInfoMessageBold(fmt.Sprintf("Volume %s deleted", args[0])) - return nil - }, -} diff --git a/apps/cli/cmd/volume/get.go b/apps/cli/cmd/volume/get.go deleted file mode 100644 index 7e1208303..000000000 --- a/apps/cli/cmd/volume/get.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "context" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/views/volume" - "github.com/spf13/cobra" -) - -var GetCmd = &cobra.Command{ - Use: "get [VOLUME_ID]", - Short: "Get volume details", - Args: cobra.ExactArgs(1), - Aliases: common.GetAliases("get"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - vol, res, err := apiClient.VolumesAPI.GetVolume(ctx, args[0]).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(vol) - formattedData.Print() - return nil - } - - volume.RenderInfo(vol, false) - return nil - }, -} - -func init() { - common.RegisterFormatFlag(GetCmd) -} diff --git a/apps/cli/cmd/volume/list.go b/apps/cli/cmd/volume/list.go deleted file mode 100644 index 3dfe5e307..000000000 --- a/apps/cli/cmd/volume/list.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "context" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/boxlite-ai/boxlite/cli/cmd/common" - "github.com/boxlite-ai/boxlite/cli/config" - "github.com/boxlite-ai/boxlite/cli/views/volume" - "github.com/spf13/cobra" -) - -var ListCmd = &cobra.Command{ - Use: "list", - Short: "List all volumes", - Args: cobra.NoArgs, - Aliases: common.GetAliases("list"), - RunE: func(cmd *cobra.Command, args []string) error { - ctx := context.Background() - - apiClient, err := apiclient.GetApiClient(nil, nil) - if err != nil { - return err - } - - volumes, res, err := apiClient.VolumesAPI.ListVolumes(ctx).Execute() - if err != nil { - return apiclient.HandleErrorResponse(res, err) - } - - if common.FormatFlag != "" { - formattedData := common.NewFormatter(volumes) - formattedData.Print() - return nil - } - - var activeOrganizationName *string - - if !config.IsApiKeyAuth() { - name, err := common.GetActiveOrganizationName(apiClient, ctx) - if err != nil { - return err - } - activeOrganizationName = &name - } - - volume.ListVolumes(volumes, activeOrganizationName) - return nil - }, -} - -func init() { - common.RegisterFormatFlag(ListCmd) -} diff --git a/apps/cli/cmd/volume/volume.go b/apps/cli/cmd/volume/volume.go deleted file mode 100644 index d680bbaf2..000000000 --- a/apps/cli/cmd/volume/volume.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/spf13/cobra" -) - -var VolumeCmd = &cobra.Command{ - Use: "volume", - Short: "Manage BoxLite volumes", - Long: "Commands for managing BoxLite volumes", - Aliases: []string{"volumes"}, - GroupID: internal.SANDBOX_GROUP, -} - -func init() { - VolumeCmd.AddCommand(ListCmd) - VolumeCmd.AddCommand(CreateCmd) - VolumeCmd.AddCommand(GetCmd) - VolumeCmd.AddCommand(DeleteCmd) -} diff --git a/apps/cli/config/config.go b/apps/cli/config/config.go deleted file mode 100644 index 7e6bc7a7b..000000000 --- a/apps/cli/config/config.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package config - -import ( - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/boxlite-ai/boxlite/cli/cmd" - "github.com/boxlite-ai/boxlite/cli/internal" -) - -const BOXLITE_API_URL_ENV_VAR = "BOXLITE_API_URL" -const BOXLITE_API_KEY_ENV_VAR = "BOXLITE_API_KEY" - -type Config struct { - ActiveProfileId string `json:"activeProfile"` - Profiles []Profile `json:"profiles"` -} - -type Profile struct { - Id string `json:"id"` - Name string `json:"name"` - Api ServerApi `json:"api"` - ActiveOrganizationId *string `json:"activeOrganizationId"` - ToolboxProxyUrls map[string]string `json:"toolboxProxyUrls,omitempty"` // Cache proxy URLs by region -} - -type ServerApi struct { - Url string `json:"url"` - Key *string `json:"key"` - Token *Token `json:"token"` -} - -type Token struct { - AccessToken string `json:"accessToken"` - RefreshToken string `json:"refreshToken"` - ExpiresAt time.Time `json:"expiresAt"` -} - -func GetConfig() (*Config, error) { - configFilePath, err := getConfigPath() - if err != nil { - return nil, err - } - - _, err = os.Stat(configFilePath) - if os.IsNotExist(err) { - // Setup autocompletion when adding initial config - _ = cmd.DetectShellAndSetupAutocompletion(cmd.AutoCompleteCmd.Root()) - - config := &Config{} - return config, config.Save() - } - - if err != nil { - return nil, err - } - - var c Config - configContent, err := os.ReadFile(configFilePath) - if err != nil { - return nil, err - } - - err = json.Unmarshal(configContent, &c) - if err != nil { - return nil, err - } - - return &c, nil -} - -var ErrNoProfilesFound = errors.New("no profiles found. Run `boxlite login` to authenticate") - -func (c *Config) GetActiveProfile() (Profile, error) { - apiUrl := os.Getenv(BOXLITE_API_URL_ENV_VAR) - apiKey := os.Getenv(BOXLITE_API_KEY_ENV_VAR) - - if apiUrl != "" && apiKey != "" { - return Profile{ - Id: "env", - Api: ServerApi{ - Url: apiUrl, - Key: &apiKey, - }, - }, nil - } - - if len(c.Profiles) == 0 { - return Profile{}, ErrNoProfilesFound - } - - for _, profile := range c.Profiles { - if profile.Id == c.ActiveProfileId { - return profile, nil - } - } - - return Profile{}, ErrNoActiveProfile -} - -var ErrNoActiveProfile = errors.New("no active profile found. Run `boxlite login` to authenticate") -var ErrNoActiveOrganization = errors.New("no active organization found. Run `boxlite organization use` to select an organization") - -func (c *Config) Save() error { - configFilePath, err := getConfigPath() - if err != nil { - return err - } - - err = os.MkdirAll(filepath.Dir(configFilePath), 0755) - if err != nil { - return err - } - - configContent, err := json.MarshalIndent(c, "", " ") - if err != nil { - return err - } - - return os.WriteFile(configFilePath, configContent, 0644) -} - -func (c *Config) AddProfile(profile Profile) error { - c.Profiles = append(c.Profiles, profile) - c.ActiveProfileId = profile.Id - - return c.Save() -} - -func (c *Config) EditProfile(profile Profile) error { - for i, p := range c.Profiles { - if p.Id == profile.Id { - c.Profiles[i] = profile - - return c.Save() - } - } - - return fmt.Errorf("profile with id %s not found", profile.Id) -} - -func (c *Config) RemoveProfile(profileId string) error { - if c.ActiveProfileId == profileId { - return errors.New("cannot remove active profile") - } - - var profiles []Profile - for _, profile := range c.Profiles { - if profile.Id != profileId { - profiles = append(profiles, profile) - } - } - - c.Profiles = profiles - - return c.Save() -} - -func (c *Config) GetProfile(profileId string) (Profile, error) { - for _, profile := range c.Profiles { - if profile.Id == profileId { - return profile, nil - } - } - - return Profile{}, errors.New("profile not found") -} - -func getConfigPath() (string, error) { - configDir, err := GetConfigDir() - if err != nil { - return "", err - } - - return filepath.Join(configDir, "config.json"), nil -} - -func GetConfigDir() (string, error) { - boxliteConfigDir := os.Getenv("BOXLITE_CONFIG_DIR") - if boxliteConfigDir != "" { - return boxliteConfigDir, nil - } - - userConfigDir, err := os.UserConfigDir() - if err != nil { - return "", err - } - - return filepath.Join(userConfigDir, "boxlite"), nil -} - -func DeleteConfigDir() error { - configDir, err := GetConfigDir() - if err != nil { - return err - } - - return os.RemoveAll(configDir) -} - -func GetActiveOrganizationId() (string, error) { - c, err := GetConfig() - if err != nil { - return "", err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return "", err - } - - if activeProfile.ActiveOrganizationId == nil { - return "", ErrNoActiveOrganization - } - - return *activeProfile.ActiveOrganizationId, nil -} - -func IsApiKeyAuth() bool { - c, err := GetConfig() - if err != nil { - return false - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return false - } - - return activeProfile.Api.Key != nil && activeProfile.Api.Token == nil -} - -func GetAuth0Domain() string { - auth0Domain := os.Getenv("BOXLITE_AUTH0_DOMAIN") - if auth0Domain == "" { - auth0Domain = internal.Auth0Domain - } - - return auth0Domain -} - -func GetAuth0ClientId() string { - auth0ClientId := os.Getenv("BOXLITE_AUTH0_CLIENT_ID") - if auth0ClientId == "" { - auth0ClientId = internal.Auth0ClientId - } - - return auth0ClientId -} - -func GetAuth0ClientSecret() string { - auth0ClientSecret := os.Getenv("BOXLITE_AUTH0_CLIENT_SECRET") - if auth0ClientSecret == "" { - auth0ClientSecret = internal.Auth0ClientSecret - } - - return auth0ClientSecret -} - -func GetAuth0CallbackPort() string { - auth0CallbackPort := os.Getenv("BOXLITE_AUTH0_CALLBACK_PORT") - if auth0CallbackPort == "" { - auth0CallbackPort = internal.Auth0CallbackPort - } - - return auth0CallbackPort -} - -func GetAuth0Audience() string { - auth0Audience := os.Getenv("BOXLITE_AUTH0_AUDIENCE") - if auth0Audience == "" { - auth0Audience = internal.Auth0Audience - } - - return auth0Audience -} - -func GetBoxliteApiUrl() string { - boxliteApiUrl := os.Getenv("BOXLITE_API_URL") - if boxliteApiUrl == "" { - boxliteApiUrl = internal.BoxliteApiUrl - } - - return boxliteApiUrl -} - -func GetToolboxProxyUrl(region string) (string, error) { - c, err := GetConfig() - if err != nil { - return "", err - } - - activeProfile, err := c.GetActiveProfile() - if err != nil { - return "", err - } - - if activeProfile.ToolboxProxyUrls == nil { - return "", nil - } - - return activeProfile.ToolboxProxyUrls[region], nil -} - -func SetToolboxProxyUrl(region, url string) error { - c, err := GetConfig() - if err != nil { - return err - } - - // Find and update the active profile - for i, profile := range c.Profiles { - if profile.Id == c.ActiveProfileId { - if c.Profiles[i].ToolboxProxyUrls == nil { - c.Profiles[i].ToolboxProxyUrls = make(map[string]string) - } - c.Profiles[i].ToolboxProxyUrls[region] = url - return c.Save() - } - } - - return ErrNoActiveProfile -} diff --git a/apps/cli/docker/build.go b/apps/cli/docker/build.go deleted file mode 100644 index 9e83b5356..000000000 --- a/apps/cli/docker/build.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package docker - -import ( - "context" - "fmt" - "slices" - - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/client" -) - -func ImageExistsLocally(ctx context.Context, dockerClient *client.Client, imageName string) (bool, error) { - images, err := dockerClient.ImageList(ctx, image.ListOptions{}) - if err != nil { - return false, fmt.Errorf("failed to list images: %w", err) - } - - for _, image := range images { - for _, tag := range image.RepoTags { - if tag == imageName { - return true, nil - } - } - } - return false, nil -} - -func CheckAmdArchitecture(ctx context.Context, dockerClient *client.Client, imageName string) (bool, error) { - inspect, err := dockerClient.ImageInspect(ctx, imageName) - if err != nil { - return false, fmt.Errorf("failed to inspect image: %w", err) - } - - x64Architectures := []string{"amd64", "x86_64"} - - if slices.Contains(x64Architectures, inspect.Architecture) { - return true, nil - } - - return false, nil -} diff --git a/apps/cli/docs/boxlite.md b/apps/cli/docs/boxlite.md deleted file mode 100644 index 0f3ba7dd4..000000000 --- a/apps/cli/docs/boxlite.md +++ /dev/null @@ -1,40 +0,0 @@ -## boxlite - -BoxLite CLI - -### Synopsis - -Command line interface for BoxLite Sandboxes - -``` -boxlite [flags] -``` - -### Options - -``` - --help help for boxlite - -v, --version Display the version of BoxLite -``` - -### SEE ALSO - -* [boxlite archive](boxlite_archive.md) - Archive a sandbox -* [boxlite autocomplete](boxlite_autocomplete.md) - Adds a completion script for your shell environment -* [boxlite create](boxlite_create.md) - Create a new sandbox -* [boxlite delete](boxlite_delete.md) - Delete a sandbox -* [boxlite docs](boxlite_docs.md) - Opens the BoxLite documentation in your default browser. -* [boxlite exec](boxlite_exec.md) - Execute a command in a sandbox -* [boxlite info](boxlite_info.md) - Get sandbox info -* [boxlite list](boxlite_list.md) - List sandboxes -* [boxlite login](boxlite_login.md) - Log in to BoxLite -* [boxlite logout](boxlite_logout.md) - Logout from BoxLite -* [boxlite mcp](boxlite_mcp.md) - Manage BoxLite MCP Server -* [boxlite organization](boxlite_organization.md) - Manage BoxLite organizations -* [boxlite preview-url](boxlite_preview-url.md) - Get signed preview URL for a sandbox port -* [boxlite snapshot](boxlite_snapshot.md) - Manage BoxLite snapshots -* [boxlite ssh](boxlite_ssh.md) - SSH into a sandbox -* [boxlite start](boxlite_start.md) - Start a sandbox -* [boxlite stop](boxlite_stop.md) - Stop a sandbox -* [boxlite version](boxlite_version.md) - Print the version number -* [boxlite volume](boxlite_volume.md) - Manage BoxLite volumes diff --git a/apps/cli/docs/boxlite_archive.md b/apps/cli/docs/boxlite_archive.md deleted file mode 100644 index 279e1e7cf..000000000 --- a/apps/cli/docs/boxlite_archive.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite archive - -Archive a sandbox - -``` -boxlite archive [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_autocomplete.md b/apps/cli/docs/boxlite_autocomplete.md deleted file mode 100644 index fc08ca504..000000000 --- a/apps/cli/docs/boxlite_autocomplete.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite autocomplete - -Adds a completion script for your shell environment - -``` -boxlite autocomplete [bash|zsh|fish|powershell] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_create.md b/apps/cli/docs/boxlite_create.md deleted file mode 100644 index fd7406802..000000000 --- a/apps/cli/docs/boxlite_create.md +++ /dev/null @@ -1,42 +0,0 @@ -## boxlite create - -Create a new sandbox - -``` -boxlite create [flags] -``` - -### Options - -``` - --auto-archive int32 Auto-archive interval in minutes (0 means the maximum interval will be used) (default 10080) - --auto-delete int32 Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) (default -1) - --auto-stop int32 Auto-stop interval in minutes (0 means disabled) (default 15) - --class string Sandbox class type (small, medium, large) - -c, --context stringArray Files or directories to include in the build context (can be specified multiple times) - --cpu int32 CPU cores allocated to the sandbox - --disk int32 Disk space allocated to the sandbox in GB - -f, --dockerfile string Path to Dockerfile for Sandbox snapshot - -e, --env stringArray Environment variables (format: KEY=VALUE) - --gpu int32 GPU units allocated to the sandbox - -l, --label stringArray Labels (format: KEY=VALUE) - --memory int32 Memory allocated to the sandbox in MB - --name string Name of the sandbox - --network-allow-list string Comma-separated list of allowed CIDR network addresses for the sandbox - --network-block-all Whether to block all network access for the sandbox - --public Make sandbox publicly accessible - --snapshot string Snapshot to use for the sandbox - --target string Target region (eu, us) - --user string User associated with the sandbox - -v, --volume stringArray Volumes to mount (format: VOLUME_NAME:MOUNT_PATH) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_delete.md b/apps/cli/docs/boxlite_delete.md deleted file mode 100644 index ac7de1d0b..000000000 --- a/apps/cli/docs/boxlite_delete.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite delete - -Delete a sandbox - -``` -boxlite delete [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options - -``` - -a, --all Delete all sandboxes -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_docs.md b/apps/cli/docs/boxlite_docs.md deleted file mode 100644 index 90ca4845c..000000000 --- a/apps/cli/docs/boxlite_docs.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite docs - -Opens the BoxLite documentation in your default browser. - -``` -boxlite docs [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_exec.md b/apps/cli/docs/boxlite_exec.md deleted file mode 100644 index 545193adc..000000000 --- a/apps/cli/docs/boxlite_exec.md +++ /dev/null @@ -1,28 +0,0 @@ -## boxlite exec - -Execute a command in a sandbox - -### Synopsis - -Execute a command in a running sandbox - -``` -boxlite exec [SANDBOX_ID | SANDBOX_NAME] -- [COMMAND] [ARGS...] [flags] -``` - -### Options - -``` - --cwd string Working directory for command execution - --timeout int Command timeout in seconds (0 for no timeout) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_info.md b/apps/cli/docs/boxlite_info.md deleted file mode 100644 index 821205374..000000000 --- a/apps/cli/docs/boxlite_info.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite info - -Get sandbox info - -``` -boxlite info [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_list.md b/apps/cli/docs/boxlite_list.md deleted file mode 100644 index 1ca512a0c..000000000 --- a/apps/cli/docs/boxlite_list.md +++ /dev/null @@ -1,25 +0,0 @@ -## boxlite list - -List sandboxes - -``` -boxlite list [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) - -l, --limit int Maximum number of items per page (default 100) - -p, --page int Page number for pagination (starting from 1) (default 1) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_login.md b/apps/cli/docs/boxlite_login.md deleted file mode 100644 index ad4f96efe..000000000 --- a/apps/cli/docs/boxlite_login.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite login - -Log in to BoxLite - -``` -boxlite login [flags] -``` - -### Options - -``` - --api-key string API key to use for authentication -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_logout.md b/apps/cli/docs/boxlite_logout.md deleted file mode 100644 index 8ce5dd405..000000000 --- a/apps/cli/docs/boxlite_logout.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite logout - -Logout from BoxLite - -``` -boxlite logout [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_mcp.md b/apps/cli/docs/boxlite_mcp.md deleted file mode 100644 index f6b218fd9..000000000 --- a/apps/cli/docs/boxlite_mcp.md +++ /dev/null @@ -1,20 +0,0 @@ -## boxlite mcp - -Manage BoxLite MCP Server - -### Synopsis - -Commands for managing BoxLite MCP Server - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI -* [boxlite mcp config](boxlite_mcp_config.md) - Outputs JSON configuration for BoxLite MCP Server -* [boxlite mcp init](boxlite_mcp_init.md) - Initialize BoxLite MCP Server with an agent (currently supported: claude, windsurf, cursor) -* [boxlite mcp start](boxlite_mcp_start.md) - Start BoxLite MCP Server diff --git a/apps/cli/docs/boxlite_mcp_config.md b/apps/cli/docs/boxlite_mcp_config.md deleted file mode 100644 index c5a1eaf15..000000000 --- a/apps/cli/docs/boxlite_mcp_config.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite mcp config - -Outputs JSON configuration for BoxLite MCP Server - -``` -boxlite mcp config [AGENT_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite mcp](boxlite_mcp.md) - Manage BoxLite MCP Server diff --git a/apps/cli/docs/boxlite_mcp_init.md b/apps/cli/docs/boxlite_mcp_init.md deleted file mode 100644 index 7b5989a1f..000000000 --- a/apps/cli/docs/boxlite_mcp_init.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite mcp init - -Initialize BoxLite MCP Server with an agent (currently supported: claude, windsurf, cursor) - -``` -boxlite mcp init [AGENT_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite mcp](boxlite_mcp.md) - Manage BoxLite MCP Server diff --git a/apps/cli/docs/boxlite_mcp_start.md b/apps/cli/docs/boxlite_mcp_start.md deleted file mode 100644 index cb941de5d..000000000 --- a/apps/cli/docs/boxlite_mcp_start.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite mcp start - -Start BoxLite MCP Server - -``` -boxlite mcp start [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite mcp](boxlite_mcp.md) - Manage BoxLite MCP Server diff --git a/apps/cli/docs/boxlite_organization.md b/apps/cli/docs/boxlite_organization.md deleted file mode 100644 index b5d82700f..000000000 --- a/apps/cli/docs/boxlite_organization.md +++ /dev/null @@ -1,21 +0,0 @@ -## boxlite organization - -Manage BoxLite organizations - -### Synopsis - -Commands for managing BoxLite organizations - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI -* [boxlite organization create](boxlite_organization_create.md) - Create a new organization and set it as active -* [boxlite organization delete](boxlite_organization_delete.md) - Delete an organization -* [boxlite organization list](boxlite_organization_list.md) - List all organizations -* [boxlite organization use](boxlite_organization_use.md) - Set active organization diff --git a/apps/cli/docs/boxlite_organization_create.md b/apps/cli/docs/boxlite_organization_create.md deleted file mode 100644 index 95cf46d51..000000000 --- a/apps/cli/docs/boxlite_organization_create.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite organization create - -Create a new organization and set it as active - -``` -boxlite organization create [ORGANIZATION_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite organization](boxlite_organization.md) - Manage BoxLite organizations diff --git a/apps/cli/docs/boxlite_organization_delete.md b/apps/cli/docs/boxlite_organization_delete.md deleted file mode 100644 index f97b385c0..000000000 --- a/apps/cli/docs/boxlite_organization_delete.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite organization delete - -Delete an organization - -``` -boxlite organization delete [ORGANIZATION] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite organization](boxlite_organization.md) - Manage BoxLite organizations diff --git a/apps/cli/docs/boxlite_organization_list.md b/apps/cli/docs/boxlite_organization_list.md deleted file mode 100644 index 74563b7a8..000000000 --- a/apps/cli/docs/boxlite_organization_list.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite organization list - -List all organizations - -``` -boxlite organization list [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite organization](boxlite_organization.md) - Manage BoxLite organizations diff --git a/apps/cli/docs/boxlite_organization_use.md b/apps/cli/docs/boxlite_organization_use.md deleted file mode 100644 index f8140c5da..000000000 --- a/apps/cli/docs/boxlite_organization_use.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite organization use - -Set active organization - -``` -boxlite organization use [ORGANIZATION] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite organization](boxlite_organization.md) - Manage BoxLite organizations diff --git a/apps/cli/docs/boxlite_preview-url.md b/apps/cli/docs/boxlite_preview-url.md deleted file mode 100644 index 6f4990d10..000000000 --- a/apps/cli/docs/boxlite_preview-url.md +++ /dev/null @@ -1,24 +0,0 @@ -## boxlite preview-url - -Get signed preview URL for a sandbox port - -``` -boxlite preview-url [SANDBOX_ID | SANDBOX_NAME] [flags] -``` - -### Options - -``` - --expires int32 URL expiration time in seconds (default 3600) - -p, --port int32 Port number to get preview URL for (required) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_snapshot.md b/apps/cli/docs/boxlite_snapshot.md deleted file mode 100644 index aaf59ddb5..000000000 --- a/apps/cli/docs/boxlite_snapshot.md +++ /dev/null @@ -1,21 +0,0 @@ -## boxlite snapshot - -Manage BoxLite snapshots - -### Synopsis - -Commands for managing BoxLite snapshots - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI -* [boxlite snapshot create](boxlite_snapshot_create.md) - Create a snapshot -* [boxlite snapshot delete](boxlite_snapshot_delete.md) - Delete a snapshot -* [boxlite snapshot list](boxlite_snapshot_list.md) - List all snapshots -* [boxlite snapshot push](boxlite_snapshot_push.md) - Push local snapshot diff --git a/apps/cli/docs/boxlite_snapshot_create.md b/apps/cli/docs/boxlite_snapshot_create.md deleted file mode 100644 index e09778b51..000000000 --- a/apps/cli/docs/boxlite_snapshot_create.md +++ /dev/null @@ -1,30 +0,0 @@ -## boxlite snapshot create - -Create a snapshot - -``` -boxlite snapshot create [SNAPSHOT] [flags] -``` - -### Options - -``` - -c, --context stringArray Files or directories to include in the build context (can be specified multiple times). If not provided, context will be automatically determined from COPY/ADD commands in the Dockerfile - --cpu int32 CPU cores that will be allocated to the underlying sandboxes (default: 1) - --disk int32 Disk space that will be allocated to the underlying sandboxes in GB (default: 3) - -f, --dockerfile string Path to Dockerfile to build - -e, --entrypoint string The entrypoint command for the snapshot - -i, --image string The image name for the snapshot - --memory int32 Memory that will be allocated to the underlying sandboxes in GB (default: 1) - --region string ID of the region where the snapshot will be available (defaults to organization default region) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite snapshot](boxlite_snapshot.md) - Manage BoxLite snapshots diff --git a/apps/cli/docs/boxlite_snapshot_delete.md b/apps/cli/docs/boxlite_snapshot_delete.md deleted file mode 100644 index 8578906a5..000000000 --- a/apps/cli/docs/boxlite_snapshot_delete.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite snapshot delete - -Delete a snapshot - -``` -boxlite snapshot delete [SNAPSHOT_ID] [flags] -``` - -### Options - -``` - -a, --all Delete all snapshots -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite snapshot](boxlite_snapshot.md) - Manage BoxLite snapshots diff --git a/apps/cli/docs/boxlite_snapshot_list.md b/apps/cli/docs/boxlite_snapshot_list.md deleted file mode 100644 index 580709282..000000000 --- a/apps/cli/docs/boxlite_snapshot_list.md +++ /dev/null @@ -1,29 +0,0 @@ -## boxlite snapshot list - -List all snapshots - -### Synopsis - -List all available BoxLite snapshots - -``` -boxlite snapshot list [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) - -l, --limit int Maximum number of items per page (default 100) - -p, --page int Page number for pagination (starting from 1) (default 1) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite snapshot](boxlite_snapshot.md) - Manage BoxLite snapshots diff --git a/apps/cli/docs/boxlite_snapshot_push.md b/apps/cli/docs/boxlite_snapshot_push.md deleted file mode 100644 index f966aab0e..000000000 --- a/apps/cli/docs/boxlite_snapshot_push.md +++ /dev/null @@ -1,32 +0,0 @@ -## boxlite snapshot push - -Push local snapshot - -### Synopsis - -Push a local Docker image to BoxLite. To securely build it on our infrastructure, use 'boxlite snapshot build' - -``` -boxlite snapshot push [SNAPSHOT] [flags] -``` - -### Options - -``` - --cpu int32 CPU cores that will be allocated to the underlying sandboxes (default: 1) - --disk int32 Disk space that will be allocated to the underlying sandboxes in GB (default: 3) - -e, --entrypoint string The entrypoint command for the image - --memory int32 Memory that will be allocated to the underlying sandboxes in GB (default: 1) - -n, --name string Specify the Snapshot name - --region string ID of the region where the snapshot will be available (defaults to organization default region) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite snapshot](boxlite_snapshot.md) - Manage BoxLite snapshots diff --git a/apps/cli/docs/boxlite_ssh.md b/apps/cli/docs/boxlite_ssh.md deleted file mode 100644 index b5b90db64..000000000 --- a/apps/cli/docs/boxlite_ssh.md +++ /dev/null @@ -1,27 +0,0 @@ -## boxlite ssh - -SSH into a sandbox - -### Synopsis - -Establish an SSH connection to a running sandbox - -``` -boxlite ssh [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options - -``` - --expires int SSH access token expiration time in minutes (defaults to 24 hours) (default 1440) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_start.md b/apps/cli/docs/boxlite_start.md deleted file mode 100644 index 4c956818e..000000000 --- a/apps/cli/docs/boxlite_start.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite start - -Start a sandbox - -``` -boxlite start [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_stop.md b/apps/cli/docs/boxlite_stop.md deleted file mode 100644 index 77c5b7356..000000000 --- a/apps/cli/docs/boxlite_stop.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite stop - -Stop a sandbox - -``` -boxlite stop [SANDBOX_ID] | [SANDBOX_NAME] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_version.md b/apps/cli/docs/boxlite_version.md deleted file mode 100644 index ae3416d87..000000000 --- a/apps/cli/docs/boxlite_version.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite version - -Print the version number - -``` -boxlite version [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI diff --git a/apps/cli/docs/boxlite_volume.md b/apps/cli/docs/boxlite_volume.md deleted file mode 100644 index 2ab9daa88..000000000 --- a/apps/cli/docs/boxlite_volume.md +++ /dev/null @@ -1,21 +0,0 @@ -## boxlite volume - -Manage BoxLite volumes - -### Synopsis - -Commands for managing BoxLite volumes - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite](boxlite.md) - BoxLite CLI -* [boxlite volume create](boxlite_volume_create.md) - Create a volume -* [boxlite volume delete](boxlite_volume_delete.md) - Delete a volume -* [boxlite volume get](boxlite_volume_get.md) - Get volume details -* [boxlite volume list](boxlite_volume_list.md) - List all volumes diff --git a/apps/cli/docs/boxlite_volume_create.md b/apps/cli/docs/boxlite_volume_create.md deleted file mode 100644 index 7d9381fb9..000000000 --- a/apps/cli/docs/boxlite_volume_create.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite volume create - -Create a volume - -``` -boxlite volume create [NAME] [flags] -``` - -### Options - -``` - -s, --size int32 Size of the volume in GB (default 10) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite volume](boxlite_volume.md) - Manage BoxLite volumes diff --git a/apps/cli/docs/boxlite_volume_delete.md b/apps/cli/docs/boxlite_volume_delete.md deleted file mode 100644 index d558ab242..000000000 --- a/apps/cli/docs/boxlite_volume_delete.md +++ /dev/null @@ -1,17 +0,0 @@ -## boxlite volume delete - -Delete a volume - -``` -boxlite volume delete [VOLUME_ID] [flags] -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite volume](boxlite_volume.md) - Manage BoxLite volumes diff --git a/apps/cli/docs/boxlite_volume_get.md b/apps/cli/docs/boxlite_volume_get.md deleted file mode 100644 index 8cbe0f5f5..000000000 --- a/apps/cli/docs/boxlite_volume_get.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite volume get - -Get volume details - -``` -boxlite volume get [VOLUME_ID] [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite volume](boxlite_volume.md) - Manage BoxLite volumes diff --git a/apps/cli/docs/boxlite_volume_list.md b/apps/cli/docs/boxlite_volume_list.md deleted file mode 100644 index 0cbb17b93..000000000 --- a/apps/cli/docs/boxlite_volume_list.md +++ /dev/null @@ -1,23 +0,0 @@ -## boxlite volume list - -List all volumes - -``` -boxlite volume list [flags] -``` - -### Options - -``` - -f, --format string Output format. Must be one of (yaml, json) -``` - -### Options inherited from parent commands - -``` - --help help for boxlite -``` - -### SEE ALSO - -* [boxlite volume](boxlite_volume.md) - Manage BoxLite volumes diff --git a/apps/cli/go.mod b/apps/cli/go.mod deleted file mode 100644 index 0bf84e41c..000000000 --- a/apps/cli/go.mod +++ /dev/null @@ -1,101 +0,0 @@ -module github.com/boxlite-ai/boxlite/cli - -go 1.25.4 - -require ( - github.com/boxlite-ai/boxlite/libs/api-client-go v0.159.0 - github.com/charmbracelet/bubbletea v1.1.0 - github.com/docker/docker v28.5.2+incompatible - github.com/mark3labs/mcp-go v0.32.0 - github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.10.1 - golang.org/x/oauth2 v0.35.0 -) - -require ( - github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/catppuccin/go v0.2.0 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/x/ansi v0.4.2 // indirect - github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect - github.com/charmbracelet/x/term v0.2.0 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect - github.com/containerd/log v0.1.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect - github.com/creack/pty v1.1.23 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-connections v0.5.0 // indirect - github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-ini/ini v1.67.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.4 // indirect - github.com/go-logr/logr v1.4.3 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/goccy/go-json v0.10.5 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/klauspost/compress v1.18.1 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/minio/crc64nvme v1.0.1 // indirect - github.com/minio/md5-simd v1.1.2 // indirect - github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/moby/term v0.5.2 // indirect - github.com/morikuni/aec v1.0.0 // indirect - github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect - github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/rivo/uniseg v0.4.7 // indirect - github.com/rs/xid v1.6.0 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sahilm/fuzzy v0.1.1 // indirect - github.com/spf13/cast v1.7.1 // indirect - github.com/yosida95/uritemplate/v3 v3.0.2 // indirect - go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/net v0.52.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.10.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - gotest.tools/v3 v3.5.1 // indirect -) - -require ( - github.com/charmbracelet/bubbles v0.20.0 - github.com/charmbracelet/huh v0.6.0 - github.com/charmbracelet/lipgloss v1.0.0 - github.com/coreos/go-oidc/v3 v3.12.0 - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/joho/godotenv v1.5.1 - github.com/minio/minio-go/v7 v7.0.91 - github.com/spf13/pflag v1.0.9 // indirect - golang.org/x/term v0.41.0 - gopkg.in/yaml.v2 v2.4.0 -) - -replace github.com/boxlite-ai/boxlite/libs/api-client-go => ../api-client-go diff --git a/apps/cli/go.sum b/apps/cli/go.sum deleted file mode 100644 index ddb7019fd..000000000 --- a/apps/cli/go.sum +++ /dev/null @@ -1,228 +0,0 @@ -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= -github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= -github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= -github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= -github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= -github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= -github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c= -github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= -github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= -github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= -github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= -github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk= -github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= -github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b h1:MnAMdlwSltxJyULnrYbkZpp4k58Co7Tah3ciKhSNo0Q= -github.com/charmbracelet/x/exp/golden v0.0.0-20240815200342-61de596daa2b/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= -github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= -github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= -github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= -github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= -github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= -github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= -github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= -github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= -github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= -github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -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/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= -github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= -github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY= -github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= -github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= -github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc= -github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= -github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= -github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= -github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= -github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= -github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= -github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= -github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= -github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= -github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= -go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= -golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= -google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= -google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= diff --git a/apps/cli/hack/build.sh b/apps/cli/hack/build.sh deleted file mode 100755 index 3a93bb2db..000000000 --- a/apps/cli/hack/build.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash -# Copyright 2025 Daytona Platforms Inc. -# SPDX-License-Identifier: AGPL-3.0 - - -# Exit on error -set -e - -# Get absolute path of script directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" -DIST_DIR="$(cd "${SCRIPT_DIR}/../../.." && pwd)" - -# Environment file precedence: -# 1. BOXLITE_ENV_FILE environment variable if set -# 2. .env file in CLI directory -# 3. .env file in project root -# 4. Default values - -load_env_file() { - local env_file="$1" - if [ -f "$env_file" ]; then - source "$env_file" - return 0 - fi - return 1 -} - -# If --skip-env-file is passed, skip loading env files -for arg in "$@"; do - if [ "$arg" == "--skip-env-file" ]; then - echo "Skipping loading of environment files" - SKIP_ENV_FILE=true - break - fi -done - -if [ "$SKIP_ENV_FILE" != "true" ]; then - echo "Loading environment files" - # Try loading environment files in order of precedence - if [ -n "$BOXLITE_ENV_FILE" ]; then - if ! load_env_file "$BOXLITE_ENV_FILE"; then - echo "Warning: Environment file specified by BOXLITE_ENV_FILE ($BOXLITE_ENV_FILE) not found" - fi - elif load_env_file "${SCRIPT_DIR}/../.env.local"; then - : # Successfully loaded CLI .env - elif load_env_file "${SCRIPT_DIR}/../.env"; then - : # Successfully loaded CLI .env - elif load_env_file "${PROJECT_ROOT}/.env.local"; then - : # Successfully loaded root .env - elif load_env_file "${PROJECT_ROOT}/.env"; then - : # Successfully loaded root .env - else - echo "Note: No .env file found, using default values" - fi -fi - -# Set default values -BOXLITE_VERSION=${VERSION:-v0.0.0-dev} -GOOS=${GOOS:-linux} -GOARCH=${GOARCH:-amd64} -CGO_ENABLED=${CGO_ENABLED:-0} - -# Validate required variables -REQUIRED_VARS=( - "BOXLITE_API_URL" - "BOXLITE_AUTH0_DOMAIN" - "BOXLITE_AUTH0_CLIENT_ID" - "BOXLITE_AUTH0_CALLBACK_PORT" - "BOXLITE_AUTH0_AUDIENCE" -) - -MISSING_VARS=() -for var in "${REQUIRED_VARS[@]}"; do - if [ -z "${!var}" ]; then - MISSING_VARS+=("$var") - fi -done - -if [ ${#MISSING_VARS[@]} -ne 0 ]; then - echo "Error: Missing required environment variables:" - printf '%s\n' "${MISSING_VARS[@]}" - exit 1 -fi - -# Create build directory if it doesn't exist -mkdir -p "${DIST_DIR}/dist/apps/cli" - -# Set output filename with .exe extension for Windows -OUTPUT_FILE="boxlite-${GOOS}-${GOARCH}" -if [ "$GOOS" == "windows" ]; then - OUTPUT_FILE="${OUTPUT_FILE}.exe" -fi - -# Build the binary -echo "Building BoxLite CLI with version: $BOXLITE_VERSION" -go build \ - -ldflags "-X 'github.com/boxlite-ai/boxlite/cli/internal.Version=${BOXLITE_VERSION}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.BoxLiteApiUrl=${BOXLITE_API_URL}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.Auth0Domain=${BOXLITE_AUTH0_DOMAIN}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.Auth0ClientId=${BOXLITE_AUTH0_CLIENT_ID}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.Auth0ClientSecret=${BOXLITE_AUTH0_CLIENT_SECRET}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.Auth0CallbackPort=${BOXLITE_AUTH0_CALLBACK_PORT}' \ - -X 'github.com/boxlite-ai/boxlite/cli/internal.Auth0Audience=${BOXLITE_AUTH0_AUDIENCE}'" \ - -o "${DIST_DIR}/dist/apps/cli/${OUTPUT_FILE}" main.go - -echo "Build complete: ${DIST_DIR}/dist/apps/cli/${OUTPUT_FILE}" diff --git a/apps/cli/hack/docs/boxlite.yaml b/apps/cli/hack/docs/boxlite.yaml deleted file mode 100644 index a06e7aeda..000000000 --- a/apps/cli/hack/docs/boxlite.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: boxlite -synopsis: BoxLite CLI -description: Command line interface for BoxLite Sandboxes -usage: boxlite [flags] -options: - - name: help - default_value: 'false' - usage: help for boxlite - - name: version - shorthand: v - default_value: 'false' - usage: Display the version of BoxLite -see_also: - - boxlite archive - Archive a sandbox - - boxlite autocomplete - Adds a completion script for your shell environment - - boxlite create - Create a new sandbox - - boxlite delete - Delete a sandbox - - boxlite docs - Opens the BoxLite documentation in your default browser. - - boxlite exec - Execute a command in a sandbox - - boxlite info - Get sandbox info - - boxlite list - List sandboxes - - boxlite login - Log in to BoxLite - - boxlite logout - Logout from BoxLite - - boxlite mcp - Manage BoxLite MCP Server - - boxlite organization - Manage BoxLite organizations - - boxlite preview-url - Get signed preview URL for a sandbox port - - boxlite snapshot - Manage BoxLite snapshots - - boxlite ssh - SSH into a sandbox - - boxlite start - Start a sandbox - - boxlite stop - Stop a sandbox - - boxlite version - Print the version number - - boxlite volume - Manage BoxLite volumes diff --git a/apps/cli/hack/docs/boxlite_archive.yaml b/apps/cli/hack/docs/boxlite_archive.yaml deleted file mode 100644 index 4fe55625e..000000000 --- a/apps/cli/hack/docs/boxlite_archive.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite archive -synopsis: Archive a sandbox -usage: boxlite archive [SANDBOX_ID] | [SANDBOX_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_autocomplete.yaml b/apps/cli/hack/docs/boxlite_autocomplete.yaml deleted file mode 100644 index fb0e43a83..000000000 --- a/apps/cli/hack/docs/boxlite_autocomplete.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite autocomplete -synopsis: Adds a completion script for your shell environment -usage: boxlite autocomplete [bash|zsh|fish|powershell] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_create.yaml b/apps/cli/hack/docs/boxlite_create.yaml deleted file mode 100644 index c8fa463de..000000000 --- a/apps/cli/hack/docs/boxlite_create.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: boxlite create -synopsis: Create a new sandbox -usage: boxlite create [flags] -options: - - name: auto-archive - default_value: '10080' - usage: | - Auto-archive interval in minutes (0 means the maximum interval will be used) - - name: auto-delete - default_value: '-1' - usage: | - Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - - name: auto-stop - default_value: '15' - usage: Auto-stop interval in minutes (0 means disabled) - - name: class - usage: Sandbox class type (small, medium, large) - - name: context - shorthand: c - default_value: '[]' - usage: | - Files or directories to include in the build context (can be specified multiple times) - - name: cpu - default_value: '0' - usage: CPU cores allocated to the sandbox - - name: disk - default_value: '0' - usage: Disk space allocated to the sandbox in GB - - name: dockerfile - shorthand: f - usage: Path to Dockerfile for Sandbox snapshot - - name: env - shorthand: e - default_value: '[]' - usage: 'Environment variables (format: KEY=VALUE)' - - name: gpu - default_value: '0' - usage: GPU units allocated to the sandbox - - name: label - shorthand: l - default_value: '[]' - usage: 'Labels (format: KEY=VALUE)' - - name: memory - default_value: '0' - usage: Memory allocated to the sandbox in MB - - name: name - usage: Name of the sandbox - - name: network-allow-list - usage: | - Comma-separated list of allowed CIDR network addresses for the sandbox - - name: network-block-all - default_value: 'false' - usage: Whether to block all network access for the sandbox - - name: public - default_value: 'false' - usage: Make sandbox publicly accessible - - name: snapshot - usage: Snapshot to use for the sandbox - - name: target - usage: Target region (eu, us) - - name: user - usage: User associated with the sandbox - - name: volume - shorthand: v - default_value: '[]' - usage: 'Volumes to mount (format: VOLUME_NAME:MOUNT_PATH)' -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_delete.yaml b/apps/cli/hack/docs/boxlite_delete.yaml deleted file mode 100644 index 1fe2b9e41..000000000 --- a/apps/cli/hack/docs/boxlite_delete.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: boxlite delete -synopsis: Delete a sandbox -usage: boxlite delete [SANDBOX_ID] | [SANDBOX_NAME] [flags] -options: - - name: all - shorthand: a - default_value: 'false' - usage: Delete all sandboxes -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_docs.yaml b/apps/cli/hack/docs/boxlite_docs.yaml deleted file mode 100644 index 1b58937dd..000000000 --- a/apps/cli/hack/docs/boxlite_docs.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite docs -synopsis: Opens the BoxLite documentation in your default browser. -usage: boxlite docs [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_exec.yaml b/apps/cli/hack/docs/boxlite_exec.yaml deleted file mode 100644 index e11f59074..000000000 --- a/apps/cli/hack/docs/boxlite_exec.yaml +++ /dev/null @@ -1,16 +0,0 @@ -name: boxlite exec -synopsis: Execute a command in a sandbox -description: Execute a command in a running sandbox -usage: boxlite exec [SANDBOX_ID | SANDBOX_NAME] -- [COMMAND] [ARGS...] [flags] -options: - - name: cwd - usage: Working directory for command execution - - name: timeout - default_value: '0' - usage: Command timeout in seconds (0 for no timeout) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_info.yaml b/apps/cli/hack/docs/boxlite_info.yaml deleted file mode 100644 index c992d3cec..000000000 --- a/apps/cli/hack/docs/boxlite_info.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite info -synopsis: Get sandbox info -usage: boxlite info [SANDBOX_ID] | [SANDBOX_NAME] [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_list.yaml b/apps/cli/hack/docs/boxlite_list.yaml deleted file mode 100644 index f6825a40b..000000000 --- a/apps/cli/hack/docs/boxlite_list.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: boxlite list -synopsis: List sandboxes -usage: boxlite list [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) - - name: limit - shorthand: l - default_value: '100' - usage: Maximum number of items per page - - name: page - shorthand: p - default_value: '1' - usage: Page number for pagination (starting from 1) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_login.yaml b/apps/cli/hack/docs/boxlite_login.yaml deleted file mode 100644 index 8b4e0e1e6..000000000 --- a/apps/cli/hack/docs/boxlite_login.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: boxlite login -synopsis: Log in to BoxLite -usage: boxlite login [flags] -options: - - name: api-key - usage: API key to use for authentication -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_logout.yaml b/apps/cli/hack/docs/boxlite_logout.yaml deleted file mode 100644 index 49508ba3f..000000000 --- a/apps/cli/hack/docs/boxlite_logout.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite logout -synopsis: Logout from BoxLite -usage: boxlite logout [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_mcp.yaml b/apps/cli/hack/docs/boxlite_mcp.yaml deleted file mode 100644 index 13f80ca6b..000000000 --- a/apps/cli/hack/docs/boxlite_mcp.yaml +++ /dev/null @@ -1,12 +0,0 @@ -name: boxlite mcp -synopsis: Manage BoxLite MCP Server -description: Commands for managing BoxLite MCP Server -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI - - boxlite mcp config - Outputs JSON configuration for BoxLite MCP Server - - 'boxlite mcp init - Initialize BoxLite MCP Server with an agent (currently supported: claude, windsurf, cursor)' - - boxlite mcp start - Start BoxLite MCP Server diff --git a/apps/cli/hack/docs/boxlite_mcp_config.yaml b/apps/cli/hack/docs/boxlite_mcp_config.yaml deleted file mode 100644 index 3c2b0d4e9..000000000 --- a/apps/cli/hack/docs/boxlite_mcp_config.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite mcp config -synopsis: Outputs JSON configuration for BoxLite MCP Server -usage: boxlite mcp config [AGENT_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite mcp - Manage BoxLite MCP Server diff --git a/apps/cli/hack/docs/boxlite_mcp_init.yaml b/apps/cli/hack/docs/boxlite_mcp_init.yaml deleted file mode 100644 index 0c2bf5bef..000000000 --- a/apps/cli/hack/docs/boxlite_mcp_init.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: boxlite mcp init -synopsis: | - Initialize BoxLite MCP Server with an agent (currently supported: claude, windsurf, cursor) -usage: boxlite mcp init [AGENT_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite mcp - Manage BoxLite MCP Server diff --git a/apps/cli/hack/docs/boxlite_mcp_start.yaml b/apps/cli/hack/docs/boxlite_mcp_start.yaml deleted file mode 100644 index 02cfea8c9..000000000 --- a/apps/cli/hack/docs/boxlite_mcp_start.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite mcp start -synopsis: Start BoxLite MCP Server -usage: boxlite mcp start [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite mcp - Manage BoxLite MCP Server diff --git a/apps/cli/hack/docs/boxlite_organization.yaml b/apps/cli/hack/docs/boxlite_organization.yaml deleted file mode 100644 index cf01c978a..000000000 --- a/apps/cli/hack/docs/boxlite_organization.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite organization -synopsis: Manage BoxLite organizations -description: Commands for managing BoxLite organizations -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI - - boxlite organization create - Create a new organization and set it as active - - boxlite organization delete - Delete an organization - - boxlite organization list - List all organizations - - boxlite organization use - Set active organization diff --git a/apps/cli/hack/docs/boxlite_organization_create.yaml b/apps/cli/hack/docs/boxlite_organization_create.yaml deleted file mode 100644 index d8083a8c3..000000000 --- a/apps/cli/hack/docs/boxlite_organization_create.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite organization create -synopsis: Create a new organization and set it as active -usage: boxlite organization create [ORGANIZATION_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite organization - Manage BoxLite organizations diff --git a/apps/cli/hack/docs/boxlite_organization_delete.yaml b/apps/cli/hack/docs/boxlite_organization_delete.yaml deleted file mode 100644 index f553ffad7..000000000 --- a/apps/cli/hack/docs/boxlite_organization_delete.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite organization delete -synopsis: Delete an organization -usage: boxlite organization delete [ORGANIZATION] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite organization - Manage BoxLite organizations diff --git a/apps/cli/hack/docs/boxlite_organization_list.yaml b/apps/cli/hack/docs/boxlite_organization_list.yaml deleted file mode 100644 index 7b1c5b971..000000000 --- a/apps/cli/hack/docs/boxlite_organization_list.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite organization list -synopsis: List all organizations -usage: boxlite organization list [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite organization - Manage BoxLite organizations diff --git a/apps/cli/hack/docs/boxlite_organization_use.yaml b/apps/cli/hack/docs/boxlite_organization_use.yaml deleted file mode 100644 index 9cc4d71ae..000000000 --- a/apps/cli/hack/docs/boxlite_organization_use.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite organization use -synopsis: Set active organization -usage: boxlite organization use [ORGANIZATION] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite organization - Manage BoxLite organizations diff --git a/apps/cli/hack/docs/boxlite_preview-url.yaml b/apps/cli/hack/docs/boxlite_preview-url.yaml deleted file mode 100644 index e4de9fd9f..000000000 --- a/apps/cli/hack/docs/boxlite_preview-url.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: boxlite preview-url -synopsis: Get signed preview URL for a sandbox port -usage: boxlite preview-url [SANDBOX_ID | SANDBOX_NAME] [flags] -options: - - name: expires - default_value: '3600' - usage: URL expiration time in seconds - - name: port - shorthand: p - default_value: '0' - usage: Port number to get preview URL for (required) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_snapshot.yaml b/apps/cli/hack/docs/boxlite_snapshot.yaml deleted file mode 100644 index 84f271290..000000000 --- a/apps/cli/hack/docs/boxlite_snapshot.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite snapshot -synopsis: Manage BoxLite snapshots -description: Commands for managing BoxLite snapshots -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI - - boxlite snapshot create - Create a snapshot - - boxlite snapshot delete - Delete a snapshot - - boxlite snapshot list - List all snapshots - - boxlite snapshot push - Push local snapshot diff --git a/apps/cli/hack/docs/boxlite_snapshot_create.yaml b/apps/cli/hack/docs/boxlite_snapshot_create.yaml deleted file mode 100644 index e2037ab8c..000000000 --- a/apps/cli/hack/docs/boxlite_snapshot_create.yaml +++ /dev/null @@ -1,39 +0,0 @@ -name: boxlite snapshot create -synopsis: Create a snapshot -usage: boxlite snapshot create [SNAPSHOT] [flags] -options: - - name: context - shorthand: c - default_value: '[]' - usage: | - Files or directories to include in the build context (can be specified multiple times). If not provided, context will be automatically determined from COPY/ADD commands in the Dockerfile - - name: cpu - default_value: '0' - usage: | - CPU cores that will be allocated to the underlying sandboxes (default: 1) - - name: disk - default_value: '0' - usage: | - Disk space that will be allocated to the underlying sandboxes in GB (default: 3) - - name: dockerfile - shorthand: f - usage: Path to Dockerfile to build - - name: entrypoint - shorthand: e - usage: The entrypoint command for the snapshot - - name: image - shorthand: i - usage: The image name for the snapshot - - name: memory - default_value: '0' - usage: | - Memory that will be allocated to the underlying sandboxes in GB (default: 1) - - name: region - usage: | - ID of the region where the snapshot will be available (defaults to organization default region) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite snapshot - Manage BoxLite snapshots diff --git a/apps/cli/hack/docs/boxlite_snapshot_delete.yaml b/apps/cli/hack/docs/boxlite_snapshot_delete.yaml deleted file mode 100644 index 594c57ea3..000000000 --- a/apps/cli/hack/docs/boxlite_snapshot_delete.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: boxlite snapshot delete -synopsis: Delete a snapshot -usage: boxlite snapshot delete [SNAPSHOT_ID] [flags] -options: - - name: all - shorthand: a - default_value: 'false' - usage: Delete all snapshots -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite snapshot - Manage BoxLite snapshots diff --git a/apps/cli/hack/docs/boxlite_snapshot_list.yaml b/apps/cli/hack/docs/boxlite_snapshot_list.yaml deleted file mode 100644 index 8a750d5f2..000000000 --- a/apps/cli/hack/docs/boxlite_snapshot_list.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: boxlite snapshot list -synopsis: List all snapshots -description: List all available BoxLite snapshots -usage: boxlite snapshot list [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) - - name: limit - shorthand: l - default_value: '100' - usage: Maximum number of items per page - - name: page - shorthand: p - default_value: '1' - usage: Page number for pagination (starting from 1) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite snapshot - Manage BoxLite snapshots diff --git a/apps/cli/hack/docs/boxlite_snapshot_push.yaml b/apps/cli/hack/docs/boxlite_snapshot_push.yaml deleted file mode 100644 index 3d55ae000..000000000 --- a/apps/cli/hack/docs/boxlite_snapshot_push.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: boxlite snapshot push -synopsis: Push local snapshot -description: | - Push a local Docker image to BoxLite. To securely build it on our infrastructure, use 'boxlite snapshot build' -usage: boxlite snapshot push [SNAPSHOT] [flags] -options: - - name: cpu - default_value: '0' - usage: | - CPU cores that will be allocated to the underlying sandboxes (default: 1) - - name: disk - default_value: '0' - usage: | - Disk space that will be allocated to the underlying sandboxes in GB (default: 3) - - name: entrypoint - shorthand: e - usage: The entrypoint command for the image - - name: memory - default_value: '0' - usage: | - Memory that will be allocated to the underlying sandboxes in GB (default: 1) - - name: name - shorthand: 'n' - usage: Specify the Snapshot name - - name: region - usage: | - ID of the region where the snapshot will be available (defaults to organization default region) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite snapshot - Manage BoxLite snapshots diff --git a/apps/cli/hack/docs/boxlite_ssh.yaml b/apps/cli/hack/docs/boxlite_ssh.yaml deleted file mode 100644 index cae39a75a..000000000 --- a/apps/cli/hack/docs/boxlite_ssh.yaml +++ /dev/null @@ -1,15 +0,0 @@ -name: boxlite ssh -synopsis: SSH into a sandbox -description: Establish an SSH connection to a running sandbox -usage: boxlite ssh [SANDBOX_ID] | [SANDBOX_NAME] [flags] -options: - - name: expires - default_value: '1440' - usage: | - SSH access token expiration time in minutes (defaults to 24 hours) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_start.yaml b/apps/cli/hack/docs/boxlite_start.yaml deleted file mode 100644 index e7d087faa..000000000 --- a/apps/cli/hack/docs/boxlite_start.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite start -synopsis: Start a sandbox -usage: boxlite start [SANDBOX_ID] | [SANDBOX_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_stop.yaml b/apps/cli/hack/docs/boxlite_stop.yaml deleted file mode 100644 index 239ea95f9..000000000 --- a/apps/cli/hack/docs/boxlite_stop.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite stop -synopsis: Stop a sandbox -usage: boxlite stop [SANDBOX_ID] | [SANDBOX_NAME] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_version.yaml b/apps/cli/hack/docs/boxlite_version.yaml deleted file mode 100644 index 608e25d16..000000000 --- a/apps/cli/hack/docs/boxlite_version.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite version -synopsis: Print the version number -usage: boxlite version [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI diff --git a/apps/cli/hack/docs/boxlite_volume.yaml b/apps/cli/hack/docs/boxlite_volume.yaml deleted file mode 100644 index f7d86cdb1..000000000 --- a/apps/cli/hack/docs/boxlite_volume.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite volume -synopsis: Manage BoxLite volumes -description: Commands for managing BoxLite volumes -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite - BoxLite CLI - - boxlite volume create - Create a volume - - boxlite volume delete - Delete a volume - - boxlite volume get - Get volume details - - boxlite volume list - List all volumes diff --git a/apps/cli/hack/docs/boxlite_volume_create.yaml b/apps/cli/hack/docs/boxlite_volume_create.yaml deleted file mode 100644 index 3c7c03072..000000000 --- a/apps/cli/hack/docs/boxlite_volume_create.yaml +++ /dev/null @@ -1,14 +0,0 @@ -name: boxlite volume create -synopsis: Create a volume -usage: boxlite volume create [NAME] [flags] -options: - - name: size - shorthand: s - default_value: '10' - usage: Size of the volume in GB -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite volume - Manage BoxLite volumes diff --git a/apps/cli/hack/docs/boxlite_volume_delete.yaml b/apps/cli/hack/docs/boxlite_volume_delete.yaml deleted file mode 100644 index 108d24730..000000000 --- a/apps/cli/hack/docs/boxlite_volume_delete.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: boxlite volume delete -synopsis: Delete a volume -usage: boxlite volume delete [VOLUME_ID] [flags] -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite volume - Manage BoxLite volumes diff --git a/apps/cli/hack/docs/boxlite_volume_get.yaml b/apps/cli/hack/docs/boxlite_volume_get.yaml deleted file mode 100644 index 687b9d3d3..000000000 --- a/apps/cli/hack/docs/boxlite_volume_get.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite volume get -synopsis: Get volume details -usage: boxlite volume get [VOLUME_ID] [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite volume - Manage BoxLite volumes diff --git a/apps/cli/hack/docs/boxlite_volume_list.yaml b/apps/cli/hack/docs/boxlite_volume_list.yaml deleted file mode 100644 index aa43378d0..000000000 --- a/apps/cli/hack/docs/boxlite_volume_list.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: boxlite volume list -synopsis: List all volumes -usage: boxlite volume list [flags] -options: - - name: format - shorthand: f - usage: Output format. Must be one of (yaml, json) -inherited_options: - - name: help - default_value: 'false' - usage: help for boxlite -see_also: - - boxlite volume - Manage BoxLite volumes diff --git a/apps/cli/hack/generate-cli-docs.sh b/apps/cli/hack/generate-cli-docs.sh deleted file mode 100755 index 3de1ab24f..000000000 --- a/apps/cli/hack/generate-cli-docs.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Copyright 2025 Daytona Platforms Inc. -# SPDX-License-Identifier: AGPL-3.0 - - -# Clean up existing documentation files -rm -rf docs hack/docs - -# Generate default CLI documentation files in folder "docs" -go run main.go generate-docs diff --git a/apps/cli/internal/buildinfo.go b/apps/cli/internal/buildinfo.go deleted file mode 100644 index 6705496c4..000000000 --- a/apps/cli/internal/buildinfo.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package internal - -var ( - Version = "v0.0.0-dev" - BoxliteApiUrl = "" - Auth0Domain = "" - Auth0ClientId = "" - Auth0ClientSecret = "" - Auth0CallbackPort = "" - Auth0Audience = "" -) diff --git a/apps/cli/internal/cmd.go b/apps/cli/internal/cmd.go deleted file mode 100644 index 45812d3f8..000000000 --- a/apps/cli/internal/cmd.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package internal - -const ( - USER_GROUP = "user" - SANDBOX_GROUP = "sandbox" -) - -var ( - SuppressVersionMismatchWarning = false -) diff --git a/apps/cli/main.go b/apps/cli/main.go deleted file mode 100644 index ee66e6c31..000000000 --- a/apps/cli/main.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package main - -import ( - "os" - - log "github.com/sirupsen/logrus" - - "github.com/boxlite-ai/boxlite/cli/cmd" - "github.com/boxlite-ai/boxlite/cli/cmd/auth" - "github.com/boxlite-ai/boxlite/cli/cmd/mcp" - "github.com/boxlite-ai/boxlite/cli/cmd/organization" - "github.com/boxlite-ai/boxlite/cli/cmd/sandbox" - "github.com/boxlite-ai/boxlite/cli/cmd/snapshot" - "github.com/boxlite-ai/boxlite/cli/cmd/volume" - "github.com/boxlite-ai/boxlite/cli/internal" - "github.com/joho/godotenv" - "github.com/spf13/cobra" -) - -var rootCmd = &cobra.Command{ - Use: "boxlite", - Short: "BoxLite CLI", - Long: "Command line interface for BoxLite Sandboxes", - DisableAutoGenTag: true, - SilenceUsage: true, - SilenceErrors: true, - RunE: func(cmd *cobra.Command, args []string) error { - return cmd.Help() - }, -} - -func init() { - rootCmd.AddGroup(&cobra.Group{ID: internal.USER_GROUP, Title: "User"}) - rootCmd.AddGroup(&cobra.Group{ID: internal.SANDBOX_GROUP, Title: "Sandbox"}) - - rootCmd.AddCommand(auth.LoginCmd) - rootCmd.AddCommand(auth.LogoutCmd) - rootCmd.AddCommand(sandbox.SandboxCmd) - rootCmd.AddCommand(snapshot.SnapshotsCmd) - rootCmd.AddCommand(volume.VolumeCmd) - rootCmd.AddCommand(organization.OrganizationCmd) - rootCmd.AddCommand(mcp.MCPCmd) - rootCmd.AddCommand(cmd.DocsCmd) - rootCmd.AddCommand(cmd.AutoCompleteCmd) - rootCmd.AddCommand(cmd.GenerateDocsCmd) - rootCmd.AddCommand(cmd.VersionCmd) - - // Add sandbox subcommands as top-level shortcuts - rootCmd.AddCommand(createSandboxShortcut(sandbox.CreateCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.DeleteCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.InfoCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.ListCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.StartCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.StopCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.ArchiveCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.SSHCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.ExecCmd)) - rootCmd.AddCommand(createSandboxShortcut(sandbox.PreviewUrlCmd)) - - rootCmd.CompletionOptions.HiddenDefaultCmd = true - rootCmd.PersistentFlags().BoolP("help", "", false, "help for boxlite") - rootCmd.Flags().BoolP("version", "v", false, "Display the version of BoxLite") - - rootCmd.PreRun = func(command *cobra.Command, args []string) { - versionFlag, _ := command.Flags().GetBool("version") - if versionFlag { - err := cmd.VersionCmd.RunE(command, []string{}) - if err != nil { - log.Fatal(err) - } - os.Exit(0) - } - } -} - -// createSandboxShortcut creates a top-level shortcut for a sandbox subcommand -func createSandboxShortcut(original *cobra.Command) *cobra.Command { - shortcut := &cobra.Command{ - Use: original.Use, - Short: original.Short, - Long: original.Long, - Args: original.Args, - Aliases: original.Aliases, - GroupID: internal.SANDBOX_GROUP, - RunE: original.RunE, - } - shortcut.Flags().AddFlagSet(original.Flags()) - return shortcut -} - -func main() { - _ = godotenv.Load() - - err := rootCmd.Execute() - if err != nil { - log.Fatal(err) - } -} diff --git a/apps/cli/mcp/README.md b/apps/cli/mcp/README.md deleted file mode 100644 index d3fa652fa..000000000 --- a/apps/cli/mcp/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# BoxLite MCP (Model Context Protocol) Server - -BoxLite MCP Server allows AI agents to utilize: - -- BoxLite Sandbox Management (Create, Destroy) -- Execute commands in BoxLite Sandboxes -- File Operations in BoxLite sandboxes -- Generate preview links for web applications running in BoxLite Sandboxes - -## Prerequisites - -- BoxLite account -- BoxLite CLI installed -- A compatible AI agent (Claude Desktop App, Claude Code, Cursor, Windsurf) - -## Steps to Integrate BoxLite MCP Server with an AI Agent - -1. **Install the BoxLite CLI:** - -**Mac/Linux** - -```bash -brew install boxlite-ai/cli/boxlite -``` - -**Windows** - -```bash -powershell -Command "irm https://get.boxlite.io/windows | iex" -``` - -2. **Log in to your BoxLite account:** - -```bash -boxlite login -``` - -3. **Initialize the BoxLite MCP server with Claude Desktop/Claude Code/Cursor/Windsurf:** - -```bash -boxlite mcp init [claude/cursor/windsurf] -``` - -4. **Open Agent App** - -## Integrating with Other AI Agents Apps - -**Run the following command to get a JSON BoxLite MCP configuration which you can c/p to your agent configuration:** - -```bash -boxlite mcp config -``` - -**Command outputs the following:** - -```json -{ - "mcpServers": { - "boxlite-mcp": { - "command": "boxlite", - "args": ["mcp", "start"], - "env": { - "HOME": "${HOME}", - "PATH": "${HOME}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/homebrew/bin" - }, - "logFile": "${HOME}/Library/Logs/boxlite/boxlite-mcp-server.log" - } - } -} -``` - -Note: if you are running BoxLite MCP Server on Windows OS, add the following to the env field of the configuration: - -```json -"APPDATA": "${APPDATA}" -``` - -**Finally, open or restart your AI agent** - -## Available Tools - -### Sandbox Management - -- `create_sandbox`: Create a new sandbox with BoxLite - - - Parameters: - - `id` (optional): Sandbox ID - if provided, an existing sandbox will be used, new one will be created otherwise - - `target` (optional): Target region of the sandbox (if not provided, default region of the organization is used) - - `image`: Image of the sandbox (optional) - - `auto_stop_interval` (default: "15"): Auto-stop interval in minutes (0 means disabled) - - `auto_archive_interval` (default: "10080"): Auto-archive interval in minutes (0 means the maximum interval will be used) - - `auto_delete_interval` (default: "-1"): Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) - -- `destroy_sandbox`: Destroy a sandbox with BoxLite - -### File Operations - -- `upload_file`: Upload a file to the BoxLite sandbox - - - Files can be text or base64-encoded binary content - - Creates necessary parent directories automatically - - Files persist during the session and have appropriate permissions - - Supports overwrite controls and maintains original files formats - - Parameters: - - `id` (optional): Sandbox ID - - `file_path`: Path to the file to upload - - `content`: Content of the file to upload - - `encoding`: Encoding of the file to upload - - `overwrite`: Overwrite the file if it already exists - -- `download_file`: Download a file from the BoxLite sandbox - - - Returns file content as text or base64 encoded image - - Handles special cases like matplotlib plots stored as JSON - - Parameters: - - `id` (optional): Sandbox ID - - `file_path`: Path to the file to download - -- `create_folder`: Create a new folder in the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `folder_path`: Path to the folder to create - - `mode`: Mode of the folder to create (defaults to 0755) - -- `get_file_info`: Get information about a file in the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `file_path`: Path to the file to get information about - -- `list_files`: List files in a directory in the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `path`: Path to the directory to list files from (defaults to current directory) - -- `move_file`: Move or rename a file in the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `source_path`: Source path of the file to move - - `dest_path`: Destination path where to move the file - -- `delete_file`: Delete a file or directory in the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `file_path`: Path to the file or directory to delete - -### Git Operations - -- `git_clone`: Clone a Git repository into the BoxLite sandbox - - - Parameters: - - `id` (optional): Sandbox ID - - `url`: URL of the Git repository to clone - - `path`: Directory to clone the repository into (defaults to current directory) - - `branch`: Branch to clone - - `commit_id`: Commit ID to clone - - `username`: Username to clone the repository with - - `password`: Password to clone the repository with - -### Command Execution - -- `execute_command`: Execute shell commands in the ephemeral BoxLite Linux environment - - - Returns full stdout and stderr output with exit codes - - Commands have sandbox user permissions - - Parameters: - - `id` (optional): Sandbox ID - - `command`: Command to execute - -### Preview - -- `preview_link`: Generate accessible preview URLs for web applications running in the BoxLite sandbox - - - Creates a secure tunnel to expose local ports externally without configuration - - Validates if a server is actually running on the specified port - - Provides diagnostic information for troubleshooting - - Supports custom descriptions and metadata for better organization of multiple services - - Parameters: - - `id` (optional): Sandbox ID - - `port`: Port to expose - - `description`: Description of the service - - `check_server`: Check if a server is running - -## Troubleshooting - -- **Authentication issues:** Run `boxlite login` to refresh your credentials -- **Connection errors:** Ensure that the BoxLite MCP Server is properly configured -- **Sandbox errors:** Check sandbox status with `boxlite sandbox list` - -## Support - -For more information, visit [boxlite.io](https://boxlite.io) or contact support at support@boxlite.io. diff --git a/apps/cli/mcp/server.go b/apps/cli/mcp/server.go deleted file mode 100644 index 263a7da78..000000000 --- a/apps/cli/mcp/server.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package mcp - -import ( - "github.com/boxlite-ai/boxlite/cli/mcp/tools" - "github.com/mark3labs/mcp-go/mcp" - "github.com/mark3labs/mcp-go/server" -) - -type BoxliteMCPServer struct { - server.MCPServer -} - -func NewBoxliteMCPServer() *BoxliteMCPServer { - s := &BoxliteMCPServer{} - - s.MCPServer = *server.NewMCPServer( - "BoxLite MCP Server", - "0.0.0-dev", - server.WithRecovery(), - server.WithPromptCapabilities(false), - server.WithResourceCapabilities(false, false), - server.WithToolCapabilities(true), - server.WithLogging(), - ) - - s.addTools() - - return s -} - -func (s *BoxliteMCPServer) Start() error { - return server.ServeStdio(&s.MCPServer) -} - -func (s *BoxliteMCPServer) addTools() { - s.AddTool(tools.GetCreateSandboxTool(), mcp.NewTypedToolHandler(tools.CreateSandbox)) - s.AddTool(tools.GetDestroySandboxTool(), mcp.NewTypedToolHandler(tools.DestroySandbox)) - - s.AddTool(tools.GetFileUploadTool(), mcp.NewTypedToolHandler(tools.FileUpload)) - s.AddTool(tools.GetFileDownloadTool(), mcp.NewTypedToolHandler(tools.FileDownload)) - s.AddTool(tools.GetFileInfoTool(), mcp.NewTypedToolHandler(tools.FileInfo)) - s.AddTool(tools.GetListFilesTool(), mcp.NewTypedToolHandler(tools.ListFiles)) - s.AddTool(tools.GetMoveFileTool(), mcp.NewTypedToolHandler(tools.MoveFile)) - s.AddTool(tools.GetDeleteFileTool(), mcp.NewTypedToolHandler(tools.DeleteFile)) - s.AddTool(tools.GetCreateFolderTool(), mcp.NewTypedToolHandler(tools.CreateFolder)) - - s.AddTool(tools.GetExecuteCommandTool(), mcp.NewTypedToolHandler(tools.ExecuteCommand)) - s.AddTool(tools.GetPreviewLinkTool(), mcp.NewTypedToolHandler(tools.PreviewLink)) - s.AddTool(tools.GetGitCloneTool(), mcp.NewTypedToolHandler(tools.GitClone)) -} diff --git a/apps/cli/mcp/tools/common.go b/apps/cli/mcp/tools/common.go deleted file mode 100644 index 985c9f64e..000000000 --- a/apps/cli/mcp/tools/common.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import "github.com/boxlite-ai/boxlite/cli/apiclient" - -var boxliteMCPHeaders map[string]string = map[string]string{ - apiclient.BoxliteSourceHeader: "boxlite-mcp", -} diff --git a/apps/cli/mcp/tools/create_folder.go b/apps/cli/mcp/tools/create_folder.go deleted file mode 100644 index d8ba0961e..000000000 --- a/apps/cli/mcp/tools/create_folder.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type CreateFolderArgs struct { - Id *string `json:"id,omitempty"` - FolderPath *string `json:"folderPath,omitempty"` - Mode *string `json:"mode,omitempty"` -} - -func GetCreateFolderTool() mcp.Tool { - return mcp.NewTool("create_folder", - mcp.WithDescription("Create a new folder in the BoxLite sandbox."), - mcp.WithString("folderPath", mcp.Required(), mcp.Description("Path to the folder to create.")), - mcp.WithString("mode", mcp.Description("Mode of the folder to create (defaults to 0755).")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to create the folder in.")), - ) -} - -func CreateFolder(ctx context.Context, request mcp.CallToolRequest, args CreateFolderArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.FolderPath == nil || *args.FolderPath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("folderPath parameter is required") - } - - mode := "0755" // default mode - if args.Mode == nil || *args.Mode == "" { - args.Mode = &mode - } - - // Create the folder - _, err = apiClient.ToolboxAPI.CreateFolderDeprecated(ctx, *args.Id).Path(*args.FolderPath).Mode(*args.Mode).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error creating folder: %v", err) - } - - log.Infof("Created folder: %s", *args.FolderPath) - - return mcp.NewToolResultText(fmt.Sprintf("Created folder: %s", *args.FolderPath)), nil -} diff --git a/apps/cli/mcp/tools/create_sandbox.go b/apps/cli/mcp/tools/create_sandbox.go deleted file mode 100644 index 72ada56a8..000000000 --- a/apps/cli/mcp/tools/create_sandbox.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - "strings" - "time" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type CreateSandboxArgs struct { - Id *string `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Target *string `json:"target,omitempty"` - Snapshot *string `json:"snapshot,omitempty"` - User *string `json:"user,omitempty"` - Env *map[string]string `json:"env,omitempty"` - Labels *map[string]string `json:"labels,omitempty"` - Public *bool `json:"public,omitempty"` - Cpu *int32 `json:"cpu,omitempty"` - Gpu *int32 `json:"gpu,omitempty"` - Memory *int32 `json:"memory,omitempty"` - Disk *int32 `json:"disk,omitempty"` - AutoStopInterval *int32 `json:"autoStopInterval,omitempty"` - AutoArchiveInterval *int32 `json:"autoArchiveInterval,omitempty"` - AutoDeleteInterval *int32 `json:"autoDeleteInterval,omitempty"` - Volumes *[]apiclient.SandboxVolume `json:"volumes,omitempty"` - BuildInfo *apiclient.CreateBuildInfo `json:"buildInfo,omitempty"` - NetworkBlockAll *bool `json:"networkBlockAll,omitempty"` - NetworkAllowList *string `json:"networkAllowList,omitempty"` -} - -func GetCreateSandboxTool() mcp.Tool { - return mcp.NewTool("create_sandbox", - mcp.WithDescription("Create a new sandbox with BoxLite"), - mcp.WithString("id", mcp.Description("If a sandbox ID is provided it is first checked if it exists and is running, if so, the existing sandbox will be used. However, a model is not able to provide custom sandbox ID but only the ones BoxLite commands return and should always leave ID field empty if the intention is to create a new sandbox.")), - mcp.WithString("name", mcp.Description("Name of the sandbox. If not provided, the sandbox ID will be used as the name.")), - mcp.WithString("target", mcp.DefaultString("us"), mcp.Description("Target region of the sandbox.")), - mcp.WithString("snapshot", mcp.Description("Snapshot of the sandbox (don't specify any if not explicitly instructed from user). Cannot be specified when using a build info entry.")), - mcp.WithString("user", mcp.Description("User associated with the sandbox.")), - mcp.WithObject("env", mcp.Description("Environment variables for the sandbox. Format: {\"key\": \"value\", \"key2\": \"value2\"}"), mcp.AdditionalProperties(map[string]any{"type": "string"})), - mcp.WithObject("labels", mcp.Description("Labels for the sandbox. Format: {\"key\": \"value\", \"key2\": \"value2\"}"), mcp.AdditionalProperties(map[string]any{"type": "string"})), - mcp.WithBoolean("public", mcp.Description("Whether the sandbox http preview is publicly accessible.")), - mcp.WithNumber("cpu", mcp.Description("CPU cores allocated to the sandbox. Cannot specify sandbox resources when using a snapshot."), mcp.Max(4)), - mcp.WithNumber("gpu", mcp.Description("GPU units allocated to the sandbox. Cannot specify sandbox resources when using a snapshot."), mcp.Max(1)), - mcp.WithNumber("memory", mcp.Description("Memory allocated to the sandbox in GB. Cannot specify sandbox resources when using a snapshot."), mcp.Max(8)), - mcp.WithNumber("disk", mcp.Description("Disk space allocated to the sandbox in GB. Cannot specify sandbox resources when using a snapshot."), mcp.Max(10)), - mcp.WithNumber("autoStopInterval", mcp.DefaultNumber(15), mcp.Min(0), mcp.Description("Auto-stop interval in minutes (0 means disabled) for the sandbox.")), - mcp.WithNumber("autoArchiveInterval", mcp.DefaultNumber(10080), mcp.Min(0), mcp.Description("Auto-archive interval in minutes (0 means the maximum interval will be used) for the sandbox.")), - mcp.WithNumber("autoDeleteInterval", mcp.DefaultNumber(-1), mcp.Description("Auto-delete interval in minutes (negative value means disabled, 0 means delete immediately upon stopping) for the sandbox.")), - mcp.WithArray("volumes", mcp.Description("Volumes to attach to the sandbox."), mcp.Items(map[string]any{"type": "object", "properties": map[string]any{"volumeId": map[string]any{"type": "string"}, "mountPath": map[string]any{"type": "string"}}})), - mcp.WithObject("buildInfo", mcp.Description("Build information for the sandbox."), mcp.Properties(map[string]any{"dockerfileContent": map[string]any{"type": "string"}, "contextHashes": map[string]any{"type": "array", "items": map[string]any{"type": "string"}}})), - mcp.WithBoolean("networkBlockAll", mcp.Description("Whether to block all network access to the sandbox.")), - mcp.WithString("networkAllowList", mcp.Description("Comma-separated list of domains to allow network access to the sandbox.")), - ) -} - -func CreateSandbox(ctx context.Context, request mcp.CallToolRequest, args CreateSandboxArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient_cli.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - sandboxId := "" - if args.Id != nil && *args.Id != "" { - sandboxId = *args.Id - } - - if sandboxId != "" { - sandbox, _, err := apiClient.SandboxAPI.GetSandbox(ctx, sandboxId).Execute() - if err == nil && sandbox.State != nil && *sandbox.State == apiclient.SANDBOXSTATE_STARTED { - return mcp.NewToolResultText(fmt.Sprintf("Reusing existing sandbox %s", sandboxId)), nil - } - - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox %s not found or not running", sandboxId) - } - - createSandboxReq, err := createSandboxRequest(args) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - // Create new sandbox with retries - maxRetries := 3 - retryDelay := time.Second * 2 - - for retry := range maxRetries { - sandbox, _, err := apiClient.SandboxAPI.CreateSandbox(ctx).CreateSandbox(*createSandboxReq).Execute() - if err != nil { - if strings.Contains(err.Error(), "Total CPU quota exceeded") { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("CPU quota exceeded. Please delete unused sandboxes or upgrade your plan") - } - - if retry == maxRetries-1 { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to create sandbox after %d retries: %v", maxRetries, err) - } - - log.Infof("Sandbox creation failed, retrying: %v", err) - - time.Sleep(retryDelay) - retryDelay = retryDelay * 3 / 2 // Exponential backoff - continue - } - - log.Infof("Created new sandbox: %s", sandbox.Id) - - return mcp.NewToolResultText(fmt.Sprintf("Created new sandbox %s", sandbox.Id)), nil - } - - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to create sandbox after %d retries", maxRetries) -} - -func createSandboxRequest(args CreateSandboxArgs) (*apiclient.CreateSandbox, error) { - createSandbox := apiclient.NewCreateSandbox() - - if args.Name != nil && *args.Name != "" { - createSandbox.SetName(*args.Name) - } - - if args.BuildInfo != nil { - if args.Snapshot != nil && *args.Snapshot != "" { - return nil, fmt.Errorf("cannot specify a snapshot when using a build info entry") - } - } else { - if args.Cpu != nil || args.Gpu != nil || args.Memory != nil || args.Disk != nil { - return nil, fmt.Errorf("cannot specify sandbox resources when using a snapshot") - } - } - - if args.Snapshot != nil && *args.Snapshot != "" { - createSandbox.SetSnapshot(*args.Snapshot) - } - - if args.Target != nil && *args.Target != "" { - createSandbox.SetTarget(*args.Target) - } - - if args.AutoStopInterval != nil { - createSandbox.SetAutoStopInterval(*args.AutoStopInterval) - } - - if args.AutoArchiveInterval != nil { - createSandbox.SetAutoArchiveInterval(*args.AutoArchiveInterval) - } - - if args.AutoDeleteInterval != nil { - createSandbox.SetAutoDeleteInterval(*args.AutoDeleteInterval) - } - - if args.User != nil && *args.User != "" { - createSandbox.SetUser(*args.User) - } - - if args.Env != nil { - createSandbox.SetEnv(*args.Env) - } - - if args.Labels != nil { - createSandbox.SetLabels(*args.Labels) - } - - if args.Public != nil { - createSandbox.SetPublic(*args.Public) - } - - if args.Cpu != nil { - createSandbox.SetCpu(*args.Cpu) - } - - if args.Memory != nil { - createSandbox.SetMemory(*args.Memory) - } - - if args.Disk != nil { - createSandbox.SetDisk(*args.Disk) - } - - if args.Volumes != nil { - createSandbox.SetVolumes(*args.Volumes) - } - - if args.BuildInfo != nil { - createSandbox.SetBuildInfo(*args.BuildInfo) - } - - if args.NetworkBlockAll != nil { - createSandbox.SetNetworkBlockAll(*args.NetworkBlockAll) - } - - if args.NetworkAllowList != nil { - createSandbox.SetNetworkAllowList(*args.NetworkAllowList) - } - - return createSandbox, nil -} diff --git a/apps/cli/mcp/tools/delete_file.go b/apps/cli/mcp/tools/delete_file.go deleted file mode 100644 index 1e50ad607..000000000 --- a/apps/cli/mcp/tools/delete_file.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type DeleteFileArgs struct { - Id *string `json:"id,omitempty"` - FilePath *string `json:"filePath,omitempty"` -} - -func GetDeleteFileTool() mcp.Tool { - return mcp.NewTool("delete_file", - mcp.WithDescription("Delete a file or directory in the BoxLite sandbox."), - mcp.WithString("filePath", mcp.Required(), mcp.Description("Path to the file or directory to delete.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to delete the file in.")), - ) -} - -func DeleteFile(ctx context.Context, request mcp.CallToolRequest, args DeleteFileArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient_cli.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.FilePath == nil || *args.FilePath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("filePath parameter is required") - } - - // Execute delete command - execResponse, _, err := apiClient.ToolboxAPI.ExecuteCommandDeprecated(ctx, *args.Id). - ExecuteRequest(*apiclient.NewExecuteRequest(fmt.Sprintf("rm -rf %s", *args.FilePath))). - Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error deleting file: %v", err) - } - - log.Infof("Deleted file: %s", *args.FilePath) - - return mcp.NewToolResultText(fmt.Sprintf("Deleted file: %s\nOutput: %s", *args.FilePath, execResponse.Result)), nil -} diff --git a/apps/cli/mcp/tools/destroy_sandbox.go b/apps/cli/mcp/tools/destroy_sandbox.go deleted file mode 100644 index 0acdde5d8..000000000 --- a/apps/cli/mcp/tools/destroy_sandbox.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - "time" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type DestroySandboxArgs struct { - Id *string `json:"id,omitempty"` -} - -func GetDestroySandboxTool() mcp.Tool { - return mcp.NewTool("destroy_sandbox", - mcp.WithDescription("Destroy a sandbox with BoxLite"), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to destroy.")), - ) -} - -func DestroySandbox(ctx context.Context, request mcp.CallToolRequest, args DestroySandboxArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - // Destroy sandbox with retries - maxRetries := 3 - retryDelay := time.Second * 2 - - for retry := range maxRetries { - _, _, err := apiClient.SandboxAPI.DeleteSandbox(ctx, *args.Id).Execute() - if err != nil { - if retry == maxRetries-1 { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to destroy sandbox after %d retries: %v", maxRetries, err) - } - - log.Infof("Sandbox creation failed, retrying: %v", err) - - time.Sleep(retryDelay) - retryDelay = retryDelay * 3 / 2 // Exponential backoff - continue - } - - log.Infof("Destroyed sandbox with ID: %s", *args.Id) - - return mcp.NewToolResultText(fmt.Sprintf("Destroyed sandbox with ID %s", *args.Id)), nil - } - - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to destroy sandbox after %d retries", maxRetries) -} diff --git a/apps/cli/mcp/tools/download_file.go b/apps/cli/mcp/tools/download_file.go deleted file mode 100644 index 89f186763..000000000 --- a/apps/cli/mcp/tools/download_file.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "encoding/json" - "fmt" - "io" - "path/filepath" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" -) - -type FileDownloadArgs struct { - Id *string `json:"id,omitempty"` - FilePath *string `json:"filePath,omitempty"` -} - -type Content struct { - Type string `json:"type"` - Text string `json:"text,omitempty"` - Data string `json:"data,omitempty"` -} - -func GetFileDownloadTool() mcp.Tool { - return mcp.NewTool("file_download", - mcp.WithDescription("Download a file from the BoxLite sandbox. Returns the file content either as text or as a base64 encoded image. Handles special cases like matplotlib plots stored as JSON with embedded base64 images."), - mcp.WithString("filePath", mcp.Required(), mcp.Description("Path to the file to download.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to download the file from.")), - ) -} - -func FileDownload(ctx context.Context, request mcp.CallToolRequest, args FileDownloadArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.FilePath == nil || *args.FilePath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("filePath parameter is required") - } - - // Download the file - file, _, err := apiClient.ToolboxAPI.DownloadFileDeprecated(ctx, *args.Id).Path(*args.FilePath).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error downloading file: %v", err) - } - defer file.Close() - - // Read file content - content, err := io.ReadAll(file) - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error reading file content: %v", err) - } - - // Process file content based on file type - ext := filepath.Ext(*args.FilePath) - var result []Content - - switch ext { - case ".png", ".jpg", ".jpeg", ".gif": - // For image files, return as base64 encoded data - result = []Content{{ - Type: "image", - Data: string(content), - }} - case ".json": - // For JSON files, try to parse and handle special cases like matplotlib plots - var jsonData map[string]interface{} - if err := json.Unmarshal(content, &jsonData); err != nil { - // If not valid JSON, return as text - result = []Content{{ - Type: "text", - Text: string(content), - }} - } else { - // Check if it's a matplotlib plot - if _, ok := jsonData["data"]; ok { - result = []Content{{ - Type: "image", - Data: jsonData["data"].(string), - }} - } else { - result = []Content{{ - Type: "text", - Text: string(content), - }} - } - } - default: - // For all other files, return as text - result = []Content{{ - Type: "text", - Text: string(content), - }} - } - - // Convert result to JSON - resultJSON, err := json.Marshal(result) - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error marshaling result: %v", err) - } - - return mcp.NewToolResultText(string(resultJSON)), nil -} diff --git a/apps/cli/mcp/tools/execute_command.go b/apps/cli/mcp/tools/execute_command.go deleted file mode 100644 index 45562ec98..000000000 --- a/apps/cli/mcp/tools/execute_command.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "encoding/json" - "fmt" - "strings" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type ExecuteCommandArgs struct { - Id *string `json:"id,omitempty"` - Command *string `json:"command,omitempty"` -} - -type CommandResult struct { - Stdout string `json:"stdout"` - Stderr string `json:"stderr"` - ExitCode int `json:"exitCode"` - ErrorType string `json:"errorType,omitempty"` -} - -func GetExecuteCommandTool() mcp.Tool { - return mcp.NewTool("execute_command", - mcp.WithDescription("Execute shell commands in the ephemeral BoxLite Linux environment. Returns full stdout and stderr output with exit codes. Commands have sandbox user permissions and can install packages, modify files, and interact with running services. Always use /tmp directory. Use verbose flags where available for better output."), - mcp.WithString("command", mcp.Required(), mcp.Description("Command to execute.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to execute the command in.")), - ) -} - -func ExecuteCommand(ctx context.Context, request mcp.CallToolRequest, args ExecuteCommandArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient_cli.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return returnCommandError("Sandbox ID is required", "SandboxError") - } - - if args.Command == nil || *args.Command == "" { - return returnCommandError("Command must be a non-empty string", "ValueError") - } - - // Process the command - command := strings.TrimSpace(*args.Command) - if strings.Contains(command, "&&") || strings.HasPrefix(command, "cd ") { - // Wrap complex commands in /bin/sh -c - command = fmt.Sprintf("/bin/sh -c %s", shellQuote(command)) - } - - log.Infof("Executing command: %s", command) - - // Execute the command - result, _, err := apiClient.ToolboxAPI.ExecuteCommandDeprecated(ctx, *args.Id). - ExecuteRequest(*apiclient.NewExecuteRequest(command)). - Execute() - - if err != nil { - // Classify error types - errStr := err.Error() - switch { - case strings.Contains(errStr, "Connection") || strings.Contains(errStr, "Timeout"): - return returnCommandError(fmt.Sprintf("Network error during command execution: %s", errStr), "NetworkError") - case strings.Contains(errStr, "Unauthorized") || strings.Contains(errStr, "401"): - return returnCommandError("Authentication failed during command execution. Please check your API key", "NetworkError") - default: - return returnCommandError(fmt.Sprintf("Command execution failed: %s", errStr), "CommandExecutionError") - } - } - - // Process command output - cmdResult := CommandResult{ - Stdout: strings.TrimSpace(result.Result), - ExitCode: int(result.ExitCode), - } - - // Log truncated output - outputLen := len(cmdResult.Stdout) - logOutput := cmdResult.Stdout - if outputLen > 500 { - logOutput = cmdResult.Stdout[:500] + "..." - } - - log.Infof("Command completed - exit code: %d, output length: %d", cmdResult.ExitCode, outputLen) - - log.Debugf("Command output (truncated): %s", logOutput) - - // Check for non-zero exit code - if cmdResult.ExitCode > 0 { - log.Infof("Command exited with non-zero status - exit code: %d", cmdResult.ExitCode) - } - - // Convert result to JSON - resultJSON, err := json.MarshalIndent(cmdResult, "", " ") - if err != nil { - return returnCommandError(fmt.Sprintf("Error marshaling result: %v", err), "CommandExecutionError") - } - - return mcp.NewToolResultText(string(resultJSON)), nil -} - -// Helper function to return command errors in a consistent format -func returnCommandError(message, errorType string) (*mcp.CallToolResult, error) { - return &mcp.CallToolResult{ - IsError: true, - Result: mcp.Result{ - Meta: map[string]interface{}{ - "Stdout": "", - "Stderr": message, - "ExitCode": -1, - "ErrorType": errorType, - }, - }, - }, nil -} - -// Helper function to quote shell commands -func shellQuote(s string) string { - // Simple shell quoting - wrap in single quotes and escape existing single quotes - return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'" -} diff --git a/apps/cli/mcp/tools/file_info.go b/apps/cli/mcp/tools/file_info.go deleted file mode 100644 index 5b578de93..000000000 --- a/apps/cli/mcp/tools/file_info.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type FileInfoArgs struct { - Id *string `json:"id,omitempty"` - FilePath *string `json:"filePath,omitempty"` -} - -func GetFileInfoTool() mcp.Tool { - return mcp.NewTool("get_file_info", - mcp.WithDescription("Get information about a file in the BoxLite sandbox."), - mcp.WithString("filePath", mcp.Required(), mcp.Description("Path to the file to get information about.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to get the file information from.")), - ) -} - -func FileInfo(ctx context.Context, request mcp.CallToolRequest, args FileInfoArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.FilePath == nil || *args.FilePath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("filePath parameter is required") - } - - // Get file info - fileInfo, _, err := apiClient.ToolboxAPI.GetFileInfoDeprecated(ctx, *args.Id).Path(*args.FilePath).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error getting file info: %v", err) - } - - // Convert file info to JSON - fileInfoJSON, err := json.MarshalIndent(fileInfo, "", " ") - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error marshaling file info: %v", err) - } - - log.Infof("Retrieved file info for: %s", *args.FilePath) - - return mcp.NewToolResultText(string(fileInfoJSON)), nil -} diff --git a/apps/cli/mcp/tools/git_clone.go b/apps/cli/mcp/tools/git_clone.go deleted file mode 100644 index 439f9e80e..000000000 --- a/apps/cli/mcp/tools/git_clone.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type GitCloneArgs struct { - Id *string `json:"id,omitempty"` - Url *string `json:"url,omitempty"` - Path *string `json:"path,omitempty"` - Branch *string `json:"branch,omitempty"` - CommitId *string `json:"commitId,omitempty"` - Username *string `json:"username,omitempty"` - Password *string `json:"password,omitempty"` -} - -func GetGitCloneTool() mcp.Tool { - return mcp.NewTool("git_clone", - mcp.WithDescription("Clone a Git repository into the BoxLite sandbox."), - mcp.WithString("url", mcp.Required(), mcp.Description("URL of the Git repository to clone.")), - mcp.WithString("path", mcp.Description("Directory to clone the repository into (defaults to current directory).")), - mcp.WithString("branch", mcp.Description("Branch to clone.")), - mcp.WithString("commitId", mcp.Description("Commit ID to clone.")), - mcp.WithString("username", mcp.Description("Username to clone the repository with.")), - mcp.WithString("password", mcp.Description("Password to clone the repository with.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to clone the repository in.")), - ) -} - -func GitClone(ctx context.Context, request mcp.CallToolRequest, args GitCloneArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient_cli.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - gitCloneRequest, err := getGitCloneRequest(args) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - _, err = apiClient.ToolboxAPI.GitCloneRepositoryDeprecated(ctx, *args.Id).GitCloneRequest(*gitCloneRequest).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error cloning repository: %v", err) - } - - log.Infof("Cloned repository: %s to %s", gitCloneRequest.Url, gitCloneRequest.Path) - - return mcp.NewToolResultText(fmt.Sprintf("Cloned repository: %s to %s", gitCloneRequest.Url, gitCloneRequest.Path)), nil -} - -func getGitCloneRequest(args GitCloneArgs) (*apiclient.GitCloneRequest, error) { - gitCloneRequest := apiclient.GitCloneRequest{} - - if args.Url == nil || *args.Url == "" { - return nil, fmt.Errorf("url parameter is required") - } - - gitCloneRequest.Url = *args.Url - - gitCloneRequest.Path = "." - if args.Path != nil && *args.Path != "" { - gitCloneRequest.Path = *args.Path - } - - if args.Branch != nil && *args.Branch != "" { - gitCloneRequest.Branch = args.Branch - } - - if args.CommitId != nil && *args.CommitId != "" { - gitCloneRequest.CommitId = args.CommitId - } - - if args.Username != nil && *args.Username != "" { - gitCloneRequest.Username = args.Username - } - - if args.Password != nil && *args.Password != "" { - gitCloneRequest.Password = args.Password - } - - return &gitCloneRequest, nil -} diff --git a/apps/cli/mcp/tools/list_files.go b/apps/cli/mcp/tools/list_files.go deleted file mode 100644 index 533eea203..000000000 --- a/apps/cli/mcp/tools/list_files.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type ListFilesArgs struct { - Id *string `json:"id,omitempty"` - Path *string `json:"path,omitempty"` -} - -func GetListFilesTool() mcp.Tool { - return mcp.NewTool("list_files", - mcp.WithDescription("List files in a directory in the BoxLite sandbox."), - mcp.WithString("path", mcp.Description("Path to the directory to list files from (defaults to current directory).")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to list the files from.")), - ) -} - -func ListFiles(ctx context.Context, request mcp.CallToolRequest, args ListFilesArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - // Get directory path from request arguments (optional) - dirPath := "." - if args.Path != nil && *args.Path != "" { - dirPath = *args.Path - } - - // List files - files, _, err := apiClient.ToolboxAPI.ListFilesDeprecated(ctx, *args.Id).Path(dirPath).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error listing files: %v", err) - } - - // Convert files to JSON - filesJSON, err := json.MarshalIndent(files, "", " ") - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error marshaling files: %v", err) - } - - log.Infof("Listed files in directory: %s", dirPath) - - return mcp.NewToolResultText(string(filesJSON)), nil -} diff --git a/apps/cli/mcp/tools/move_file.go b/apps/cli/mcp/tools/move_file.go deleted file mode 100644 index 46a3bcfe0..000000000 --- a/apps/cli/mcp/tools/move_file.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type MoveFileArgs struct { - Id *string `json:"id,omitempty"` - SourcePath *string `json:"sourcePath,omitempty"` - DestPath *string `json:"destPath,omitempty"` -} - -func GetMoveFileTool() mcp.Tool { - return mcp.NewTool("move_file", - mcp.WithDescription("Move or rename a file in the BoxLite sandbox."), - mcp.WithString("sourcePath", mcp.Required(), mcp.Description("Source path of the file to move.")), - mcp.WithString("destPath", mcp.Required(), mcp.Description("Destination path where to move the file.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to move the file in.")), - ) -} - -func MoveFile(ctx context.Context, request mcp.CallToolRequest, args MoveFileArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return &mcp.CallToolResult{IsError: true}, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - // Get source and destination paths from request arguments - if args.SourcePath == nil || *args.SourcePath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sourcePath parameter is required") - } - - if args.DestPath == nil || *args.DestPath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("destPath parameter is required") - } - - _, err = apiClient.ToolboxAPI.MoveFileDeprecated(ctx, *args.Id).Source(*args.SourcePath).Destination(*args.DestPath).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error moving file: %v", err) - } - - log.Infof("Moved file from %s to %s", *args.SourcePath, *args.DestPath) - - return mcp.NewToolResultText(fmt.Sprintf("Moved file from %s to %s", *args.SourcePath, *args.DestPath)), nil -} diff --git a/apps/cli/mcp/tools/preview_link.go b/apps/cli/mcp/tools/preview_link.go deleted file mode 100644 index 3fec3a2d2..000000000 --- a/apps/cli/mcp/tools/preview_link.go +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "fmt" - "strconv" - "strings" - - apiclient_cli "github.com/boxlite-ai/boxlite/cli/apiclient" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type PreviewLinkArgs struct { - Id *string `json:"id,omitempty"` - Port *int32 `json:"port,omitempty"` - CheckServer *bool `json:"checkServer,omitempty"` - Description *string `json:"description,omitempty"` -} - -func GetPreviewLinkTool() mcp.Tool { - return mcp.NewTool("preview_link", - mcp.WithDescription("Generate accessible preview URLs for web applications running in the BoxLite sandbox. Creates a secure tunnel to expose local ports externally without configuration. Validates if a server is actually running on the specified port and provides diagnostic information for troubleshooting. Supports custom descriptions and metadata for better organization of multiple services."), - mcp.WithNumber("port", mcp.Required(), mcp.Description("Port to expose.")), - mcp.WithString("description", mcp.Required(), mcp.Description("Description of the service.")), - mcp.WithBoolean("checkServer", mcp.Required(), mcp.Description("Check if a server is running on the specified port.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to generate the preview link for.")), - ) -} - -func PreviewLink(ctx context.Context, request mcp.CallToolRequest, args PreviewLinkArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient_cli.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return nil, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.Port == nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("port parameter is required") - } - - checkServer := false - if args.CheckServer != nil && *args.CheckServer { - checkServer = *args.CheckServer - } - - log.Infof("Generating preview link - port: %d", *args.Port) - - // Get the sandbox using sandbox ID - sandbox, _, err := apiClient.SandboxAPI.GetSandbox(ctx, *args.Id).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to get sandbox: %v", err) - } - - if sandbox == nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("no sandbox available") - } - - // Check if server is running on specified port - if checkServer { - log.Infof("Checking if server is running - port: %d", *args.Port) - - checkCmd := fmt.Sprintf("curl -s -o /dev/null -w '%%{http_code}' http://localhost:%d --max-time 2 || echo 'error'", *args.Port) - result, _, err := apiClient.ToolboxAPI.ExecuteCommandDeprecated(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(checkCmd)).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error checking server: %v", err) - } - - response := strings.TrimSpace(result.Result) - if response == "error" || strings.HasPrefix(response, "0") { - log.Infof("No server detected - port: %d", *args.Port) - - // Check what might be using the port - psCmd := fmt.Sprintf("ps aux | grep ':%d' | grep -v grep || echo 'No process found'", *args.Port) - psResult, _, err := apiClient.ToolboxAPI.ExecuteCommandDeprecated(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(psCmd)).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error checking processes: %v", err) - } - - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("no server detected on port %d. Process info: %s", *args.Port, strings.TrimSpace(psResult.Result)) - } - } - - // Fetch preview URL - previewURL, _, err := apiClient.SandboxAPI.GetPortPreviewUrl(ctx, *args.Id, float32(*args.Port)).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to get preview URL: %v", err) - } - - // Test URL accessibility if requested - var accessible bool - var statusCode string - if checkServer { - checkCmd := fmt.Sprintf("curl -s -o /dev/null -w '%%{http_code}' %s --max-time 3 || echo 'error'", previewURL.Url) - result, _, err := apiClient.ToolboxAPI.ExecuteCommandDeprecated(ctx, *args.Id).ExecuteRequest(*apiclient.NewExecuteRequest(checkCmd)).Execute() - if err != nil { - log.Errorf("Error checking preview URL: %v", err) - } else { - response := strings.TrimSpace(result.Result) - accessible = response != "error" && !strings.HasPrefix(response, "0") - if _, err := strconv.Atoi(response); err == nil { - statusCode = response - } - } - } - - log.Infof("Preview link generated: %s", previewURL.Url) - log.Infof("Accessible: %t", accessible) - log.Infof("Status code: %s", statusCode) - - return mcp.NewToolResultText(fmt.Sprintf("Preview link generated: %s", previewURL.Url)), nil -} diff --git a/apps/cli/mcp/tools/upload_file.go b/apps/cli/mcp/tools/upload_file.go deleted file mode 100644 index b6732e7d7..000000000 --- a/apps/cli/mcp/tools/upload_file.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package tools - -import ( - "context" - "encoding/base64" - "fmt" - "os" - "path/filepath" - - "github.com/boxlite-ai/boxlite/cli/apiclient" - "github.com/mark3labs/mcp-go/mcp" - - log "github.com/sirupsen/logrus" -) - -type FileUploadArgs struct { - Id *string `json:"id,omitempty"` - FilePath *string `json:"filePath,omitempty"` - Content *string `json:"content,omitempty"` - Encoding *string `json:"encoding,omitempty"` - Overwrite *bool `json:"overwrite,omitempty"` -} - -func GetFileUploadTool() mcp.Tool { - return mcp.NewTool("file_upload", - mcp.WithDescription("Upload files to the BoxLite sandbox from text or base64-encoded binary content. Creates necessary parent directories automatically and verifies successful writes. Files persist during the session and have appropriate permissions for further tool operations. Supports overwrite controls and maintains original file formats."), - mcp.WithString("filePath", mcp.Required(), mcp.Description("Path to the file to upload. Files should always be uploaded to the /tmp directory if user doesn't specify otherwise.")), - mcp.WithString("content", mcp.Required(), mcp.Description("Content of the file to upload.")), - mcp.WithString("encoding", mcp.Required(), mcp.Description("Encoding of the file to upload.")), - mcp.WithBoolean("overwrite", mcp.Required(), mcp.Description("Overwrite the file if it already exists.")), - mcp.WithString("id", mcp.Required(), mcp.Description("ID of the sandbox to upload the file to.")), - ) -} - -func FileUpload(ctx context.Context, request mcp.CallToolRequest, args FileUploadArgs) (*mcp.CallToolResult, error) { - apiClient, err := apiclient.GetApiClient(nil, boxliteMCPHeaders) - if err != nil { - return nil, err - } - - if args.Id == nil || *args.Id == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("sandbox ID is required") - } - - if args.FilePath == nil || *args.FilePath == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("filePath parameter is required") - } - - if args.Content == nil || *args.Content == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("content parameter is required") - } - - if args.Encoding == nil || *args.Encoding == "" { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("encoding parameter is required") - } - - overwrite := false - if args.Overwrite != nil && *args.Overwrite { - overwrite = *args.Overwrite - } - - // Get the sandbox using sandbox ID - sandbox, _, err := apiClient.SandboxAPI.GetSandbox(ctx, *args.Id).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("failed to get sandbox: %v", err) - } - - if sandbox == nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("no sandbox available") - } - - // Check if file exists and handle overwrite - if !overwrite { - fileInfo, _, err := apiClient.ToolboxAPI.GetFileInfoDeprecated(ctx, *args.Id).Path(*args.FilePath).Execute() - if err == nil && fileInfo != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("file '%s' already exists and overwrite=false", *args.FilePath) - } - } - - // Prepare content based on encoding - var binaryContent []byte - if *args.Encoding == "base64" { - var err error - binaryContent, err = base64.StdEncoding.DecodeString(*args.Content) - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("invalid base64 encoding: %v", err) - } - } else { - // Default is text encoding - binaryContent = []byte(*args.Content) - } - - // Create parent directories if they don't exist - parentDir := filepath.Dir(*args.FilePath) - if parentDir != "" { - _, err := apiClient.ToolboxAPI.CreateFolderDeprecated(ctx, *args.Id).Path(parentDir).Mode("0755").Execute() - if err != nil { - log.Errorf("Error creating parent directory: %v", err) - // Continue anyway as upload might handle this - } - } - - // Upload the file - tempFile, err := os.CreateTemp("", "upload-*") - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error creating temp file: %v", err) - } - defer os.Remove(tempFile.Name()) // Clean up temp file when done - defer tempFile.Close() - - // Write content to temp file - if _, err := tempFile.Write(binaryContent); err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error writing to temp file: %v", err) - } - - // Reset file pointer to beginning - if _, err := tempFile.Seek(0, 0); err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error seeking temp file: %v", err) - } - - // Upload the file - _, err = apiClient.ToolboxAPI.UploadFileDeprecated(ctx, *args.Id).Path(*args.FilePath).File(tempFile).Execute() - if err != nil { - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error uploading file: %v", err) - } - - // Get file info for size - fileInfo, _, err := apiClient.ToolboxAPI.GetFileInfoDeprecated(ctx, *args.Id).Path(*args.FilePath).Execute() - if err != nil { - log.Errorf("Error getting file info after upload: %v", err) - - return &mcp.CallToolResult{IsError: true}, fmt.Errorf("error getting file info after upload: %v", err) - } - - fileSizeKB := float64(fileInfo.Size) / 1024 - log.Infof("File uploaded successfully: %s, size: %.2fKB", *args.FilePath, fileSizeKB) - - return mcp.NewToolResultText(fmt.Sprintf("File uploaded successfully: %s", *args.FilePath)), nil -} diff --git a/apps/cli/pkg/minio/minio.go b/apps/cli/pkg/minio/minio.go deleted file mode 100644 index 0a990e3cf..000000000 --- a/apps/cli/pkg/minio/minio.go +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package minio - -import ( - "archive/tar" - "bytes" - "context" - "crypto/sha256" - "encoding/hex" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" -) - -const CONTEXT_TAR_FILE_NAME = "context.tar" - -type Client struct { - minioClient *minio.Client - bucket string -} - -func NewClient(endpoint, accessKey, secretKey, bucket string, useSSL bool, sessionToken string) (*Client, error) { - minioClient, err := minio.New(endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(accessKey, secretKey, sessionToken), - Secure: useSSL, - }) - if err != nil { - return nil, fmt.Errorf("failed to create MinIO client: %w", err) - } - - return &Client{ - minioClient: minioClient, - bucket: bucket, - }, nil -} - -func (c *Client) UploadFile(ctx context.Context, objectName string, data []byte) error { - exists, err := c.minioClient.BucketExists(ctx, c.bucket) - if err != nil { - return fmt.Errorf("error checking bucket existence: %w", err) - } - if !exists { - err = c.minioClient.MakeBucket(ctx, c.bucket, minio.MakeBucketOptions{}) - if err != nil { - return fmt.Errorf("error creating bucket: %w", err) - } - } - - reader := bytes.NewReader(data) - objectSize := int64(len(data)) - - _, err = c.minioClient.PutObject(ctx, c.bucket, objectName, reader, objectSize, minio.PutObjectOptions{ - ContentType: "application/octet-stream", - }) - if err != nil { - return fmt.Errorf("error uploading file: %w", err) - } - - return nil -} - -func readIgnoreFile(rootPath, filename string) []string { - ignoreFile := filepath.Join(rootPath, filename) - content, err := os.ReadFile(ignoreFile) - if err != nil { - return nil - } - - var patterns []string - lines := strings.Split(string(content), "\n") - for _, line := range lines { - line = strings.TrimSpace(line) - // Skip empty lines and comments - if line == "" || strings.HasPrefix(line, "#") { - continue - } - patterns = append(patterns, line) - } - return patterns -} - -func matchPattern(filePath string, patterns []string) bool { - filePath = filepath.ToSlash(filePath) - - for _, pattern := range patterns { - pattern = filepath.ToSlash(pattern) - - if strings.HasSuffix(pattern, "/") { - pattern = strings.TrimSuffix(pattern, "/") - if strings.HasPrefix(filePath, pattern+"/") || filePath == pattern { - return true - } - continue - } - - // Handle double star patterns (**) - matches any number of directories - if strings.Contains(pattern, "**") { - // Convert ** to a simpler pattern for basic matching - parts := strings.Split(pattern, "**") - if len(parts) == 2 { - prefix := parts[0] - suffix := parts[1] - - // Remove trailing/leading slashes from prefix/suffix - prefix = strings.TrimSuffix(prefix, "/") - suffix = strings.TrimPrefix(suffix, "/") - - if prefix == "" && suffix != "" { - // Pattern like **/node_modules - if strings.Contains(filePath, "/"+suffix) || strings.HasSuffix(filePath, suffix) || filePath == suffix { - return true - } - } else if prefix != "" && suffix == "" { - // Pattern like .git/** - if strings.HasPrefix(filePath, prefix+"/") || filePath == prefix { - return true - } - } else if prefix != "" && suffix != "" { - // Pattern like src/**/test - if strings.HasPrefix(filePath, prefix+"/") && (strings.Contains(filePath, "/"+suffix) || strings.HasSuffix(filePath, suffix)) { - return true - } - } - } - continue - } - - if strings.Contains(pattern, "*") { - matched, err := filepath.Match(pattern, filepath.Base(filePath)) - if err == nil && matched { - return true - } - // Also check full path for patterns like */node_modules - matched, err = filepath.Match(pattern, filePath) - if err == nil && matched { - return true - } - continue - } - - // Handle exact matches and prefix matches - if filePath == pattern || - strings.HasPrefix(filePath, pattern+"/") || - filepath.Base(filePath) == pattern { - return true - } - } - return false -} - -func shouldExcludeFile(filePath, rootPath string) bool { - relPath, err := filepath.Rel(rootPath, filePath) - if err != nil { - return false - } - - dockerignorePatterns := readIgnoreFile(rootPath, ".dockerignore") - - if len(dockerignorePatterns) == 0 { - return false - } - - return matchPattern(relPath, dockerignorePatterns) -} - -func (c *Client) ListObjects(ctx context.Context, prefix string) ([]string, error) { - var objects []string - - objectCh := c.minioClient.ListObjects(ctx, c.bucket, minio.ListObjectsOptions{ - Prefix: prefix, - }) - - for object := range objectCh { - if object.Err != nil { - return nil, object.Err - } - objects = append(objects, object.Key) - } - - return objects, nil -} - -func (c *Client) ProcessDirectory(ctx context.Context, dirPath, orgID string, existingObjects map[string]bool) ([]string, error) { - // Check if .dockerignore exists and provide helpful message if context is large - dockerignoreExists := false - if _, err := os.Stat(filepath.Join(dirPath, ".dockerignore")); err == nil { - dockerignoreExists = true - } - - tarFile, err := os.Create(CONTEXT_TAR_FILE_NAME) - if err != nil { - return nil, fmt.Errorf("failed to create tar file: %w", err) - } - defer tarFile.Close() - - tw := tar.NewWriter(tarFile) - defer tw.Close() - - fileCount := 0 - totalSize := int64(0) - - warned := false - err = filepath.Walk(dirPath, func(file string, fi os.FileInfo, err error) error { - if err != nil { - return err - } - - if fi.Name() == CONTEXT_TAR_FILE_NAME { - return nil - } - - if shouldExcludeFile(file, dirPath) { - relPath, _ := filepath.Rel(dirPath, file) - if fi.IsDir() { - fmt.Printf("Excluding directory: %s\n", relPath) - return filepath.SkipDir - } - fmt.Printf("Excluding file: %s\n", relPath) - return nil - } - - header, err := tar.FileInfoHeader(fi, fi.Name()) - if err != nil { - return err - } - - relPath, err := filepath.Rel(dirPath, file) - if err != nil { - return err - } - header.Name = relPath - - if err := tw.WriteHeader(header); err != nil { - return err - } - - // Write file contents if regular file - if fi.Mode().IsRegular() { - fileCount++ - totalSize += fi.Size() - - if fileCount%1000 == 0 { - fmt.Printf("Processing... %d files, %.2f MB total\n", fileCount, float64(totalSize)/(1024*1024)) - } - - // Warn if context is getting very large (only warn once) - if totalSize > 100*1024*1024 && !dockerignoreExists && !warned { - fmt.Printf("Warning: Context size exceeds 100MB. Consider adding a .dockerignore file to exclude unnecessary files.\n") - warned = true - } - - f, err := os.Open(file) - if err != nil { - return err - } - defer f.Close() - - _, err = io.Copy(tw, f) - return err - } - return nil - }) - - if err != nil { - if strings.Contains(err.Error(), "write too long") { - return nil, fmt.Errorf("context directory is too large for tar archive. Please create a .dockerignore file to exclude large directories like .git, node_modules, dist, etc. Original error: %w", err) - } - return nil, fmt.Errorf("failed to process directory: %w", err) - } - - fmt.Printf("Context processing complete: %d files, %.2f MB total\n", fileCount, float64(totalSize)/(1024*1024)) - - // Seek to start of tar file before calculating hash - if _, err := tarFile.Seek(0, 0); err != nil { - return nil, fmt.Errorf("failed to seek tar file: %w", err) - } - - // Calculate hash of tar contents - hasher := sha256.New() - if _, err := io.Copy(hasher, tarFile); err != nil { - return nil, fmt.Errorf("failed to hash tar: %w", err) - } - hash := hex.EncodeToString(hasher.Sum(nil)) - - objectName := fmt.Sprintf("%s/%s", orgID, hash) - if _, exists := existingObjects[objectName]; !exists { - err = c.CreateDirectory(ctx, objectName) - if err != nil { - return nil, err - } - - if _, err := tarFile.Seek(0, 0); err != nil { - return nil, fmt.Errorf("failed to seek tar file: %w", err) - } - - tarContent, err := io.ReadAll(tarFile) - if err != nil { - return nil, fmt.Errorf("failed to read tar file: %w", err) - } - - err = c.UploadFile(ctx, fmt.Sprintf("%s/%s", objectName, CONTEXT_TAR_FILE_NAME), tarContent) - if err != nil { - return nil, fmt.Errorf("failed to upload tar: %w", err) - } - - if err := os.Remove(CONTEXT_TAR_FILE_NAME); err != nil { - return nil, fmt.Errorf("failed to remove tar file: %w", err) - } - } else { - fmt.Printf("Directory %s with hash %s already exists in storage\n", dirPath, hash) - } - - return []string{hash}, nil -} - -func (c *Client) ProcessFile(ctx context.Context, filePath, orgID string, existingObjects map[string]bool) (string, error) { - fileContent, err := os.ReadFile(filePath) - if err != nil { - return "", fmt.Errorf("failed to read file: %w", err) - } - - hasher := sha256.New() - hasher.Write(fileContent) - hash := hex.EncodeToString(hasher.Sum(nil)) - - objectName := fmt.Sprintf("%s/%s", orgID, hash) - if _, exists := existingObjects[objectName]; !exists { - var tarBuffer bytes.Buffer - tarWriter := tar.NewWriter(&tarBuffer) - - fileName := filepath.Base(filePath) - - fileInfo, err := os.Stat(filePath) - if err != nil { - return "", fmt.Errorf("failed to stat file: %w", err) - } - - header, err := tar.FileInfoHeader(fileInfo, "") - if err != nil { - return "", fmt.Errorf("failed to create tar header: %w", err) - } - header.Name = fileName - - if err := tarWriter.WriteHeader(header); err != nil { - return "", fmt.Errorf("failed to write tar header: %w", err) - } - - if _, err := tarWriter.Write(fileContent); err != nil { - return "", fmt.Errorf("failed to write file to tar: %w", err) - } - - if err := tarWriter.Close(); err != nil { - return "", fmt.Errorf("failed to close tar writer: %w", err) - } - - err = c.CreateDirectory(ctx, objectName) - if err != nil { - return "", err - } - - // Upload tar file instead of raw content - err = c.UploadFile(ctx, fmt.Sprintf("%s/%s", objectName, CONTEXT_TAR_FILE_NAME), tarBuffer.Bytes()) - if err != nil { - return "", err - } - } else { - fmt.Printf("File %s with hash %s already exists in storage - skipping\n", filePath, hash) - } - - return hash, nil -} - -func (c *Client) CreateDirectory(ctx context.Context, directoryPath string) error { - exists, err := c.minioClient.BucketExists(ctx, c.bucket) - if err != nil { - return fmt.Errorf("error checking bucket existence: %w", err) - } - if !exists { - return fmt.Errorf("bucket does not exist") - } - - // Ensure the directory path ends with a slash to represent a directory - if !strings.HasSuffix(directoryPath, "/") { - directoryPath = directoryPath + "/" - } - - // Create an empty object to represent the directory - emptyContent := []byte{} - reader := bytes.NewReader(emptyContent) - _, err = c.minioClient.PutObject(ctx, c.bucket, directoryPath, reader, 0, minio.PutObjectOptions{ - ContentType: "application/directory", - }) - if err != nil { - return fmt.Errorf("error creating directory marker: %w", err) - } - - return nil -} diff --git a/apps/cli/project.json b/apps/cli/project.json deleted file mode 100644 index 36c384431..000000000 --- a/apps/cli/project.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "cli", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "projectType": "application", - "sourceRoot": "apps/cli", - "tags": [], - "targets": { - "build": { - "executor": "@nx-go/nx-go:build", - "inputs": ["goProduction"], - "options": { - "main": "{projectRoot}/main.go", - "outputPath": "dist/apps/cli/cli" - } - }, - "format": { - "executor": "nx:run-commands", - "options": { - "command": "cd {projectRoot} && go fmt ./... && prettier --write \"**/*.{yaml,html}\"" - } - }, - "test": { - "executor": "@nx-go/nx-go:test" - }, - "lint": { - "executor": "@nx-go/nx-go:lint" - } - } -} diff --git a/apps/cli/toolbox/toolbox.go b/apps/cli/toolbox/toolbox.go deleted file mode 100644 index 166f4612b..000000000 --- a/apps/cli/toolbox/toolbox.go +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright BoxLite AI (originally Daytona Platforms Inc. -// SPDX-License-Identifier: AGPL-3.0 - -package toolbox - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "strings" - - "github.com/boxlite-ai/boxlite/cli/config" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -type ExecuteRequest struct { - Command string `json:"command"` - Cwd *string `json:"cwd,omitempty"` - Timeout *float32 `json:"timeout,omitempty"` -} - -type ExecuteResponse struct { - ExitCode float32 `json:"exitCode"` - Result string `json:"result"` -} - -type Client struct { - apiClient *apiclient.APIClient -} - -func NewClient(apiClient *apiclient.APIClient) *Client { - return &Client{ - apiClient: apiClient, - } -} - -// Gets the toolbox proxy URL for a sandbox, caching by region in config -func (c *Client) getProxyURL(ctx context.Context, sandboxId, region string) (string, error) { - // Check config cache first - cachedURL, err := config.GetToolboxProxyUrl(region) - if err == nil && cachedURL != "" { - return cachedURL, nil - } - - // Fetch from API - toolboxProxyUrl, _, err := c.apiClient.SandboxAPI.GetToolboxProxyUrl(ctx, sandboxId).Execute() - if err != nil { - return "", fmt.Errorf("failed to get toolbox proxy URL: %w", err) - } - - // Best-effort caching - _ = config.SetToolboxProxyUrl(region, toolboxProxyUrl.Url) - - return toolboxProxyUrl.Url, nil -} - -func (c *Client) ExecuteCommand(ctx context.Context, sandbox *apiclient.Sandbox, request ExecuteRequest) (*ExecuteResponse, error) { - proxyURL, err := c.getProxyURL(ctx, sandbox.Id, sandbox.Target) - if err != nil { - return nil, err - } - - return c.executeCommandViaProxy(ctx, proxyURL, sandbox.Id, request) -} - -// TODO: replace this with the toolbox api client at some point -func (c *Client) executeCommandViaProxy(ctx context.Context, proxyURL, sandboxId string, request ExecuteRequest) (*ExecuteResponse, error) { - // Build the URL: {proxyUrl}/{sandboxId}/process/execute - url := fmt.Sprintf("%s/%s/process/execute", strings.TrimSuffix(proxyURL, "/"), sandboxId) - - body, err := json.Marshal(request) - if err != nil { - return nil, fmt.Errorf("failed to marshal request: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body)) - if err != nil { - return nil, fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Content-Type", "application/json") - - cfg, err := config.GetConfig() - if err != nil { - return nil, err - } - - activeProfile, err := cfg.GetActiveProfile() - if err != nil { - return nil, err - } - - if activeProfile.Api.Key != nil { - req.Header.Set("Authorization", "Bearer "+*activeProfile.Api.Key) - } else if activeProfile.Api.Token != nil { - req.Header.Set("Authorization", "Bearer "+activeProfile.Api.Token.AccessToken) - } - - if activeProfile.ActiveOrganizationId != nil { - req.Header.Set("X-BoxLite-Organization-ID", *activeProfile.ActiveOrganizationId) - } - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to execute request: %w", err) - } - defer resp.Body.Close() - - respBody, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read response: %w", err) - } - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody)) - } - - var response ExecuteResponse - if err := json.Unmarshal(respBody, &response); err != nil { - return nil, fmt.Errorf("failed to parse response: %w", err) - } - - return &response, nil -} diff --git a/apps/cli/util/pointer.go b/apps/cli/util/pointer.go deleted file mode 100644 index b11a7da58..000000000 --- a/apps/cli/util/pointer.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -// Use generics to create a pointer to a value -func Pointer[T any](d T) *T { - return &d -} diff --git a/apps/cli/views/common/common.go b/apps/cli/views/common/common.go deleted file mode 100644 index 4f2c2672e..000000000 --- a/apps/cli/views/common/common.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - "strings" - - "github.com/charmbracelet/huh" - "github.com/charmbracelet/lipgloss" -) - -var DefaultLayoutMarginTop = 1 - -var DefaultHorizontalMargin = 1 - -var TUITableMinimumWidth = 80 - -var SeparatorString = lipgloss.NewStyle().Foreground(LightGray).Render("===") - -var Checkmark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓").String() - -var ( - minimumWidth = 40 - maximumWidth = 160 - widthBreakpoints = []int{60, 80, 100, 120, 140, 160} -) - -func RenderMainTitle(title string) { - fmt.Println(lipgloss.NewStyle().Foreground(Green).Bold(true).Padding(1, 0, 1, 0).Render(title)) -} - -func RenderTip(message string) { - fmt.Println(lipgloss.NewStyle().Padding(0, 0, 1, 1).Render(message)) -} - -func RenderInfoMessage(message string) { - fmt.Println(lipgloss.NewStyle().PaddingLeft(1).Render(message)) -} - -func RenderInfoMessageBold(message string) { - fmt.Println(lipgloss.NewStyle().Bold(true).Padding(1, 0, 1, 1).Render(message)) -} - -func GetStyledMainTitle(content string) string { - return lipgloss.NewStyle().Foreground(Dark).Background(Light).Padding(0, 1).MarginTop(1).Render(content) -} - -func GetInfoMessage(message string) string { - return lipgloss.NewStyle().Padding(1, 0, 1, 1).Render(message) -} - -func GetContainerBreakpointWidth(terminalWidth int) int { - if terminalWidth < minimumWidth { - return 0 - } - for _, width := range widthBreakpoints { - if terminalWidth < width { - return width - 20 - DefaultHorizontalMargin - DefaultHorizontalMargin - } - } - return maximumWidth -} - -func GetEnvVarsInput(envVars *map[string]string) *huh.Text { - if envVars == nil { - return nil - } - - var inputText string - for key, value := range *envVars { - inputText += fmt.Sprintf("%s=%s\n", key, value) - } - inputText = strings.TrimSuffix(inputText, "\n") - - return huh.NewText(). - Title("Environment Variables"). - Description("Enter environment variables in the format KEY=VALUE\nTo pass machine env variables at runtime, use $VALUE"). - CharLimit(-1). - Value(&inputText). - Validate(func(str string) error { - tempEnvVars := map[string]string{} - for i, line := range strings.Split(str, "\n") { - if line == "" { - continue - } - - parts := strings.SplitN(line, "=", 2) - if len(parts) != 2 { - return fmt.Errorf("invalid format: %s on line %d", line, i+1) - } - - tempEnvVars[parts[0]] = parts[1] - } - *envVars = tempEnvVars - - return nil - }) -} - -// Bolds the message and prepends a checkmark -func GetPrettyLogLine(message string) string { - return fmt.Sprintf("%s \033[1m%s\033[0m\n", lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓").String(), message) -} diff --git a/apps/cli/views/common/prompt.go b/apps/cli/views/common/prompt.go deleted file mode 100644 index 3b52ca267..000000000 --- a/apps/cli/views/common/prompt.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - "strings" - - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -type promptModel struct { - textInput textinput.Model - err error - done bool - title string - desc string -} - -func (m promptModel) Init() tea.Cmd { - return textinput.Blink -} - -func (m promptModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.Type { - case tea.KeyEnter: - m.done = true - return m, tea.Quit - case tea.KeyCtrlC: - m.done = true - m.err = fmt.Errorf("user cancelled") - return m, tea.Quit - } - } - - m.textInput, cmd = m.textInput.Update(msg) - return m, cmd -} - -func (m promptModel) View() string { - if m.done { - return "" - } - - titleStyle := lipgloss.NewStyle(). - Bold(true). - MarginLeft(2). - MarginTop(1) - - descStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("241")). - MarginLeft(2) - - return fmt.Sprintf("\n%s\n%s\n\n %s\n\n", - titleStyle.Render(m.title), - descStyle.Render(m.desc), - m.textInput.View()) -} - -func PromptForInput(prompt, title, desc string) (string, error) { - ti := textinput.New() - ti.Focus() - ti.CharLimit = 156 - ti.Width = 80 - ti.Prompt = "› " - - m := promptModel{ - textInput: ti, - title: title, - desc: desc, - } - - p := tea.NewProgram(m, tea.WithAltScreen()) - model, err := p.Run() - if err != nil { - return "", fmt.Errorf("error running prompt: %w", err) - } - - finalModel, ok := model.(promptModel) - if !ok { - return "", fmt.Errorf("could not read model state") - } - - if finalModel.err != nil { - return "", finalModel.err - } - - return strings.TrimSpace(finalModel.textInput.Value()), nil -} diff --git a/apps/cli/views/common/select.go b/apps/cli/views/common/select.go deleted file mode 100644 index 67d26783e..000000000 --- a/apps/cli/views/common/select.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// SelectItem represents an item in the selection list -type SelectItem struct { - Title string - Desc string -} - -// SelectModel represents the selection UI model -type SelectModel struct { - Title string - Items []SelectItem - Selected int - Choice string - Quitting bool -} - -// NewSelectModel creates a new select model with the given title and items -func NewSelectModel(title string, items []SelectItem) SelectModel { - return SelectModel{ - Title: title, - Items: items, - Selected: 0, - } -} - -func (m SelectModel) Init() tea.Cmd { - return nil -} - -func (m SelectModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.KeyMsg: - switch msg.String() { - case "ctrl+c": - m.Quitting = true - return m, tea.Quit - case "up", "k": - if m.Selected > 0 { - m.Selected-- - } - case "down", "j": - if m.Selected < len(m.Items)-1 { - m.Selected++ - } - case "enter": - m.Choice = m.Items[m.Selected].Title - return m, tea.Quit - } - } - return m, nil -} - -func (m SelectModel) View() string { - if m.Quitting { - return "" - } - - s := lipgloss.NewStyle(). - Bold(true). - MarginLeft(2). - MarginTop(1). - Render(m.Title) + "\n\n" - - for i, item := range m.Items { - cursor := " " - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color("151")). - PaddingLeft(2) - - if i == m.Selected { - cursor = "› " - style = style.Foreground(lipgloss.Color("42")).Bold(true) - } - - s += style.Render(cursor+item.Title) + "\n" - s += lipgloss.NewStyle(). - PaddingLeft(4). - Foreground(lipgloss.Color("241")). - Render(item.Desc) + "\n\n" - } - - return s -} - -// Select displays a selection prompt with the given title and items -// Returns the selected item's title and any error that occurred -func Select(title string, items []SelectItem) (string, error) { - p := tea.NewProgram(NewSelectModel(title, items), tea.WithAltScreen()) - m, err := p.Run() - if err != nil { - return "", err - } - - finalModel, ok := m.(SelectModel) - if !ok { - return "", nil - } - - if finalModel.Quitting { - return "", nil - } - - return finalModel.Choice, nil -} diff --git a/apps/cli/views/common/styles.go b/apps/cli/views/common/styles.go deleted file mode 100644 index 70f52090d..000000000 --- a/apps/cli/views/common/styles.go +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package common - -import ( - "fmt" - "os" - - "github.com/charmbracelet/bubbles/list" - "github.com/charmbracelet/huh" - "github.com/charmbracelet/lipgloss" -) - -var ( - ListNavigationText = "load more" - ListNavigationRenderText = "+ Load more.." -) - -var ( - Green = lipgloss.AdaptiveColor{Light: "#23cc71", Dark: "#23cc71"} - Blue = lipgloss.AdaptiveColor{Light: "#017ffe", Dark: "#017ffe"} - Yellow = lipgloss.AdaptiveColor{Light: "#d4ed2d", Dark: "#d4ed2d"} - Cyan = lipgloss.AdaptiveColor{Light: "#3ef7e5", Dark: "#3ef7e5"} - DimmedGreen = lipgloss.AdaptiveColor{Light: "#7be0a9", Dark: "#7be0a9"} - Orange = lipgloss.AdaptiveColor{Light: "#e3881b", Dark: "#e3881b"} - Light = lipgloss.AdaptiveColor{Light: "#000", Dark: "#fff"} - Dark = lipgloss.AdaptiveColor{Light: "#fff", Dark: "#000"} - Gray = lipgloss.AdaptiveColor{Light: "243", Dark: "243"} - LightGray = lipgloss.AdaptiveColor{Light: "#828282", Dark: "#828282"} - Red = lipgloss.AdaptiveColor{Light: "#FF4672", Dark: "#ED567A"} -) - -var ( - ColorPending = lipgloss.AdaptiveColor{Light: "#cce046", Dark: "#cce046"} - ColorSuccess = lipgloss.AdaptiveColor{Light: "#2ecc71", Dark: "#2ecc71"} - ColorStarting = ColorSuccess - ColorStopped = lipgloss.AdaptiveColor{Light: "#a2a2a2", Dark: "#a2a2a2"} - ColorStopping = ColorStopped - ColorError = lipgloss.AdaptiveColor{Light: "#e74c3c", Dark: "#e74c3c"} - ColorDeleting = ColorStopped - ColorDeleted = ColorStopped - ColorUnresponsive = ColorError -) - -var ( - BaseTableStyleHorizontalPadding = 4 - BaseTableStyle = lipgloss.NewStyle(). - PaddingLeft(BaseTableStyleHorizontalPadding). - PaddingRight(BaseTableStyleHorizontalPadding). - PaddingTop(1). - Margin(1, 0) - - NameStyle = lipgloss.NewStyle().Foreground(Light) - LinkStyle = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("12")) - ActiveStyle = lipgloss.NewStyle().Foreground(Green) - InactiveStyle = lipgloss.NewStyle().Foreground(Orange) - DefaultRowDataStyle = lipgloss.NewStyle().Foreground(Gray) - BaseCellStyle = lipgloss.NewRenderer(os.Stdout).NewStyle().Padding(0, 4, 1, 0) - TableHeaderStyle = BaseCellStyle.Foreground(LightGray).Bold(false).Padding(0).MarginRight(4) -) - -var ( - UndefinedStyle = lipgloss.NewStyle().Foreground(ColorPending) - PendingStyle = lipgloss.NewStyle().Foreground(ColorPending) - RunningStyle = lipgloss.NewStyle().Foreground(ColorPending) - RunSuccessfulStyle = lipgloss.NewStyle().Foreground(ColorSuccess) - CreatingStyle = lipgloss.NewStyle().Foreground(ColorPending) - StartedStyle = lipgloss.NewStyle().Foreground(ColorSuccess) - StartingStyle = lipgloss.NewStyle().Foreground(ColorStarting) - StoppedStyle = lipgloss.NewStyle().Foreground(ColorStopped) - StoppingStyle = lipgloss.NewStyle().Foreground(ColorStopping) - ErrorStyle = lipgloss.NewStyle().Foreground(ColorError) - DeletingStyle = lipgloss.NewStyle().Foreground(ColorDeleting) - DeletedStyle = lipgloss.NewStyle().Foreground(ColorDeleted) - UnresponsiveStyle = lipgloss.NewStyle().Foreground(ColorUnresponsive) -) - -var LogPrefixColors = []lipgloss.AdaptiveColor{ - Blue, Orange, Cyan, Yellow, -} - -type SelectionListOptions struct { - ParentIdentifier string - IsPaginationDisabled bool - CursorIndex int -} - -func GetStyledSelectList(items []list.Item, listOptions ...SelectionListOptions) list.Model { - - d := list.NewDefaultDelegate() - - d.Styles.SelectedTitle = lipgloss.NewStyle(). - Border(lipgloss.NormalBorder(), false, false, false, true). - BorderForeground(Green). - Foreground(Green). - Bold(true). - Padding(0, 0, 0, 1) - - d.Styles.SelectedDesc = d.Styles.SelectedTitle.Foreground(DimmedGreen).Bold(false) - - l := list.New(items, d, 0, 0) - - if listOptions != nil { - // Sets the mouse cursor to point to the first index of newly loaded items - if listOptions[0].CursorIndex > 0 { - l.Select(listOptions[0].CursorIndex) - } - - if !listOptions[0].IsPaginationDisabled { - // Add the 'Load More' option in search filter results - l.Filter = func(term string, targets []string) []list.Rank { - ranks := list.DefaultFilter(term, targets) - - loadMoreIdx := -1 - // Ideally 'Load More' option if present should be found at the last index - for i := len(targets) - 1; i >= 0; i-- { - if targets[i] == ListNavigationRenderText { - loadMoreIdx = i - break - } - } - - if loadMoreIdx == -1 { - return ranks - } - - // Return if already present - for i := range ranks { - if ranks[i].Index == loadMoreIdx { - return ranks - } - } - - // Append 'Load More' option in search filter results - ranks = append(ranks, list.Rank{ - Index: loadMoreIdx, - }) - - return ranks - } - } - } - - l.Styles.FilterPrompt = lipgloss.NewStyle().Foreground(Green) - l.Styles.FilterCursor = lipgloss.NewStyle().Foreground(Green).Background(Green) - l.Styles.Title = lipgloss.NewStyle().Foreground(Dark).Bold(true). - Background(lipgloss.Color("#fff")).Padding(0) - - l.FilterInput.PromptStyle = lipgloss.NewStyle().Foreground(Green) - l.FilterInput.TextStyle = lipgloss.NewStyle().Foreground(Green) - - singularItemName := "item " + SeparatorString - var pluralItemName string - if listOptions == nil { - pluralItemName = fmt.Sprintf("items\n\n%s", SeparatorString) - } else if len(listOptions[0].ParentIdentifier) > 0 { - pluralItemName = fmt.Sprintf("items (%s)\n\n%s", listOptions[0].ParentIdentifier, SeparatorString) - } - - l.SetStatusBarItemName(singularItemName, pluralItemName) - - return l -} - -func GetCustomTheme() *huh.Theme { - t := huh.ThemeCharm() - - t.Blurred.FocusedButton = t.Blurred.FocusedButton.Background(Green) - t.Blurred.FocusedButton = t.Blurred.FocusedButton.Bold(true) - t.Blurred.TextInput.Prompt = t.Blurred.TextInput.Prompt.Foreground(Light) - t.Blurred.TextInput.Cursor = t.Blurred.TextInput.Cursor.Foreground(Light) - t.Blurred.SelectSelector = t.Blurred.SelectSelector.Foreground(Green) - t.Blurred.Title = t.Blurred.Title.Foreground(Gray).Bold(true) - t.Blurred.Description = t.Blurred.Description.Foreground(LightGray) - - t.Focused.Title = t.Focused.Title.Foreground(Green).Bold(true) - t.Focused.Description = t.Focused.Description.Foreground(LightGray).Bold(true) - t.Focused.FocusedButton = t.Focused.FocusedButton.Bold(true) - t.Focused.FocusedButton = t.Focused.FocusedButton.Background(Green) - t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(Green) - t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(Light) - t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(Green) - t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(Green) - - t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(Red) - t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(Red) - - t.Focused.Base = t.Focused.Base.BorderForeground(Green) - t.Focused.Base = t.Focused.Base.BorderBottomForeground(Green) - - t.Focused.Base = t.Focused.Base.MarginTop(DefaultLayoutMarginTop) - t.Blurred.Base = t.Blurred.Base.MarginTop(DefaultLayoutMarginTop) - - return t -} - -func GetInitialCommandTheme() *huh.Theme { - - newTheme := huh.ThemeCharm() - - newTheme.Blurred.Title = newTheme.Focused.Title - - b := &newTheme.Blurred - b.FocusedButton = b.FocusedButton.Background(Green) - b.FocusedButton = b.FocusedButton.Bold(true) - b.TextInput.Prompt = b.TextInput.Prompt.Foreground(Green) - b.TextInput.Cursor = b.TextInput.Cursor.Foreground(Green) - b.SelectSelector = b.SelectSelector.Foreground(Green) - - f := &newTheme.Focused - f.Base = f.Base.BorderForeground(lipgloss.Color("fff")) - f.Title = f.Title.Foreground(Green).Bold(true) - f.FocusedButton = f.FocusedButton.Bold(true) - f.FocusedButton = f.FocusedButton.Background(Green) - f.TextInput.Prompt = f.TextInput.Prompt.Foreground(Green) - f.TextInput.Cursor = f.TextInput.Cursor.Foreground(Light) - f.SelectSelector = f.SelectSelector.Foreground(Green) - - f.Base = f.Base.UnsetMarginLeft() - f.Base = f.Base.UnsetPaddingLeft() - f.Base = f.Base.BorderLeft(false) - - f.SelectedOption = lipgloss.NewStyle().Foreground(Green) - - return newTheme -} diff --git a/apps/cli/views/organization/info.go b/apps/cli/views/organization/info.go deleted file mode 100644 index 8944dcd94..000000000 --- a/apps/cli/views/organization/info.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/lipgloss" - "golang.org/x/term" -) - -func RenderInfo(organization *apiclient.Organization, forceUnstyled bool) { - var output string - nameLabel := "Organization" - - output += "\n" - output += getInfoLine(nameLabel, organization.Name) + "\n" - output += getInfoLine("Created", util.GetTimeSinceLabel(organization.CreatedAt)) + "\n" - output += getInfoLine("ID", organization.Id) + "\n" - - terminalWidth, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - fmt.Println(output) - return - } - - if terminalWidth < common.TUITableMinimumWidth || forceUnstyled { - renderUnstyledInfo(output) - return - } - - output = common.GetStyledMainTitle("Organization Info") + "\n" + output - - renderTUIView(output, common.GetContainerBreakpointWidth(terminalWidth)) -} - -func renderUnstyledInfo(output string) { - fmt.Println(output) -} - -func renderTUIView(output string, width int) { - output = lipgloss.NewStyle().PaddingLeft(3).Render(output) - - content := lipgloss. - NewStyle().Width(width). - Render(output) - - fmt.Println(content) -} - -func getInfoLine(key, value string) string { - return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n" -} diff --git a/apps/cli/views/organization/list.go b/apps/cli/views/organization/list.go deleted file mode 100644 index 631a7dd67..000000000 --- a/apps/cli/views/organization/list.go +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "fmt" - "sort" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -type RowData struct { - Name string - Id string - Created string -} - -func ListOrganizations(organizationList []apiclient.Organization, activeOrganizationId *string) { - if len(organizationList) == 0 { - util.NotifyEmptyOrganizationList(true) - return - } - - SortOrganizations(&organizationList) - - headers := []string{"Name", "Id", "Created"} - - data := [][]string{} - - for _, o := range organizationList { - var rowData *RowData - var row []string - - rowData = getTableRowData(o, activeOrganizationId) - row = getRowFromRowData(*rowData) - data = append(data, row) - } - - table := util.GetTableView(data, headers, nil, func() { - renderUnstyledList(organizationList) - }) - - fmt.Println(table) -} - -func SortOrganizations(organizationList *[]apiclient.Organization) { - sort.Slice(*organizationList, func(i, j int) bool { - return (*organizationList)[i].CreatedAt.After((*organizationList)[j].CreatedAt) - }) -} - -func getTableRowData(organization apiclient.Organization, activeOrganizationId *string) *RowData { - rowData := RowData{"", "", ""} - rowData.Name = organization.Name + util.AdditionalPropertyPadding - - if activeOrganizationId != nil && *activeOrganizationId == organization.Id { - rowData.Name = "*" + rowData.Name - } - - rowData.Id = organization.Id - rowData.Created = util.GetTimeSinceLabel(organization.CreatedAt) - - return &rowData -} - -func renderUnstyledList(organizationList []apiclient.Organization) { - for _, organization := range organizationList { - RenderInfo(&organization, true) - - if organization.Id != organizationList[len(organizationList)-1].Id { - fmt.Printf("\n%s\n\n", common.SeparatorString) - } - - } -} - -func getRowFromRowData(rowData RowData) []string { - row := []string{ - common.NameStyle.Render(rowData.Name), - common.DefaultRowDataStyle.Render(rowData.Id), - common.DefaultRowDataStyle.Render(rowData.Created), - } - - return row -} diff --git a/apps/cli/views/organization/select.go b/apps/cli/views/organization/select.go deleted file mode 100644 index 859440004..000000000 --- a/apps/cli/views/organization/select.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package organization - -import ( - "github.com/boxlite-ai/boxlite/cli/views/common" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/huh" -) - -func GetOrganizationIdFromPrompt(organizationList []apiclient.Organization) (*apiclient.Organization, error) { - var chosenOrganizationId string - var organizationOptions []huh.Option[string] - - for _, organization := range organizationList { - organizationOptions = append(organizationOptions, huh.NewOption(organization.Name, organization.Id)) - } - - form := huh.NewForm( - huh.NewGroup( - huh.NewSelect[string](). - Title("Choose an Organization"). - Options( - organizationOptions..., - ). - Value(&chosenOrganizationId), - ).WithTheme(common.GetCustomTheme()), - ) - - if err := form.Run(); err != nil { - return nil, err - } - - for _, organization := range organizationList { - if organization.Id == chosenOrganizationId { - return &organization, nil - } - } - - return nil, nil -} diff --git a/apps/cli/views/sandbox/info.go b/apps/cli/views/sandbox/info.go deleted file mode 100644 index 55ccef331..000000000 --- a/apps/cli/views/sandbox/info.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "fmt" - "os" - "strings" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/lipgloss" - "golang.org/x/term" -) - -func RenderInfo(sandbox *apiclient.Sandbox, forceUnstyled bool) { - var output string - - output += "\n" - - output += getInfoLine("ID", sandbox.Id) + "\n" - - if sandbox.State != nil { - output += getInfoLine("State", getStateLabel(*sandbox.State)) + "\n" - } - - if sandbox.Snapshot != nil { - output += getInfoLine("Snapshot", *sandbox.Snapshot) + "\n" - } - - output += getInfoLine("Region", sandbox.Target) + "\n" - - if sandbox.Class != nil { - output += getInfoLine("Class", *sandbox.Class) + "\n" - } - - if sandbox.CreatedAt != nil { - output += getInfoLine("Created", util.GetTimeSinceLabelFromString(*sandbox.CreatedAt)) + "\n" - } - - if sandbox.UpdatedAt != nil { - output += getInfoLine("Last Event", util.GetTimeSinceLabelFromString(*sandbox.UpdatedAt)) + "\n" - } - - terminalWidth, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - fmt.Println(output) - return - } - if terminalWidth < common.TUITableMinimumWidth || forceUnstyled { - renderUnstyledInfo(output) - return - } - - output = common.GetStyledMainTitle("Sandbox Info") + "\n" + output - - if len(sandbox.Labels) > 0 { - labels := "" - i := 0 - for k, v := range sandbox.Labels { - label := fmt.Sprintf("%s=%s\n", k, v) - if i == 0 { - labels += label + "\n" - } else { - labels += getInfoLine("", fmt.Sprintf("%s=%s\n", k, v)) - } - i++ - } - labels = strings.TrimSuffix(labels, "\n") - output += "\n" + strings.TrimSuffix(getInfoLine("Labels", labels), "\n") - } - - renderTUIView(output, common.GetContainerBreakpointWidth(terminalWidth)) -} - -func renderUnstyledInfo(output string) { - fmt.Println(output) -} - -func renderTUIView(output string, width int) { - output = lipgloss.NewStyle().PaddingLeft(3).Render(output) - - content := lipgloss. - NewStyle().Width(width). - Render(output) - - fmt.Println(content) -} - -func getInfoLine(key, value string) string { - return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n" -} - -func getStateLabel(state apiclient.SandboxState) string { - switch state { - case apiclient.SANDBOXSTATE_CREATING: - return common.CreatingStyle.Render("CREATING") - case apiclient.SANDBOXSTATE_RESTORING: - return common.CreatingStyle.Render("RESTORING") - case apiclient.SANDBOXSTATE_DESTROYED: - return common.DeletedStyle.Render("DESTROYED") - case apiclient.SANDBOXSTATE_DESTROYING: - return common.DeletedStyle.Render("DESTROYING") - case apiclient.SANDBOXSTATE_STARTED: - return common.StartedStyle.Render("STARTED") - case apiclient.SANDBOXSTATE_STOPPED: - return common.StoppedStyle.Render("STOPPED") - case apiclient.SANDBOXSTATE_STARTING: - return common.StartingStyle.Render("STARTING") - case apiclient.SANDBOXSTATE_STOPPING: - return common.StoppingStyle.Render("STOPPING") - case apiclient.SANDBOXSTATE_PULLING_SNAPSHOT: - return common.CreatingStyle.Render("PULLING SNAPSHOT") - case apiclient.SANDBOXSTATE_ARCHIVING: - return common.CreatingStyle.Render("ARCHIVING") - case apiclient.SANDBOXSTATE_ARCHIVED: - return common.StoppedStyle.Render("ARCHIVED") - case apiclient.SANDBOXSTATE_ERROR: - return common.ErrorStyle.Render("ERROR") - case apiclient.SANDBOXSTATE_BUILD_FAILED: - return common.ErrorStyle.Render("BUILD FAILED") - case apiclient.SANDBOXSTATE_UNKNOWN: - return common.UndefinedStyle.Render("UNKNOWN") - default: - return common.UndefinedStyle.Render("/") - } -} diff --git a/apps/cli/views/sandbox/list.go b/apps/cli/views/sandbox/list.go deleted file mode 100644 index 7fcceb445..000000000 --- a/apps/cli/views/sandbox/list.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package sandbox - -import ( - "fmt" - "sort" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -type RowData struct { - Name string - State string - Region string - Class string - LastEvent string -} - -func ListSandboxes(sandboxList []apiclient.Sandbox, activeOrganizationName *string) { - if len(sandboxList) == 0 { - util.NotifyEmptySandboxList(true) - return - } - - headers := []string{"Sandbox", "State", "Region", "Class", "Last Event"} - - data := [][]string{} - - for _, s := range sandboxList { - var rowData *RowData - var row []string - - rowData = getTableRowData(s) - row = getRowFromRowData(*rowData) - data = append(data, row) - } - - table := util.GetTableView(data, headers, activeOrganizationName, func() { - renderUnstyledList(sandboxList) - }) - - fmt.Println(table) -} - -func SortSandboxes(sandboxList *[]apiclient.Sandbox) { - sort.Slice(*sandboxList, func(i, j int) bool { - pi, pj := getStateSortPriorities(*(*sandboxList)[i].State, *(*sandboxList)[j].State) - if pi != pj { - return pi < pj - } - - if (*sandboxList)[i].CreatedAt == nil || (*sandboxList)[j].CreatedAt == nil { - return true - } - - // If two sandboxes have the same state priority, compare the UpdatedAt property - return *(*sandboxList)[i].CreatedAt > *(*sandboxList)[j].CreatedAt - }) -} - -func getTableRowData(sandbox apiclient.Sandbox) *RowData { - rowData := RowData{"", "", "", "", ""} - rowData.Name = sandbox.Id + util.AdditionalPropertyPadding - if sandbox.State != nil { - rowData.State = getStateLabel(*sandbox.State) - } - - rowData.Region = sandbox.Target - if sandbox.Class != nil { - rowData.Class = *sandbox.Class - } - - if sandbox.UpdatedAt != nil { - rowData.LastEvent = util.GetTimeSinceLabelFromString(*sandbox.UpdatedAt) - } - - return &rowData -} - -func renderUnstyledList(sandboxList []apiclient.Sandbox) { - for _, sandbox := range sandboxList { - RenderInfo(&sandbox, true) - - if sandbox.Id != sandboxList[len(sandboxList)-1].Id { - fmt.Printf("\n%s\n\n", common.SeparatorString) - } - - } -} - -func getRowFromRowData(rowData RowData) []string { - row := []string{ - common.NameStyle.Render(rowData.Name), - rowData.State, - common.DefaultRowDataStyle.Render(rowData.Region), - common.DefaultRowDataStyle.Render(rowData.Class), - common.DefaultRowDataStyle.Render(rowData.LastEvent), - } - - return row -} - -func getStateSortPriorities(state1, state2 apiclient.SandboxState) (int, int) { - pi, ok := sandboxListStatePriorities[state1] - if !ok { - pi = 99 - } - pj, ok2 := sandboxListStatePriorities[state2] - if !ok2 { - pj = 99 - } - - return pi, pj -} - -// Sandboxes that have actions being performed on them have a higher priority when listing -var sandboxListStatePriorities = map[apiclient.SandboxState]int{ - "pending": 1, - "pending-start": 1, - "deleting": 1, - "creating": 1, - "started": 2, - "undefined": 2, - "error": 3, - "build-failed": 3, - "stopped": 4, -} diff --git a/apps/cli/views/snapshot/info.go b/apps/cli/views/snapshot/info.go deleted file mode 100644 index b1d8a237d..000000000 --- a/apps/cli/views/snapshot/info.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/lipgloss" - "golang.org/x/term" -) - -func RenderInfo(snapshot *apiclient.SnapshotDto, forceUnstyled bool) { - var output string - nameLabel := "Snapshot" - - output += "\n" - output += getInfoLine(nameLabel, snapshot.Name) + "\n" - output += getInfoLine("State", getStateLabel(snapshot.State)) + "\n" - - if size := snapshot.Size.Get(); size != nil { - output += getInfoLine("Size", fmt.Sprintf("%.2f GB", *size)) + "\n" - } else { - output += getInfoLine("Size", "-") + "\n" - } - output += getInfoLine("Created", util.GetTimeSinceLabel(snapshot.CreatedAt)) + "\n" - - output += getInfoLine("ID", snapshot.Id) + "\n" - - terminalWidth, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - fmt.Println(output) - return - } - if terminalWidth < common.TUITableMinimumWidth || forceUnstyled { - renderUnstyledInfo(output) - return - } - - output = common.GetStyledMainTitle("Snapshot Info") + "\n" + output - - renderTUIView(output, common.GetContainerBreakpointWidth(terminalWidth)) -} - -func renderUnstyledInfo(output string) { - fmt.Println(output) -} - -func renderTUIView(output string, width int) { - output = lipgloss.NewStyle().PaddingLeft(3).Render(output) - - content := lipgloss. - NewStyle().Width(width). - Render(output) - - fmt.Println(content) -} - -func getInfoLine(key, value string) string { - return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n" -} - -func getStateLabel(state apiclient.SnapshotState) string { - switch state { - case apiclient.SNAPSHOTSTATE_PENDING: - return common.CreatingStyle.Render("PENDING") - case apiclient.SNAPSHOTSTATE_PULLING: - return common.CreatingStyle.Render("PULLING SNAPSHOT") - case apiclient.SNAPSHOTSTATE_ACTIVE: - return common.StartedStyle.Render("ACTIVE") - case apiclient.SNAPSHOTSTATE_ERROR: - return common.ErrorStyle.Render("ERROR") - case apiclient.SNAPSHOTSTATE_BUILD_FAILED: - return common.ErrorStyle.Render("BUILD FAILED") - case apiclient.SNAPSHOTSTATE_REMOVING: - return common.DeletedStyle.Render("REMOVING") - default: - return common.UndefinedStyle.Render("/") - } -} diff --git a/apps/cli/views/snapshot/list.go b/apps/cli/views/snapshot/list.go deleted file mode 100644 index b67ea0955..000000000 --- a/apps/cli/views/snapshot/list.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package snapshot - -import ( - "fmt" - "sort" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -type RowData struct { - Name string - State string - Size string - Created string -} - -func ListSnapshots(snapshotList []apiclient.SnapshotDto, activeOrganizationName *string) { - if len(snapshotList) == 0 { - util.NotifyEmptySnapshotList(true) - return - } - - SortSnapshots(&snapshotList) - - headers := []string{"Snapshot", "State", "Size", "Created"} - - data := [][]string{} - - for _, img := range snapshotList { - var rowData *RowData - var row []string - - rowData = getTableRowData(img) - row = getRowFromRowData(*rowData) - data = append(data, row) - } - - table := util.GetTableView(data, headers, activeOrganizationName, func() { - renderUnstyledList(snapshotList) - }) - - fmt.Println(table) -} - -func SortSnapshots(snapshotList *[]apiclient.SnapshotDto) { - sort.Slice(*snapshotList, func(i, j int) bool { - pi, pj := getStateSortPriorities((*snapshotList)[i].State, (*snapshotList)[j].State) - if pi != pj { - return pi < pj - } - - // If two snapshots have the same state priority, compare the UpdatedAt property - return (*snapshotList)[i].CreatedAt.After((*snapshotList)[j].CreatedAt) - }) -} - -func getTableRowData(snapshot apiclient.SnapshotDto) *RowData { - rowData := RowData{"", "", "", ""} - rowData.Name = snapshot.Name + util.AdditionalPropertyPadding - rowData.State = getStateLabel(snapshot.State) - - if snapshot.Size.IsSet() && snapshot.Size.Get() != nil { - rowData.Size = fmt.Sprintf("%.2f GB", *snapshot.Size.Get()) - } else { - rowData.Size = "-" - } - - rowData.Created = util.GetTimeSinceLabel(snapshot.CreatedAt) - return &rowData -} - -func renderUnstyledList(snapshotList []apiclient.SnapshotDto) { - for _, snapshot := range snapshotList { - RenderInfo(&snapshot, true) - - if snapshot.Id != snapshotList[len(snapshotList)-1].Id { - fmt.Printf("\n%s\n\n", common.SeparatorString) - } - } -} - -func getRowFromRowData(rowData RowData) []string { - row := []string{ - common.NameStyle.Render(rowData.Name), - rowData.State, - common.DefaultRowDataStyle.Render(rowData.Size), - common.DefaultRowDataStyle.Render(rowData.Created), - } - - return row -} - -func getStateSortPriorities(state1, state2 apiclient.SnapshotState) (int, int) { - pi, ok := snapshotListStatePriorities[state1] - if !ok { - pi = 99 - } - pj, ok2 := snapshotListStatePriorities[state2] - if !ok2 { - pj = 99 - } - - return pi, pj -} - -// snapshots that have actions being performed on them have a higher priority when listing -var snapshotListStatePriorities = map[apiclient.SnapshotState]int{ - apiclient.SNAPSHOTSTATE_PENDING: 1, - apiclient.SNAPSHOTSTATE_PULLING: 1, - apiclient.SNAPSHOTSTATE_ERROR: 2, - apiclient.SNAPSHOTSTATE_ACTIVE: 3, - apiclient.SNAPSHOTSTATE_REMOVING: 4, -} diff --git a/apps/cli/views/util/empty_list.go b/apps/cli/views/util/empty_list.go deleted file mode 100644 index babcdd946..000000000 --- a/apps/cli/views/util/empty_list.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -import ( - "github.com/boxlite-ai/boxlite/cli/views/common" -) - -func NotifyEmptySandboxList(tip bool) { - common.RenderInfoMessageBold("No sandboxes found") - if tip { - common.RenderTip("Use the BoxLite SDK to get started.") - } -} - -func NotifyEmptySnapshotList(tip bool) { - common.RenderInfoMessageBold("No snapshots found") - if tip { - common.RenderTip("Use 'boxlite snapshot push' to push a snapshot.") - } -} - -func NotifyEmptyOrganizationList(tip bool) { - common.RenderInfoMessageBold("No organizations found") - if tip { - common.RenderTip("Use 'boxlite organization create' to create an organization.") - } -} - -func NotifyEmptyVolumeList(tip bool) { - common.RenderInfoMessageBold("No volumes found") - if tip { - common.RenderTip("Use 'boxlite volume create' to create a volume.") - } -} diff --git a/apps/cli/views/util/info.go b/apps/cli/views/util/info.go deleted file mode 100644 index b0816b061..000000000 --- a/apps/cli/views/util/info.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -import ( - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/charmbracelet/lipgloss" -) - -const PropertyNameWidth = 16 - -var PropertyNameStyle = lipgloss.NewStyle(). - Foreground(common.LightGray) - -var PropertyValueStyle = lipgloss.NewStyle(). - Foreground(common.Light). - Bold(true) diff --git a/apps/cli/views/util/spinner.go b/apps/cli/views/util/spinner.go deleted file mode 100644 index 9719fd70a..000000000 --- a/apps/cli/views/util/spinner.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -import ( - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/charmbracelet/bubbles/spinner" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - log "github.com/sirupsen/logrus" - "golang.org/x/term" -) - -var isAborted bool - -type model struct { - spinner spinner.Model - quitting bool - message string - inline bool -} - -type Msg string - -func initialModel(message string, inline bool) model { - s := spinner.New() - s.Spinner = spinner.Dot - s.Style = lipgloss.NewStyle().Foreground(common.Green) - return model{spinner: s, message: message, inline: inline} -} - -func (m model) Init() tea.Cmd { - return m.spinner.Tick -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case Msg: - m.quitting = true - return m, tea.Quit - - case tea.KeyMsg: - switch keypress := msg.String(); keypress { - case "ctrl+c": - isAborted = true - m.quitting = true - return m, tea.Quit - } - } - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(msg) - return m, cmd - -} - -func WithSpinner(message string, fn func() error) error { - if isTTY() { - p := start(message, false) - defer stop(p) - } - return fn() -} - -func WithInlineSpinner(message string, fn func() error) error { - if isTTY() { - p := start(message, true) - defer stop(p) - } - return fn() -} - -func start(message string, inline bool) *tea.Program { - var p *tea.Program - if inline { - p = tea.NewProgram(initialModel(message, true)) - } else { - p = tea.NewProgram(initialModel(message, false), tea.WithAltScreen()) - } - go func() { - if _, err := p.Run(); err != nil { - fmt.Println(err) - os.Exit(1) - } - if isAborted { - fmt.Println("Operation cancelled") - os.Exit(1) - } - }() - return p - -} - -func stop(p *tea.Program) { - p.Send(Msg("quit")) - err := p.ReleaseTerminal() - if err != nil { - log.Fatal(err) - } -} - -func (m model) View() string { - if m.quitting { - return "" - } - - str := "" - if m.inline { - str = common.GetInfoMessage(fmt.Sprintf("%s %s ...", m.spinner.View(), m.message)) - } else { - str = common.NameStyle.Render(fmt.Sprintf("\n\n %s %s ...\n\n", m.spinner.View(), m.message)) - } - - return str -} - -func isTTY() bool { - return term.IsTerminal(int(os.Stdout.Fd())) -} diff --git a/apps/cli/views/util/table.go b/apps/cli/views/util/table.go deleted file mode 100644 index a18857d4e..000000000 --- a/apps/cli/views/util/table.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -import ( - "fmt" - "os" - "regexp" - "strings" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/lipgloss/table" - "golang.org/x/term" -) - -var AdditionalPropertyPadding = " " - -// Left border, BaseTableStyle padding left, additional padding for target name and target config, BaseTableStyle padding right, BaseCellStyle padding right, right border -var RowWhiteSpace = 1 + 4 + len(AdditionalPropertyPadding)*2 + 4 + 4 + 1 -var ArbitrarySpace = 10 - -// Gets the table view string or falls back to an unstyled view for lower terminal widths -func GetTableView(data [][]string, headers []string, activeOrganizationName *string, fallbackRender func()) string { - re := lipgloss.NewRenderer(os.Stdout) - - terminalWidth, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - fmt.Println(data) - return "" - } - - breakpointWidth := common.GetContainerBreakpointWidth(terminalWidth) - - minWidth := getMinimumWidth(data) - - if breakpointWidth == 0 || minWidth > breakpointWidth { - fallbackRender() - return "" - } - - t := table.New(). - Headers(headers...). - Rows(data...). - BorderStyle(re.NewStyle().Foreground(common.LightGray)). - BorderRow(false).BorderColumn(false).BorderLeft(false).BorderRight(false).BorderTop(false).BorderBottom(false). - StyleFunc(func(_, _ int) lipgloss.Style { - return common.BaseCellStyle - }).Width(breakpointWidth - 2*common.BaseTableStyleHorizontalPadding - 1) - - table := t.String() - - if activeOrganizationName != nil { - activeOrgMessage := common.GetInfoMessage(fmt.Sprintf("Active organization: %s", *activeOrganizationName)) - rightAlignedStyle := lipgloss.NewStyle().Width(breakpointWidth - 2*common.BaseTableStyleHorizontalPadding - 1).Align(lipgloss.Right) - table += "\n" + rightAlignedStyle.Render(activeOrgMessage) - } - - return common.BaseTableStyle.Render(table) -} - -func getMinimumWidth(data [][]string) int { - width := 0 - widestRow := 0 - for _, row := range data { - for _, cell := range row { - // Remove ANSI escape codes - regex := regexp.MustCompile("\x1b\\[[0-9;]*[a-zA-Z]") - strippedCell := regex.ReplaceAllString(cell, "") - width += longestLineLength(strippedCell) - if width > widestRow { - widestRow = width - } - } - width = 0 - } - return widestRow -} - -// Returns the length of the longest line in a string -func longestLineLength(input string) int { - lines := strings.Split(input, "\n") - maxLength := 0 - - for _, line := range lines { - if len(line) > maxLength { - maxLength = len(line) - } - } - - return maxLength -} diff --git a/apps/cli/views/util/time.go b/apps/cli/views/util/time.go deleted file mode 100644 index bbb643273..000000000 --- a/apps/cli/views/util/time.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package util - -import ( - "fmt" - "time" -) - -var timeLayout = "2006-01-02T15:04:05.999999999Z07:00" - -func GetTimeSinceLabelFromString(input string) string { - t, err := time.Parse(timeLayout, input) - if err != nil { - return "/" - } - - return GetTimeSinceLabel(t) -} - -func GetTimeSinceLabel(t time.Time) string { - duration := time.Since(t) - - if duration < time.Minute { - return "< 1 minute ago" - } else if duration < time.Hour { - minutes := int(duration.Minutes()) - if minutes == 1 { - return "1 minute ago" - } - return fmt.Sprintf("%d minutes ago", minutes) - } else if duration < 24*time.Hour { - hours := int(duration.Hours()) - if hours == 1 { - return "1 hour ago" - } - return fmt.Sprintf("%d hours ago", hours) - } else { - days := int(duration.Hours() / 24) - if days == 1 { - return "1 day ago" - } - return fmt.Sprintf("%d days ago", days) - } -} diff --git a/apps/cli/views/volume/info.go b/apps/cli/views/volume/info.go deleted file mode 100644 index d32a47567..000000000 --- a/apps/cli/views/volume/info.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "fmt" - "os" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" - "github.com/charmbracelet/lipgloss" - "golang.org/x/term" -) - -func RenderInfo(volume *apiclient.VolumeDto, forceUnstyled bool) { - var output string - nameLabel := "Volume" - - output += "\n" - output += getInfoLine(nameLabel, volume.Name) + "\n" - output += getInfoLine("ID", volume.Id) + "\n" - output += getInfoLine("State", getStateLabel(volume.State)) + "\n" - - output += getInfoLine("Created", util.GetTimeSinceLabelFromString(volume.CreatedAt)) + "\n" - - terminalWidth, _, err := term.GetSize(int(os.Stdout.Fd())) - if err != nil { - fmt.Println(output) - return - } - if terminalWidth < common.TUITableMinimumWidth || forceUnstyled { - renderUnstyledInfo(output) - return - } - - output = common.GetStyledMainTitle("Volume Info") + "\n" + output - - renderTUIView(output, common.GetContainerBreakpointWidth(terminalWidth)) -} - -func renderUnstyledInfo(output string) { - fmt.Println(output) -} - -func renderTUIView(output string, width int) { - output = lipgloss.NewStyle().PaddingLeft(3).Render(output) - - content := lipgloss. - NewStyle().Width(width). - Render(output) - - fmt.Println(content) -} - -func getInfoLine(key, value string) string { - return util.PropertyNameStyle.Render(fmt.Sprintf("%-*s", util.PropertyNameWidth, key)) + util.PropertyValueStyle.Render(value) + "\n" -} - -func getStateLabel(state apiclient.VolumeState) string { - switch state { - case apiclient.VOLUMESTATE_PENDING_CREATE: - return common.CreatingStyle.Render("PENDING CREATE") - case apiclient.VOLUMESTATE_CREATING: - return common.CreatingStyle.Render("CREATING") - case apiclient.VOLUMESTATE_READY: - return common.StartedStyle.Render("READY") - case apiclient.VOLUMESTATE_PENDING_DELETE: - return common.DeletedStyle.Render("PENDING DELETE") - case apiclient.VOLUMESTATE_DELETING: - return common.DeletedStyle.Render("DELETING") - case apiclient.VOLUMESTATE_DELETED: - return common.DeletedStyle.Render("DELETED") - case apiclient.VOLUMESTATE_ERROR: - return common.ErrorStyle.Render("ERROR") - default: - return common.UndefinedStyle.Render("/") - } -} diff --git a/apps/cli/views/volume/list.go b/apps/cli/views/volume/list.go deleted file mode 100644 index 8d9067b6b..000000000 --- a/apps/cli/views/volume/list.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2025 BoxLite AI (originally Daytona Platforms Inc. -// Modified by BoxLite AI, 2025-2026 -// SPDX-License-Identifier: AGPL-3.0 - -package volume - -import ( - "fmt" - "sort" - - "github.com/boxlite-ai/boxlite/cli/views/common" - "github.com/boxlite-ai/boxlite/cli/views/util" - apiclient "github.com/boxlite-ai/boxlite/libs/api-client-go" -) - -type RowData struct { - Name string - State string - Size string - Created string -} - -func ListVolumes(volumeList []apiclient.VolumeDto, activeOrganizationName *string) { - if len(volumeList) == 0 { - util.NotifyEmptyVolumeList(true) - return - } - - SortVolumes(&volumeList) - - headers := []string{"Volume", "State", "Size", "Created"} - - data := [][]string{} - - for _, v := range volumeList { - var rowData *RowData - var row []string - - rowData = getTableRowData(v) - row = getRowFromRowData(*rowData) - data = append(data, row) - } - - table := util.GetTableView(data, headers, activeOrganizationName, func() { - renderUnstyledList(volumeList) - }) - - fmt.Println(table) -} - -func SortVolumes(volumeList *[]apiclient.VolumeDto) { - sort.Slice(*volumeList, func(i, j int) bool { - if (*volumeList)[i].State != (*volumeList)[j].State { - pi, pj := getStateSortPriorities((*volumeList)[i].State, (*volumeList)[j].State) - return pi < pj - } - - // If two volumes have the same state priority, compare the CreatedAt property - return (*volumeList)[i].CreatedAt > (*volumeList)[j].CreatedAt - }) -} - -func getTableRowData(volume apiclient.VolumeDto) *RowData { - rowData := RowData{"", "", "", ""} - rowData.Name = volume.Name + util.AdditionalPropertyPadding - rowData.State = getStateLabel(volume.State) - rowData.Created = util.GetTimeSinceLabelFromString(volume.CreatedAt) - return &rowData -} - -func renderUnstyledList(volumeList []apiclient.VolumeDto) { - for _, volume := range volumeList { - RenderInfo(&volume, true) - - if volume.Id != volumeList[len(volumeList)-1].Id { - fmt.Printf("\n%s\n\n", common.SeparatorString) - } - } -} - -func getRowFromRowData(rowData RowData) []string { - row := []string{ - common.NameStyle.Render(rowData.Name), - rowData.State, - common.DefaultRowDataStyle.Render(rowData.Size), - common.DefaultRowDataStyle.Render(rowData.Created), - } - - return row -} - -func getStateSortPriorities(state1, state2 apiclient.VolumeState) (int, int) { - pi, ok := volumeListStatePriorities[state1] - if !ok { - pi = 99 - } - pj, ok2 := volumeListStatePriorities[state2] - if !ok2 { - pj = 99 - } - - return pi, pj -} - -// Volumes that have actions being performed on them have a higher priority when listing -var volumeListStatePriorities = map[apiclient.VolumeState]int{ - apiclient.VOLUMESTATE_PENDING_CREATE: 1, - apiclient.VOLUMESTATE_CREATING: 1, - apiclient.VOLUMESTATE_PENDING_DELETE: 1, - apiclient.VOLUMESTATE_DELETING: 1, - apiclient.VOLUMESTATE_READY: 2, - apiclient.VOLUMESTATE_ERROR: 3, - apiclient.VOLUMESTATE_DELETED: 4, -} diff --git a/apps/common-go/go.mod b/apps/common-go/go.mod index 9f0d1efd1..381c1d2dc 100644 --- a/apps/common-go/go.mod +++ b/apps/common-go/go.mod @@ -62,11 +62,11 @@ require ( go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect golang.org/x/arch v0.20.0 // indirect - golang.org/x/crypto v0.49.0 // indirect - golang.org/x/net v0.52.0 // indirect + golang.org/x/crypto v0.50.0 // indirect + golang.org/x/net v0.53.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.0 // indirect - golang.org/x/text v0.35.0 // indirect + golang.org/x/sys v0.43.0 // indirect + golang.org/x/text v0.36.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect google.golang.org/grpc v1.80.0 // indirect diff --git a/apps/common-go/go.sum b/apps/common-go/go.sum index 4920cff02..3dc95fee7 100644 --- a/apps/common-go/go.sum +++ b/apps/common-go/go.sum @@ -145,20 +145,16 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= diff --git a/apps/common-go/pkg/proxy/proxy.go b/apps/common-go/pkg/proxy/proxy.go index 4d433713a..35746606a 100644 --- a/apps/common-go/pkg/proxy/proxy.go +++ b/apps/common-go/pkg/proxy/proxy.go @@ -22,19 +22,19 @@ var proxyTransport = &http.Transport{ }).DialContext, } -// ProxyRequest handles proxying requests to a sandbox's container +// ProxyRequest handles proxying requests to a box's container // // @Tags toolbox -// @Summary Proxy requests to the sandbox toolbox -// @Description Forwards the request to the specified sandbox's container -// @Param workspaceId path string true "Sandbox ID" +// @Summary Proxy requests to the box toolbox +// @Description Forwards the request to the specified box's container +// @Param workspaceId path string true "Box ID" // @Param projectId path string true "Project ID" // @Param path path string true "Path to forward" // @Success 200 {object} string "Proxied response" // @Failure 400 {object} string "Bad request" // @Failure 401 {object} string "Unauthorized" -// @Failure 404 {object} string "Sandbox container not found" -// @Failure 409 {object} string "Sandbox container conflict" +// @Failure 404 {object} string "Box container not found" +// @Failure 409 {object} string "Box container conflict" // @Failure 500 {object} string "Internal server error" // @Router /workspaces/{workspaceId}/{projectId}/toolbox/{path} [get] func NewProxyRequestHandler(getProxyTarget func(*gin.Context) (targetUrl *url.URL, extraHeaders map[string]string, err error), modifyResponse func(*http.Response) error) gin.HandlerFunc { diff --git a/apps/daemon/cmd/daemon/config/config.go b/apps/daemon/cmd/daemon/config/config.go index 1adf077cc..9cb78fab8 100644 --- a/apps/daemon/cmd/daemon/config/config.go +++ b/apps/daemon/cmd/daemon/config/config.go @@ -14,13 +14,14 @@ import ( type Config struct { DaemonLogFilePath string `envconfig:"BOXLITE_DAEMON_LOG_FILE_PATH"` UserHomeAsWorkDir bool `envconfig:"BOXLITE_USER_HOME_AS_WORKDIR"` - SandboxId string `envconfig:"BOXLITE_SANDBOX_ID" validate:"required"` + BoxId string `envconfig:"BOXLITE_BOX_ID" validate:"required"` OtelEndpoint *string `envconfig:"BOXLITE_OTEL_ENDPOINT"` TerminationCheckInterval time.Duration `envconfig:"BOXLITE_TERMINATION_CHECK_INTERVAL" default:"100ms" validate:"min_duration=1ms"` TerminationGracePeriod time.Duration `envconfig:"BOXLITE_TERMINATION_GRACE_PERIOD" default:"5s" validate:"min_duration=1s"` RecordingsDir string `envconfig:"BOXLITE_RECORDINGS_DIR"` OrganizationId *string `envconfig:"BOXLITE_ORGANIZATION_ID"` RegionId *string `envconfig:"BOXLITE_REGION_ID"` + TraceParent *string `envconfig:"BOXLITE_TRACEPARENT"` } var defaultDaemonLogFilePath = "/tmp/boxlite-daemon.log" diff --git a/apps/daemon/cmd/daemon/main.go b/apps/daemon/cmd/daemon/main.go index 347126234..e117f4154 100644 --- a/apps/daemon/cmd/daemon/main.go +++ b/apps/daemon/cmd/daemon/main.go @@ -71,7 +71,7 @@ func run() int { err := util.ReadEntrypointLogs(entrypointLogFilePath) if err != nil { if errors.Is(err, os.ErrNotExist) { - logger.Warn("Logs not found, please check if correct entrypoint was provided for sandbox.") + logger.Warn("Logs not found, please check if correct entrypoint was provided for box.") } else { logger.Error("Failed to read entrypoint log file", "error", err) } @@ -173,11 +173,12 @@ func run() int { WorkDir: workDir, ConfigDir: configDir, OtelEndpoint: c.OtelEndpoint, - SandboxId: c.SandboxId, + BoxId: c.BoxId, SessionService: sessionService, RecordingService: recordingService, OrganizationId: c.OrganizationId, RegionId: c.RegionId, + TraceParent: c.TraceParent, EntrypointLogFilePath: entrypointLogFilePath, }) diff --git a/apps/daemon/go.mod b/apps/daemon/go.mod index 157958e48..9da985ac1 100644 --- a/apps/daemon/go.mod +++ b/apps/daemon/go.mod @@ -13,7 +13,7 @@ require ( github.com/creack/pty v1.1.23 github.com/gin-gonic/gin v1.10.1 github.com/gliderlabs/ssh v0.3.8 - github.com/go-git/go-git/v5 v5.18.0 + github.com/go-git/go-git/v5 v5.19.1 github.com/go-playground/validator/v10 v10.27.0 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 @@ -33,10 +33,10 @@ require ( github.com/swaggo/swag v1.16.4 go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.63.0 go.opentelemetry.io/otel/sdk v1.43.0 - go.opentelemetry.io/otel/sdk/log v0.14.0 + go.opentelemetry.io/otel/sdk/log v0.19.0 go.opentelemetry.io/otel/sdk/metric v1.43.0 - golang.org/x/crypto v0.48.0 - golang.org/x/sys v0.42.0 + golang.org/x/crypto v0.50.0 + golang.org/x/sys v0.43.0 gopkg.in/ini.v1 v1.67.0 ) @@ -51,7 +51,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/cloudwego/base64x v0.1.6 // indirect - github.com/cyphar/filepath-securejoin v0.4.1 // indirect + github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.9.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect @@ -59,7 +59,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.8.0 // indirect + github.com/go-git/go-billy/v5 v5.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect @@ -87,7 +87,7 @@ require ( github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/oklog/run v1.0.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pjbgf/sha1cd v0.3.2 // indirect + github.com/pjbgf/sha1cd v0.6.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect @@ -100,16 +100,16 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.14.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 // indirect + go.opentelemetry.io/otel/log v0.19.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect golang.org/x/arch v0.20.0 // indirect - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/net v0.51.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/tools v0.41.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/grpc v1.79.3 // indirect + golang.org/x/net v0.53.0 // indirect + golang.org/x/text v0.36.0 // indirect + golang.org/x/tools v0.44.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/grpc v1.80.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/apps/daemon/go.sum b/apps/daemon/go.sum index a704cc56c..c3b82fffe 100644 --- a/apps/daemon/go.sum +++ b/apps/daemon/go.sum @@ -27,8 +27,8 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= +github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -54,12 +54,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= -github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= +github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA= +github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= -github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -158,8 +158,8 @@ github.com/orcaman/concurrent-map/v2 v2.0.1 h1:jOJ5Pg2w1oeB6PeDurIYf6k9PQ+aTITr/ github.com/orcaman/concurrent-map/v2 v2.0.1/go.mod h1:9Eq3TG2oBe5FirmYWQfYO5iH1q0Jv47PLaNK++uCdOM= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= -github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= +github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU= +github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= @@ -221,16 +221,13 @@ go.opentelemetry.io/contrib/propagators/b3 v1.38.0 h1:uHsCCOSKl0kLrV2dLkFK+8Ywk9 go.opentelemetry.io/contrib/propagators/b3 v1.38.0/go.mod h1:wMRSZJZcY8ya9mApLLhwIMjqmApy2o/Ml+62lhvxyHU= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE= -go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= -go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw= +go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= -go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= +go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= @@ -243,25 +240,25 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +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/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= -golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -282,34 +279,31 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= +golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= -google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/apps/daemon/internal/util/sandbox.go b/apps/daemon/internal/util/box.go similarity index 100% rename from apps/daemon/internal/util/sandbox.go rename to apps/daemon/internal/util/box.go diff --git a/apps/daemon/pkg/ssh/server.go b/apps/daemon/pkg/ssh/server.go index ced17b4f1..b18aab3a6 100644 --- a/apps/daemon/pkg/ssh/server.go +++ b/apps/daemon/pkg/ssh/server.go @@ -50,8 +50,8 @@ func (s *Server) Start() error { } else { s.logger.Debug("Received empty password") } - // Only allow authentication with the hardcoded password 'sandbox-ssh' - authenticated := password == "sandbox-ssh" + // Only allow authentication with the hardcoded password 'box-ssh' + authenticated := password == "box-ssh" if authenticated { s.logger.Debug("Password authentication succeeded", "user", ctx.User()) } else { diff --git a/apps/daemon/pkg/terminal/static/index.html b/apps/daemon/pkg/terminal/static/index.html index b37b408a4..26e54f923 100644 --- a/apps/daemon/pkg/terminal/static/index.html +++ b/apps/daemon/pkg/terminal/static/index.html @@ -2,6 +2,10 @@ Web Terminal + @@ -10,26 +14,166 @@ body { margin: 0; padding: 0; - height: 100vh; background: #000; + color: #fff; + overscroll-behavior: none; + } + html, + body { + height: 100svh; + height: 100dvh; + } + body { + display: flex; + flex-direction: column; } #terminal { - height: 100%; + flex: 1 1 auto; + min-height: 0; width: 100%; } + .xterm-viewport { + overflow-y: auto !important; + } + /* Mirrors dashboard dark tokens; iframe boundaries prevent cascade. */ + :root { + --foreground: 40 18% 92%; + --secondary: 220 11% 14%; + --secondary-foreground: 40 18% 92%; + --accent: 220 11% 16%; + --accent-foreground: 40 18% 92%; + --border: 220 10% 20%; + --primary: 40 18% 92%; + --primary-foreground: 220 16% 8%; + --ring: 40 18% 92%; + --radius: 0.375rem; + } + #softkeys { + display: none; + flex-wrap: nowrap; + align-items: center; + gap: 6px; + background: rgba(0, 0, 0, 0.92); + border-top: 1px solid hsl(var(--border)); + overflow-x: auto; + overscroll-behavior: contain; + -webkit-user-select: none; + user-select: none; + padding: 6px 6px calc(6px + env(safe-area-inset-bottom, 0px)); + } + #softkeys button { + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 36px; + height: 32px; + padding: 0 12px; + font-size: 13px; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + font-weight: 500; + color: hsl(var(--secondary-foreground)); + background: hsl(var(--secondary)); + border: 1px solid hsl(var(--border)); + border-radius: calc(var(--radius) - 1px); + transition: + background-color 120ms ease, + color 120ms ease, + border-color 120ms ease, + box-shadow 120ms ease; + touch-action: manipulation; + cursor: pointer; + white-space: nowrap; + } + @media (hover: hover) { + #softkeys button:hover { + background: hsl(var(--accent)); + color: hsl(var(--accent-foreground)); + } + } + #softkeys button:active { + background: hsl(var(--accent)); + } + #softkeys button:focus-visible { + outline: none; + border-color: hsl(var(--ring)); + box-shadow: 0 0 0 2px hsl(var(--ring) / 0.25); + } + #softkeys button.armed, + #softkeys button.locked, + #softkeys button.trackpad-active { + background: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + border-color: hsl(var(--primary)); + } + #softkeys button.locked { + box-shadow: 0 0 0 2px hsl(var(--ring) / 0.35); + } + @media (max-width: 768px), (pointer: coarse) { + #softkeys { + display: flex; + } + }
+ diff --git a/apps/daemon/pkg/toolbox/controller.go b/apps/daemon/pkg/toolbox/controller.go index 8faaf5432..3e13bb08e 100644 --- a/apps/daemon/pkg/toolbox/controller.go +++ b/apps/daemon/pkg/toolbox/controller.go @@ -22,7 +22,7 @@ import ( // @Router /init [post] // // @id Initialize -func (s *server) Initialize(otelServiceName string, entrypointLogFilePath string, organizationId, regionId *string) gin.HandlerFunc { +func (s *server) Initialize(otelServiceName string, entrypointLogFilePath string, organizationId, regionId, traceParent *string) gin.HandlerFunc { return func(ctx *gin.Context) { var req InitializeRequest if err := ctx.ShouldBindJSON(&req); err != nil { @@ -32,7 +32,7 @@ func (s *server) Initialize(otelServiceName string, entrypointLogFilePath string s.authToken = req.Token - err := s.initTelemetry(ctx.Request.Context(), otelServiceName, entrypointLogFilePath, organizationId, regionId) + err := s.initTelemetry(ctx.Request.Context(), otelServiceName, entrypointLogFilePath, organizationId, regionId, traceParent) if err != nil { ctx.AbortWithError(http.StatusBadRequest, err) return diff --git a/apps/daemon/pkg/toolbox/docs/docs.go b/apps/daemon/pkg/toolbox/docs/docs.go index 7f82aa97c..51ae39e8b 100644 --- a/apps/daemon/pkg/toolbox/docs/docs.go +++ b/apps/daemon/pkg/toolbox/docs/docs.go @@ -2585,7 +2585,7 @@ const docTemplate = `{ }, "/process/session/entrypoint/logs": { "get": { - "description": "Get logs for a sandbox entrypoint session. Supports both HTTP and WebSocket streaming.", + "description": "Get logs for a box entrypoint session. Supports both HTTP and WebSocket streaming.", "produces": [ "text/plain" ], diff --git a/apps/daemon/pkg/toolbox/docs/swagger.json b/apps/daemon/pkg/toolbox/docs/swagger.json index 18238f4c7..d93d00f72 100644 --- a/apps/daemon/pkg/toolbox/docs/swagger.json +++ b/apps/daemon/pkg/toolbox/docs/swagger.json @@ -2223,7 +2223,7 @@ }, "/process/session/entrypoint/logs": { "get": { - "description": "Get logs for a sandbox entrypoint session. Supports both HTTP and WebSocket streaming.", + "description": "Get logs for a box entrypoint session. Supports both HTTP and WebSocket streaming.", "produces": ["text/plain"], "tags": ["process"], "summary": "Get entrypoint logs", diff --git a/apps/daemon/pkg/toolbox/docs/swagger.yaml b/apps/daemon/pkg/toolbox/docs/swagger.yaml index dc9381af2..17fdb309f 100644 --- a/apps/daemon/pkg/toolbox/docs/swagger.yaml +++ b/apps/daemon/pkg/toolbox/docs/swagger.yaml @@ -2761,8 +2761,8 @@ paths: - process /process/session/entrypoint/logs: get: - description: Get logs for a sandbox entrypoint session. Supports both HTTP and - WebSocket streaming. + description: Get logs for a box entrypoint session. Supports both HTTP and WebSocket + streaming. operationId: GetEntrypointLogs parameters: - description: Follow logs in real-time (WebSocket only) diff --git a/apps/daemon/pkg/toolbox/process/session/log.go b/apps/daemon/pkg/toolbox/process/session/log.go index 94d5b1fa2..e8271010c 100644 --- a/apps/daemon/pkg/toolbox/process/session/log.go +++ b/apps/daemon/pkg/toolbox/process/session/log.go @@ -58,7 +58,7 @@ func (s *SessionController) GetSessionCommandLogs(c *gin.Context) { // GetEntrypointLogs godoc // // @Summary Get entrypoint logs -// @Description Get logs for a sandbox entrypoint session. Supports both HTTP and WebSocket streaming. +// @Description Get logs for a box entrypoint session. Supports both HTTP and WebSocket streaming. // @Tags process // @Produce text/plain // @Param follow query boolean false "Follow logs in real-time (WebSocket only)" diff --git a/apps/daemon/pkg/toolbox/process/types.go b/apps/daemon/pkg/toolbox/process/types.go index a92893dd3..c32a73e4d 100644 --- a/apps/daemon/pkg/toolbox/process/types.go +++ b/apps/daemon/pkg/toolbox/process/types.go @@ -12,7 +12,7 @@ type ExecuteRequest struct { Cwd *string `json:"cwd,omitempty" validate:"optional"` } // @name ExecuteRequest -// TODO: Set ExitCode as required once all sandboxes migrated to the new daemon +// TODO: Set ExitCode as required once all boxes migrated to the new daemon type ExecuteResponse struct { ExitCode int `json:"exitCode"` Result string `json:"result" validate:"required"` diff --git a/apps/daemon/pkg/toolbox/server.go b/apps/daemon/pkg/toolbox/server.go index a50bff087..9624f3dcc 100644 --- a/apps/daemon/pkg/toolbox/server.go +++ b/apps/daemon/pkg/toolbox/server.go @@ -55,12 +55,13 @@ type ServerConfig struct { WorkDir string ConfigDir string ComputerUse computeruse.IComputerUse - SandboxId string + BoxId string OtelEndpoint *string SessionService *session_svc.SessionService RecordingService *recording.RecordingService OrganizationId *string RegionId *string + TraceParent *string EntrypointLogFilePath string } @@ -68,7 +69,7 @@ func NewServer(config ServerConfig) *server { return &server{ logger: config.Logger.With(slog.String("component", "toolbox_server")), WorkDir: config.WorkDir, - SandboxId: config.SandboxId, + BoxId: config.BoxId, otelEndpoint: config.OtelEndpoint, telemetry: Telemetry{}, sessionService: config.SessionService, @@ -76,6 +77,7 @@ func NewServer(config ServerConfig) *server { recordingService: config.RecordingService, organizationId: config.OrganizationId, regionId: config.RegionId, + traceParent: config.TraceParent, entrypointLogFilePath: config.EntrypointLogFilePath, } } @@ -83,7 +85,7 @@ func NewServer(config ServerConfig) *server { type server struct { WorkDir string ComputerUse computeruse.IComputerUse - SandboxId string + BoxId string logger *slog.Logger otelEndpoint *string authToken string @@ -96,6 +98,7 @@ type server struct { httpServer *http.Server organizationId *string regionId *string + traceParent *string ctx context.Context cancel context.CancelFunc } @@ -120,7 +123,7 @@ func (s *server) Start() error { gin.SetMode(gin.ReleaseMode) } - otelServiceName := fmt.Sprintf("sandbox-%s", s.SandboxId) + otelServiceName := fmt.Sprintf("box-%s", s.BoxId) r := gin.New() r.Use(common_errors.Recovery()) @@ -152,7 +155,7 @@ func (s *server) Start() error { r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler)) } - r.POST("/init", s.Initialize(otelServiceName, s.entrypointLogFilePath, s.organizationId, s.regionId)) + r.POST("/init", s.Initialize(otelServiceName, s.entrypointLogFilePath, s.organizationId, s.regionId, s.traceParent)) r.GET("/version", s.GetVersion) diff --git a/apps/daemon/pkg/toolbox/telemetry.go b/apps/daemon/pkg/toolbox/telemetry.go index dcfbce21e..c8f002b3d 100644 --- a/apps/daemon/pkg/toolbox/telemetry.go +++ b/apps/daemon/pkg/toolbox/telemetry.go @@ -11,9 +11,25 @@ import ( "github.com/boxlite-ai/common-go/pkg/log" "github.com/boxlite-ai/common-go/pkg/telemetry" "github.com/boxlite-ai/daemon/internal" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" ) -func (s *server) initTelemetry(ctx context.Context, serviceName, entrypointLogFilePath string, organizationId, regionId *string) error { +// seedBootSpanFromTraceParent starts+ends one "box.boot" span parented on the propagated +// W3C traceparent (BOXLITE_TRACEPARENT, injected by the runner at box create) so the box's +// telemetry joins the SAME traceId as the api->runner spans instead of rooting a fresh trace. +// No-op when traceParent is nil/empty, so behavior is unchanged unless a trace was propagated. +// Pure (takes the tracer) so it is unit-testable with an in-memory TracerProvider. +func seedBootSpanFromTraceParent(ctx context.Context, tracer trace.Tracer, traceParent *string) { + if traceParent == nil || *traceParent == "" { + return + } + bootCtx := propagation.TraceContext{}.Extract(ctx, propagation.MapCarrier{"traceparent": *traceParent}) + _, bootSpan := tracer.Start(bootCtx, "box.boot") + bootSpan.End() +} + +func (s *server) initTelemetry(ctx context.Context, serviceName, entrypointLogFilePath string, organizationId, regionId, traceParent *string) error { if s.otelEndpoint == nil { s.logger.InfoContext(ctx, "Otel endpoint not provided, skipping telemetry initialization") return nil @@ -42,7 +58,7 @@ func (s *server) initTelemetry(ctx context.Context, serviceName, entrypointLogFi ServiceVersion: internal.Version, Endpoint: *s.otelEndpoint, Headers: map[string]string{ - "sandbox-auth-token": s.authToken, + "box-auth-token": s.authToken, }, } @@ -110,7 +126,7 @@ func (s *server) initTelemetry(ctx context.Context, serviceName, entrypointLogFi }() // Initialize OpenTelemetry metrics - mp, err := telemetry.InitMetrics(ctx, config, "boxlite.sandbox") + mp, err := telemetry.InitMetrics(ctx, config, "boxlite.box") if err != nil { if shutDownErr := lp.Shutdown(telemetryContext); shutDownErr != nil { s.logger.ErrorContext(ctx, "Failed to shutdown logger after metrics initialization failure", "shutdownErr", shutDownErr) @@ -134,6 +150,9 @@ func (s *server) initTelemetry(ctx context.Context, serviceName, entrypointLogFi s.telemetry.MeterProvider = mp s.telemetry.LoggerProvider = lp + // Make the box's telemetry join the api->runner traceId (instead of rooting a fresh trace). + seedBootSpanFromTraceParent(ctx, tp.Tracer("boxlite.box"), traceParent) + s.logger.InfoContext(ctx, "Telemetry initialized successfully") return nil } diff --git a/apps/daemon/pkg/toolbox/telemetry_traceparent_test.go b/apps/daemon/pkg/toolbox/telemetry_traceparent_test.go new file mode 100644 index 000000000..6714f0d34 --- /dev/null +++ b/apps/daemon/pkg/toolbox/telemetry_traceparent_test.go @@ -0,0 +1,53 @@ +// Copyright 2026 BoxLite AI +// SPDX-License-Identifier: AGPL-3.0 + +package toolbox + +import ( + "context" + "testing" + + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +// With a propagated W3C traceparent, the daemon's boot span must share the SAME traceId as the +// api->runner spans and carry a remote parent — this is what makes "one traceId finds the box". +// TraceID comes from production code (Extract+Start), so the assertion is non-tautological. +func TestSeedBootSpanFromTraceParentJoinsApiTraceId(t *testing.T) { + exp := tracetest.NewInMemoryExporter() + tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp)) + defer func() { _ = tp.Shutdown(context.Background()) }() + + traceParent := "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" + seedBootSpanFromTraceParent(context.Background(), tp.Tracer("boxlite.box"), &traceParent) + + spans := exp.GetSpans() + if len(spans) != 1 { + t.Fatalf("want exactly 1 boot span, got %d", len(spans)) + } + if spans[0].Name != "box.boot" { + t.Fatalf("boot span name = %q, want box.boot", spans[0].Name) + } + if got := spans[0].SpanContext.TraceID().String(); got != "0af7651916cd43dd8448eb211c80319c" { + t.Fatalf("boot span traceID = %s, want api traceID 0af7651916cd43dd8448eb211c80319c", got) + } + if !spans[0].Parent.IsRemote() { + t.Fatalf("boot span parent must be the remote api/runner span context") + } +} + +// No traceparent => no boot span (behavior identical to before the fix; safe to ship dark). +func TestSeedBootSpanFromTraceParentNoopWhenAbsent(t *testing.T) { + exp := tracetest.NewInMemoryExporter() + tp := sdktrace.NewTracerProvider(sdktrace.WithSyncer(exp)) + defer func() { _ = tp.Shutdown(context.Background()) }() + + seedBootSpanFromTraceParent(context.Background(), tp.Tracer("boxlite.box"), nil) + empty := "" + seedBootSpanFromTraceParent(context.Background(), tp.Tracer("boxlite.box"), &empty) + + if n := len(exp.GetSpans()); n != 0 { + t.Fatalf("expected no boot span when traceparent absent, got %d", n) + } +} diff --git a/apps/daemon/project.json b/apps/daemon/project.json index 25b393f2f..b839b86d4 100644 --- a/apps/daemon/project.json +++ b/apps/daemon/project.json @@ -2,7 +2,7 @@ "name": "daemon", "$schema": "../../node_modules/nx/schemas/project-schema.json", "projectType": "application", - "sourceRoot": "apps/daemon", + "sourceRoot": "daemon", "tags": [], "targets": { "prepare": { diff --git a/apps/daemon/tools/xterm.go b/apps/daemon/tools/xterm.go index 891d53421..26ddb2f37 100644 --- a/apps/daemon/tools/xterm.go +++ b/apps/daemon/tools/xterm.go @@ -14,6 +14,11 @@ import ( "runtime" ) +// Pinned to the last release of the legacy `xterm` npm package. +// xterm moved to `@xterm/xterm` from v6.x onwards; v6.0.0 also shipped a +// touch-scroll regression (xtermjs/xterm.js#5489, fixed only in v7.0.0 +// via #5563). Do not bump to 6.x. If we ever migrate to @xterm/xterm, +// target >=7.0.0 and re-verify touch scrolling on mobile. const ( XTERM_VERSION = "5.3.0" XTERM_FIT_VERSION = "0.8.0" diff --git a/apps/dashboard/.storybook/main.ts b/apps/dashboard/.storybook/main.ts index 8ff6f84da..cfcac376f 100644 --- a/apps/dashboard/.storybook/main.ts +++ b/apps/dashboard/.storybook/main.ts @@ -21,10 +21,6 @@ const config: StorybookConfig = { return mergeConfig(config, { resolve: { alias: [ - { - find: '@boxlite-ai/sdk', - replacement: path.resolve(__dirname, '../../../libs/sdk-typescript/src'), - }, { find: '@', replacement: path.resolve(__dirname, '../src'), diff --git a/apps/dashboard/CLAUDE.md b/apps/dashboard/CLAUDE.md new file mode 100644 index 000000000..14061e294 --- /dev/null +++ b/apps/dashboard/CLAUDE.md @@ -0,0 +1,93 @@ +# CLAUDE.md — apps/dashboard + +> Scope note: this file is auto-loaded for work under `apps/dashboard`. The repo-root +> [CLAUDE.md](../../CLAUDE.md) (Workflow / Code Style, hook-audited) still applies on top. +> For local run commands see [apps/CLAUDE.md](../CLAUDE.md). + +## Project Background + +`apps/dashboard` is the **only** frontend in the BoxLite monorepo — the web console for the +BoxLite compute platform (lightweight stateful VMs / "Boxes"). All other `apps/*` are Go/Rust +backend services and are **out of scope** for frontend work. + +Stack: **Vite + React + TypeScript + Tailwind + shadcn/ui (Radix)**, nx workspace, yarn. +Data: **TanStack React Query**. Auth: **OIDC** (`react-oidc-context`). Realtime: **socket.io** ++ **Svix**. Server APIs are consumed via the OpenAPI-generated `@boxlite-ai/api-client` / +`@boxlite-ai/analytics-api-client` packages — the dashboard never hand-writes HTTP. + +## The Current Goal — "Face-Swap" UI Rewrite (branch `feat/dashboard-floating-restyle`, PR #820) + +**Completely rebuild the dashboard UI/UX and visual design language, while adding ZERO new +server APIs or endpoints.** Tear out the presentation layer and rebuild it; reconnect the new +UI to the **existing** data hooks, unchanged. The server contract is frozen. + +This is only safe because the codebase has a clean seam: UI never touches HTTP directly — it +goes through React Query hooks, which go through one `ApiClient`, which wraps generated clients. + +``` +server API ─(same-origin /api, Vite proxy)─ @boxlite-ai/*-api-client (OpenAPI, generated) + └─ src/api/apiClient.ts ←token← providers/ApiProvider (OIDC) + └─ hooks/queries/* (19) + hooks/mutations/* (27) ← FUNCTIONAL CONTRACT + └─ pages/* + components/* ← THE "FACE" — rebuild this +``` + +## PRESERVE vs REBUILD — the hard boundary + +| Layer | Paths | Action | +|---|---|---| +| Generated API clients | `@boxlite-ai/api-client`, `@boxlite-ai/analytics-api-client` | **Never touch** | +| API wrapper | `src/api/apiClient.ts`, `src/billing-api/`, `src/services/webhookService.ts` | **Preserve** | +| Data hooks (the contract) | `src/hooks/queries/*`, `src/hooks/mutations/*`, `src/hooks/queries/queryKeys.ts` | **Preserve** — new UI imports these as-is | +| Auth / config / context | `src/providers/*`, `src/contexts/*` | **Preserve** (incl. nesting order, see below) | +| Routing & boot | `src/enums/RoutePath.ts`, `src/App.tsx`, `src/main.tsx` | **Preserve structure**; route visibility may change | +| Mock target (MSW) | `src/mocks/*` | **Preserve** — backend-free dev (`npm run start:mock`) | +| Design system | `tailwind.config.js`, `src/index.css`, `src/components/ui/*`, `.storybook/`, ui stories | **Rebuild** | +| Business UI | `src/pages/*` (27), `src/components/*` (~200, except contexts/providers logic) | **Rebuild** | + +Rule of thumb: if a file decides **what data to fetch / how state flows / who you are**, preserve it. +If it decides **how it looks**, rebuild it. + +## Non-negotiable behavioral constraints (must survive the rewrite) + +1. **Org scoping** — nearly every hook depends on `useSelectedOrganization()` and passes + `organizationId` to the API. The new UI MUST keep a current-organization selector and thread + the org id through. Dropping it breaks every data call. +2. **Realtime & polling** — keep: `useBoxQuery` 3s polling while a box is transitioning; + socket.io (`/api/socket.io/`) box/runner/volume events; Svix webhook portal. Don't strip + subscriptions when replacing components. +3. **Provider nesting order is load-bearing** (each depends on the one above). Preserve: + `Query → Theme → Config(/api/config + OIDC) → PostHog → [/dashboard] Api → Organizations → + SelectedOrganization → Regions → NotificationSocket → CommandPalette → Banner`. +4. **Cache invalidation lives in the mutations** — reuse the existing mutation hooks and you + inherit correct invalidation for free (e.g. create/delete → invalidate list; tier/wallet → + also usage.overview; coupon → wallet+tier+usage). Do not re-implement writes in components. + +## Functional contract surface (domains the UI must keep wiring up) + +Boxes (core: `useBoxes`, `useBoxQuery`, `useTerminalSessionQuery`; create/start/stop/delete/ +recover/ssh mutations) · API Keys · Organizations · Billing/Wallet/Invoices/Tiers (via +`BillingApiClient`, owner-scoped helpers in `billingQueries.ts`) · Volumes · Audit · Regions · +Runners · Webhooks (Svix) · Analytics/Usage (needs `config.analyticsApiUrl`) · Users. + +## Current visibility + +Only **9 pages are active**; 13 are redirected to `/boxes` via `HIDDEN_DASHBOARD_ROUTES` in +`App.tsx` (Images, Volumes, Limits, BillingSpending, BillingWallet, Members, Roles, AuditLogs, +Regions, Runners, Experimental, Webhooks, WebhookEndpointDetails). Their hooks/components still +exist. Active: Landing · Dashboard (shell/nav) · **Boxes** (list + detail + fullscreen terminal + +lifecycle + SSH + onboarding — the core) · Keys · Billing · Admin · OrganizationSettings · +EmailVerify · Logout. + +## Recommended rebuild path + +1. Develop against `npm run start:mock` (MSW, no backend, no login) for fast UI iteration. +2. Rebuild bottom-up: design tokens (`tailwind.config.js` + `index.css`) → `components/ui/*` + primitives → business `components/*` → `pages/*` → `Dashboard.tsx` shell. **The hook layer + stays untouched throughout.** +3. Prioritize the Dashboard shell + the full Boxes experience — ~80% of visible value. +4. Keep the 13 hidden pages hidden during the main restyle; un-hide + restyle them afterwards. + +## Dev commands + +Run from `apps/` (nx root): `npm run start:mock` (MSW, recommended for restyle) · +`npm run start` (dev API) · Storybook for the design system. See [apps/CLAUDE.md](../CLAUDE.md). diff --git a/apps/dashboard/RESTYLE_PLAN.md b/apps/dashboard/RESTYLE_PLAN.md new file mode 100644 index 000000000..52c2eda99 --- /dev/null +++ b/apps/dashboard/RESTYLE_PLAN.md @@ -0,0 +1,115 @@ +# BoxLite Console Restyle — Execution Plan + +Companion to [CLAUDE.md](./CLAUDE.md). This is the working spec for the "face-swap" rewrite: +rebuild the UI to the new ASCII/terminal design, reconnect to the **existing** data hooks +unchanged, add **zero** new server APIs. + +Design source (visual/interaction spec only, NOT importable code — DCLogic prototypes): +`~/Downloads/ASCII-BOXLITE-CONSOLE-PAGE/*.dc.html`. + +## Locked decisions (2026-06-19) + +1. **Login** → re-skin OIDC. Keep `signinRedirect`; SSO buttons trigger OIDC; the email/password + + signup form is visual-only / forwards to hosted login. **No backend auth API is added.** +2. **Billing** → ship the "Billing is on the way" empty state only (matches current live behavior). + The rich usage/plans/invoices design is deferred (maps to currently-hidden Spending/Wallet/Limits). +3. **Org switcher** → standalone control in the top Nav (all hooks are org-scoped; must not drop it). +4. **Undesigned pages** → rebuild needed active pages (Admin, OrganizationSettings, EmailVerify) in the + new design language; the 13 hidden pages stay hidden. + +## New design language + +- Font **IBM Plex Mono** everywhere, 13px base. Corners **square (radius 0)**. Dotted/dashed dividers. +- Tokens (dark): bg `#13161B` · card `#1A1D24` · term `#0D0F13` · fg `#FFF` · dim `#8C919C` · + border `#2A2F3A` · accent `#00B0F0` · up `#5ad67d`. +- Tokens (light): bg `#FFF` · card `#F3F4F6` · term `#F5F7FA` · fg `#13161B` · dim `#6B7079` · + border `#E2E5EA` · accent `#00B0F0`. +- Status: RUNNING `#5ad67d` · IDLE `#e0b341` · STOPPED/ERROR `#e0564a`. +- Theme persisted to localStorage `boxlite-theme` (system/light/dark) — unify with `ThemeContext`. +- Motifs: ASCII activity strips (4-level blue), ASCII orb (plan cards), live-ticking numbers, + scanline animation, `▸` prefixes. Nav is a **top horizontal bar** (replaces the shadcn sidebar). + +## Page → feature → hooks map (contract preserved) + +| New design | Existing page/components | Reused hooks (unchanged) | +|---|---|---| +| Login.dc | LandingPage.tsx | react-oidc-context `signinRedirect` | +| Nav.dc | Dashboard.tsx shell | ThemeContext, useAuth (sign out), useSelectedOrganization (+ org switcher), quickstart flag | +| Boxes page.dc | Boxes.tsx + BoxTable + CreateBoxDialog | useBoxes, useCreateBoxMutation, useStart/Stop/DeleteBoxMutation, useCreateSshAccessMutation; stat cards → analytics usage (graceful-degrade if `analyticsApiUrl` unset) | +| Box detail.dc | components/boxes/* (BoxDetails, BoxTerminalTab) | useBoxQuery (transition poll), start/stop/delete/recover, useTerminalSessionQuery, SSH mutations. NOTE: new design shows only spec panel + shell terminal, so the old logs/metrics/traces/spending tabs (BoxContentTabs) are NOT rendered anymore — their components/hooks still exist and can be re-added if wanted. | +| API keys.dc | Keys.tsx + ApiKeyTable + CreateApiKeyDialog | useApiKeysQuery, useCreateApiKeyMutation, useRevokeApiKeyMutation | +| Quickstart.dc | Onboarding / OnboardingGuideDialog | useCreateApiKeyMutation (run-step is decorative) | +| Billing.dc (empty) | Billing.tsx | none (static) | +| Usage and billings.dc / Billing.standalone.dc | (deferred) Spending/Wallet/Limits | analytics + billing hooks — NOT in this pass | + +## Progress + +- ✅ **P0 done & verified** (mock render): index.css token map, IBM Plex Mono, square corners, + `brand` blue, theme key `boxlite-theme` (default dark). All 41 `components/ui/*` re-skinned via token swap. +- ✅ **P1 done & verified**: `components/Sidebar.tsx` rewritten as the Nav.dc top bar (logo · Boxes · + Billing · Admin[if perm] · Search⌘K · API Keys · Guide progress · **standalone org switcher** · + profile menu with appearance/docs/discord/sign out). All command/onboarding wiring preserved. + Org switcher lists orgs + calls `onSelectOrganization`; verified via Playwright. +- ✅ **P2 done & verified** (Boxes core): `pages/Boxes.tsx` rebuilt to the `Boxes page.dc.html` layout + (Fleet header + 3 stat cards w/ LIVE pulse, fed real box-derived data: running/total/vCPU) · + `components/BoxTable/index.tsx` rewritten as the design table (▸ name, status dot+label at exact + hex, CPU/RAM/DISK quota, relative Created, row actions play/pause/recover + terminal + more-menu) · + `components/Box/CreateBoxDialog.tsx` rebuilt as the New Box modal (name, image dropdown, segmented + CPU/Mem/Disk, live price/hr). All wired to existing handlers + useCreateBoxMutation; verified via Playwright. + Note: bulk-select & column-sort are not in the new design, so they're dropped from this view (handlers preserved in Boxes.tsx). +- ✅ **P4 done & verified** (API Keys): `pages/Keys.tsx` design header + table; `components/ApiKeyTable.tsx` + rebuilt as the design grid (Name/Key/Permissions/Created/Last Used/Expires + trash revoke + div empty + state + "25 per page" footer); `components/CreateApiKeyDialog.tsx` rebuilt as the Create modal (name, + expires preset dropdown, Boxes-API-access info) + one-time reveal modal (blue key box + Copy). Full + create→reveal flow verified against the real useCreateApiKeyMutation (returns mock key). +- ✅ **P6 Billing done & verified**: `pages/Billing.tsx` rebuilt to `Billing.dc.html` empty state + (centered "Billing is on the way" + 4 metering-dimension cards with seg-wave animation + Back to Fleet). + Added `seg-wave` keyframe to index.css. +- ✅ **Login done** (compiled; mock auto-auths so not screenshot-verifiable here): `pages/LandingPage.tsx` + rebuilt to `Login.dc.html` (SIGN IN/SIGN UP tabs, Google/GitHub SSO, email/password, show/hide, remember, + signup confirm). Every action calls OIDC signinRedirect — no new auth API. +- ✅ **P3 done & verified** (Box detail): `components/boxes/BoxDetails.tsx` render rewritten to + `Box detail.dc.html` (breadcrumb · identity strip with status badge + image chip + start/stop/recover/ + ssh/more/refresh · left spec readout GENERAL/RESOURCES/LIFECYCLE/TIMESTAMPS with dotted leaders · right + SHELL panel embedding the real BoxTerminalTab). All hooks/handlers (useBoxQuery poll, ws sync, + start/stop/recover/delete, SSH dialogs, onboarding) preserved. Verified via Playwright. +- ✅ **P5 done & verified** (Quickstart): `components/OnboardingGuideDialog.tsx` rebuilt as the + `Quickstart.dc.html` 3-step wizard (stage rail · step1 real key creation via apiKeyApi.createApiKey · + step2 language tabs + install cmd · step3 run animation · "Box is live." confetti finale). Marks + onboarding progress + boxlite-quickstart-done. Verified via Playwright. + +**ALL designed surfaces complete** (Login · Nav · Boxes+NewBox · Box detail+terminal · API Keys+create/reveal · +Quickstart · Billing). Every page is pixel-rebuilt to the .dc.html design and wired to the unchanged +hooks/mutations/providers. + +- ✅ **P7 done** (undesigned active pages reshelled in the new language): + - `pages/OrganizationSettings.tsx` — mono header + bordered details card (name+Save, id+copy, default region). Verified. + - `pages/EmailVerify.tsx` — mono centered status card (loading/success/error). Compiled. + - `pages/Admin.tsx` — page chrome reshelled (mono header, bordered segmented view-switch, brand search); + heavy data-dense sub-views (AdminStatusStrip/Overview/People/Fleet/TelemetryDrawer) kept on the new + tokens. 403-gated in mock (redirects), so not screenshot-verifiable here; compiles + gate works. + +**Restyle complete.** Outstanding (non-blocking): full end-to-end verification against a real dev API +(login flow, terminal session, org switch reload, billing/analytics-gated paths) + Storybook refresh. +Verification screenshots saved at repo root (p0–p7 *.png). + +## Phases + +- **P0 — Design-system foundation.** New token set + `boxlite-theme` unified into ThemeContext; + rewrite `index.css` + `tailwind.config.js`; re-skin `components/ui/*` (keep Radix behavior): + button, dialog, table, input, badge, tabs, dropdown-menu, card, tooltip, sonner, etc. +- **P1 — Shell.** Dashboard.tsx sidebar → top Nav (theme switch, sign out, Guide, **org switcher**). + Preserve Outlet, routing, and the full provider nesting order. +- **P2 — Boxes (core).** Fleet list + stat cards + New Box modal + inline start/stop/ssh/delete. +- **P3 — Box detail.** Spec readout + real terminal panel (useTerminalSessionQuery + existing + BoxTerminal*) + lifecycle. (Design = spec + shell only; logs/metrics/traces/spending tabs dropped from the UI.) +- **P4 — API Keys.** Table + create modal + one-time key reveal. +- **P5 — Quickstart/Onboarding.** 3-step guide wired to key creation. +- **P6 — Billing.** Empty-state page only. +- **P7 — Finish.** New-language shells for Admin / OrganizationSettings / EmailVerify; hidden pages + stay hidden. Full `start:mock` regression + real dev-API verification; update Storybook. + +## Invariants (every phase) + +Org scoping · transition polling + socket.io realtime · cache invalidation via reused mutations · +provider nesting order. Develop on `npm run start:mock` (from `apps/`); verify against dev API. diff --git a/apps/dashboard/eslint.config.mjs b/apps/dashboard/eslint.config.mjs index f4b30e51b..02d720fcd 100644 --- a/apps/dashboard/eslint.config.mjs +++ b/apps/dashboard/eslint.config.mjs @@ -1,5 +1,5 @@ import nx from '@nx/eslint-plugin' -import baseConfig from '../../eslint.config.mjs' +import baseConfig from '../eslint.config.mjs' export default [ ...baseConfig, diff --git a/apps/dashboard/index.html b/apps/dashboard/index.html index be460bda0..bd5e97552 100644 --- a/apps/dashboard/index.html +++ b/apps/dashboard/index.html @@ -6,15 +6,23 @@ BoxLite + + +
diff --git a/apps/dashboard/project.json b/apps/dashboard/project.json index a4702b6ab..778cf7978 100644 --- a/apps/dashboard/project.json +++ b/apps/dashboard/project.json @@ -1,7 +1,7 @@ { "name": "dashboard", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/dashboard/src", + "sourceRoot": "dashboard/src", "projectType": "application", "tags": [], "targets": { @@ -14,7 +14,7 @@ "dependsOn": [ { "target": "build", - "projects": "sdk-typescript" + "projects": ["api-client", "analytics-api-client"] } ] }, diff --git a/apps/dashboard/public/favicon.ico b/apps/dashboard/public/favicon.ico index 602d9e3e4..b46868ca5 100644 Binary files a/apps/dashboard/public/favicon.ico and b/apps/dashboard/public/favicon.ico differ diff --git a/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx index 30cd32371..b5e277545 100644 --- a/apps/dashboard/src/App.tsx +++ b/apps/dashboard/src/App.tsx @@ -4,21 +4,14 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' -import Onboarding from '@/pages/Onboarding' -import OrganizationMembers from '@/pages/OrganizationMembers' -import OrganizationSettings from '@/pages/OrganizationSettings' -import UserOrganizationInvitations from '@/pages/UserOrganizationInvitations' import { NotificationSocketProvider } from '@/providers/NotificationSocketProvider' import { OrganizationsProvider } from '@/providers/OrganizationsProvider' import { SelectedOrganizationProvider } from '@/providers/SelectedOrganizationProvider' -import { UserOrganizationInvitationsProvider } from '@/providers/UserOrganizationInvitationsProvider' import { initPylon } from '@/vendor/pylon' -import { OrganizationRolePermissionsEnum, OrganizationUserRoleEnum } from '@boxlite-ai/api-client' -import { useFeatureFlagEnabled, usePostHog } from 'posthog-js/react' +import { usePostHog } from 'posthog-js/react' import React, { Suspense, useEffect } from 'react' import { useAuth } from 'react-oidc-context' -import { Navigate, Route, Routes, useLocation } from 'react-router-dom' +import { Navigate, Outlet, Route, Routes, useLocation } from 'react-router-dom' import { BannerProvider } from './components/Banner' import { CommandPaletteProvider } from './components/CommandPalette' import LoadingFallback from './components/LoadingFallback' @@ -32,34 +25,46 @@ import { DialogTitle, } from './components/ui/dialog' import { BOXLITE_DOCS_URL, BOXLITE_SLACK_URL } from './constants/ExternalLinks' -import { FeatureFlags } from './enums/FeatureFlags' import { RoutePath, getRouteSubPath } from './enums/RoutePath' import { useConfig } from './hooks/useConfig' -import AccountSettings from './pages/AccountSettings' -import AuditLogs from './pages/AuditLogs' import Dashboard from './pages/Dashboard' -import EmailVerify from './pages/EmailVerify' -import Experimental from './pages/Experimental' -import Keys from './pages/Keys' import LandingPage from './pages/LandingPage' -import Limits from './pages/Limits' import Logout from './pages/Logout' import NotFound from './pages/NotFound' -import Playground from './pages/Playground' -import Regions from './pages/Regions' -import Registries from './pages/Registries' -import Runners from './pages/Runners' -import Sandboxes from './pages/Sandboxes' -import Snapshots from './pages/Snapshots' -import Spending from './pages/Spending' -import Volumes from './pages/Volumes' -import Wallet from './pages/Wallet' -import WebhookEndpointDetails from './pages/WebhookEndpointDetails' -import Webhooks from './pages/Webhooks' -import { SandboxDetails } from './components/sandboxes' +import Boxes from './pages/Boxes' + +// Code-split the heavier, not-first-paint routes out of the main bundle. They +// load on demand under the dashboard Suspense boundary, so the initial +// /boxes paint no longer ships Admin/Billing/Settings/Keys/box-details and their +// recharts/terminal deps. Boxes + the Dashboard shell stay eager (first paint). +const Keys = React.lazy(() => import('./pages/Keys')) +const Billing = React.lazy(() => import('./pages/Billing')) +const Admin = React.lazy(() => import('./pages/Admin')) +const EmailVerify = React.lazy(() => import('./pages/EmailVerify')) +const OrganizationSettings = React.lazy(() => import('@/pages/OrganizationSettings')) +const BoxDetails = React.lazy(() => import('./components/boxes').then((m) => ({ default: m.BoxDetails }))) +const BoxTerminalFullscreen = React.lazy(() => + import('./components/boxes').then((m) => ({ default: m.BoxTerminalFullscreen })), +) import { ApiProvider } from './providers/ApiProvider' import { RegionsProvider } from './providers/RegionsProvider' -import { SvixProvider } from './providers/SvixProvider' +import { BoxSessionProvider } from './providers/BoxSessionProvider' + +const HIDDEN_DASHBOARD_ROUTES = [ + RoutePath.IMAGES, + RoutePath.VOLUMES, + RoutePath.LIMITS, + RoutePath.BILLING_SPENDING, + RoutePath.BILLING_WALLET, + RoutePath.MEMBERS, + RoutePath.ROLES, + RoutePath.AUDIT_LOGS, + RoutePath.REGIONS, + RoutePath.RUNNERS, + RoutePath.EXPERIMENTAL, + RoutePath.WEBHOOKS, + RoutePath.WEBHOOK_ENDPOINT_DETAILS, +] // Simple redirection components for external URLs const DocsRedirect = () => { @@ -78,12 +83,16 @@ const SlackRedirect = () => { return null } +// Same-origin OIDC silent-renew iframes are legitimate, so frame refusal +// belongs in deployment headers. The terminal Paste action also refuses to +// read clipboard when the dashboard itself is framed. + function App() { const config = useConfig() const location = useLocation() const posthog = usePostHog() - - const { error: authError, isAuthenticated, user, signoutRedirect } = useAuth() + const { error: authError, isAuthenticated, user, removeUser } = useAuth() + const boxesRedirect = `${RoutePath.BOXES}${location.search}` useEffect(() => { if (isAuthenticated && user && posthog?.get_distinct_id() !== user.profile.sub) { @@ -123,7 +132,14 @@ function App() { {authError.message} - + @@ -133,9 +149,18 @@ function App() { return ( } /> + } /> } /> } /> } /> + } + /> + } + /> - - - - - - - - - + + + + + + + @@ -160,133 +183,36 @@ function App() { } > - } /> + } /> } /> - } /> - } /> - } /> - } /> - - - - } - /> - - - - } - /> - {config.billingApiUrl && ( - <> - - - - } - /> - - - - } - /> - } /> - - )} - - - - } - /> - { - // TODO: uncomment when we allow creating custom roles - /* - - - - - } - /> */ - } + } /> + } /> + } /> + } /> + {/* TODO(image-rewrite): legacy /dashboard/templates route removed with the templates page. */} + {/* Pathless layout route: a single BoxSessionProvider fiber + persists across the three box routes, so activation state + (e.g. "terminal connected") survives navigation between the + details view and its fullscreen siblings. Per-route providers + held state in a useRef that died with each unmount. */} - - + + + } - /> + > + } /> + } /> + + {HIDDEN_DASHBOARD_ROUTES.map((path) => ( + } /> + ))} + } /> } /> - - - } - /> - - - - - - } - /> - } - /> - } /> - } /> - - - - } - /> - } /> - - - - } - /> - - - - } + path={getRouteSubPath(RoutePath.ONBOARDING)} + element={} /> } /> @@ -294,50 +220,4 @@ function App() { ) } -function NonPersonalOrganizationPageWrapper({ children }: { children: React.ReactNode }) { - const { selectedOrganization } = useSelectedOrganization() - - if (selectedOrganization?.personal) { - return - } - - return children -} - -function OwnerAccessOrganizationPageWrapper({ children }: { children: React.ReactNode }) { - const { authenticatedUserOrganizationMember } = useSelectedOrganization() - - if (authenticatedUserOrganizationMember?.role !== OrganizationUserRoleEnum.OWNER) { - return - } - - return children -} - -function RequiredPermissionsOrganizationPageWrapper({ - children, - requiredPermissions, -}: { - children: React.ReactNode - requiredPermissions: OrganizationRolePermissionsEnum[] -}) { - const { authenticatedUserHasPermission } = useSelectedOrganization() - - if (!requiredPermissions.every((permission) => authenticatedUserHasPermission(permission))) { - return - } - - return children -} - -function RequiredFeatureFlagWrapper({ children, flagKey }: { children: React.ReactNode; flagKey: FeatureFlags }) { - const flagEnabled = useFeatureFlagEnabled(flagKey) - - if (!flagEnabled) { - return - } - - return children -} - export default App diff --git a/apps/dashboard/src/api/apiClient.test.ts b/apps/dashboard/src/api/apiClient.test.ts new file mode 100644 index 000000000..1933720a1 --- /dev/null +++ b/apps/dashboard/src/api/apiClient.test.ts @@ -0,0 +1,65 @@ +// @vitest-environment jsdom +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +const config = { apiUrl: 'http://api.test/api' } as never + +// Fresh module per test so the module-level `isHandlingUnauthorized` guard +// doesn't leak across cases. A custom axios adapter makes every request resolve +// to `status` with an empty body, so the response interceptor sees the 401. +async function makeClient(onUnauthorized: () => Promise | void, status = 401) { + vi.resetModules() + const axios = (await import('axios')).default + // A custom adapter must settle the response itself (axios doesn't re-apply + // validateStatus to a custom adapter's return), so reject non-2xx with an + // AxiosError carrying `.response`, exactly like the built-in adapters. + axios.defaults.adapter = (async (cfg: unknown) => { + const response = { data: {}, status, statusText: '', headers: {}, config: cfg } + if (status >= 200 && status < 300) return response + const err = new Error(`Request failed with status code ${status}`) as Error & Record + err.response = response + err.config = cfg + err.isAxiosError = true + throw err + }) as never + const { ApiClient } = await import('./apiClient') + return new ApiClient(config, 'tok', onUnauthorized) +} + +describe('ApiClient 401 -> bounded re-login recovery', () => { + beforeEach(() => { + window.sessionStorage.clear() + }) + afterEach(() => { + vi.restoreAllMocks() + }) + + it('first 401 triggers onUnauthorized once and suspends the caller (no error flash)', async () => { + const onUnauthorized = vi.fn(() => Promise.resolve()) + const api = await makeClient(onUnauthorized) + let settled = false + void api.organizationsApi.listOrganizations().then( + () => (settled = true), + () => (settled = true), + ) + await new Promise((r) => setTimeout(r, 30)) + expect(onUnauthorized).toHaveBeenCalledTimes(1) + // Never-settling while the redirect navigates the page away — not an error. + expect(settled).toBe(false) + }) + + it('a 401 that persists after a re-auth attempt rejects instead of bouncing forever', async () => { + window.sessionStorage.setItem('boxlite.reauth-attempted', '1') + const onUnauthorized = vi.fn(() => Promise.resolve()) + const api = await makeClient(onUnauthorized) + await expect(api.organizationsApi.listOrganizations()).rejects.toBeTruthy() + expect(onUnauthorized).not.toHaveBeenCalled() + }) + + it('a rejecting onUnauthorized resets state and surfaces an error (no hang)', async () => { + const onUnauthorized = vi.fn(() => Promise.reject(new Error('redirect start failed'))) + const api = await makeClient(onUnauthorized) + await expect(api.organizationsApi.listOrganizations()).rejects.toBeTruthy() + // Marker cleared so a later genuine 401 still gets its one recovery attempt. + expect(window.sessionStorage.getItem('boxlite.reauth-attempted')).toBeNull() + }) +}) diff --git a/apps/dashboard/src/api/apiClient.ts b/apps/dashboard/src/api/apiClient.ts index b377dd60c..f67727ad8 100644 --- a/apps/dashboard/src/api/apiClient.ts +++ b/apps/dashboard/src/api/apiClient.ts @@ -15,13 +15,10 @@ import { ApiKeysApi, AuditApi, Configuration, - DockerRegistryApi, OrganizationsApi, RegionsApi, RunnersApi, - SandboxApi, - SnapshotsApi, - ToolboxApi, + BoxApi, UsersApi, VolumesApi, WebhooksApi, @@ -29,17 +26,54 @@ import { import axios, { AxiosError } from 'axios' import { BoxliteError } from './errors' +// A burst of in-flight requests can all 401 at once when the access token goes +// invalid; this in-page guard ensures at most one re-login redirect per page +// load. It is reset if onUnauthorized throws (a failed handler must not wedge +// recovery) and is naturally cleared when signinRedirect reloads the page. +let isHandlingUnauthorized = false + +// Survives the full-page reload signinRedirect triggers, so a first stale-token +// 401 (recover silently) is distinguishable from one that persists *after* a +// re-auth already happened this session (revoked user / wrong audience / backend +// auth bug). Without a cross-reload marker a fresh-but-still-rejected token would +// bounce to login forever. sessionStorage scopes it to this tab. +const REAUTH_ATTEMPTED_KEY = 'boxlite.reauth-attempted' + +function reauthAlreadyAttempted(): boolean { + try { + return window.sessionStorage.getItem(REAUTH_ATTEMPTED_KEY) !== null + } catch { + // sessionStorage can be unavailable (privacy mode, sandboxed iframe). Treat + // as "not attempted" so we still try recovery once. + return false + } +} + +function markReauthAttempted(): void { + try { + window.sessionStorage.setItem(REAUTH_ATTEMPTED_KEY, '1') + } catch { + // best-effort; see reauthAlreadyAttempted + } +} + +function clearReauthAttempted(): void { + try { + window.sessionStorage.removeItem(REAUTH_ATTEMPTED_KEY) + } catch { + // best-effort; see reauthAlreadyAttempted + } +} + export class ApiClient { private config: Configuration - private _snapshotApi: SnapshotsApi - private _sandboxApi: SandboxApi + private onUnauthorized?: () => Promise | void + private _boxApi: BoxApi private _userApi: UsersApi private _apiKeyApi: ApiKeysApi - private _dockerRegistryApi: DockerRegistryApi private _organizationsApi: OrganizationsApi private _billingApi: BillingApiClient private _volumeApi: VolumesApi - private _toolboxApi: ToolboxApi private _auditApi: AuditApi private _regionsApi: RegionsApi private _runnersApi: RunnersApi @@ -47,18 +81,39 @@ export class ApiClient { private _analyticsUsageApi: AnalyticsUsageApi | null private _analyticsTelemetryApi: AnalyticsTelemetryApi | null - constructor(config: DashboardConfig, accessToken: string) { + constructor(config: DashboardConfig, accessToken: string, onUnauthorized?: () => Promise | void) { + this.onUnauthorized = onUnauthorized this.config = new Configuration({ basePath: config.apiUrl, accessToken: accessToken, }) const axiosInstance = axios.create() + axiosInstance.interceptors.request.use((request) => { + request.headers?.delete?.('User-Agent') + if (request.headers) { + delete (request.headers as Record)['User-Agent'] + } + return request + }) axiosInstance.interceptors.response.use( (response) => { + // A request succeeded → the token is good again; clear the cross-reload + // marker so a future stale token still gets its one silent recovery. + clearReauthAttempted() return response }, (error) => { + // A 401 means the access token is no longer accepted — it expired, or + // (the common case in the local Dex stack) it was signed by a key that + // rotated when the Dex box was recreated. oidc-client-ts only tracks + // local expiry, so it still believes the user is signed in and keeps + // replaying the dead token on every reload. Drop the session and bounce + // to a fresh login instead of a dead-end "Unauthorized" screen. + if (error?.response?.status === 401 && this.onUnauthorized) { + return this.handleUnauthorized(error) + } + let errorMessage: string if (error instanceof AxiosError && error.message.includes('timeout of')) { @@ -72,15 +127,12 @@ export class ApiClient { ) // Initialize APIs - this._snapshotApi = new SnapshotsApi(this.config, undefined, axiosInstance) - this._sandboxApi = new SandboxApi(this.config, undefined, axiosInstance) + this._boxApi = new BoxApi(this.config, undefined, axiosInstance) this._userApi = new UsersApi(this.config, undefined, axiosInstance) this._apiKeyApi = new ApiKeysApi(this.config, undefined, axiosInstance) - this._dockerRegistryApi = new DockerRegistryApi(this.config, undefined, axiosInstance) this._organizationsApi = new OrganizationsApi(this.config, undefined, axiosInstance) this._billingApi = new BillingApiClient(config.billingApiUrl || window.location.origin, accessToken) this._volumeApi = new VolumesApi(this.config, undefined, axiosInstance) - this._toolboxApi = new ToolboxApi(this.config, undefined, axiosInstance) this._auditApi = new AuditApi(this.config, undefined, axiosInstance) this._regionsApi = new RegionsApi(this.config, undefined, axiosInstance) this._runnersApi = new RunnersApi(this.config, undefined, axiosInstance) @@ -104,16 +156,55 @@ export class ApiClient { } } - public setAccessToken(accessToken: string) { - this.config.accessToken = accessToken + // Recovery is bounded to ONE re-login attempt per session: the first 401 + // drops the session and bounces to a fresh login (suppressing the error so + // the user sees a loading state, not a dead-end screen); a 401 that persists + // *after* that re-auth is surfaced as an error instead of bouncing forever. + private async handleUnauthorized(error: unknown): Promise { + // De-dupe a concurrent 401 burst: only the first drives recovery, the rest + // suspend on the in-flight redirect. Checked BEFORE the cross-reload marker + // so a second concurrent 401 isn't misread as a failed post-reauth attempt. + if (isHandlingUnauthorized) { + return new Promise(() => {}) + } + + if (reauthAlreadyAttempted()) { + // We already sent the user through a fresh login this session and the new + // token is still rejected (revoked user / wrong audience / backend bug). + // Bouncing again would loop forever, so surface the failure. + throw BoxliteError.fromString('Authentication failed after re-login. Please sign in again.', { + cause: error instanceof Error ? error : undefined, + }) + } + + isHandlingUnauthorized = true + markReauthAttempted() + try { + // onUnauthorized clears the OIDC user; ApiProvider's effect then runs + // signinRedirect, which navigates the page away. Awaited so a failed + // start (e.g. a rejecting removeUser) surfaces an error instead of + // hanging forever on the suspend below. + await this.onUnauthorized?.() + } catch (handlerError) { + isHandlingUnauthorized = false + clearReauthAttempted() + throw BoxliteError.fromString('Failed to start re-authentication.', { + cause: handlerError instanceof Error ? handlerError : undefined, + }) + } + + // Suspend the caller (never-settling promise) so it shows the loading + // state, not an error, while the redirect navigates the page away. Reached + // only on a successfully-started re-auth. + return new Promise(() => {}) } - public get snapshotApi() { - return this._snapshotApi + public setAccessToken(accessToken: string) { + this.config.accessToken = accessToken } - public get sandboxApi() { - return this._sandboxApi + public get boxApi() { + return this._boxApi } public get userApi() { @@ -124,10 +215,6 @@ export class ApiClient { return this._apiKeyApi } - public get dockerRegistryApi() { - return this._dockerRegistryApi - } - public get organizationsApi() { return this._organizationsApi } @@ -140,10 +227,6 @@ export class ApiClient { return this._volumeApi } - public get toolboxApi() { - return this._toolboxApi - } - public get auditApi() { return this._auditApi } diff --git a/apps/dashboard/src/assets/Logo.tsx b/apps/dashboard/src/assets/Logo.tsx index 3a30b49f7..acfbf6712 100644 --- a/apps/dashboard/src/assets/Logo.tsx +++ b/apps/dashboard/src/assets/Logo.tsx @@ -4,18 +4,38 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import bboxLogoDark from './bbox-logo-dark.png' -import bboxLogoLight from './bbox-logo-light.png' +import boxliteIconBlack from './boxlite-icon-black.png' +import boxliteIconLight from './boxlite-icon-light.png' +import boxliteLogoBlack from './boxlite-black.png' +import boxliteLogoLight from './boxlite-light.png' + +type LogoProps = { + className?: string + decorative?: boolean +} + +type LogoTextProps = { + className?: string +} + +export function Logo({ className = 'h-7 w-7', decorative = false }: LogoProps) { + const imageProps = decorative ? { alt: '', 'aria-hidden': true } : { alt: 'BoxLite' } -export function Logo() { return ( - BoxLite - BoxLite + + ) } -export function LogoText() { - return BoxLite +export function LogoText({ className = 'h-9 w-auto' }: LogoTextProps = {}) { + const imageClassName = `${className} object-contain` + + return ( + + BoxLite + BoxLite + + ) } diff --git a/apps/dashboard/src/assets/bbox-logo-dark.png b/apps/dashboard/src/assets/bbox-logo-dark.png deleted file mode 100644 index 3e718f54d..000000000 Binary files a/apps/dashboard/src/assets/bbox-logo-dark.png and /dev/null differ diff --git a/apps/dashboard/src/assets/bbox-logo-light.png b/apps/dashboard/src/assets/bbox-logo-light.png deleted file mode 100644 index adafb5b54..000000000 Binary files a/apps/dashboard/src/assets/bbox-logo-light.png and /dev/null differ diff --git a/apps/dashboard/src/assets/boxlite-black.png b/apps/dashboard/src/assets/boxlite-black.png new file mode 100644 index 000000000..f325a13cf Binary files /dev/null and b/apps/dashboard/src/assets/boxlite-black.png differ diff --git a/apps/dashboard/src/assets/boxlite-full-black.png b/apps/dashboard/src/assets/boxlite-full-black.png deleted file mode 100644 index d7c257f47..000000000 Binary files a/apps/dashboard/src/assets/boxlite-full-black.png and /dev/null differ diff --git a/apps/dashboard/src/assets/boxlite-full-white.png b/apps/dashboard/src/assets/boxlite-full-white.png deleted file mode 100644 index b308546e2..000000000 Binary files a/apps/dashboard/src/assets/boxlite-full-white.png and /dev/null differ diff --git a/apps/dashboard/src/assets/boxlite-icon-black.png b/apps/dashboard/src/assets/boxlite-icon-black.png new file mode 100644 index 000000000..51ffa3c9b Binary files /dev/null and b/apps/dashboard/src/assets/boxlite-icon-black.png differ diff --git a/apps/dashboard/src/assets/boxlite-icon-light.png b/apps/dashboard/src/assets/boxlite-icon-light.png new file mode 100644 index 000000000..c81e365f9 Binary files /dev/null and b/apps/dashboard/src/assets/boxlite-icon-light.png differ diff --git a/apps/dashboard/src/assets/boxlite-light.png b/apps/dashboard/src/assets/boxlite-light.png new file mode 100644 index 000000000..2e6264ec7 Binary files /dev/null and b/apps/dashboard/src/assets/boxlite-light.png differ diff --git a/apps/dashboard/src/assets/boxlite-logo-black.png b/apps/dashboard/src/assets/boxlite-logo-black.png deleted file mode 100644 index 71d80a9d7..000000000 Binary files a/apps/dashboard/src/assets/boxlite-logo-black.png and /dev/null differ diff --git a/apps/dashboard/src/assets/boxlite-logo-white.png b/apps/dashboard/src/assets/boxlite-logo-white.png deleted file mode 100644 index 48149330f..000000000 Binary files a/apps/dashboard/src/assets/boxlite-logo-white.png and /dev/null differ diff --git a/apps/dashboard/src/assets/fonts/Space-Grotesk-OFL.txt b/apps/dashboard/src/assets/fonts/Space-Grotesk-OFL.txt new file mode 100644 index 000000000..d5666d708 --- /dev/null +++ b/apps/dashboard/src/assets/fonts/Space-Grotesk-OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Space Grotesk Project Authors (https://github.com/floriankarsten/space-grotesk) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/apps/dashboard/src/assets/fonts/space-grotesk-latin-variable.woff2 b/apps/dashboard/src/assets/fonts/space-grotesk-latin-variable.woff2 new file mode 100644 index 000000000..0f3474ee8 Binary files /dev/null and b/apps/dashboard/src/assets/fonts/space-grotesk-latin-variable.woff2 differ diff --git a/apps/dashboard/src/assets/go.svg b/apps/dashboard/src/assets/go.svg new file mode 100644 index 000000000..6fa20fc72 --- /dev/null +++ b/apps/dashboard/src/assets/go.svg @@ -0,0 +1,7 @@ + + Go + + diff --git a/apps/dashboard/src/assets/react.svg b/apps/dashboard/src/assets/react.svg deleted file mode 100644 index 6c87de9bb..000000000 --- a/apps/dashboard/src/assets/react.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/dashboard/src/assets/rust.svg b/apps/dashboard/src/assets/rust.svg new file mode 100644 index 000000000..2d3dffdcd --- /dev/null +++ b/apps/dashboard/src/assets/rust.svg @@ -0,0 +1,7 @@ + + Rust + + diff --git a/apps/dashboard/src/components/AccountProviderIcon.tsx b/apps/dashboard/src/components/AccountProviderIcon.tsx index 989616afd..c39943234 100644 --- a/apps/dashboard/src/components/AccountProviderIcon.tsx +++ b/apps/dashboard/src/components/AccountProviderIcon.tsx @@ -5,7 +5,7 @@ */ import { ComponentType } from 'react' -import { Github, Link2, Mail, LucideProps } from 'lucide-react' +import { Github, Link2, Mail, LucideProps } from '@/components/ui/icon' type Props = { provider: string diff --git a/apps/dashboard/src/components/AnnouncementBanner.tsx b/apps/dashboard/src/components/AnnouncementBanner.tsx index daf2aeb5e..1ae200b82 100644 --- a/apps/dashboard/src/components/AnnouncementBanner.tsx +++ b/apps/dashboard/src/components/AnnouncementBanner.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { X, Info } from 'lucide-react' +import { X, Info } from '@/components/ui/icon' import { Button } from './ui/button' interface AnnouncementBannerProps { diff --git a/apps/dashboard/src/components/ApiKeyTable.tsx b/apps/dashboard/src/components/ApiKeyTable.tsx index 44adb7670..7a72a0bda 100644 --- a/apps/dashboard/src/components/ApiKeyTable.tsx +++ b/apps/dashboard/src/components/ApiKeyTable.tsx @@ -5,25 +5,11 @@ */ import { CREATE_API_KEY_PERMISSIONS_GROUPS } from '@/constants/CreateApiKeyPermissionsGroups' -import { DEFAULT_PAGE_SIZE } from '@/constants/Pagination' import { getRelativeTimeString } from '@/lib/utils' -import { ApiKeyList, ApiKeyListPermissionsEnum, CreateApiKeyPermissionsEnum } from '@boxlite-ai/api-client' - -import { - ColumnDef, - flexRender, - getCoreRowModel, - getPaginationRowModel, - getSortedRowModel, - SortingState, - useReactTable, -} from '@tanstack/react-table' -import { KeyRound, Loader2 } from 'lucide-react' -import { useMemo, useState } from 'react' -import { Pagination } from './Pagination' -import { TableEmptyState } from './TableEmptyState' +import { ApiKeyList, ApiKeyListPermissionsEnum } from '@boxlite-ai/api-client' +import { KeyRound, Loader2, Trash2 } from '@/components/ui/icon' +import { useMemo } from 'react' import { Badge } from './ui/badge' -import { Button } from './ui/button' import { Dialog, DialogClose, @@ -35,9 +21,7 @@ import { DialogTrigger, } from './ui/dialog' import { Popover, PopoverContent, PopoverTrigger } from './ui/popover' -import { Skeleton } from './ui/skeleton' -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table' -import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip' +import { Button } from './ui/button' interface DataTableProps { data: ApiKeyList[] @@ -46,322 +30,171 @@ interface DataTableProps { onRevoke: (key: ApiKeyList) => void } -export function ApiKeyTable({ data, loading, isLoadingKey, onRevoke }: DataTableProps) { - const [sorting, setSorting] = useState([]) - const columns = getColumns({ onRevoke, isLoadingKey }) - const table = useReactTable({ - data, - columns, - getCoreRowModel: getCoreRowModel(), - getPaginationRowModel: getPaginationRowModel(), - onSortingChange: setSorting, - getSortedRowModel: getSortedRowModel(), - state: { - sorting, - }, - initialState: { - pagination: { - pageSize: DEFAULT_PAGE_SIZE, - }, - }, - }) +// Mirrors the Boxes (BoxTable) layout: borderless full-height column, header with a +// bottom rule, hover-highlighted rows, and a plain "Showing N" footer. +const GRID = 'grid-cols-[1.4fr_1.6fr_1.2fr_1fr_1fr_1fr_44px] gap-x-4' +export function ApiKeyTable({ data, loading, isLoadingKey, onRevoke }: DataTableProps) { return ( -
-
- - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - - ) - })} - - ))} - - - {loading ? ( - <> - {Array.from(new Array(5)).map((_, i) => ( - - {table.getVisibleLeafColumns().map((column, i, arr) => - i === arr.length - 1 ? null : ( - - - - ), - )} - - ))} - - ) : table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - } - description={ -
-

API Keys authenticate requests made through the BoxLite SDK or CLI.

-

- Generate one and{' '} - + {/* header */} +

+ + {/* rows */} +
+ {loading ? ( + Array.from({ length: 4 }).map((_, i) => ( +
+
+
+ )) + ) : data.length === 0 ? ( +
+ ) : ( + data.map((key) => { + const busy = isLoadingKey(key) + return ( +
+ {key.name} + {key.value} + + + + + {getRelativeTimeString(key.createdAt).relativeTimeString} + + + {key.lastUsedAt ? getRelativeTimeString(key.lastUsedAt).relativeTimeString : 'Never'} + + + {key.expiresAt ? getRelativeTimeString(key.expiresAt).relativeTimeString : 'Never'} + + + + +
- } - /> - )} - -
+ {busy ? : } + + + + + Confirm Key Revocation + + Are you absolutely sure? This action cannot be undone and will permanently delete this API + key. + + + + + + + + + + + + + +
+ ) + }) + )} +
+ + {/* footer */} +
+ + Showing {data.length} key{data.length === 1 ? '' : 's'} +
- ) } -const getExpiresAtColor = (expiresAt: Date | null) => { - if (!expiresAt) { - return 'text-foreground' - } - - const MILLISECONDS_IN_MINUTE = 1000 * 60 - const MINUTES_IN_DAY = 24 * 60 - - const diffInMinutes = Math.floor((new Date(expiresAt).getTime() - new Date().getTime()) / MILLISECONDS_IN_MINUTE) - - // Already expired - if (diffInMinutes < 0) { - return 'text-red-500' - } - - // Expires within a day - if (diffInMinutes < MINUTES_IN_DAY) { - return 'text-yellow-600 dark:text-yellow-400' - } - - // Expires in more than a day - return 'text-foreground' -} - -const getColumns = ({ - onRevoke, - isLoadingKey, -}: { - onRevoke: (key: ApiKeyList) => void - isLoadingKey: (key: ApiKeyList) => boolean -}): ColumnDef[] => { - const columns: ColumnDef[] = [ - { - accessorKey: 'name', - header: 'Name', - }, - { - accessorKey: 'value', - header: 'Key', - }, - { - accessorKey: 'permissions', - header: () => { - return
Permissions
- }, - cell: ({ row }) => { - return - }, - }, - { - accessorKey: 'createdAt', - header: 'Created', - cell: ({ row }) => { - const createdAt = row.original.createdAt - const relativeTime = getRelativeTimeString(createdAt).relativeTimeString - const fullDate = new Date(createdAt).toLocaleString() - - return ( - - - {relativeTime} - - -

{fullDate}

-
-
- ) - }, - }, - { - accessorKey: 'lastUsedAt', - header: 'Last Used', - cell: ({ row }) => { - const lastUsedAt = row.original.lastUsedAt - const relativeTime = getRelativeTimeString(lastUsedAt).relativeTimeString - - if (!lastUsedAt) { - return {relativeTime} - } - - const fullDate = new Date(lastUsedAt).toLocaleString() - - return ( - - - {relativeTime} - - -

{fullDate}

-
-
- ) - }, - }, - { - accessorKey: 'expiresAt', - header: 'Expires', - cell: ({ row }) => { - const expiresAt = row.original.expiresAt - const relativeTime = getRelativeTimeString(expiresAt).relativeTimeString - - if (!expiresAt) { - return {relativeTime} - } - - const fullDate = new Date(expiresAt).toLocaleString() - const color = getExpiresAtColor(expiresAt) +const visiblePermissions = CREATE_API_KEY_PERMISSIONS_GROUPS.flatMap((group) => group.permissions) +const IMPLICIT_READ_RESOURCES = ['Boxes'] - return ( - - - {relativeTime} - - -

{fullDate}

-
-
- ) - }, - }, - { - id: 'actions', - size: 80, - cell: ({ row }) => { - const isLoading = isLoadingKey(row.original) - - return ( - - - - - - - Confirm Key Revocation - - Are you absolutely sure? This action cannot be undone. This will permanently delete this API key. - - - - - - - - - - - - - ) - }, - }, - ] - - return columns -} - -const allPermissions = Object.values(CreateApiKeyPermissionsEnum) - -const IMPLICIT_READ_RESOURCES = ['Sandboxes', 'Snapshots', 'Registries', 'Regions'] - -function PermissionsTooltip({ - permissions, - availablePermissions, -}: { - permissions: ApiKeyListPermissionsEnum[] - availablePermissions: CreateApiKeyPermissionsEnum[] -}) { - const isFullAccess = allPermissions.length === permissions.length +function PermissionsTooltip({ permissions }: { permissions: ApiKeyListPermissionsEnum[] }) { + const isFullAccess = visiblePermissions.every((permission) => permissions.includes(permission)) const isSingleResourceAccess = CREATE_API_KEY_PERMISSIONS_GROUPS.find( (group) => group.permissions.length === permissions.length && group.permissions.every((p) => permissions.includes(p)), ) - const availableGroups = useMemo(() => { - return CREATE_API_KEY_PERMISSIONS_GROUPS.map((group) => ({ - ...group, - permissions: group.permissions.filter((p) => availablePermissions.includes(p)), - })).filter((group) => group.permissions.length > 0) - }, [availablePermissions]) + const availableGroups = useMemo( + () => CREATE_API_KEY_PERMISSIONS_GROUPS.filter((group) => group.permissions.length > 0), + [], + ) - const badgeVariant = isFullAccess ? 'warning' : 'outline' - const badgeText = isFullAccess ? 'Full' : isSingleResourceAccess ? isSingleResourceAccess.name : 'Restricted' + const badgeText = isSingleResourceAccess ? isSingleResourceAccess.name : isFullAccess ? 'Full' : 'Restricted' return ( - - {badgeText} Access - + + {badgeText} + -

Permissions

+

Permissions

{availableGroups.map((group) => { const selectedPermissions = group.permissions.filter((p) => permissions.includes(p)) const hasImplicitRead = IMPLICIT_READ_RESOURCES.includes(group.name) - - if (selectedPermissions.length === 0 && !hasImplicitRead) { - return null - } - + if (selectedPermissions.length === 0 && !hasImplicitRead) return null return ( -
+

{group.name}

-
+
{hasImplicitRead && ( - + Read )} {selectedPermissions.map((p) => ( - + {p.split(':')[0]} ))} diff --git a/apps/dashboard/src/components/AuditLogTable.tsx b/apps/dashboard/src/components/AuditLogTable.tsx index ed405400a..ff9a51829 100644 --- a/apps/dashboard/src/components/AuditLogTable.tsx +++ b/apps/dashboard/src/components/AuditLogTable.tsx @@ -12,7 +12,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip import { getRelativeTimeString } from '@/lib/utils' import { AuditLog } from '@boxlite-ai/api-client' import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table' -import { TextSearch } from 'lucide-react' +import { TextSearch } from '@/components/ui/icon' interface Props { data: AuditLog[] diff --git a/apps/dashboard/src/components/Banner.tsx b/apps/dashboard/src/components/Banner.tsx index 20434a200..ae254af39 100644 --- a/apps/dashboard/src/components/Banner.tsx +++ b/apps/dashboard/src/components/Banner.tsx @@ -14,7 +14,7 @@ import { InfoIcon, MegaphoneIcon, XIcon, -} from 'lucide-react' +} from '@/components/ui/icon' import { AnimatePresence, motion } from 'motion/react' import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' import { v4 as uuidv4 } from 'uuid' diff --git a/apps/dashboard/src/components/Box/CreateBoxDialog.tsx b/apps/dashboard/src/components/Box/CreateBoxDialog.tsx new file mode 100644 index 000000000..17c900b69 --- /dev/null +++ b/apps/dashboard/src/components/Box/CreateBoxDialog.tsx @@ -0,0 +1,302 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' +import { RoutePath } from '@/enums/RoutePath' +import { useCreateBoxMutation } from '@/hooks/mutations/useCreateBoxMutation' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { getBoxRouteId } from '@/lib/box-identity' +import { handleApiError } from '@/lib/error-handling' +import { cn } from '@/lib/utils' +import type { Box } from '@boxlite-ai/api-client' +import { ChevronDown, Plus } from '@/components/ui/icon' +import { useEffect, useState } from 'react' +import { generatePath, useNavigate } from 'react-router-dom' +import { toast } from 'sonner' + +const NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/ + +const SUPPORTED_BOX_IMAGES = [ + { id: 'base', name: 'Base', ref: 'ghcr.io/boxlite-ai/boxlite-agent-base:20260605-p0-r3', isDefault: true }, + { id: 'python', name: 'Python', ref: 'ghcr.io/boxlite-ai/boxlite-agent-python:20260605-p0-r3', isDefault: false }, + { id: 'node', name: 'Node.js', ref: 'ghcr.io/boxlite-ai/boxlite-agent-node:20260605-p0-r3', isDefault: false }, +] as const + +const DEFAULTS = { cpu: 1, memory: 1, disk: 10 } +const LIMITS = { cpu: 8, memory: 32, disk: 50 } + +// Stepper: − / editable value / + . Accepts any integer ≥ min (the backend takes arbitrary +// cpu/memory/disk); click +/− or type directly (commits/clamps on blur or Enter). +function Stepper({ + value, + onChange, + min = 1, + max, +}: { + value: number + onChange: (v: number) => void + min?: number + max?: number +}) { + const [text, setText] = useState(String(value)) + useEffect(() => { + setText(String(value)) + }, [value]) + const clamp = (n: number) => { + const v = Math.max(min, n) + return max != null ? Math.min(max, v) : v + } + const commit = (raw: string) => { + const n = parseInt(raw, 10) + onChange(Number.isFinite(n) ? clamp(n) : min) + } + const btn = + 'flex size-11 flex-none items-center justify-center font-mono text-[15px] text-muted-foreground transition-colors enabled:hover:bg-accent enabled:hover:text-foreground disabled:cursor-not-allowed disabled:opacity-40 sm:size-9' + return ( +
+ + setText(e.target.value.replace(/[^0-9]/g, ''))} + onBlur={(e) => commit(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter') (e.target as HTMLInputElement).blur() + }} + className="min-w-0 flex-1 bg-transparent py-[9px] text-center font-mono text-[13px] text-foreground outline-none" + /> + +
+ ) +} + +export const CreateBoxDialog = ({ + className, + triggerClassName, + open: controlledOpen, + onOpenChange, + onCreated, +}: { + className?: string + triggerClassName?: string + open?: boolean + onOpenChange?: (open: boolean) => void + onCreated?: (box: Box) => void +}) => { + const navigate = useNavigate() + const [internalOpen, setInternalOpen] = useState(false) + const open = controlledOpen ?? internalOpen + const setOpen = onOpenChange ?? setInternalOpen + + const { selectedOrganization } = useSelectedOrganization() + const createBoxMutation = useCreateBoxMutation() + const defaultImage = SUPPORTED_BOX_IMAGES.find((i) => i.isDefault) ?? SUPPORTED_BOX_IMAGES[0] + + const [name, setName] = useState('') + const [imageRef, setImageRef] = useState(defaultImage.ref) + const [cpu, setCpu] = useState(DEFAULTS.cpu) + const [memory, setMemory] = useState(DEFAULTS.memory) + const [disk, setDisk] = useState(DEFAULTS.disk) + const [advancedOpen, setAdvancedOpen] = useState(false) + const [submitting, setSubmitting] = useState(false) + + useEffect(() => { + if (open) { + setName('') + setImageRef(defaultImage.ref) + setCpu(DEFAULTS.cpu) + setMemory(DEFAULTS.memory) + setDisk(DEFAULTS.disk) + setAdvancedOpen(false) + setSubmitting(false) + } + }, [open, defaultImage.ref]) + + const selectedImage = SUPPORTED_BOX_IMAGES.find((i) => i.ref === imageRef) ?? defaultImage + const nameValid = !name || NAME_REGEX.test(name) + + const handleCreate = async () => { + if (!selectedOrganization?.id) { + toast.error('Select an organization to create a box.') + return + } + if (!nameValid) { + toast.error('Only letters, digits, dots, underscores and dashes are allowed in the name.') + return + } + setSubmitting(true) + try { + const box = await createBoxMutation.mutateAsync({ + name: name.trim() || undefined, + image: imageRef || defaultImage.ref, + network: { mode: 'enabled' }, + resources: { cpu, memory, disk }, + }) + onCreated?.(box) + toast.success('Box created') + setOpen(false) + const boxId = getBoxRouteId(box) + if (boxId) { + navigate(generatePath(RoutePath.BOX_DETAILS, { boxId })) + } + } catch (error) { + handleApiError(error, 'Failed to create box') + } finally { + setSubmitting(false) + } + } + + return ( + + + + + + + + Create a box for your agent + + +
+ {/* name */} +
+
Name
+ setName(e.target.value)} + placeholder="my-new-box" + aria-invalid={!nameValid} + className="w-full border border-border bg-card px-[13px] py-[11px] font-mono text-[13px] text-foreground outline-none focus:border-brand aria-[invalid=true]:border-destructive" + /> +
+ + {/* image */} +
+
Image
+ + + {selectedImage.name} + + + + {SUPPORTED_BOX_IMAGES.map((img) => ( + setImageRef(img.ref)} + > + {img.name} + + ))} + + +
+ + {/* advanced */} +
+ + {advancedOpen && ( +
+
+
+ CPU (vCPU) +
+ +
+
+
+ Memory (GiB) +
+ +
+
+
+ Disk (GiB) +
+ +
+
+ )} +
+
+ + {/* price — billing is not enabled yet, so everything is free ($0) */} +
+ Price per hour + + $0.00 / hr · free in preview + +
+ + {/* footer */} +
+ + +
+
+
+ ) +} diff --git a/apps/dashboard/src/components/BoxDetailsSheet.tsx b/apps/dashboard/src/components/BoxDetailsSheet.tsx new file mode 100644 index 000000000..81f7083e7 --- /dev/null +++ b/apps/dashboard/src/components/BoxDetailsSheet.tsx @@ -0,0 +1,288 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Button } from '@/components/ui/button' +import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { getBoxPublicId, getBoxPublicIdLabel, getBoxRouteId } from '@/lib/box-identity' +import { formatDuration, formatTimestamp, getRelativeTimeString } from '@/lib/utils' +import { Box, BoxState } from '@boxlite-ai/api-client' +import { Play, Tag, Trash, Wrench, X } from '@/components/ui/icon' +import React, { useState } from 'react' +import { Link, generatePath } from 'react-router-dom' +import { RoutePath } from '@/enums/RoutePath' +import { CopyButton } from './CopyButton' +import { ResourceChip } from './ResourceChip' +import { BoxState as BoxStateComponent } from './BoxTable/BoxState' +import { TimestampTooltip } from './TimestampTooltip' +import { LogsTab, TracesTab, MetricsTab } from './telemetry' +import { BoxSpendingTab } from './spending' +import { useFeatureFlagEnabled } from 'posthog-js/react' +import { FeatureFlags } from '@/enums/FeatureFlags' +import { useConfig } from '@/hooks/useConfig' + +interface BoxDetailsSheetProps { + box: Box | null + open: boolean + onOpenChange: (open: boolean) => void + boxIsLoading: Record + handleStart: (id: string) => void + handleStop: (id: string) => void + handleDelete: (id: string) => void + getWebTerminalUrl: (id: string) => Promise + writePermitted: boolean + deletePermitted: boolean + handleRecover: (id: string) => void +} + +const BoxDetailsSheet: React.FC = ({ + box, + open, + onOpenChange, + boxIsLoading, + handleStart, + handleStop, + handleDelete, + getWebTerminalUrl, + writePermitted, + deletePermitted, + handleRecover, +}) => { + const [terminalUrl, setTerminalUrl] = useState(null) + const experimentsEnabled = useFeatureFlagEnabled(FeatureFlags.ORGANIZATION_EXPERIMENTS) + const spendingEnabled = useFeatureFlagEnabled(FeatureFlags.BOX_SPENDING) + const config = useConfig() + const spendingTabAvailable = spendingEnabled && !!config.analyticsApiUrl + + // TODO: uncomment when we enable the terminal tab + // useEffect(() => { + // const getTerminalUrl = async () => { + // if (!box?.id) { + // setTerminalUrl(null) + // return + // } + + // const url = await getWebTerminalUrl(box.id) + // setTerminalUrl(url) + // } + + // getTerminalUrl() + // }, [box?.id, getWebTerminalUrl]) + + if (!box) return null + const publicBoxId = getBoxPublicId(box) + + const getLastEvent = (box: Box): { date: Date; relativeTimeString: string } => { + return getRelativeTimeString(box.updatedAt) + } + + return ( + + + + Box Details +
+ + {writePermitted && ( + <> + {box.state === BoxState.STARTED && ( + + )} + {box.state === BoxState.STOPPED && !box.recoverable && ( + + )} + {box.state === BoxState.ERROR && box.recoverable && ( + + )} + + )} + {deletePermitted && ( + + )} + +
+
+ + + {experimentsEnabled && ( + + + Overview + + + Logs + + + Traces + + + Metrics + + {spendingTabAvailable && ( + + Spending + + )} + + )} + + +
+
+

Name

+
+

{box.name}

+ +
+
+
+

Box ID

+
+

{getBoxPublicIdLabel(box)}

+ {publicBoxId && } +
+
+
+ +
+
+

State

+
+ +
+
+
+
+
+

Last event

+

+ {getLastEvent(box).relativeTimeString} +

+
+
+

Created at

+

+ {formatTimestamp(box.createdAt)} +

+
+
+ +
+
+

Auto-stop

+

+ {box.autoStopInterval ? formatDuration(box.autoStopInterval) : 'Disabled'} +

+
+
+

Auto-delete

+

+ {box.autoDeleteInterval !== undefined && box.autoDeleteInterval >= 0 + ? box.autoDeleteInterval === 0 + ? 'On stop' + : formatDuration(box.autoDeleteInterval) + : 'Disabled'} +

+
+
+ +
+
+

Resources

+
+ + + +
+
+
+
+

Labels

+
+ {Object.entries(box.labels ?? {}).length > 0 ? ( + Object.entries(box.labels ?? {}).map(([key, value]) => ( +
+
{key}
+
{value}
+
+ )) + ) : ( +
+ + No labels found +
+ )} +
+
+
+ + + + + + + + + + + + + + + + + + {spendingTabAvailable && ( + + + + )} +
+
+
+ ) +} + +export default BoxDetailsSheet diff --git a/apps/dashboard/src/components/BoxSearchCommands.tsx b/apps/dashboard/src/components/BoxSearchCommands.tsx new file mode 100644 index 000000000..7e8a3638a --- /dev/null +++ b/apps/dashboard/src/components/BoxSearchCommands.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { RoutePath } from '@/enums/RoutePath' +import { useApi } from '@/hooks/useApi' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { getBoxDisplayName, getBoxPublicIdLabel, getBoxRouteId } from '@/lib/box-identity' +import { useQuery } from '@tanstack/react-query' +import { Container } from '@/components/ui/icon' +import { useEffect, useMemo, useState } from 'react' +import { generatePath, useNavigate } from 'react-router-dom' +import { CommandConfig, useCommandPalette, useCommandPaletteActions, useRegisterCommands } from './CommandPalette' + +// Surfaces live box results inside the command palette: typing a name/ID in the +// global search (⌘K) queries boxes and shows matches that jump to the box detail. +export function BoxSearchCommands() { + const isOpen = useCommandPalette((state) => state.isOpen) + const activePageId = useCommandPalette((state) => state.activePageId) + const search = useCommandPalette((state) => state.searchByPage.get(state.activePageId) ?? '') + const { boxApi } = useApi() + const { selectedOrganization } = useSelectedOrganization() + const { setIsOpen } = useCommandPaletteActions() + const navigate = useNavigate() + + const [debounced, setDebounced] = useState('') + useEffect(() => { + const timeout = setTimeout(() => setDebounced(search.trim()), 200) + return () => clearTimeout(timeout) + }, [search]) + + const enabled = isOpen && activePageId === 'root' && debounced.length > 0 && !!selectedOrganization + + const { data } = useQuery({ + queryKey: ['command-palette-boxes', selectedOrganization?.id, debounced], + queryFn: async () => + (await boxApi.listBoxesPaginated(selectedOrganization!.id, 1, 6, undefined, debounced)).data, + enabled, + staleTime: 15_000, + }) + + const commands = useMemo(() => { + if (!enabled || !data?.items?.length) { + return [] + } + return data.items.map((box) => ({ + id: `box-search-${box.id}`, + label: getBoxDisplayName(box), + icon: , + // Tag with the active query so the palette's client-side filter always + // keeps server-matched results (e.g. ID match when the name differs). + keywords: [debounced, box.id, getBoxPublicIdLabel(box)], + onSelect: () => { + setIsOpen(false) + navigate(generatePath(RoutePath.BOX_DETAILS, { boxId: getBoxRouteId(box) })) + }, + })) + }, [enabled, data, debounced, navigate, setIsOpen]) + + useRegisterCommands(commands, { groupId: 'boxes-search', groupLabel: 'Boxes', groupOrder: 0 }) + + return null +} diff --git a/apps/dashboard/src/components/BoxTable/BoxState.tsx b/apps/dashboard/src/components/BoxTable/BoxState.tsx new file mode 100644 index 000000000..b592421c7 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/BoxState.tsx @@ -0,0 +1,111 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { cn } from '@/lib/utils' +import { BoxState as BoxStateType } from '@boxlite-ai/api-client' +import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' +import { getStateLabel } from './constants' +import { STATE_ICONS } from './state-icons' + +interface BoxStateProps { + state?: BoxStateType + errorReason?: string + recoverable?: boolean + className?: string + iconOnly?: boolean + pill?: boolean +} + +function getStateTint(state: BoxStateType, recoverable?: boolean): string { + if (recoverable) return 'border-yellow-600/30 bg-yellow-600/10 text-yellow-700 dark:text-yellow-400' + switch (state) { + case BoxStateType.STARTED: + return 'border-green-600/30 bg-green-600/10 text-green-700 dark:text-green-400' + case BoxStateType.ERROR: + return 'border-destructive/30 bg-destructive/10 text-destructive' + default: + return 'border-border bg-muted text-muted-foreground' + } +} + +export function BoxState({ state, errorReason, recoverable, className, iconOnly, pill }: BoxStateProps) { + if (!state) return null + const stateIcon = recoverable ? STATE_ICONS['RECOVERY'] : STATE_ICONS[state] || STATE_ICONS[BoxStateType.UNKNOWN] + const label = getStateLabel(state) + + if (pill) { + const badge = ( + + {stateIcon} + {label} + + ) + if (state === BoxStateType.ERROR && errorReason) { + return ( + + {badge} + +

{errorReason}

+
+
+ ) + } + return badge + } + + if (iconOnly) { + const tip = state === BoxStateType.ERROR && errorReason ? errorReason : label + return ( + + +
{stateIcon}
+
+ +

{tip}

+
+
+ ) + } + + if (state === BoxStateType.ERROR) { + const errorColor = recoverable ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400' + + const errorContent = ( +
+
{stateIcon}
+ {label} +
+ ) + + if (!errorReason) { + return errorContent + } + + return ( + + {errorContent} + +

{errorReason}

+
+
+ ) + } + + return ( +
+
{stateIcon}
+ {label} +
+ ) +} diff --git a/apps/dashboard/src/components/BoxTable/BoxTableActions.tsx b/apps/dashboard/src/components/BoxTable/BoxTableActions.tsx new file mode 100644 index 000000000..826f45449 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/BoxTableActions.tsx @@ -0,0 +1,208 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { RoutePath } from '@/enums/RoutePath' +import { getBoxRouteId } from '@/lib/box-identity' +import { BoxState } from '@boxlite-ai/api-client' +import { MoreVertical, Play, Square, Loader2, Wrench } from '@/components/ui/icon' +import { generatePath, useNavigate } from 'react-router-dom' +import { useMemo } from 'react' +import TooltipButton from '../TooltipButton' +import { Button } from '../ui/button' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '../ui/dropdown-menu' +import { BoxTableActionsProps } from './types' + +export function BoxTableActions({ + box, + layout = 'table', + writePermitted, + deletePermitted, + isLoading, + onStart, + onStop, + onDelete, + onRecover, +}: BoxTableActionsProps) { + const navigate = useNavigate() + const isTransitioning = box.state === BoxState.STARTING || box.state === BoxState.STOPPING + + const primaryAction = useMemo(() => { + if (box.state === BoxState.STARTED) { + return { + label: 'Stop', + icon: , + onClick: () => onStop(box.id), + } + } + + if (isTransitioning) { + return { + label: 'Working', + icon: , + onClick: undefined, + } + } + + if (box.state === BoxState.ERROR && box.recoverable) { + return { + label: 'Recover', + icon: , + onClick: () => onRecover(box.id), + } + } + + return { + label: 'Start', + icon: , + onClick: () => onStart(box.id), + } + }, [isTransitioning, onRecover, onStart, onStop, box.id, box.recoverable, box.state]) + + const menuItems = useMemo(() => { + const items = [] + + items.push({ + key: 'open', + label: 'View Details', + onClick: () => navigate(generatePath(RoutePath.BOX_DETAILS, { boxId: getBoxRouteId(box) })), + disabled: isLoading, + }) + + if (deletePermitted) { + if (items.length > 0) { + items.push({ key: 'separator', type: 'separator' }) + } + + items.push({ + key: 'delete', + label: 'Delete', + onClick: () => onDelete(box.id), + disabled: isLoading, + className: 'text-red-600 dark:text-red-400', + }) + } + + return items + }, [deletePermitted, box.id, isLoading, onDelete, navigate]) + + if (!writePermitted && !deletePermitted) { + return null + } + + if (layout === 'mobile') { + return ( +
+ {writePermitted && ( + + )} + + + + + + + {menuItems.map((item) => { + if (item.type === 'separator') { + return + } + + return ( + { + e.stopPropagation() + item.onClick?.() + }} + className={`cursor-pointer ${item.className || ''}`} + disabled={item.disabled} + > + {item.label} + + ) + })} + + +
+ ) + } + + return ( +
+ { + e.stopPropagation() + primaryAction.onClick?.() + }} + > + {primaryAction.icon} + + + + + + + + {menuItems.map((item) => { + if (item.type === 'separator') { + return + } + + return ( + { + e.stopPropagation() + item.onClick?.() + }} + className={`cursor-pointer ${item.className || ''}`} + disabled={item.disabled} + > + {item.label} + + ) + })} + + +
+ ) +} diff --git a/apps/dashboard/src/components/BoxTable/BulkActionAlertDialog.tsx b/apps/dashboard/src/components/BoxTable/BulkActionAlertDialog.tsx new file mode 100644 index 000000000..d492bd92f --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/BulkActionAlertDialog.tsx @@ -0,0 +1,87 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '../ui/alert-dialog' + +export enum BulkAction { + Delete = 'delete', + Start = 'start', + Stop = 'stop', +} + +interface BulkActionData { + title: string + description: string + buttonLabel: string + buttonVariant?: 'destructive' +} + +function getBulkActionData(action: BulkAction, count: number): BulkActionData { + const countText = count === 1 ? 'this box' : `these ${count} selected boxes` + + switch (action) { + case BulkAction.Delete: + return { + title: 'Delete Boxes', + description: `Are you sure you want to delete ${countText}? This action cannot be undone.`, + buttonLabel: 'Delete', + buttonVariant: 'destructive', + } + case BulkAction.Start: + return { + title: 'Start Boxes', + description: `Are you sure you want to start ${countText}?`, + buttonLabel: 'Start', + } + case BulkAction.Stop: + return { + title: 'Stop Boxes', + description: `Are you sure you want to stop ${countText}?`, + buttonLabel: 'Stop', + } + } +} + +interface BulkActionAlertDialogProps { + action: BulkAction | null + count: number + onConfirm: () => void + onCancel: () => void +} + +export function BulkActionAlertDialog({ action, count, onConfirm, onCancel }: BulkActionAlertDialogProps) { + const data = action ? getBulkActionData(action, count) : null + + if (!data) return null + + return ( + !open && onCancel()}> + + <> + + {data.title} + {data.description} + + + Cancel + + {data.buttonLabel} + + + + + + ) +} diff --git a/apps/dashboard/src/components/BoxTable/columns.tsx b/apps/dashboard/src/components/BoxTable/columns.tsx new file mode 100644 index 000000000..b8332f815 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/columns.tsx @@ -0,0 +1,243 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { getBoxDisplayName, getBoxPublicIdLabel } from '@/lib/box-identity' +import { formatTimestamp, getRelativeTimeString } from '@/lib/utils' +import { Box, BoxState } from '@boxlite-ai/api-client' +import { ColumnDef } from '@tanstack/react-table' +import { ArrowDown, ArrowUp } from '@/components/ui/icon' +import React from 'react' +import { ResourceChip } from '../ResourceChip' +import { Checkbox } from '../ui/checkbox' +import { BoxState as BoxStateComponent } from './BoxState' +import { BoxTableActions } from './BoxTableActions' + +interface SortableHeaderProps { + column: any + label: string + dataState?: string +} + +const SortableHeader: React.FC = ({ column, label, dataState }) => { + return ( + + ) +} + +interface GetColumnsProps { + handleStart: (id: string) => void + handleStop: (id: string) => void + handleDelete: (id: string) => void + boxIsLoading: Record + writePermitted: boolean + deletePermitted: boolean + handleRecover: (id: string) => void +} + +export function getColumns({ + handleStart, + handleStop, + handleDelete, + boxIsLoading, + writePermitted, + deletePermitted, + handleRecover, +}: GetColumnsProps): ColumnDef[] { + const columns: ColumnDef[] = [ + { + id: 'select', + size: 30, + header: ({ table }) => ( + { + for (const row of table.getRowModel().rows) { + if (boxIsLoading[row.original.id] || row.original.state === BoxState.DESTROYED) { + row.toggleSelected(false) + } else { + row.toggleSelected(!!value) + } + } + }} + aria-label="Select all" + className="translate-y-[2px]" + /> + ), + cell: ({ row }) => { + return ( +
+ row.toggleSelected(!!value)} + aria-label="Select row" + onClick={(e) => e.stopPropagation()} + className="translate-y-[1px]" + /> +
+ ) + }, + + enableSorting: false, + enableHiding: false, + }, + { + id: 'name', + size: 220, + enableSorting: true, + enableHiding: false, + header: ({ column }) => { + return + }, + accessorKey: 'name', + cell: ({ row }) => { + const displayName = getBoxDisplayName(row.original) + return ( +
+ + {displayName} +
+ ) + }, + }, + { + id: 'id', + size: 140, + enableSorting: true, + enableHiding: true, + header: ({ column }) => { + return + }, + accessorKey: 'id', + cell: ({ row }) => { + return ( +
+ {getBoxPublicIdLabel(row.original)} +
+ ) + }, + }, + { + id: 'state', + size: 120, + enableSorting: true, + enableHiding: true, + header: ({ column }) => { + return + }, + cell: ({ row }) => ( +
+ +
+ ), + accessorKey: 'state', + }, + { + id: 'resources', + size: 230, + enableSorting: false, + enableHiding: true, + header: () => { + return Resources + }, + cell: ({ row }) => { + return ( +
+ + + +
+ ) + }, + }, + { + id: 'lastEvent', + size: 105, + enableSorting: true, + enableHiding: false, + header: ({ column }) => { + return + }, + accessorFn: (row) => getBoxLastEvent(row).date, + cell: ({ row }) => { + const lastEvent = getBoxLastEvent(row.original) + return ( +
+ {lastEvent.relativeTimeString} +
+ ) + }, + }, + { + id: 'createdAt', + size: 170, + enableSorting: true, + enableHiding: true, + header: ({ column }) => { + return + }, + cell: ({ row }) => { + const timestamp = formatTimestamp(row.original.createdAt) + return ( +
+ {timestamp} +
+ ) + }, + }, + { + id: 'actions', + size: 100, + enableHiding: false, + cell: ({ row }) => ( +
+ +
+ ), + }, + ] + + return columns +} + +export { getBoxDisplayName, getBoxPublicIdLabel } + +export function getBoxLastEvent(box: Box): { date: Date; relativeTimeString: string } { + return getRelativeTimeString(box.updatedAt) +} diff --git a/apps/dashboard/src/components/BoxTable/constants.ts b/apps/dashboard/src/components/BoxTable/constants.ts new file mode 100644 index 000000000..172a5d31f --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/constants.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxState } from '@boxlite-ai/api-client' +import { CheckCircle, Circle, AlertTriangle, Timer } from '@/components/ui/icon' +import { FacetedFilterOption } from './types' + +const STATE_LABEL_MAPPING: Record = { + [BoxState.STARTED]: 'Running', + [BoxState.STOPPED]: 'Stopped', + [BoxState.ERROR]: 'Error', + [BoxState.RESTORING]: 'Restoring', + [BoxState.ARCHIVED]: 'Archived', + [BoxState.CREATING]: 'Creating', + [BoxState.STARTING]: 'Starting', + [BoxState.STOPPING]: 'Stopping', + [BoxState.DESTROYING]: 'Deleting', + [BoxState.DESTROYED]: 'Deleted', + [BoxState.UNKNOWN]: 'Unknown', + [BoxState.UNKNOWN_DEFAULT_OPEN_API]: 'Unknown', + [BoxState.ARCHIVING]: 'Archiving', + [BoxState.RESIZING]: 'Resizing', +} + +export const STATUSES: FacetedFilterOption[] = [ + { + label: getStateLabel(BoxState.STARTED), + value: BoxState.STARTED, + icon: CheckCircle, + }, + { label: getStateLabel(BoxState.STOPPED), value: BoxState.STOPPED, icon: Circle }, + { label: getStateLabel(BoxState.ERROR), value: BoxState.ERROR, icon: AlertTriangle }, + { label: getStateLabel(BoxState.STARTING), value: BoxState.STARTING, icon: Timer }, + { label: getStateLabel(BoxState.STOPPING), value: BoxState.STOPPING, icon: Timer }, + { label: getStateLabel(BoxState.DESTROYING), value: BoxState.DESTROYING, icon: Timer }, +] + +export function getStateLabel(state?: BoxState): string { + if (!state) { + return 'Unknown' + } + return STATE_LABEL_MAPPING[state] +} diff --git a/apps/dashboard/src/components/SandboxTable/filters/LabelFilter.tsx b/apps/dashboard/src/components/BoxTable/filters/LabelFilter.tsx similarity index 98% rename from apps/dashboard/src/components/SandboxTable/filters/LabelFilter.tsx rename to apps/dashboard/src/components/BoxTable/filters/LabelFilter.tsx index 98de0461c..63cfedd0d 100644 --- a/apps/dashboard/src/components/SandboxTable/filters/LabelFilter.tsx +++ b/apps/dashboard/src/components/BoxTable/filters/LabelFilter.tsx @@ -8,7 +8,7 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' -import { Plus, Trash2, X } from 'lucide-react' +import { Plus, Trash2, X } from '@/components/ui/icon' import { useState } from 'react' interface LabelFilterProps { diff --git a/apps/dashboard/src/components/SandboxTable/filters/LastEventFilter.tsx b/apps/dashboard/src/components/BoxTable/filters/LastEventFilter.tsx similarity index 98% rename from apps/dashboard/src/components/SandboxTable/filters/LastEventFilter.tsx rename to apps/dashboard/src/components/BoxTable/filters/LastEventFilter.tsx index fd9b6a1fe..f10a50c27 100644 --- a/apps/dashboard/src/components/SandboxTable/filters/LastEventFilter.tsx +++ b/apps/dashboard/src/components/BoxTable/filters/LastEventFilter.tsx @@ -11,7 +11,7 @@ import { Calendar } from '@/components/ui/calendar' import { Label } from '@/components/ui/label' import { useState } from 'react' import { format } from 'date-fns' -import { CalendarIcon, X } from 'lucide-react' +import { CalendarIcon, X } from '@/components/ui/icon' interface LastEventFilterProps { value: (Date | undefined)[] diff --git a/apps/dashboard/src/components/SandboxTable/filters/RegionFilter.tsx b/apps/dashboard/src/components/BoxTable/filters/RegionFilter.tsx similarity index 98% rename from apps/dashboard/src/components/SandboxTable/filters/RegionFilter.tsx rename to apps/dashboard/src/components/BoxTable/filters/RegionFilter.tsx index 47cf1ca29..15d246f0e 100644 --- a/apps/dashboard/src/components/SandboxTable/filters/RegionFilter.tsx +++ b/apps/dashboard/src/components/BoxTable/filters/RegionFilter.tsx @@ -14,7 +14,7 @@ import { CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' -import { Loader2, X } from 'lucide-react' +import { Loader2, X } from '@/components/ui/icon' import { FacetedFilterOption } from '../types' interface RegionFilterProps { diff --git a/apps/dashboard/src/components/SandboxTable/filters/ResourceFilter.tsx b/apps/dashboard/src/components/BoxTable/filters/ResourceFilter.tsx similarity index 99% rename from apps/dashboard/src/components/SandboxTable/filters/ResourceFilter.tsx rename to apps/dashboard/src/components/BoxTable/filters/ResourceFilter.tsx index 56f8c8a54..59d851580 100644 --- a/apps/dashboard/src/components/SandboxTable/filters/ResourceFilter.tsx +++ b/apps/dashboard/src/components/BoxTable/filters/ResourceFilter.tsx @@ -9,7 +9,7 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useMemo } from 'react' -import { X } from 'lucide-react' +import { X } from '@/components/ui/icon' export interface ResourceFilterValue { cpu?: { min?: number; max?: number } diff --git a/apps/dashboard/src/components/SandboxTable/filters/StateFilter.tsx b/apps/dashboard/src/components/BoxTable/filters/StateFilter.tsx similarity index 94% rename from apps/dashboard/src/components/SandboxTable/filters/StateFilter.tsx rename to apps/dashboard/src/components/BoxTable/filters/StateFilter.tsx index a5834675f..670203fc1 100644 --- a/apps/dashboard/src/components/SandboxTable/filters/StateFilter.tsx +++ b/apps/dashboard/src/components/BoxTable/filters/StateFilter.tsx @@ -13,8 +13,8 @@ import { CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' -import { SandboxState } from '@boxlite-ai/api-client' -import { X } from 'lucide-react' +import { BoxState } from '@boxlite-ai/api-client' +import { X } from '@/components/ui/icon' import { STATUSES, getStateLabel } from '../constants' interface StateFilterProps { @@ -23,7 +23,7 @@ interface StateFilterProps { } export function StateFilterIndicator({ value, onFilterChange }: StateFilterProps) { - const selectedStates = value.map((v) => getStateLabel(v as SandboxState)) + const selectedStates = value.map((v) => getStateLabel(v as BoxState)) return (
diff --git a/apps/dashboard/src/components/BoxTable/index.tsx b/apps/dashboard/src/components/BoxTable/index.tsx new file mode 100644 index 000000000..573c96ef2 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/index.tsx @@ -0,0 +1,380 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' +import { getBoxDisplayName, getBoxPublicIdLabel } from '@/lib/box-identity' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { getRelativeTimeString } from '@/lib/utils' +import { isRecoverable, isStartable, isStoppable, isTransitioning } from '@/lib/utils/box' +import { OrganizationRolePermissionsEnum, Box, BoxState } from '@boxlite-ai/api-client' +import { + ChevronLeft, + ChevronRight, + ChevronsLeft, + ChevronsRight, + Container, + Loader2, + MoreHorizontal, + Pause, + Play, + RotateCcw, + Trash2, +} from '@/components/ui/icon' +import { type ReactNode } from 'react' +import { BoxTableProps } from './types' + +// Exact design palette (Boxes page.dc.html statusColors) — used inline so the +// status dots/labels render at the precise hue, not a token approximation. +const STATUS = { + running: '#5ad67d', + idle: '#e0b341', + stopped: '#8C919C', + error: '#e0564a', + dim: '#5b616e', +} as const + +function statusOf(box: Box): { label: string; color: string } { + switch (box.state) { + case BoxState.STARTED: + return { label: 'RUNNING', color: STATUS.running } + case BoxState.STOPPED: + return { label: 'STOPPED', color: STATUS.stopped } + case BoxState.ERROR: + return { label: 'ERROR', color: STATUS.error } + case BoxState.CREATING: + case BoxState.STARTING: + case BoxState.RESTORING: + return { label: 'STARTING', color: STATUS.idle } + case BoxState.STOPPING: + return { label: 'STOPPING', color: STATUS.idle } + case BoxState.DESTROYING: + return { label: 'DELETING', color: STATUS.idle } + case BoxState.RESIZING: + return { label: 'RESIZING', color: STATUS.idle } + case BoxState.DESTROYED: + return { label: 'DELETED', color: STATUS.dim } + default: + return { label: (box.state ?? 'UNKNOWN').toUpperCase(), color: STATUS.dim } + } +} + +// Proportional columns so the gaps stay even as the table widens, instead of +// dumping all the slack into the Name column. +const GRID = 'grid-cols-[2fr_1.3fr_1fr_120px] gap-x-4' + +function IconButton({ + title, + onClick, + children, + className, + disabled, +}: { + title: string + onClick: (e: React.MouseEvent) => void + children: ReactNode + className?: string + disabled?: boolean +}) { + return ( + + ) +} + +function Quota({ label, value, unit }: { label: string; value: number; unit: string }) { + return ( + + {label} + {value} + {unit} + + ) +} + +export function BoxTable({ + data, + boxIsLoading, + boxStateIsTransitioning, + loading, + handleStart, + handleStop, + handleDelete, + onRowClick, + pagination, + pageCount, + totalItems, + onPaginationChange, + handleRecover, +}: BoxTableProps) { + const { authenticatedUserHasPermission } = useSelectedOrganization() + const writePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.WRITE_BOXES) + const deletePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.DELETE_BOXES) + + const pageIndex = pagination.pageIndex + const goTo = (index: number) => onPaginationChange({ pageIndex: index, pageSize: pagination.pageSize }) + const canPrev = pageIndex > 0 + const canNext = pageCount > 0 && pageIndex < pageCount - 1 + + const renderActions = (box: Box, buttonClassName?: string, iconClassName = 'size-[13px]') => { + const startable = isStartable(box) + const stoppable = isStoppable(box) + const recoverable = isRecoverable(box) + const transitioning = isTransitioning(box) + + return ( + <> + {writePermitted && recoverable && ( + handleRecover(box.id)} className={buttonClassName}> + + + )} + {writePermitted && startable && ( + handleStart(box.id)} className={buttonClassName}> + + + )} + {writePermitted && stoppable && ( + handleStop(box.id)} className={buttonClassName}> + + + )} + {writePermitted && transitioning && !startable && !stoppable && !recoverable && ( + undefined} disabled className={buttonClassName}> + + + )} + {deletePermitted && ( + + + + + + handleDelete(box.id)} + > + + Delete + + + + )} + + ) + } + + return ( +
+ {/* header */} +
+ Name + Box ID + Status + Actions +
+ + {/* rows */} +
+ {loading ? ( + Array.from({ length: 5 }).map((_, i) => ( +
+
+
+ )) + ) : data.length === 0 ? ( +
+ +
No boxes yet.
+
+ Spin up a Box with the BoxLite SDK or CLI, or hit “New Box”. +
+
+ ) : ( + data.map((box) => { + const st = statusOf(box) + const name = getBoxDisplayName(box) + const busy = boxIsLoading[box.id] + const transitioning = boxStateIsTransitioning[box.id] + + return ( +
onRowClick?.(box)} + className={`grid ${GRID} items-center border-b border-border px-[18px] py-3 text-[13px] transition-colors hover:bg-card ${ + onRowClick ? 'cursor-pointer' : '' + } ${busy ? 'pointer-events-none opacity-70' : ''} ${transitioning ? 'animate-pulse' : ''}`} + > + {/* name */} + + + ▸ + + {name} + + + {/* box id */} + {getBoxPublicIdLabel(box)} + + {/* status */} + + + {st.label} + + + {/* actions */} +
e.stopPropagation()}> + {renderActions(box)} +
+
+ ) + }) + )} +
+ +
+ {loading ? ( + Array.from({ length: 4 }).map((_, i) => ( +
+
+
+
+ )) + ) : data.length === 0 ? ( +
+ +
No boxes yet.
+
+ Spin up a Box with the BoxLite SDK or CLI, or hit “New Box”. +
+
+ ) : ( + data.map((box) => { + const st = statusOf(box) + const name = getBoxDisplayName(box) + const created = getRelativeTimeString(box.createdAt).relativeTimeString.toUpperCase() + const busy = boxIsLoading[box.id] + const transitioning = boxStateIsTransitioning[box.id] + + return ( +
onRowClick?.(box)} + onKeyDown={(e) => { + if (!onRowClick || (e.key !== 'Enter' && e.key !== ' ')) return + e.preventDefault() + onRowClick(box) + }} + className={`w-full border border-border bg-background p-4 text-left transition-colors hover:bg-card focus-visible:border-brand focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand/35 ${ + onRowClick ? 'cursor-pointer' : '' + } ${busy ? 'pointer-events-none opacity-70' : ''} ${transitioning ? 'animate-pulse' : ''}`} + > +
+ + + + ▸ + + {name} + + + {getBoxPublicIdLabel(box)} + + + + + {st.label} + +
+ +
+ + + +
+ +
+ + Created {created} + + e.stopPropagation()}> + {renderActions(box, 'size-10', 'size-4')} + +
+
+ ) + }) + )} +
+ + {/* footer */} +
+ + Showing {data.length} of {totalItems.toLocaleString('en-US')} boxes + + {pageCount > 1 && ( +
+ + Page {pageIndex + 1} of {pageCount} + + goTo(0)}> + + + goTo(pageIndex - 1)}> + + + goTo(pageIndex + 1)}> + + + goTo(pageCount - 1)}> + + +
+ )} +
+
+ ) +} + +function PagerButton({ + disabled, + title, + onClick, + children, +}: { + disabled: boolean + title: string + onClick: () => void + children: ReactNode +}) { + return ( + + ) +} diff --git a/apps/dashboard/src/components/BoxTable/state-icons.tsx b/apps/dashboard/src/components/BoxTable/state-icons.tsx new file mode 100644 index 000000000..cac4e3103 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/state-icons.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { BoxState } from '@boxlite-ai/api-client' +import { Loader2 } from '@/components/ui/icon' + +interface SquareProps { + color: string +} + +function Square({ color }: SquareProps) { + return ( +
+
+
+ ) +} + +export const STATE_ICONS: Record = { + [BoxState.UNKNOWN]: , + [BoxState.CREATING]: , + [BoxState.STARTING]: , + [BoxState.STARTED]: , + [BoxState.STOPPING]: , + [BoxState.STOPPED]: , + [BoxState.DESTROYING]: , + [BoxState.DESTROYED]: , + [BoxState.ERROR]: , + [BoxState.ARCHIVED]: , + [BoxState.ARCHIVING]: , + [BoxState.RESTORING]: , + [BoxState.RESIZING]: , + [BoxState.UNKNOWN_DEFAULT_OPEN_API]: , + RECOVERY: , +} diff --git a/apps/dashboard/src/components/BoxTable/types.ts b/apps/dashboard/src/components/BoxTable/types.ts new file mode 100644 index 000000000..4a97ef1a8 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/types.ts @@ -0,0 +1,273 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { DEFAULT_BOX_SORTING, BoxFilters, BoxSorting } from '@/hooks/useBoxes' +import { + ListBoxesPaginatedOrderEnum, + ListBoxesPaginatedSortEnum, + ListBoxesPaginatedStatesEnum, + Box, + BoxState, +} from '@boxlite-ai/api-client' +import { ColumnFiltersState, SortingState } from '@tanstack/react-table' + +export interface BoxTableProps { + data: Box[] + boxIsLoading: Record + boxStateIsTransitioning: Record + loading: boolean + handleStart: (id: string) => void + handleStop: (id: string) => void + handleDelete: (id: string) => void + handleBulkDelete: (ids: string[]) => void + handleBulkStart: (ids: string[]) => void + handleBulkStop: (ids: string[]) => void + onRowClick?: (box: Box) => void + pagination: { + pageIndex: number + pageSize: number + } + pageCount: number + totalItems: number + onPaginationChange: (pagination: { pageIndex: number; pageSize: number }) => void + sorting: BoxSorting + onSortingChange: (sorting: BoxSorting) => void + filters: BoxFilters + onFiltersChange: (filters: BoxFilters) => void + handleRecover: (id: string) => void +} + +export interface BoxTableActionsProps { + box: Box + layout?: 'table' | 'mobile' + writePermitted: boolean + deletePermitted: boolean + isLoading: boolean + onStart: (id: string) => void + onStop: (id: string) => void + onDelete: (id: string) => void + onRecover: (id: string) => void +} + +export interface FacetedFilterOption { + label: string + value: string | BoxState + icon?: any +} + +export const convertTableSortingToApiSorting = (sorting: SortingState): BoxSorting => { + if (!sorting.length) { + return DEFAULT_BOX_SORTING + } + + const sort = sorting[0] + let field: ListBoxesPaginatedSortEnum + + switch (sort.id) { + case 'id': + field = ListBoxesPaginatedSortEnum.ID + break + case 'name': + field = ListBoxesPaginatedSortEnum.NAME + break + case 'state': + field = ListBoxesPaginatedSortEnum.STATE + break + // TODO(image-rewrite): template sort removed with the image/template subsystem. + case 'region': + case 'target': + field = ListBoxesPaginatedSortEnum.REGION + break + case 'lastEvent': + case 'updatedAt': + field = ListBoxesPaginatedSortEnum.UPDATED_AT + break + case 'createdAt': + default: + field = ListBoxesPaginatedSortEnum.CREATED_AT + break + } + + return { + field, + direction: sort.desc ? ListBoxesPaginatedOrderEnum.DESC : ListBoxesPaginatedOrderEnum.ASC, + } +} + +export const convertTableFiltersToApiFilters = (columnFilters: ColumnFiltersState): BoxFilters => { + const filters: BoxFilters = {} + + columnFilters.forEach((filter) => { + switch (filter.id) { + case 'name': + if (filter.value && typeof filter.value === 'string') { + filters.idOrName = filter.value + } + break + case 'state': + if (Array.isArray(filter.value) && filter.value.length > 0) { + filters.states = filter.value as ListBoxesPaginatedStatesEnum[] + } + break + // TODO(image-rewrite): template filter removed with the image/template subsystem. + case 'region': + case 'target': + if (Array.isArray(filter.value) && filter.value.length > 0) { + filters.regions = filter.value as string[] + } + break + case 'labels': + if (Array.isArray(filter.value) && filter.value.length > 0) { + const labelObj: Record = {} + filter.value.forEach((label: string) => { + const [key, value] = label.split(': ') + if (key && value) { + labelObj[key] = value + } + }) + if (Object.keys(labelObj).length > 0) { + filters.labels = labelObj + } + } + break + case 'resources': + if (filter.value && typeof filter.value === 'object') { + const resourceValue = filter.value as { + cpu?: { min?: number; max?: number } + memory?: { min?: number; max?: number } + disk?: { min?: number; max?: number } + } + + if (resourceValue.cpu?.min !== undefined) { + filters.minCpu = resourceValue.cpu.min + } + if (resourceValue.cpu?.max !== undefined) { + filters.maxCpu = resourceValue.cpu.max + } + if (resourceValue.memory?.min !== undefined) { + filters.minMemoryGiB = resourceValue.memory.min + } + if (resourceValue.memory?.max !== undefined) { + filters.maxMemoryGiB = resourceValue.memory.max + } + if (resourceValue.disk?.min !== undefined) { + filters.minDiskGiB = resourceValue.disk.min + } + if (resourceValue.disk?.max !== undefined) { + filters.maxDiskGiB = resourceValue.disk.max + } + } + break + case 'lastEvent': + if (Array.isArray(filter.value) && filter.value.length > 0) { + const dateRange = filter.value as (Date | undefined)[] + if (dateRange[0]) { + filters.lastEventAfter = dateRange[0] + } + if (dateRange[1]) { + filters.lastEventBefore = dateRange[1] + } + } + break + } + }) + + return filters +} + +export const convertApiSortingToTableSorting = (sorting: BoxSorting): SortingState => { + if (!sorting.field || !sorting.direction) { + return [{ id: 'lastEvent', desc: true }] + } + + let id: string + switch (sorting.field) { + case ListBoxesPaginatedSortEnum.ID: + id = 'id' + break + case ListBoxesPaginatedSortEnum.NAME: + id = 'name' + break + case ListBoxesPaginatedSortEnum.STATE: + id = 'state' + break + // TODO(image-rewrite): template sort removed with the image/template subsystem. + case ListBoxesPaginatedSortEnum.REGION: + id = 'region' + break + case ListBoxesPaginatedSortEnum.UPDATED_AT: + id = 'lastEvent' + break + case ListBoxesPaginatedSortEnum.CREATED_AT: + default: + id = 'createdAt' + break + } + + return [{ id, desc: sorting.direction === ListBoxesPaginatedOrderEnum.DESC }] +} + +export const convertApiFiltersToTableFilters = (filters: BoxFilters): ColumnFiltersState => { + const columnFilters: ColumnFiltersState = [] + + if (filters.idOrName) { + columnFilters.push({ id: 'name', value: filters.idOrName }) + } + + if (filters.states && filters.states.length > 0) { + columnFilters.push({ id: 'state', value: filters.states }) + } + + // TODO(image-rewrite): template filter removed with the image/template subsystem. + + if (filters.regions && filters.regions.length > 0) { + columnFilters.push({ id: 'region', value: filters.regions }) + } + + if (filters.labels && Object.keys(filters.labels).length > 0) { + const labelArray = Object.entries(filters.labels).map(([key, value]) => `${key}: ${value}`) + columnFilters.push({ id: 'labels', value: labelArray }) + } + + // Convert resource filters back to table format + const resourceValue: { + cpu?: { min?: number; max?: number } + memory?: { min?: number; max?: number } + disk?: { min?: number; max?: number } + } = {} + + if (filters.minCpu !== undefined || filters.maxCpu !== undefined) { + resourceValue.cpu = {} + if (filters.minCpu !== undefined) resourceValue.cpu.min = filters.minCpu + if (filters.maxCpu !== undefined) resourceValue.cpu.max = filters.maxCpu + } + + if (filters.minMemoryGiB !== undefined || filters.maxMemoryGiB !== undefined) { + resourceValue.memory = {} + if (filters.minMemoryGiB !== undefined) resourceValue.memory.min = filters.minMemoryGiB + if (filters.maxMemoryGiB !== undefined) resourceValue.memory.max = filters.maxMemoryGiB + } + + if (filters.minDiskGiB !== undefined || filters.maxDiskGiB !== undefined) { + resourceValue.disk = {} + if (filters.minDiskGiB !== undefined) resourceValue.disk.min = filters.minDiskGiB + if (filters.maxDiskGiB !== undefined) resourceValue.disk.max = filters.maxDiskGiB + } + + if (Object.keys(resourceValue).length > 0) { + columnFilters.push({ id: 'resources', value: resourceValue }) + } + + // Convert date range filters back to table format + if (filters.lastEventAfter || filters.lastEventBefore) { + const dateRange: (Date | undefined)[] = [undefined, undefined] + if (filters.lastEventAfter) dateRange[0] = filters.lastEventAfter + if (filters.lastEventBefore) dateRange[1] = filters.lastEventBefore + columnFilters.push({ id: 'lastEvent', value: dateRange }) + } + + return columnFilters +} diff --git a/apps/dashboard/src/components/BoxTable/useBoxCommands.tsx b/apps/dashboard/src/components/BoxTable/useBoxCommands.tsx new file mode 100644 index 000000000..441c34671 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/useBoxCommands.tsx @@ -0,0 +1,102 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { pluralize } from '@/lib/utils' +import { BulkActionCounts } from '@/lib/utils/box' +import { CheckSquare2Icon, MinusSquareIcon, PlayIcon, SquareIcon, TrashIcon } from '@/components/ui/icon' +import { useMemo } from 'react' +import { CommandConfig, useRegisterCommands } from '../CommandPalette' + +interface UseBoxCommandsProps { + writePermitted: boolean + deletePermitted: boolean + selectedCount: number + totalCount: number + selectableCount: number + toggleAllRowsSelected: (selected: boolean) => void + bulkActionCounts: BulkActionCounts + onDelete: () => void + onStart: () => void + onStop: () => void +} + +export function useBoxCommands({ + writePermitted, + deletePermitted, + selectedCount, + selectableCount, + totalCount, + toggleAllRowsSelected, + bulkActionCounts, + onDelete, + onStart, + onStop, +}: UseBoxCommandsProps) { + const rootCommands: CommandConfig[] = useMemo(() => { + const commands: CommandConfig[] = [] + + if (selectableCount !== selectedCount) { + commands.push({ + id: 'select-all-boxes', + label: 'Select All Boxes', + icon: , + onSelect: () => toggleAllRowsSelected(true), + chainable: true, + }) + } + + if (selectedCount > 0) { + commands.push({ + id: 'deselect-all-boxes', + label: 'Deselect All Boxes', + icon: , + onSelect: () => toggleAllRowsSelected(false), + chainable: true, + }) + } + + if (writePermitted && bulkActionCounts.startable > 0) { + commands.push({ + id: 'start-boxes', + label: `Start ${pluralize(bulkActionCounts.startable, 'Box', 'Boxes')}`, + icon: , + onSelect: onStart, + }) + } + + if (writePermitted && bulkActionCounts.stoppable > 0) { + commands.push({ + id: 'stop-boxes', + label: `Stop ${pluralize(bulkActionCounts.stoppable, 'Box', 'Boxes')}`, + icon: , + onSelect: onStop, + }) + } + + if (deletePermitted && bulkActionCounts.deletable > 0) { + commands.push({ + id: 'delete-boxes', + label: `Delete ${pluralize(bulkActionCounts.deletable, 'Box', 'Boxes')}`, + icon: , + onSelect: onDelete, + }) + } + + return commands + }, [ + selectedCount, + selectableCount, + toggleAllRowsSelected, + writePermitted, + deletePermitted, + bulkActionCounts, + onDelete, + onStart, + onStop, + ]) + + useRegisterCommands(rootCommands, { groupId: 'box-actions', groupLabel: 'Box actions', groupOrder: 0 }) +} diff --git a/apps/dashboard/src/components/BoxTable/useBoxTable.ts b/apps/dashboard/src/components/BoxTable/useBoxTable.ts new file mode 100644 index 000000000..8774aa8c8 --- /dev/null +++ b/apps/dashboard/src/components/BoxTable/useBoxTable.ts @@ -0,0 +1,150 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Box } from '@boxlite-ai/api-client' +import { + useReactTable, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getPaginationRowModel, + VisibilityState, +} from '@tanstack/react-table' +import { useMemo, useState, useEffect } from 'react' +import { getColumns } from './columns' +import { + convertApiSortingToTableSorting, + convertApiFiltersToTableFilters, + convertTableSortingToApiSorting, + convertTableFiltersToApiFilters, +} from './types' +import { BoxFilters, BoxSorting } from '@/hooks/useBoxes' +import { LocalStorageKey } from '@/enums/LocalStorageKey' +import { getLocalStorageItem, setLocalStorageItem } from '@/lib/local-storage' + +interface UseBoxTableProps { + data: Box[] + boxIsLoading: Record + writePermitted: boolean + deletePermitted: boolean + handleStart: (id: string) => void + handleStop: (id: string) => void + handleDelete: (id: string) => void + pagination: { + pageIndex: number + pageSize: number + } + pageCount: number + onPaginationChange: (pagination: { pageIndex: number; pageSize: number }) => void + sorting: BoxSorting + onSortingChange: (sorting: BoxSorting) => void + filters: BoxFilters + onFiltersChange: (filters: BoxFilters) => void + handleRecover: (id: string) => void +} + +export function useBoxTable({ + data, + boxIsLoading, + writePermitted, + deletePermitted, + handleStart, + handleStop, + handleDelete, + pagination, + pageCount, + onPaginationChange, + sorting, + onSortingChange, + filters, + onFiltersChange, + handleRecover, +}: UseBoxTableProps) { + // Column visibility state management with persistence. + // Minimal default (exe.dev-style): only name + last event + actions show; the rest + // (id / state / resources / created at) are hidden but toggleable via the View menu. + // State still leads the name cell as a status dot regardless of the column's visibility. + const [columnVisibility, setColumnVisibility] = useState(() => { + const defaults: VisibilityState = { id: false, state: false, resources: false, createdAt: false, labels: false } + const saved = getLocalStorageItem(LocalStorageKey.BoxTableColumnVisibility) + if (saved) { + try { + return { ...defaults, ...JSON.parse(saved) } + } catch { + return defaults + } + } + return defaults + }) + + useEffect(() => { + setLocalStorageItem(LocalStorageKey.BoxTableColumnVisibility, JSON.stringify(columnVisibility)) + }, [columnVisibility]) + + // Convert API sorting and filters to table format for internal use + const tableSorting = useMemo(() => convertApiSortingToTableSorting(sorting), [sorting]) + const tableFilters = useMemo(() => convertApiFiltersToTableFilters(filters), [filters]) + + const columns = useMemo( + () => + getColumns({ + handleStart, + handleStop, + handleDelete, + boxIsLoading, + writePermitted, + deletePermitted, + handleRecover, + }), + [handleStart, handleStop, handleDelete, boxIsLoading, writePermitted, deletePermitted, handleRecover], + ) + + const table = useReactTable({ + data, + columns, + manualFiltering: true, + onColumnFiltersChange: (updater) => { + const newTableFilters = typeof updater === 'function' ? updater(table.getState().columnFilters) : updater + const newApiFilters = convertTableFiltersToApiFilters(newTableFilters) + onFiltersChange(newApiFilters) + }, + getCoreRowModel: getCoreRowModel(), + manualSorting: true, + onSortingChange: (updater) => { + const newTableSorting = typeof updater === 'function' ? updater(table.getState().sorting) : updater + const newApiSorting = convertTableSortingToApiSorting(newTableSorting) + onSortingChange(newApiSorting) + }, + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + manualPagination: true, + pageCount: pageCount, + onPaginationChange: (updater) => { + const newPagination = typeof updater === 'function' ? updater(table.getState().pagination) : updater + onPaginationChange(newPagination) + }, + getPaginationRowModel: getPaginationRowModel(), + state: { + sorting: tableSorting, + columnFilters: tableFilters, + columnVisibility, + pagination: { + pageIndex: pagination.pageIndex, + pageSize: pagination.pageSize, + }, + }, + onColumnVisibilityChange: setColumnVisibility, + defaultColumn: { + size: 100, + }, + enableRowSelection: deletePermitted, + getRowId: (row) => row.id, + }) + + return { + table, + } +} diff --git a/apps/dashboard/src/components/CodeBlock.tsx b/apps/dashboard/src/components/CodeBlock.tsx index 748133af8..05dced968 100644 --- a/apps/dashboard/src/components/CodeBlock.tsx +++ b/apps/dashboard/src/components/CodeBlock.tsx @@ -7,6 +7,7 @@ import { useTheme } from '@/contexts/ThemeContext' import { cn } from '@/lib/utils' import { Highlight, themes, type PrismTheme, type Token } from 'prism-react-renderer' +import type { Key } from 'react' import { CopyButton } from './CopyButton' interface CodeBlockProps { @@ -28,33 +29,47 @@ const oneDark = { ...themes.oneDark, plain: { ...themes.oneDark.plain, - background: 'hsl(var(--code-background))', + background: 'transparent', + }, +} + +const oneLight = { + ...themes.oneLight, + plain: { + ...themes.oneLight.plain, + background: 'transparent', }, } const CodeBlock: React.FC = ({ code, language, showCopy = true, codeAreaClassName, className }) => { - const { theme } = useTheme() + const { resolvedTheme } = useTheme() return ( -
+
{({ style, tokens, getLineProps, getTokenProps }: HighlightProps) => ( -
+          
             {tokens.map((line, i) => {
               const props = getLineProps({ line, key: i })
-              // @ts-expect-error Workaround for the render error. Key should not be spread into JSX
-              const { key, ...rest } = props
+              const { key: lineKey, ...rest } = props as typeof props & { key?: Key }
               return (
-                
+
{line.map((token, key) => { const tokenProps = getTokenProps({ token, key }) - // @ts-expect-error Workaround for the render error. Key should not be spread into JSX - const { key: tokenKey, ...restTokenProps } = tokenProps - return + const { key: tokenKey, ...restTokenProps } = tokenProps as typeof tokenProps & { key?: Key } + return })}
) @@ -66,7 +81,7 @@ const CodeBlock: React.FC = ({ code, language, showCopy = true, )}
diff --git a/apps/dashboard/src/components/CommandPalette.tsx b/apps/dashboard/src/components/CommandPalette.tsx index 85f995428..20eb3a4e9 100644 --- a/apps/dashboard/src/components/CommandPalette.tsx +++ b/apps/dashboard/src/components/CommandPalette.tsx @@ -10,7 +10,7 @@ import { useCommandPaletteAnalytics } from '@/hooks/useCommandPaletteAnalytics' import { useDeepCompareMemo } from '@/hooks/useDeepCompareMemo' import { cn, pluralize } from '@/lib/utils' import { useCommandState } from 'cmdk' -import { AlertCircle, ChevronRight, Loader2 } from 'lucide-react' +import { AlertCircle, ChevronRight, Loader2 } from '@/components/ui/icon' import { AnimatePresence, motion, useAnimate } from 'motion/react' import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, type ReactNode } from 'react' import { createStore, useStore, type StoreApi } from 'zustand' diff --git a/apps/dashboard/src/components/ComparisonTable.tsx b/apps/dashboard/src/components/ComparisonTable.tsx index 94a7f9a6c..7dbe44f83 100644 --- a/apps/dashboard/src/components/ComparisonTable.tsx +++ b/apps/dashboard/src/components/ComparisonTable.tsx @@ -5,7 +5,7 @@ */ import { cn } from '@/lib/utils' -import { ChevronDown, ChevronRight } from 'lucide-react' +import { ChevronDown, ChevronRight } from '@/components/ui/icon' import { FC, Fragment, ReactNode, useState } from 'react' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from './ui/table' diff --git a/apps/dashboard/src/components/CopyButton.tsx b/apps/dashboard/src/components/CopyButton.tsx index 4d888db7e..a40f63efa 100644 --- a/apps/dashboard/src/components/CopyButton.tsx +++ b/apps/dashboard/src/components/CopyButton.tsx @@ -7,7 +7,7 @@ import { useCopyToClipboard } from '@/hooks/useCopyToClipboard' import { cn } from '@/lib/utils' import { AnimatePresence, motion } from 'framer-motion' -import { CheckIcon, CopyIcon } from 'lucide-react' +import { CheckIcon, CopyIcon } from '@/components/ui/icon' import { ComponentProps } from 'react' import TooltipButton from './TooltipButton' diff --git a/apps/dashboard/src/components/CreateApiKeyDialog.test.ts b/apps/dashboard/src/components/CreateApiKeyDialog.test.ts new file mode 100644 index 000000000..a2ca3713a --- /dev/null +++ b/apps/dashboard/src/components/CreateApiKeyDialog.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'vitest' +import { getCreatedApiKeyCopyButtonLabel } from '@/lib/api-key-dialog' + +describe('CreateApiKeyDialog success state', () => { + it('promotes the API key copy action and confirms after copying the current key', () => { + const apiKey = 'boxlite_test_api_key' + + expect(getCreatedApiKeyCopyButtonLabel({ copiedText: null, apiKey })).toBe('Copy API Key') + expect(getCreatedApiKeyCopyButtonLabel({ copiedText: 'different-value', apiKey })).toBe('Copy API Key') + expect(getCreatedApiKeyCopyButtonLabel({ copiedText: apiKey, apiKey })).toBe('Copied') + }) +}) diff --git a/apps/dashboard/src/components/CreateApiKeyDialog.tsx b/apps/dashboard/src/components/CreateApiKeyDialog.tsx index 17e5e9367..354f92fec 100644 --- a/apps/dashboard/src/components/CreateApiKeyDialog.tsx +++ b/apps/dashboard/src/components/CreateApiKeyDialog.tsx @@ -4,40 +4,28 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { Button } from '@/components/ui/button' -import { DatePicker } from '@/components/ui/date-picker' import { Dialog, - DialogClose, DialogContent, DialogDescription, - DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@/components/ui/dialog' -import { Spinner } from '@/components/ui/spinner' -import { AnimatePresence, motion } from 'framer-motion' -import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon, InfoIcon } from 'lucide-react' - -import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from '@/components/ui/field' -import { Input } from '@/components/ui/input' -import { InputGroup, InputGroupButton, InputGroupInput } from '@/components/ui/input-group' -import { Label } from '@/components/ui/label' -import { CREATE_API_KEY_PERMISSIONS_GROUPS } from '@/constants/CreateApiKeyPermissionsGroups' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' import { useCreateApiKeyMutation } from '@/hooks/mutations/useCreateApiKeyMutation' import { useCopyToClipboard } from '@/hooks/useCopyToClipboard' import { handleApiError } from '@/lib/error-handling' -import { getMaskedToken } from '@/lib/utils' -import { ApiKeyResponse, CreateApiKeyPermissionsEnum } from '@boxlite-ai/api-client' -import { useForm } from '@tanstack/react-form' -import { Plus } from 'lucide-react' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import { cn } from '@/lib/utils' +import { CreateApiKeyPermissionsEnum } from '@boxlite-ai/api-client' +import { Calendar, Info, Plus } from '@/components/ui/icon' +import React, { useEffect, useState } from 'react' import { toast } from 'sonner' -import { z } from 'zod' -import { Alert, AlertDescription, AlertTitle } from './ui/alert' -import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs' -import { ToggleGroup, ToggleGroupItem } from './ui/toggle-group' interface CreateApiKeyDialogProps { availablePermissions: CreateApiKeyPermissionsEnum[] @@ -46,386 +34,195 @@ interface CreateApiKeyDialogProps { organizationId?: string } -const isReadPermission = (permission: CreateApiKeyPermissionsEnum) => permission.startsWith('read:') -const isWritePermission = (permission: CreateApiKeyPermissionsEnum) => permission.startsWith('write:') -const isDeletePermission = (permission: CreateApiKeyPermissionsEnum) => permission.startsWith('delete:') - -const IMPLICIT_READ_RESOURCES = ['Sandboxes', 'Snapshots', 'Registries', 'Regions'] - -const formSchema = z.object({ - name: z.string().min(1, 'Name is required'), - expiresAt: z.date().optional(), - permissions: z.array(z.enum(CreateApiKeyPermissionsEnum)), -}) - -type FormValues = z.infer +const EXPIRY_OPTS: { label: string; days: number | null }[] = [ + { label: 'No expiration', days: null }, + { label: '30 days', days: 30 }, + { label: '90 days', days: 90 }, + { label: '1 year', days: 365 }, +] export const CreateApiKeyDialog: React.FC = ({ availablePermissions, - apiUrl, className, organizationId, }) => { const [open, setOpen] = useState(false) + const [name, setName] = useState('') + const [expiryIdx, setExpiryIdx] = useState(0) + const [submitting, setSubmitting] = useState(false) + const [revealKey, setRevealKey] = useState(null) + const [copied, copy] = useCopyToClipboard() - const { reset: resetCreateApiKeyMutation, ...createApiKeyMutation } = useCreateApiKeyMutation() - - const availableGroups = useMemo(() => { - return CREATE_API_KEY_PERMISSIONS_GROUPS.map((group) => ({ - ...group, - permissions: group.permissions.filter((p) => availablePermissions.includes(p)), - })).filter((group) => group.permissions.length > 0) - }, [availablePermissions]) - - const form = useForm({ - defaultValues: { - name: '', - expiresAt: undefined, - permissions: availablePermissions, - } as FormValues, - validators: { - onSubmit: formSchema, - }, - onSubmit: async ({ value }) => { - if (!organizationId) { - toast.error('Select an organization to create an API key.') - return - } - - try { - await createApiKeyMutation.mutateAsync({ - organizationId, - name: value.name.trim(), - permissions: value.permissions, - expiresAt: value.expiresAt ?? null, - }) - - toast.success('API key created successfully') - } catch (error) { - handleApiError(error, 'Failed to create API key') - } - }, - }) - - const resetState = useCallback(() => { - form.reset({ - name: '', - expiresAt: undefined, - permissions: availablePermissions, - }) - resetCreateApiKeyMutation() - }, [resetCreateApiKeyMutation, form, availablePermissions]) + const { mutateAsync } = useCreateApiKeyMutation() useEffect(() => { if (open) { - resetState() + setName('') + setExpiryIdx(0) + setSubmitting(false) + setRevealKey(null) } - }, [open, resetState]) + }, [open]) - const createdKey = createApiKeyMutation.data + const handleCreate = async () => { + if (!organizationId) { + toast.error('Select an organization to create an API key.') + return + } + if (!name.trim()) return + setSubmitting(true) + try { + const opt = EXPIRY_OPTS[expiryIdx] + const expiresAt = opt.days ? new Date(Date.now() + opt.days * 86400000) : null + const created = await mutateAsync({ + organizationId, + name: name.trim(), + permissions: availablePermissions, + expiresAt, + }) + toast.success('API key created successfully') + setRevealKey(created.value) + } catch (error) { + handleApiError(error, 'Failed to create API key') + } finally { + setSubmitting(false) + } + } + + const keyCopied = revealKey != null && copied === revealKey return ( - { - setOpen(isOpen) - }} - > + - + - - - {createdKey ? 'API Key Created' : 'Create New API Key'} - - {createdKey - ? 'Your API key has been created successfully.' - : 'Choose which actions this API key will be authorized to perform.'} - - - {createdKey ? ( - + + {revealKey ? ( + <> + + API Key Created + + Copy your key now. For security, you won't be able to see it again. + + +
+
+ {revealKey} + +
+
+
+ +
+ ) : ( -
-
{ - e.preventDefault() - e.stopPropagation() - form.handleSubmit() - }} - > - - {(field) => { - const isInvalid = field.state.meta.isTouched && !field.state.meta.isValid - return ( - - Key Name - field.handleChange(e.target.value)} - placeholder="Name" - /> - {field.state.meta.errors.length > 0 && field.state.meta.isTouched && ( - - )} - - ) - }} - - - - {(field) => ( - - Expires - - Optional expiration date for the API key. - - )} - - - { - if (value === 'full-access') { - form.setFieldValue('permissions', availablePermissions) - } else if (value === 'sandbox-access') { - form.setFieldValue('permissions', [ - CreateApiKeyPermissionsEnum.WRITE_SANDBOXES, - CreateApiKeyPermissionsEnum.DELETE_SANDBOXES, - ]) - } else { - form.setFieldValue('permissions', []) - } - }} + <> + + Create New API Key + + Create a key for Boxes API access. + + + +
+ {/* name */} +
+
Key Name
+ setName(e.target.value)} + placeholder="Name" + autoFocus + className="border border-border bg-card px-[14px] py-[13px] font-mono text-[13px] text-foreground outline-none focus:border-brand" + /> +
+ + {/* expires */} +
+
Expires
+ + + + {EXPIRY_OPTS[expiryIdx].label} + + + {EXPIRY_OPTS.map((opt, i) => ( + setExpiryIdx(i)} + > + {opt.label} + + ))} + + +
Optional expiration date for the API key.
+
+ + {/* info */} +
+ +
+
Boxes API access
+
+ This key can create and manage Boxes. Shared Linux base images are available automatically. +
+
+
+
+ +
+
+ Close + + +
+ )} - - - - - {!createdKey && ( - [state.canSubmit, state.isSubmitting]} - children={([canSubmit, isSubmitting]) => ( - - )} - /> - )} -
) } - -const MotionCopyIcon = motion(CopyIcon) -const MotionCheckIcon = motion(CheckIcon) - -const iconProps = { - initial: { opacity: 0, y: 5 }, - animate: { opacity: 1, y: 0 }, - exit: { opacity: 0, y: -5 }, - transition: { duration: 0.1 }, -} - -function CreatedKeyDisplay({ createdKey, apiUrl }: { createdKey: ApiKeyResponse; apiUrl: string }) { - const [copiedApiKey, copyApiKey] = useCopyToClipboard() - const [copiedApiUrl, copyApiUrl] = useCopyToClipboard() - - const [apiKeyRevealed, setApiKeyRevealed] = useState(false) - - return ( -
- - - You can only view this key once. Store it safely. - - - - API Key - - - - setApiKeyRevealed(!apiKeyRevealed)}> - {apiKeyRevealed ? : } - - copyApiKey(createdKey.value)}> - - {copiedApiKey ? ( - - ) : ( - - )} - - - - - - - API URL - - - - copyApiUrl(apiUrl)}> - - {copiedApiUrl ? ( - - ) : ( - - )} - - - - - -
- ) -} diff --git a/apps/dashboard/src/components/CreateRegionDialog.tsx b/apps/dashboard/src/components/CreateRegionDialog.tsx index b9f997116..3d9239669 100644 --- a/apps/dashboard/src/components/CreateRegionDialog.tsx +++ b/apps/dashboard/src/components/CreateRegionDialog.tsx @@ -7,6 +7,7 @@ import React, { useState } from 'react' import { CreateRegion, CreateRegionResponse } from '@boxlite-ai/api-client' import { Button } from '@/components/ui/button' +import { CopyableValue } from '@/components/ui/copyable-value' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { @@ -20,14 +21,13 @@ import { DialogTrigger, } from '@/components/ui/dialog' import { toast } from 'sonner' -import { Plus, Copy } from 'lucide-react' +import { Plus } from '@/components/ui/icon' import { getMaskedToken } from '@/lib/utils' const DEFAULT_FORM_DATA = { name: '', proxyUrl: '', sshGatewayUrl: '', - snapshotManagerUrl: '', } interface CreateRegionDialogProps { @@ -47,7 +47,6 @@ export const CreateRegionDialog: React.FC = ({ const [createdRegion, setCreatedRegion] = useState(null) const [isProxyApiKeyRevealed, setIsProxyApiKeyRevealed] = useState(false) const [isSshGatewayApiKeyRevealed, setIsSshGatewayApiKeyRevealed] = useState(false) - const [isSnapshotManagerPasswordRevealed, setIsSnapshotManagerPasswordRevealed] = useState(false) const [formData, setFormData] = useState(DEFAULT_FORM_DATA) @@ -58,17 +57,11 @@ export const CreateRegionDialog: React.FC = ({ name: formData.name, proxyUrl: formData.proxyUrl.trim() || null, sshGatewayUrl: formData.sshGatewayUrl.trim() || null, - snapshotManagerUrl: formData.snapshotManagerUrl.trim() || null, } const region = await onCreateRegion(createRegionData) if (region) { - if ( - !region.proxyApiKey && - !region.sshGatewayApiKey && - !region.snapshotManagerUsername && - !region.snapshotManagerPassword - ) { + if (!region.proxyApiKey && !region.sshGatewayApiKey) { setOpen(false) setCreatedRegion(null) } else { @@ -105,7 +98,6 @@ export const CreateRegionDialog: React.FC = ({ setFormData(DEFAULT_FORM_DATA) setIsProxyApiKeyRevealed(false) setIsSshGatewayApiKeyRevealed(false) - setIsSnapshotManagerPasswordRevealed(false) } }} > @@ -121,95 +113,50 @@ export const CreateRegionDialog: React.FC = ({ {createdRegion ? 'New Region Created' : 'Create New Region'} {!createdRegion - ? 'Add a new region for grouping runners and sandboxes.' - : createdRegion.proxyApiKey || - createdRegion.sshGatewayApiKey || - createdRegion.snapshotManagerUsername || - createdRegion.snapshotManagerPassword + ? 'Add a new region for grouping runners and boxes.' + : createdRegion.proxyApiKey || createdRegion.sshGatewayApiKey ? "Save these credentials securely. You won't be able to see them again." : ''} - {createdRegion && - (createdRegion.proxyApiKey || - createdRegion.sshGatewayApiKey || - createdRegion.snapshotManagerUsername || - createdRegion.snapshotManagerPassword) ? ( + {createdRegion && (createdRegion.proxyApiKey || createdRegion.sshGatewayApiKey) ? (
{createdRegion.proxyApiKey && (
-
- setIsProxyApiKeyRevealed(true)} - onMouseLeave={() => setIsProxyApiKeyRevealed(false)} - > - {isProxyApiKeyRevealed ? createdRegion.proxyApiKey : getMaskedToken(createdRegion.proxyApiKey)} - - copyToClipboard(createdRegion.proxyApiKey!)} - /> -
+ setIsProxyApiKeyRevealed(true), + onMouseLeave: () => setIsProxyApiKeyRevealed(false), + }} + />
)} {createdRegion.sshGatewayApiKey && (
-
- setIsSshGatewayApiKeyRevealed(true)} - onMouseLeave={() => setIsSshGatewayApiKeyRevealed(false)} - > - {isSshGatewayApiKeyRevealed + - copyToClipboard(createdRegion.sshGatewayApiKey!)} - /> -
-
- )} - - {createdRegion.snapshotManagerUsername && ( -
- -
- - {createdRegion.snapshotManagerUsername} - - copyToClipboard(createdRegion.snapshotManagerUsername!)} - /> -
-
- )} - - {createdRegion.snapshotManagerPassword && ( -
- -
- setIsSnapshotManagerPasswordRevealed(true)} - onMouseLeave={() => setIsSnapshotManagerPasswordRevealed(false)} - > - {isSnapshotManagerPasswordRevealed - ? createdRegion.snapshotManagerPassword - : getMaskedToken(createdRegion.snapshotManagerPassword)} - - copyToClipboard(createdRegion.snapshotManagerPassword!)} - /> -
+ : getMaskedToken(createdRegion.sshGatewayApiKey) + } + copyValue={createdRegion.sshGatewayApiKey} + copyLabel="SSH gateway API key" + onCopy={copyToClipboard} + valueProps={{ + onMouseEnter: () => setIsSshGatewayApiKeyRevealed(true), + onMouseLeave: () => setIsSshGatewayApiKeyRevealed(false), + }} + />
)}
@@ -266,21 +213,6 @@ export const CreateRegionDialog: React.FC = ({ (Optional) URL of the custom SSH gateway for this region

- -
- - { - setFormData((prev) => ({ ...prev, snapshotManagerUrl: e.target.value })) - }} - placeholder="https://snapshot-manager.example.com" - /> -

- (Optional) URL of the custom snapshot manager for this region -

-
)} diff --git a/apps/dashboard/src/components/CreateRunnerDialog.tsx b/apps/dashboard/src/components/CreateRunnerDialog.tsx index 6833f7007..578ce6b2e 100644 --- a/apps/dashboard/src/components/CreateRunnerDialog.tsx +++ b/apps/dashboard/src/components/CreateRunnerDialog.tsx @@ -7,6 +7,7 @@ import React, { useState, useEffect } from 'react' import { Region, CreateRunner, CreateRunnerResponse } from '@boxlite-ai/api-client' import { Button } from '@/components/ui/button' +import { CopyableValue } from '@/components/ui/copyable-value' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { @@ -21,7 +22,7 @@ import { } from '@/components/ui/dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { toast } from 'sonner' -import { Plus, Copy } from 'lucide-react' +import { Plus } from '@/components/ui/icon' import { getMaskedToken } from '@/lib/utils' const DEFAULT_FORM_DATA = { @@ -135,19 +136,16 @@ export const CreateRunnerDialog: React.FC = ({ regions,
-
- setIsTokenRevealed(true)} - onMouseLeave={() => setIsTokenRevealed(false)} - > - {isTokenRevealed ? createdRunner.apiKey : getMaskedToken(createdRunner.apiKey)} - - copyToClipboard(createdRunner.apiKey)} - /> -
+ setIsTokenRevealed(true), + onMouseLeave: () => setIsTokenRevealed(false), + }} + />

Save this token securely. You won't be able to see it again.

diff --git a/apps/dashboard/src/components/ErrorBoundaryFallback.tsx b/apps/dashboard/src/components/ErrorBoundaryFallback.tsx index 4d832698b..e460a1b4e 100644 --- a/apps/dashboard/src/components/ErrorBoundaryFallback.tsx +++ b/apps/dashboard/src/components/ErrorBoundaryFallback.tsx @@ -9,6 +9,9 @@ import { Button } from '@/components/ui/button' import { FallbackProps } from 'react-error-boundary' export function ErrorBoundaryFallback({ error, resetErrorBoundary }: FallbackProps) { + const caughtError = error instanceof Error ? error : undefined + const errorMessage = caughtError?.message || (typeof error === 'string' ? error : 'Unknown error') + return ( @@ -23,18 +26,16 @@ export function ErrorBoundaryFallback({ error, resetErrorBoundary }: FallbackPro

Error Details:

-

- {error?.message || 'Unknown error'} -

+

{errorMessage}

- {error?.stack && ( + {caughtError?.stack && (
Stack Trace (click to expand)
-                {error.stack}
+                {caughtError.stack}
               
)} diff --git a/apps/dashboard/src/components/Invoices/InvoicesTableActions.tsx b/apps/dashboard/src/components/Invoices/InvoicesTableActions.tsx index 114eeff6b..caf26de54 100644 --- a/apps/dashboard/src/components/Invoices/InvoicesTableActions.tsx +++ b/apps/dashboard/src/components/Invoices/InvoicesTableActions.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { MoreHorizontalIcon } from 'lucide-react' +import { MoreHorizontalIcon } from '@/components/ui/icon' import { AlertDialog, AlertDialogAction, diff --git a/apps/dashboard/src/components/Invoices/InvoicesTableHeader.tsx b/apps/dashboard/src/components/Invoices/InvoicesTableHeader.tsx index 91edd0477..62d5f9df2 100644 --- a/apps/dashboard/src/components/Invoices/InvoicesTableHeader.tsx +++ b/apps/dashboard/src/components/Invoices/InvoicesTableHeader.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0 */ -import { Search } from 'lucide-react' +import { Search } from '@/components/ui/icon' import React from 'react' import { DebouncedInput } from '../DebouncedInput' import { InvoicesTableHeaderProps } from './types' diff --git a/apps/dashboard/src/components/Invoices/columns.tsx b/apps/dashboard/src/components/Invoices/columns.tsx index 6bd6a3826..aa6604ebf 100644 --- a/apps/dashboard/src/components/Invoices/columns.tsx +++ b/apps/dashboard/src/components/Invoices/columns.tsx @@ -7,7 +7,7 @@ import { Invoice } from '@/billing-api/types/Invoice' import { formatAmount } from '@/lib/utils' import { ColumnDef } from '@tanstack/react-table' -import { ArrowDown, ArrowUp } from 'lucide-react' +import { ArrowDown, ArrowUp } from '@/components/ui/icon' import React from 'react' import { Badge } from '../ui/badge' import { InvoicesTableActions } from './InvoicesTableActions' diff --git a/apps/dashboard/src/components/Invoices/index.tsx b/apps/dashboard/src/components/Invoices/index.tsx index d9fa0c941..dfdf25a5d 100644 --- a/apps/dashboard/src/components/Invoices/index.tsx +++ b/apps/dashboard/src/components/Invoices/index.tsx @@ -6,7 +6,7 @@ import { cn } from '@/lib/utils' import { flexRender } from '@tanstack/react-table' -import { FileText } from 'lucide-react' +import { FileText } from '@/components/ui/icon' import { Pagination } from '../Pagination' import { TableEmptyState } from '../TableEmptyState' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table' diff --git a/apps/dashboard/src/components/LimitUsageChart.tsx b/apps/dashboard/src/components/LimitUsageChart.tsx deleted file mode 100644 index 963ad6145..000000000 --- a/apps/dashboard/src/components/LimitUsageChart.tsx +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - ChartConfig, - ChartContainer, - ChartLegend, - ChartLegendContent, - ChartTooltip, - ChartTooltipContent, -} from '@/components/ui/chart' -import { FacetFilter } from '@/components/ui/facet-filter' -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import type { RegionUsageOverview } from '@boxlite-ai/api-client' -import { useMemo, useState } from 'react' -import { Bar, BarChart, CartesianGrid, ReferenceLine, XAxis, YAxis } from 'recharts' - -type ResourceKey = 'cpu' | 'ram' | 'storage' - -const clamp = (v: number) => Math.max(0, Math.min(100, Math.round(v * 10) / 10)) - -const formatDate = (value: string) => new Date(value).toLocaleDateString(undefined, { month: 'short', day: '2-digit' }) - -interface PastUsage { - date: string - peakCpuUsage: number - peakMemoryUsage: number - peakDiskUsage: number -} - -export function LimitUsageChart({ - defaultPeriod = 30, - defaultResources = ['ram', 'cpu', 'storage'], - pastUsage = [], - currentUsage, - title, -}: { - defaultPeriod?: number - defaultResources?: ResourceKey[] - pastUsage: PastUsage[] - currentUsage?: RegionUsageOverview | null - title?: React.ReactNode -}) { - const [period, setPeriod] = useState(defaultPeriod.toString()) - - const [selected, setSelected] = useState>(new Set(defaultResources)) - - const data = useMemo(() => { - if (!currentUsage) { - return [] - } - - const { totalCpuQuota, totalMemoryQuota, totalDiskQuota } = currentUsage - return pastUsage.slice(-Number(period)).map((r) => ({ - date: r.date, - cpu: clamp((r.peakCpuUsage / totalCpuQuota) * 100), - ram: clamp((r.peakMemoryUsage / totalMemoryQuota) * 100), - storage: clamp((r.peakDiskUsage / totalDiskQuota) * 100), - })) - }, [pastUsage, currentUsage, period]) - - const config: ChartConfig = useMemo(() => { - const full: Record = { - cpu: { label: 'CPU', color: 'hsl(var(--chart-3))' }, - ram: { label: 'RAM', color: 'hsl(var(--chart-2))' }, - storage: { label: 'Storage', color: 'hsl(var(--chart-1))' }, - } - return Object.fromEntries(Object.entries(full).filter(([k]) => selected.has(k as ResourceKey))) as ChartConfig - }, [selected]) - - return ( -
-
- {title} -
- setSelected(new Set(key as Set))} - /> - -
-
- - - - - - `${v}%`} - /> - - - formatDate(label)} - valueFormatter={(value) => `${value}%`} - /> - } - /> - } /> - {selected.has('storage') && } - {selected.has('ram') && } - {selected.has('cpu') && } - - -
- ) -} diff --git a/apps/dashboard/src/components/LiveIndicator.tsx b/apps/dashboard/src/components/LiveIndicator.tsx deleted file mode 100644 index eb2145d8a..000000000 --- a/apps/dashboard/src/components/LiveIndicator.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { useQueryCountdown } from '@/hooks/useQueryCountdown' -import { cn } from '@/lib/utils' -import { Tooltip } from './Tooltip' - -export function LiveIndicator({ - isUpdating, - intervalMs, - lastUpdatedAt, -}: { - isUpdating: boolean - intervalMs: number - lastUpdatedAt: number -}) { - const refreshingIn = useQueryCountdown(lastUpdatedAt, intervalMs) - - return ( - -
Data is refreshed every {intervalMs / 1000} seconds.
- - Refreshing{' '} - {isUpdating ? ( - '' - ) : ( - <> - in {refreshingIn}s - - )} - ... - -
- } - label={ -
-
-
-
- - Live - -
- } - /> - ) -} diff --git a/apps/dashboard/src/components/LoadingFallback.tsx b/apps/dashboard/src/components/LoadingFallback.tsx index 7da2da568..0bb6deedf 100644 --- a/apps/dashboard/src/components/LoadingFallback.tsx +++ b/apps/dashboard/src/components/LoadingFallback.tsx @@ -5,12 +5,13 @@ */ import { LogoText } from '@/assets/Logo' -import { Loader2 } from 'lucide-react' import { useEffect, useState } from 'react' -import { Skeleton } from './ui/skeleton' +// Terminal-style boot screen so the loading state matches the console's +// pixel/mono aesthetic instead of a generic skeleton shell. const LoadingFallback = () => { const [showLongLoadingMessage, setShowLongLoadingMessage] = useState(false) + const [dotCount, setDotCount] = useState(1) useEffect(() => { const timer = setTimeout(() => { @@ -20,59 +21,39 @@ const LoadingFallback = () => { return () => clearTimeout(timer) }, []) - return ( -
-
-
-
- -
-
- - - -
-
- - -
-
-
- -
-
- - -
- -
- -
- - - - -
-
+ // Typewriter ellipsis: cycle 1 → 2 → 3 dots and back, a touch retro. + useEffect(() => { + const id = setInterval(() => setDotCount((d) => (d % 3) + 1), 400) + return () => clearInterval(id) + }, []) -
- -
-

This is taking longer than expected...

-

- If this issue persists, contact us at{' '} - - support@boxlite.ai - - . -

-
+ return ( +
+ + +
+ + booting console + {/* typewriter ellipsis: 1 → 2 → 3 dots, looping */} + +
+ + {showLongLoadingMessage && ( +
+

taking longer than expected…

+

+ if it persists, ping{' '} + + support@boxlite.ai + +

-
+ )}
) } diff --git a/apps/dashboard/src/components/OnboardingDialogHost.tsx b/apps/dashboard/src/components/OnboardingDialogHost.tsx new file mode 100644 index 000000000..13deca879 --- /dev/null +++ b/apps/dashboard/src/components/OnboardingDialogHost.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { OnboardingGuideDialog } from '@/components/OnboardingGuideDialog' +import { LocalStorageKey } from '@/enums/LocalStorageKey' +import { setLocalStorageItem } from '@/lib/local-storage' +import { + ONBOARDING_ENTRY_HIGHLIGHT_EVENT, + ONBOARDING_OPEN_EVENT, + ONBOARDING_PROGRESS_EVENT, + mergeOnboardingProgress, + readOnboardingProgress, + type OnboardingProgress, +} from '@/lib/onboarding-progress' +import { useCallback, useEffect, useState } from 'react' +import { useAuth } from 'react-oidc-context' + +// Single, app-global host for the onboarding guide dialog. Rendered once in the +// dashboard shell so the "Guide" button (which dispatches ONBOARDING_OPEN_EVENT) +// opens a dialog from ANY page without navigating away. Replaces the per-page +// redirect to /dashboard/boxes?onboarding=1. First-visit auto-open and the +// ?onboarding=1 URL param are still handled by the box pages themselves. +export function OnboardingDialogHost() { + const { user } = useAuth() + const userId = user?.profile.sub + const [open, setOpen] = useState(false) + const [progress, setProgress] = useState(() => readOnboardingProgress(userId)) + + useEffect(() => { + setProgress(readOnboardingProgress(userId)) + }, [userId]) + + useEffect(() => { + const handleProgress = (event: Event) => { + const next = (event as CustomEvent).detail + setProgress(next ?? readOnboardingProgress(userId)) + } + window.addEventListener(ONBOARDING_PROGRESS_EVENT, handleProgress) + return () => window.removeEventListener(ONBOARDING_PROGRESS_EVENT, handleProgress) + }, [userId]) + + useEffect(() => { + const handleOpen = (event: Event) => { + event.preventDefault() + setOpen(true) + } + window.addEventListener(ONBOARDING_OPEN_EVENT, handleOpen) + return () => window.removeEventListener(ONBOARDING_OPEN_EVENT, handleOpen) + }, []) + + const handleProgressChange = useCallback( + (update: OnboardingProgress) => { + setProgress(mergeOnboardingProgress(userId, update)) + }, + [userId], + ) + + const handleClose = useCallback(() => { + if (userId) { + setLocalStorageItem(`${LocalStorageKey.SkipOnboardingPrefix}${userId}`, 'true') + } + setOpen(false) + window.setTimeout(() => { + window.dispatchEvent(new Event(ONBOARDING_ENTRY_HIGHLIGHT_EVENT)) + }, 220) + }, [userId]) + + return ( + (isOpen ? setOpen(true) : handleClose())} + onProgressChange={handleProgressChange} + progress={progress} + /> + ) +} diff --git a/apps/dashboard/src/components/OnboardingGuideDialog.tsx b/apps/dashboard/src/components/OnboardingGuideDialog.tsx new file mode 100644 index 000000000..8080dcef5 --- /dev/null +++ b/apps/dashboard/src/components/OnboardingGuideDialog.tsx @@ -0,0 +1,495 @@ +/* + * Copyright 2025 Daytona Platforms Inc. + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import goIcon from '@/assets/go.svg' +import pythonIcon from '@/assets/python.svg' +import rustIcon from '@/assets/rust.svg' +import typescriptIcon from '@/assets/typescript.svg' +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog' +import { useApi } from '@/hooks/useApi' +import { useConfig } from '@/hooks/useConfig' +import { getRestApiUrl } from '@/lib/environment' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { handleApiError } from '@/lib/error-handling' +import { getOnboardingCodeExamples, type OnboardingLanguage } from '@/lib/onboarding-code-examples' +import { setLocalStorageItem } from '@/lib/local-storage' +import { cn } from '@/lib/utils' +import type { OnboardingProgress } from '@/lib/onboarding-progress' +import { + CreateApiKeyPermissionsEnum, + OrganizationRolePermissionsEnum, + type ApiKeyResponse, +} from '@boxlite-ai/api-client' +import { Suspense, lazy, useEffect, useMemo, useState } from 'react' +import { toast } from 'sonner' + +// Lazy so prism-react-renderer (syntax highlighting, ~130KB) stays out of the +// first-paint bundle. CodeBlock only renders in step 2 of this dialog, which is +// closed on load — the
 fallback below is never visible during startup.
+const CodeBlock = lazy(() => import('@/components/CodeBlock'))
+
+interface OnboardingGuideDialogProps {
+  open: boolean
+  onOpenChange: (open: boolean) => void
+  onProgressChange: (progress: OnboardingProgress) => void
+  progress: OnboardingProgress
+}
+
+const STAGES = [
+  { tag: 'STEP 01', label: 'Create a key' },
+  { tag: 'STEP 02', label: 'Install the SDK' },
+  { tag: 'STEP 03', label: 'Execute code in box' },
+] as const
+
+// Quickstart scenarios. Today there is one; future scenarios slot in here and route to
+// their own guided flow. (For now every scenario uses the SDK 3-step flow below.)
+const SCENARIOS = [
+  {
+    id: 'untrusted-code',
+    tag: 'Box',
+    title: 'Box as your untrusted code container',
+    description:
+      'Run AI-generated or untrusted code in an isolated, disposable Box. Create a key, install the SDK, then execute code safely inside a box.',
+  },
+] as const
+
+type ScenarioId = (typeof SCENARIOS)[number]['id']
+
+const LANGS: { value: OnboardingLanguage; label: string; iconSrc: string }[] = [
+  { value: 'python', label: 'Python', iconSrc: pythonIcon },
+  { value: 'typescript', label: 'Node', iconSrc: typescriptIcon },
+  { value: 'go', label: 'Go', iconSrc: goIcon },
+  { value: 'rust', label: 'Rust', iconSrc: rustIcon },
+]
+
+function PrimaryBtn({ children, onClick }: { children: React.ReactNode; onClick: () => void }) {
+  return (
+    
+  )
+}
+
+export function OnboardingGuideDialog({ open, onOpenChange, onProgressChange, progress }: OnboardingGuideDialogProps) {
+  const { apiKeyApi } = useApi()
+  const { apiUrl } = useConfig()
+  const restApiUrl = getRestApiUrl(apiUrl)
+  const { selectedOrganization, authenticatedUserHasPermission } = useSelectedOrganization()
+  const canCreateApiKey = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.WRITE_BOXES)
+
+  const [scenario, setScenario] = useState(null)
+  const [step, setStep] = useState(0)
+  const [done, setDone] = useState<[boolean, boolean, boolean]>([false, false, false])
+  const [language, setLanguage] = useState('python')
+  const [createdKey, setCreatedKey] = useState(null)
+  const [creating, setCreating] = useState(false)
+  const [copied, setCopied] = useState(false)
+
+  const codeExamples = getOnboardingCodeExamples()
+  const activeExample = codeExamples[language]
+  const renderedExample = useMemo(
+    () => activeExample.example.replaceAll('your-api-url', restApiUrl),
+    [activeExample.example, restApiUrl],
+  )
+
+  const apiKeyPermissions = useMemo(() => {
+    if (!canCreateApiKey) return []
+    const permissions: CreateApiKeyPermissionsEnum[] = [CreateApiKeyPermissionsEnum.WRITE_BOXES]
+    if (authenticatedUserHasPermission(OrganizationRolePermissionsEnum.DELETE_BOXES)) {
+      permissions.push(CreateApiKeyPermissionsEnum.DELETE_BOXES)
+    }
+    return permissions
+  }, [authenticatedUserHasPermission, canCreateApiKey])
+
+  useEffect(() => {
+    if (open) {
+      setScenario(null)
+      setStep(0)
+      setDone([false, false, false])
+      setCreatedKey(null)
+      setCopied(false)
+    }
+  }, [open])
+
+  const activeScenario = SCENARIOS.find((s) => s.id === scenario)
+  const enterScenario = (id: ScenarioId) => {
+    setScenario(id)
+    setStep(0)
+    setDone([false, false, false])
+    setCreatedKey(null)
+    setCopied(false)
+  }
+  const backToScenarios = () => {
+    setScenario(null)
+    setStep(0)
+    setDone([false, false, false])
+    setCreatedKey(null)
+  }
+
+  const finished = done.every(Boolean)
+
+  const complete = (i: number) => {
+    setDone((prev) => {
+      const next = [...prev] as [boolean, boolean, boolean]
+      next[i] = true
+      if (next.every(Boolean)) {
+        setLocalStorageItem('boxlite-quickstart-done', '1')
+        onProgressChange({ boxCreated: true, sdkConnected: true })
+      }
+      return next
+    })
+    setStep(Math.min(2, i + 1))
+  }
+
+  const handleCreateKey = async () => {
+    if (!selectedOrganization || !canCreateApiKey || apiKeyPermissions.length === 0) {
+      toast.error('API key creation is not available for this user.')
+      return
+    }
+    setCreating(true)
+    try {
+      const key = (
+        await apiKeyApi.createApiKey(
+          { name: 'sdk-quickstart', permissions: apiKeyPermissions },
+          selectedOrganization.id,
+        )
+      ).data
+      setCreatedKey(key)
+      toast.success('API key created successfully')
+    } catch (error) {
+      handleApiError(error, 'Failed to create API key')
+    } finally {
+      setCreating(false)
+    }
+  }
+
+  const copyKey = (value: string) => {
+    try {
+      navigator.clipboard?.writeText(value)
+    } catch {
+      /* clipboard may be unavailable */
+    }
+    setCopied(true)
+    setTimeout(() => setCopied(false), 1400)
+  }
+
+  return (
+    
+      
+        {scenario === null ? (
+          <>
+            
+              Quickstart
+              
+                {SCENARIOS.length} scenario{SCENARIOS.length === 1 ? '' : 's'} available
+              
+            
+            
+
+ {SCENARIOS.map((sc) => ( + + ))} + + {/* coming-soon tile — keeps the grid alive + hints extensibility (ASCII shimmer) */} +
+
+ Box as your agent security runtime +
+
+ coming soon +
+
+
+
+
+
+ +
+ + ) : ( + <> + + + + {activeScenario?.title} + + + Three steps, straight from code. + + + + {/* stage rail */} +
+ {STAGES.map((s, i) => { + const isDone = done[i] + const active = step === i + return ( +
+ + {i < STAGES.length - 1 && ( + + )} +
+ ) + })} +
+ + {/* body */} +
+ {step === 0 && ( +
+
+ {createdKey ? 'Your API key · shown once' : 'Create a key to authenticate'} +
+
+ + {createdKey ? createdKey.value : 'Click “Create key” to generate a secret'} + + {createdKey && ( + + )} +
+ {createdKey && ( +
+ + + Save this as BOXLITE_API_KEY in your environment (e.g.{' '} + export BOXLITE_API_KEY=…) — the SDK reads it at runtime + so the key never lives in your code. + +
+ )} +
+ {createdKey ? ( + + ) : ( + + )} + {createdKey ? ( + complete(0)}> + {done[0] ? '✓ Secured · Next' : 'Copied · Next →'} + + ) : ( + {creating ? 'Creating…' : 'Create key'} + )} +
+
+ )} + + {step === 1 && ( +
+
+ {LANGS.map((l) => { + const on = language === l.value + return ( + + ) + })} +
+
+ Run in your local terminal +
+
+ $ + {activeExample.install} +
+
+ + + Run this command in your local development environment to + install the {LANGS.find((l) => l.value === language)?.label} library. Continue once the install + finishes. + +
+
+ complete(1)}> + {done[1] ? '✓ Installed · Next' : 'Installed · Next →'} + +
+
+ )} + + {step === 2 && ( +
+
+ Run this from your local machine +
+ + {renderedExample} +
+ } + > + + +
+ + + What it does: reads your BOXLITE_API_KEY from the + environment, creates a Box, runs a command inside it, prints the output, then removes the Box. Run + it in your terminal with the install command from the previous step. + +
+
+ complete(2)}>{done[2] ? '✓ Done' : "I've run it"} +
+
+ )} +
+ + {/* footer */} +
+ + {finished && onOpenChange(false)}>Open Fleet →} +
+ + {/* finale */} + {finished && ( +
+ {Array.from({ length: 28 }).map((_, i) => ( + + ))} +
+
✓ Mission complete
+
Box is live.
+
+ You shipped your first Box from code in three steps. +
+
+
+ )} + + )} + + + ) +} diff --git a/apps/dashboard/src/components/OrganizationMembers/CreateOrganizationInvitationDialog.tsx b/apps/dashboard/src/components/OrganizationMembers/CreateOrganizationInvitationDialog.tsx index 4ffae1b9e..317f56eb3 100644 --- a/apps/dashboard/src/components/OrganizationMembers/CreateOrganizationInvitationDialog.tsx +++ b/apps/dashboard/src/components/OrganizationMembers/CreateOrganizationInvitationDialog.tsx @@ -21,7 +21,7 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group' import { CreateOrganizationInvitationRoleEnum, OrganizationRole } from '@boxlite-ai/api-client' -import { Plus } from 'lucide-react' +import { Plus } from '@/components/ui/icon' import React, { useEffect, useState } from 'react' interface CreateOrganizationInvitationDialogProps { diff --git a/apps/dashboard/src/components/OrganizationMembers/OrganizationInvitationTable.tsx b/apps/dashboard/src/components/OrganizationMembers/OrganizationInvitationTable.tsx index 2837953ed..3a2160315 100644 --- a/apps/dashboard/src/components/OrganizationMembers/OrganizationInvitationTable.tsx +++ b/apps/dashboard/src/components/OrganizationMembers/OrganizationInvitationTable.tsx @@ -5,7 +5,7 @@ */ import { useState } from 'react' -import { MoreHorizontal } from 'lucide-react' +import { MoreHorizontal } from '@/components/ui/icon' import { ColumnDef, flexRender, diff --git a/apps/dashboard/src/components/OrganizationMembers/OrganizationMemberTable.tsx b/apps/dashboard/src/components/OrganizationMembers/OrganizationMemberTable.tsx index c192725bc..e692ee0fd 100644 --- a/apps/dashboard/src/components/OrganizationMembers/OrganizationMemberTable.tsx +++ b/apps/dashboard/src/components/OrganizationMembers/OrganizationMemberTable.tsx @@ -5,7 +5,7 @@ */ import { useState } from 'react' -import { MoreHorizontal } from 'lucide-react' +import { MoreHorizontal } from '@/components/ui/icon' import { ColumnDef, flexRender, diff --git a/apps/dashboard/src/components/OrganizationMembers/UpdateOrganizationMemberAccessDialog.tsx b/apps/dashboard/src/components/OrganizationMembers/UpdateOrganizationMemberAccessDialog.tsx index 5eb01099a..a91e7172d 100644 --- a/apps/dashboard/src/components/OrganizationMembers/UpdateOrganizationMemberAccessDialog.tsx +++ b/apps/dashboard/src/components/OrganizationMembers/UpdateOrganizationMemberAccessDialog.tsx @@ -5,7 +5,11 @@ */ import React, { useState } from 'react' -import { CreateOrganizationInvitationRoleEnum, OrganizationRole, OrganizationUserRoleEnum } from '@boxlite-ai/api-client' +import { + CreateOrganizationInvitationRoleEnum, + OrganizationRole, + OrganizationUserRoleEnum, +} from '@boxlite-ai/api-client' import { Button } from '@/components/ui/button' import { Checkbox } from '@/components/ui/checkbox' import { diff --git a/apps/dashboard/src/components/OrganizationMembers/ViewerOrganizationRoleCheckbox.tsx b/apps/dashboard/src/components/OrganizationMembers/ViewerOrganizationRoleCheckbox.tsx index b59a0b069..2329c5d2f 100644 --- a/apps/dashboard/src/components/OrganizationMembers/ViewerOrganizationRoleCheckbox.tsx +++ b/apps/dashboard/src/components/OrganizationMembers/ViewerOrganizationRoleCheckbox.tsx @@ -16,9 +16,7 @@ export const ViewerOrganizationRoleCheckbox: React.FC = () => { -

- Grants read access to sandboxes, snapshots, and registries in the organization -

+

Grants read access to boxes, images, and registries in the organization

) diff --git a/apps/dashboard/src/components/OrganizationRoles/CreateOrganizationRoleDialog.tsx b/apps/dashboard/src/components/OrganizationRoles/CreateOrganizationRoleDialog.tsx index 119ed5111..82e063d75 100644 --- a/apps/dashboard/src/components/OrganizationRoles/CreateOrganizationRoleDialog.tsx +++ b/apps/dashboard/src/components/OrganizationRoles/CreateOrganizationRoleDialog.tsx @@ -21,7 +21,7 @@ import { Label } from '@/components/ui/label' import { ORGANIZATION_ROLE_PERMISSIONS_GROUPS } from '@/constants/OrganizationPermissionsGroups' import { OrganizationRolePermissionGroup } from '@/types/OrganizationRolePermissionGroup' import { OrganizationRolePermissionsEnum } from '@boxlite-ai/api-client' -import { Plus } from 'lucide-react' +import { Plus } from '@/components/ui/icon' import React, { useState } from 'react' interface CreateOrganizationRoleDialogProps { diff --git a/apps/dashboard/src/components/OrganizationRoles/OrganizationRoleTable.tsx b/apps/dashboard/src/components/OrganizationRoles/OrganizationRoleTable.tsx index fe33a04a3..40373d919 100644 --- a/apps/dashboard/src/components/OrganizationRoles/OrganizationRoleTable.tsx +++ b/apps/dashboard/src/components/OrganizationRoles/OrganizationRoleTable.tsx @@ -22,7 +22,7 @@ import { SortingState, useReactTable, } from '@tanstack/react-table' -import { MoreHorizontal } from 'lucide-react' +import { MoreHorizontal } from '@/components/ui/icon' import { useState } from 'react' import { TableEmptyState } from '../TableEmptyState' diff --git a/apps/dashboard/src/components/Organizations/CreateOrganizationDialog.tsx b/apps/dashboard/src/components/Organizations/CreateOrganizationDialog.tsx index 93abe7ffa..926e28708 100644 --- a/apps/dashboard/src/components/Organizations/CreateOrganizationDialog.tsx +++ b/apps/dashboard/src/components/Organizations/CreateOrganizationDialog.tsx @@ -5,9 +5,9 @@ */ import { useState } from 'react' -import { Check, Copy } from 'lucide-react' import { Organization, Region } from '@boxlite-ai/api-client' import { Button } from '@/components/ui/button' +import { CopyableValue } from '@/components/ui/copyable-value' import { Dialog, DialogClose, @@ -102,15 +102,13 @@ export const CreateOrganizationDialog: React.FC =
-
- {createdOrg.id} - {(copied === 'Organization ID' && ) || ( - copyToClipboard(createdOrg.id, 'Organization ID')} - /> - )} -
+ copyToClipboard(value, 'Organization ID')} + />
{createdOrg.defaultRegionId && ( @@ -178,7 +176,7 @@ export const CreateOrganizationDialog: React.FC =

- The region that will be used as the default target for creating sandboxes in this organization. + The region that will be used as the default target for creating boxes in this organization.

diff --git a/apps/dashboard/src/components/Organizations/OrganizationPicker.tsx b/apps/dashboard/src/components/Organizations/OrganizationPicker.tsx deleted file mode 100644 index ecce51e3a..000000000 --- a/apps/dashboard/src/components/Organizations/OrganizationPicker.tsx +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' -import { SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar' -import { cn } from '@/lib/utils' -import { useApi } from '@/hooks/useApi' -import { useOrganizations } from '@/hooks/useOrganizations' -import { useRegions } from '@/hooks/useRegions' -import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' -import { handleApiError } from '@/lib/error-handling' -import { Organization } from '@boxlite-ai/api-client' -import { Building2, ChevronsUpDown, Copy, PlusCircle, SquareUserRound } from 'lucide-react' -import { useEffect, useMemo, useState } from 'react' -import { toast } from 'sonner' -import { useCopyToClipboard } from 'usehooks-ts' -import { CommandHighlight, useRegisterCommands, type CommandConfig } from '../CommandPalette' -import { CreateOrganizationDialog } from './CreateOrganizationDialog' - -function useOrganizationCommands() { - const { organizations } = useOrganizations() - const { selectedOrganization, onSelectOrganization } = useSelectedOrganization() - const [, copyToClipboard] = useCopyToClipboard() - - const commands: CommandConfig[] = useMemo(() => { - const cmds: CommandConfig[] = [] - - if (selectedOrganization) { - cmds.push({ - id: 'copy-org-id', - label: 'Copy Organization ID', - icon: , - onSelect: () => { - copyToClipboard(selectedOrganization.id) - toast.success('Organization ID copied to clipboard') - }, - }) - } - - for (const org of organizations) { - if (org.id === selectedOrganization?.id) continue - - cmds.push({ - id: `switch-org-${org.id}`, - label: ( - <> - Switch to {org.name} - - ), - value: `switch to organization ${org.name}`, - icon: , - onSelect: () => onSelectOrganization(org.id), - }) - } - - return cmds - }, [organizations, selectedOrganization, copyToClipboard, onSelectOrganization]) - - useRegisterCommands(commands, { groupId: 'organization', groupLabel: 'Organization', groupOrder: 5 }) -} - -interface OrganizationPickerProps { - variant?: 'sidebar' | 'header' -} - -export const OrganizationPicker: React.FC = ({ variant = 'sidebar' }) => { - const { organizationsApi } = useApi() - - const { organizations, refreshOrganizations } = useOrganizations() - const { selectedOrganization, onSelectOrganization } = useSelectedOrganization() - const { sharedRegions: regions, loadingSharedRegions: loadingRegions, getRegionName } = useRegions() - - const [optimisticSelectedOrganization, setOptimisticSelectedOrganization] = useState(selectedOrganization) - const [loadingSelectOrganization, setLoadingSelectOrganization] = useState(false) - - useOrganizationCommands() - - useEffect(() => { - setOptimisticSelectedOrganization(selectedOrganization) - }, [selectedOrganization]) - - const handleSelectOrganization = async (organizationId: string) => { - const organization = organizations.find((org) => org.id === organizationId) - if (!organization) { - return - } - - setOptimisticSelectedOrganization(organization) - setLoadingSelectOrganization(true) - const success = await onSelectOrganization(organizationId) - if (!success) { - setOptimisticSelectedOrganization(selectedOrganization) - } - setLoadingSelectOrganization(false) - } - - const [showCreateOrganizationDialog, setShowCreateOrganizationDialog] = useState(false) - - const handleCreateOrganization = async (name: string, defaultRegionId: string) => { - try { - const organization = ( - await organizationsApi.createOrganization({ - name: name.trim(), - defaultRegionId, - }) - ).data - toast.success('Organization created successfully') - await refreshOrganizations(organization.id) - return organization - } catch (error) { - handleApiError(error, 'Failed to create organization') - return null - } - } - - const getOrganizationIcon = (organization: Organization) => { - if (organization.personal) { - return - } - return - } - - // personal first, then alphabetical - const sortedOrganizations = useMemo(() => { - return organizations.sort((a, b) => { - if (a.personal && !b.personal) { - return -1 - } else if (!a.personal && b.personal) { - return 1 - } else { - return a.name.localeCompare(b.name) - } - }) - }, [organizations]) - - if (!optimisticSelectedOrganization) { - return null - } - - const Wrapper = variant === 'header' ? 'div' : SidebarMenuItem - - return ( - - - - -
- {optimisticSelectedOrganization.name[0].toUpperCase()} -
- {optimisticSelectedOrganization.name} - -
-
- -
- {sortedOrganizations.map((org) => ( - handleSelectOrganization(org.id)} - className="cursor-pointer flex items-center gap-2" - > - {getOrganizationIcon(org)} - {org.name} - - ))} -
- -
- setShowCreateOrganizationDialog(true)} - > - - Create Organization - -
-
-
- - -
- ) -} diff --git a/apps/dashboard/src/components/Organizations/SetDefaultRegionDialog.tsx b/apps/dashboard/src/components/Organizations/SetDefaultRegionDialog.tsx index 111ca123c..8ee9bb801 100644 --- a/apps/dashboard/src/components/Organizations/SetDefaultRegionDialog.tsx +++ b/apps/dashboard/src/components/Organizations/SetDefaultRegionDialog.tsx @@ -65,7 +65,7 @@ export const SetDefaultRegionDialog: React.FC = ({ Set Default Region - Your organization needs a default region to create sandboxes and manage resources. + Your organization needs a default region to create boxes and manage resources. {!loadingRegions && regions.length === 0 ? ( diff --git a/apps/dashboard/src/components/PageLayout.tsx b/apps/dashboard/src/components/PageLayout.tsx index 83c4073eb..cb4285c33 100644 --- a/apps/dashboard/src/components/PageLayout.tsx +++ b/apps/dashboard/src/components/PageLayout.tsx @@ -8,7 +8,8 @@ import { cn } from '@/lib/utils' import { type ComponentProps } from 'react' import { BannerStack } from './Banner' -type PageSize = 'default' | 'full' +// default = narrow forms/settings (960); content = list/detail tables centered (1040); full = wide admin tables (1440) +type PageSize = 'default' | 'content' | 'full' function PageLayout({ className, ...props }: ComponentProps<'div'>) { return
@@ -26,6 +27,7 @@ function PageHeader({ 'flex min-h-10 w-full flex-wrap items-center gap-3 gap-y-3 px-4 pt-7 pb-2 sm:px-5 2xl:px-0', { 'mx-auto max-w-[960px]': size === 'default', + 'mx-auto max-w-[1040px]': size === 'content', 'mx-auto max-w-[1440px]': size === 'full', }, className, @@ -74,6 +76,7 @@ function PageContent({ className, size = 'default', ...props }: ComponentProps<' 'flex w-full flex-col gap-4 px-4 pb-8 sm:px-5 2xl:px-0', { 'mx-auto max-w-[960px]': size === 'default', + 'mx-auto max-w-[1040px]': size === 'content', 'mx-auto max-w-[1440px]': size === 'full', }, className, diff --git a/apps/dashboard/src/components/Pagination.tsx b/apps/dashboard/src/components/Pagination.tsx index 655712dd8..eef6b84bf 100644 --- a/apps/dashboard/src/components/Pagination.tsx +++ b/apps/dashboard/src/components/Pagination.tsx @@ -7,7 +7,7 @@ import { useIsCompactScreen, useIsMobile } from '@/hooks/use-mobile' import { cn } from '@/lib/utils' import { Table } from '@tanstack/react-table' -import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react' +import { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from '@/components/ui/icon' import { PAGE_SIZE_OPTIONS } from '../constants/Pagination' import { Button } from './ui/button' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select' diff --git a/apps/dashboard/src/components/Playground/ActionForm.tsx b/apps/dashboard/src/components/Playground/ActionForm.tsx deleted file mode 100644 index bb9d73268..000000000 --- a/apps/dashboard/src/components/Playground/ActionForm.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Label } from '@/components/ui/label' -import { PlaygroundActionFormDataBasic } from '@/contexts/PlaygroundContext' -import { PlaygroundActions } from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import PlaygroundActionRunButton from './ActionRunButton' - -type PlaygroundActionFormProps = { - actionFormItem: PlaygroundActionFormDataBasic - onRunActionClick?: () => Promise - disable?: boolean - hideRunActionButton?: boolean -} - -function PlaygroundActionForm({ - actionFormItem, - onRunActionClick, - disable, - hideRunActionButton, -}: PlaygroundActionFormProps) { - const { runningActionMethod, actionRuntimeError } = usePlayground() - - return ( - <> -
-
- -

- {actionFormItem.description} -

-
- {!hideRunActionButton && ( - - )} -
-
- {actionRuntimeError[actionFormItem.methodName] && ( -

{actionRuntimeError[actionFormItem.methodName]}

- )} -
- - ) -} - -export default PlaygroundActionForm diff --git a/apps/dashboard/src/components/Playground/ActionRunButton.tsx b/apps/dashboard/src/components/Playground/ActionRunButton.tsx deleted file mode 100644 index 99d4ca3e7..000000000 --- a/apps/dashboard/src/components/Playground/ActionRunButton.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { cn } from '@/lib/utils' -import { Loader2, Play } from 'lucide-react' -import TooltipButton from '../TooltipButton' - -type PlaygroundActionRunButtonProps = { - isDisabled: boolean - isRunning: boolean - onRunActionClick?: () => Promise - className?: string -} - -const PlaygroundActionRunButton: React.FC = ({ - isDisabled, - isRunning, - onRunActionClick, - className, -}) => { - return ( - - {isRunning ? : } - - ) -} - -export default PlaygroundActionRunButton diff --git a/apps/dashboard/src/components/Playground/Inputs/CheckboxInput.tsx b/apps/dashboard/src/components/Playground/Inputs/CheckboxInput.tsx deleted file mode 100644 index 1c2aa0eb2..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/CheckboxInput.tsx +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Checkbox } from '@/components/ui/checkbox' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' - -type FormCheckboxInputProps = { - checkedValue: boolean | undefined - formItem: ParameterFormItem - onChangeHandler: (checked: boolean) => void -} - -const FormCheckboxInput: React.FC = ({ checkedValue, formItem, onChangeHandler }) => { - return ( -
- onChangeHandler(!!value)} /> -
- ) -} - -export default FormCheckboxInput diff --git a/apps/dashboard/src/components/Playground/Inputs/InlineInputFormControl.tsx b/apps/dashboard/src/components/Playground/Inputs/InlineInputFormControl.tsx deleted file mode 100644 index ccb010820..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/InlineInputFormControl.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import React, { ReactNode } from 'react' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' -import InputLabel from './Label' - -type InlineInputFormControlProps = { - formItem: ParameterFormItem - children: ReactNode -} - -const InlineInputFormControl: React.FC = ({ formItem, children }) => { - return ( -
- - {children} -
- ) -} - -export default InlineInputFormControl diff --git a/apps/dashboard/src/components/Playground/Inputs/Label.tsx b/apps/dashboard/src/components/Playground/Inputs/Label.tsx deleted file mode 100644 index 6e015161e..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/Label.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Label } from '@/components/ui/label' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' - -type InputLabelProps = { - formItem: ParameterFormItem -} - -const InputLabel: React.FC = ({ formItem }) => { - return ( - - ) -} - -export default InputLabel diff --git a/apps/dashboard/src/components/Playground/Inputs/NumberInput.tsx b/apps/dashboard/src/components/Playground/Inputs/NumberInput.tsx deleted file mode 100644 index 76ee2084b..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/NumberInput.tsx +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Input } from '@/components/ui/input' -import { NumberParameterFormItem } from '@/contexts/PlaygroundContext' -import React from 'react' - -type FormNumberInputProps = { - numberValue: number | undefined - numberFormItem: NumberParameterFormItem - onChangeHandler: (value: number | undefined) => void - disabled?: boolean -} - -const FormNumberInput: React.FC = ({ - numberValue, - numberFormItem, - onChangeHandler, - disabled, -}) => { - return ( - { - const newValue = e.target.value ? Number(e.target.value) : undefined - onChangeHandler(newValue) - }} - disabled={disabled} - /> - ) -} - -export default FormNumberInput diff --git a/apps/dashboard/src/components/Playground/Inputs/SelectInput.tsx b/apps/dashboard/src/components/Playground/Inputs/SelectInput.tsx deleted file mode 100644 index 35af52fda..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/SelectInput.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' -import { Loader2 } from 'lucide-react' - -type SelectOption = { - value: string - label: string -} - -type FormSelectInputProps = { - selectOptions: SelectOption[] - selectValue: string | undefined - formItem: ParameterFormItem - onChangeHandler: (value: string) => void - loading?: boolean -} - -const FormSelectInput: React.FC = ({ - selectOptions, - selectValue, - formItem, - onChangeHandler, - loading, -}) => { - return ( - - ) -} - -export default FormSelectInput diff --git a/apps/dashboard/src/components/Playground/Inputs/StackedInputFormControl.tsx b/apps/dashboard/src/components/Playground/Inputs/StackedInputFormControl.tsx deleted file mode 100644 index df823fffc..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/StackedInputFormControl.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import React, { ReactNode } from 'react' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' -import InputLabel from './Label' - -type StackedInputFormControlProps = { - formItem: ParameterFormItem - children: ReactNode -} - -const StackedInputFormControl: React.FC = ({ formItem, children }) => { - return ( -
- - {children} -
- ) -} - -export default StackedInputFormControl diff --git a/apps/dashboard/src/components/Playground/Inputs/TextInput.tsx b/apps/dashboard/src/components/Playground/Inputs/TextInput.tsx deleted file mode 100644 index 8598e12b0..000000000 --- a/apps/dashboard/src/components/Playground/Inputs/TextInput.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Input } from '@/components/ui/input' -import { ParameterFormItem } from '@/contexts/PlaygroundContext' - -type FormTextInputProps = { - textValue: string | undefined - formItem: ParameterFormItem - onChangeHandler: (value: string) => void -} - -const FormTextInput: React.FC = ({ textValue, formItem, onChangeHandler }) => { - return ( - onChangeHandler(e.target.value)} - /> - ) -} - -export default FormTextInput diff --git a/apps/dashboard/src/components/Playground/PlaygroundLayout.tsx b/apps/dashboard/src/components/Playground/PlaygroundLayout.tsx deleted file mode 100644 index 811e0a0e0..000000000 --- a/apps/dashboard/src/components/Playground/PlaygroundLayout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ScrollArea } from '@/components/ui/scroll-area' -import { cn } from '@/lib/utils' - -function PlaygroundLayout({ children, className }: { children: React.ReactNode; className?: string }) { - return ( -
{children}
- ) -} - -function PlaygroundLayoutSidebar({ children }: { children: React.ReactNode }) { - return ( - -
{children}
-
- ) -} - -function PlaygroundLayoutContent({ children, className }: { children: React.ReactNode; className?: string }) { - return ( -
- {children} -
- ) -} - -export { PlaygroundLayout, PlaygroundLayoutContent, PlaygroundLayoutSidebar } diff --git a/apps/dashboard/src/components/Playground/ResponseCard.tsx b/apps/dashboard/src/components/Playground/ResponseCard.tsx deleted file mode 100644 index 1e02d8d32..000000000 --- a/apps/dashboard/src/components/Playground/ResponseCard.tsx +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { ReactNode } from 'react' - -type ResponseCardProps = { - responseContent: string | ReactNode -} - -const ResponseCard: React.FC = ({ responseContent }) => { - return ( -
-
-        {typeof responseContent === 'string' ? {responseContent} : responseContent}
-      
-
- ) -} - -export default ResponseCard diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/index.ts b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/index.ts deleted file mode 100644 index f09b5af00..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { CodeLanguage } from '@boxlite-ai/sdk' -import { PythonSnippetGenerator } from './python' -import { CodeSnippetGenerator } from './types' -import { TypeScriptSnippetGenerator } from './typescript' - -export const codeSnippetGenerators: Record, CodeSnippetGenerator> = { - [CodeLanguage.PYTHON]: PythonSnippetGenerator, - [CodeLanguage.TYPESCRIPT]: TypeScriptSnippetGenerator, -} - -export type { CodeSnippetActionFlags, CodeSnippetGenerator, CodeSnippetParams } from './types' diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/python.ts b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/python.ts deleted file mode 100644 index 368667aea..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/python.ts +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { CodeSnippetGenerator } from './types' -import { joinGroupedSections } from './utils' - -export const PythonSnippetGenerator: CodeSnippetGenerator = { - getImports(p) { - return ( - [ - 'from boxlite import BoxLite as BoxLite', - p.actions.useConfigObject ? 'BoxliteConfig as BoxLiteConfig' : '', - p.config.useSandboxCreateParams - ? p.config.createSandboxFromSnapshot - ? 'CreateSandboxFromSnapshotParams' - : 'CreateSandboxFromImageParams' - : '', - p.config.useResources ? 'Resources' : '', - p.config.createSandboxFromImage ? 'Image' : '', - ] - .filter(Boolean) - .join(', ') + '\n' - ) - }, - - getConfig(p) { - if (!p.actions.useConfigObject) return '' - return ['\n# Define the configuration', 'config = BoxLiteConfig()'].filter(Boolean).join('\n') + '\n' - }, - - getClientInit(p) { - return ['# Initialize the BoxLite client', `boxlite = BoxLite(${p.actions.useConfigObject ? 'config' : ''})`] - .filter(Boolean) - .join('\n') - }, - - getResources(p) { - if (!p.config.useResources) return '' - const ind = '\t' - return [ - '\n\n# Create a Sandbox with custom resources\nresources = Resources(', - p.config.useResourcesCPU - ? `${ind}cpu=${p.state['resources']['cpu']}, # ${p.state['resources']['cpu']} CPU cores` - : '', - p.config.useResourcesMemory - ? `${ind}memory=${p.state['resources']['memory']}, # ${p.state['resources']['memory']}GB RAM` - : '', - p.config.useResourcesDisk - ? `${ind}disk=${p.state['resources']['disk']}, # ${p.state['resources']['disk']}GB disk space` - : '', - ')', - ] - .filter(Boolean) - .join('\n') - }, - - getSandboxParams(p) { - if (!p.config.useSandboxCreateParams) return '' - const ind = '\t' - return [ - `\n\nparams = ${p.config.createSandboxFromSnapshot ? 'CreateSandboxFromSnapshotParams' : 'CreateSandboxFromImageParams'}(`, - p.config.useCustomSandboxSnapshotName ? `${ind}snapshot="${p.state['snapshotName']}",` : '', - p.config.createSandboxFromImage ? `${ind}image=Image.debian_slim("3.13"),` : '', - p.config.useResources ? `${ind}resources=resources,` : '', - p.config.useLanguageParam ? `${ind}language="${p.state['language']}",` : '', - ...(p.config.createSandboxParamsExist - ? [ - p.config.useAutoStopInterval - ? `${ind}auto_stop_interval=${p.state['createSandboxBaseParams']['autoStopInterval']}, # ${p.state['createSandboxBaseParams']['autoStopInterval'] == 0 ? 'Disables the auto-stop feature' : `Sandbox will be stopped after ${p.state['createSandboxBaseParams']['autoStopInterval']} minute${(p.state['createSandboxBaseParams']['autoStopInterval'] as number) > 1 ? 's' : ''}`}` - : '', - p.config.useAutoArchiveInterval - ? `${ind}auto_archive_interval=${p.state['createSandboxBaseParams']['autoArchiveInterval']}, # Auto-archive after a Sandbox has been stopped for ${p.state['createSandboxBaseParams']['autoArchiveInterval'] == 0 ? '30 days' : `${p.state['createSandboxBaseParams']['autoArchiveInterval']} minutes`}` - : '', - p.config.useAutoDeleteInterval - ? `${ind}auto_delete_interval=${p.state['createSandboxBaseParams']['autoDeleteInterval']}, # ${p.state['createSandboxBaseParams']['autoDeleteInterval'] == 0 ? 'Sandbox will be deleted immediately after stopping' : p.state['createSandboxBaseParams']['autoDeleteInterval'] == -1 ? 'Auto-delete functionality disabled' : `Auto-delete after a Sandbox has been stopped for ${p.state['createSandboxBaseParams']['autoDeleteInterval']} minutes`}` - : '', - ] - : []), - ')', - ] - .filter(Boolean) - .join('\n') - }, - - getSandboxCreate(p) { - return [ - '\n# Create the Sandbox instance', - `sandbox = boxlite.create(${p.config.useSandboxCreateParams ? 'params' : ''})`, - 'print(f"Sandbox created:{sandbox.id}")', - ].join('\n') - }, - - getCodeRun(p) { - if (!p.actions.codeToRunExists) return '' - const ind = '\t' - return [ - '\n\n# Run code securely inside the Sandbox', - 'codeRunResponse = sandbox.process.code_run(', - `'''${p.state['codeRunParams'].languageCode}'''`, - ')', - 'if codeRunResponse.exit_code != 0:', - `${ind}print(f"Error: {codeRunResponse.exit_code} {codeRunResponse.result}")`, - 'else:', - `${ind}print(codeRunResponse.result)`, - ].join('\n') - }, - - getShellRun(p) { - if (!p.actions.shellCommandExists) return '' - return [ - '\n\n# Execute shell commands', - `shellRunResponse = sandbox.process.exec("${p.state['shellCommandRunParams'].shellCommand}")`, - 'print(shellRunResponse.result)', - ].join('\n') - }, - - getFileSystemOps(p) { - const sections: string[] = [] - const ind = '\t' - - if (p.actions.fileSystemCreateFolderParamsSet) { - sections.push( - [ - '# Create folder with specific permissions', - `sandbox.fs.create_folder("${p.state['createFolderParams'].folderDestinationPath}", "${p.state['createFolderParams'].permissions}")`, - ].join('\n'), - ) - } - - if (p.actions.fileSystemListFilesLocationSet) { - sections.push( - [ - '# List files in a directory', - `files = sandbox.fs.list_files("${p.state['listFilesParams'].directoryPath}")`, - 'for file in files:', - `${ind}print(f"Name: {file.name}")`, - `${ind}print(f"Is directory: {file.is_dir}")`, - `${ind}print(f"Size: {file.size}")`, - `${ind}print(f"Modified: {file.mod_time}")`, - ].join('\n'), - ) - } - - if (p.actions.fileSystemDeleteFileRequiredParamsSet) { - sections.push( - [ - `# Delete ${p.actions.useFileSystemDeleteFileRecursive ? 'directory' : 'file'}`, - `sandbox.fs.delete_file("${p.state['deleteFileParams'].filePath}"${p.actions.useFileSystemDeleteFileRecursive ? ', True' : ''})`, - ].join('\n'), - ) - } - - return joinGroupedSections(sections) - }, - - getGitOps(p) { - const sections: string[] = [] - const ind = '\t' - - if (p.actions.gitCloneOperationRequiredParamsSet) { - sections.push( - [ - '# Clone git repository', - 'sandbox.git.clone(', - `${ind}url="${p.state['gitCloneParams'].repositoryURL}",`, - `${ind}path="${p.state['gitCloneParams'].cloneDestinationPath}",`, - p.actions.useGitCloneBranch ? `${ind}branch="${p.state['gitCloneParams'].branchToClone}",` : '', - p.actions.useGitCloneCommitId ? `${ind}commit_id="${p.state['gitCloneParams'].commitToClone}",` : '', - p.actions.useGitCloneUsername ? `${ind}username="${p.state['gitCloneParams'].authUsername}",` : '', - p.actions.useGitClonePassword ? `${ind}password="${p.state['gitCloneParams'].authPassword}"` : '', - ')', - ] - .filter(Boolean) - .join('\n'), - ) - } - - if (p.actions.gitStatusOperationLocationSet) { - sections.push( - [ - '# Get repository status', - `status = sandbox.git.status("${p.state['gitStatusParams'].repositoryPath}")`, - 'print(f"Current branch: {status.current_branch}")', - 'print(f"Commits ahead: {status.ahead}")', - 'print(f"Commits behind: {status.behind}")', - 'for file_status in status.file_status:', - '\tprint(f"File: {file_status.name}")', - ].join('\n'), - ) - } - - if (p.actions.gitBranchesOperationLocationSet) { - sections.push( - [ - '# List branches', - `branchesResponse = sandbox.git.branches("${p.state['gitBranchesParams'].repositoryPath}")`, - 'for branch in branchesResponse.branches:', - '\tprint(f"Branch: {branch}")', - ].join('\n'), - ) - } - - return joinGroupedSections(sections) - }, - - buildFullSnippet(p) { - const imports = this.getImports(p) - const config = this.getConfig(p) - const client = this.getClientInit(p) - const resources = this.getResources(p) - const params = this.getSandboxParams(p) - const create = this.getSandboxCreate(p) - const codeRun = this.getCodeRun(p) - const shell = this.getShellRun(p) - const fsOps = this.getFileSystemOps(p) - const gitOps = this.getGitOps(p) - - return `${imports}${config}\n${client}${resources}${params}\n${create}${fsOps}${gitOps}${codeRun}${shell}` - }, -} diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/types.ts b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/types.ts deleted file mode 100644 index 9aedd0281..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/types.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxParams, SandboxParametersInfo } from '@/contexts/PlaygroundContext' - -export interface CodeSnippetActionFlags { - useConfigObject: boolean - fileSystemListFilesLocationSet: boolean - fileSystemCreateFolderParamsSet: boolean - fileSystemDeleteFileRequiredParamsSet: boolean - useFileSystemDeleteFileRecursive: boolean - shellCommandExists: boolean - codeToRunExists: boolean - gitCloneOperationRequiredParamsSet: boolean - useGitCloneBranch: boolean - useGitCloneCommitId: boolean - useGitCloneUsername: boolean - useGitClonePassword: boolean - gitStatusOperationLocationSet: boolean - gitBranchesOperationLocationSet: boolean -} - -export interface CodeSnippetParams { - state: SandboxParams - config: SandboxParametersInfo - actions: CodeSnippetActionFlags -} - -export interface CodeSnippetGenerator { - getImports(p: CodeSnippetParams): string - getConfig(p: CodeSnippetParams): string - getClientInit(p: CodeSnippetParams): string - getResources(p: CodeSnippetParams): string - getSandboxParams(p: CodeSnippetParams): string - getSandboxCreate(p: CodeSnippetParams): string - getCodeRun(p: CodeSnippetParams): string - getShellRun(p: CodeSnippetParams): string - getFileSystemOps(p: CodeSnippetParams): string - getGitOps(p: CodeSnippetParams): string - buildFullSnippet(p: CodeSnippetParams): string -} diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/typescript.ts b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/typescript.ts deleted file mode 100644 index 560ac49d7..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/typescript.ts +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { CodeSnippetGenerator } from './types' -import { joinGroupedSections } from './utils' - -export const TypeScriptSnippetGenerator: CodeSnippetGenerator = { - getImports(p) { - return ( - [ - 'import { BoxLite as BoxLite', - p.actions.useConfigObject ? 'BoxliteConfig as BoxLiteConfig' : '', - p.config.createSandboxFromImage ? 'Image' : '', - ] - .filter(Boolean) - .join(', ') + " } from '@boxlite-ai/sdk'\n" - ) - }, - - getConfig(p) { - if (!p.actions.useConfigObject) return '' - return ['\n// Define the configuration', 'const config: BoxLiteConfig = { }'].filter(Boolean).join('\n') + '\n' - }, - - getClientInit(p) { - return [ - '\t// Initialize the BoxLite client', - `\tconst boxlite = new BoxLite(${p.actions.useConfigObject ? 'config' : ''})`, - ] - .filter(Boolean) - .join('\n') - }, - - getResources(p) { - if (!p.config.useResources) return '' - const ind = '\t\t\t\t' - return [ - `${ind.slice(0, -1)}resources: {`, - p.config.useResourcesCPU - ? `${ind}cpu: ${p.state['resources']['cpu']}, // ${p.state['resources']['cpu']} CPU cores` - : '', - p.config.useResourcesMemory - ? `${ind}memory: ${p.state['resources']['memory']}, // ${p.state['resources']['memory']}GB RAM` - : '', - p.config.useResourcesDisk - ? `${ind}disk: ${p.state['resources']['disk']}, // ${p.state['resources']['disk']}GB disk space` - : '', - `${ind.slice(0, -1)}}`, - ] - .filter(Boolean) - .join('\n') - }, - - getSandboxParams(p) { - if (!p.config.useSandboxCreateParams) return '' - const ind = '\t\t\t' - return [ - `{`, - p.config.useCustomSandboxSnapshotName ? `${ind}snapshot: '${p.state['snapshotName']}',` : '', - p.config.createSandboxFromImage ? `${ind}image: Image.debianSlim("3.13"),` : '', - this.getResources(p), - p.config.useLanguageParam ? `${ind}language: '${p.state['language']}',` : '', - ...(p.config.createSandboxParamsExist - ? [ - p.config.useAutoStopInterval - ? `${ind}autoStopInterval: ${p.state['createSandboxBaseParams']['autoStopInterval']}, // ${p.state['createSandboxBaseParams']['autoStopInterval'] == 0 ? 'Disables the auto-stop feature' : `Sandbox will be stopped after ${p.state['createSandboxBaseParams']['autoStopInterval']} minute${(p.state['createSandboxBaseParams']['autoStopInterval'] as number) > 1 ? 's' : ''}`}` - : '', - p.config.useAutoArchiveInterval - ? `${ind}autoArchiveInterval: ${p.state['createSandboxBaseParams']['autoArchiveInterval']}, // Auto-archive after a Sandbox has been stopped for ${p.state['createSandboxBaseParams']['autoArchiveInterval'] == 0 ? '30 days' : `${p.state['createSandboxBaseParams']['autoArchiveInterval']} minutes`}` - : '', - p.config.useAutoDeleteInterval - ? `${ind}autoDeleteInterval: ${p.state['createSandboxBaseParams']['autoDeleteInterval']}, // ${p.state['createSandboxBaseParams']['autoDeleteInterval'] == 0 ? 'Sandbox will be deleted immediately after stopping' : p.state['createSandboxBaseParams']['autoDeleteInterval'] == -1 ? 'Auto-delete functionality disabled' : `Auto-delete after a Sandbox has been stopped for ${p.state['createSandboxBaseParams']['autoDeleteInterval']} minutes`}` - : '', - ] - : []), - `${ind.slice(0, -1)}}`, - ] - .filter(Boolean) - .join('\n') - }, - - getSandboxCreate(p) { - return [ - '\t\t// Create the Sandbox instance', - `\t\tconst sandbox = await boxlite.create(${p.config.useSandboxCreateParams ? this.getSandboxParams(p) : ''})`, - ].join('\n') - }, - - getCodeRun(p) { - if (!p.actions.codeToRunExists) return '' - const ind = '\t\t' - return [ - `\n\n${ind}// Run code securely inside the Sandbox`, - `${ind}const codeRunResponse = await sandbox.process.codeRun(\``, - `${(p.state['codeRunParams'].languageCode ?? '').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')}`, // Escape backticks and ${ to prevent breaking the template literal - `${ind}\`)`, - `${ind}if (codeRunResponse.exitCode !== 0) {`, - `${ind + '\t'}console.error("Error running code:", codeRunResponse.exitCode, codeRunResponse.result)`, - `${ind}} else {`, - `${ind + '\t'}console.log(codeRunResponse.result)`, - `${ind}}`, - ].join('\n') - }, - - getShellRun(p) { - if (!p.actions.shellCommandExists) return '' - const ind = '\t\t' - return [ - `\n\n${ind}// Execute shell commands`, - `${ind}const shellRunResponse = await sandbox.process.executeCommand('${p.state['shellCommandRunParams'].shellCommand}')`, - `${ind}console.log(shellRunResponse.result)`, - ].join('\n') - }, - - getFileSystemOps(p) { - const sections: string[] = [] - const ind = '\t\t\t' - const base = ind.slice(0, -1) - - if (p.actions.fileSystemCreateFolderParamsSet) { - sections.push( - [ - `${base}// Create folder with specific permissions`, - `${base}await sandbox.fs.createFolder("${p.state['createFolderParams'].folderDestinationPath}", "${p.state['createFolderParams'].permissions}")`, - ].join('\n'), - ) - } - - if (p.actions.fileSystemListFilesLocationSet) { - sections.push( - [ - `${base}// List files in a directory`, - `${base}const files = await sandbox.fs.listFiles("${p.state['listFilesParams'].directoryPath}")`, - `${base}files.forEach(file => {`, - `${ind}console.log(\`Name: \${file.name}\`)`, - `${ind}console.log(\`Is directory: \${file.isDir}\`)`, - `${ind}console.log(\`Size: \${file.size}\`)`, - `${ind}console.log(\`Modified: \${file.modTime}\`)`, - `${base}})`, - ].join('\n'), - ) - } - - if (p.actions.fileSystemDeleteFileRequiredParamsSet) { - sections.push( - [ - `${base}// Delete ${p.actions.useFileSystemDeleteFileRecursive ? 'directory' : 'file'}`, - `${base}await sandbox.fs.deleteFile("${p.state['deleteFileParams'].filePath}"${p.actions.useFileSystemDeleteFileRecursive ? ', true' : ''})`, - ].join('\n'), - ) - } - - return joinGroupedSections(sections) - }, - - getGitOps(p) { - const sections: string[] = [] - const ind = '\t\t\t' - const base = ind.slice(0, -1) - - if (p.actions.gitCloneOperationRequiredParamsSet) { - sections.push( - [ - `${base}// Clone git repository`, - `${base}await sandbox.git.clone(`, - `${ind}"${p.state['gitCloneParams'].repositoryURL}",`, - `${ind}"${p.state['gitCloneParams'].cloneDestinationPath}",`, - p.actions.useGitCloneBranch ? `${ind}"${p.state['gitCloneParams'].branchToClone}",` : '', - p.actions.useGitCloneCommitId ? `${ind}"${p.state['gitCloneParams'].commitToClone}",` : '', - p.actions.useGitCloneUsername ? `${ind}"${p.state['gitCloneParams'].authUsername}",` : '', - p.actions.useGitClonePassword ? `${ind}"${p.state['gitCloneParams'].authPassword}"` : '', - `${base})`, - ] - .filter(Boolean) - .join('\n'), - ) - } - - if (p.actions.gitStatusOperationLocationSet) { - sections.push( - [ - `${base}// Get repository status`, - `${base}const status = await sandbox.git.status("${p.state['gitStatusParams'].repositoryPath}")`, - `${base}console.log(\`Current branch: \${status.currentBranch}\`)`, - `${base}console.log(\`Commits ahead: \${status.ahead}\`)`, - `${base}console.log(\`Commits behind: \${status.behind}\`)`, - `${base}status.fileStatus.forEach(file => {`, - `${ind}console.log(\`File: \${file.name}\`)`, - `${base}})`, - ].join('\n'), - ) - } - - if (p.actions.gitBranchesOperationLocationSet) { - sections.push( - [ - `${base}// List branches`, - `${base}const branchesResponse = await sandbox.git.branches("${p.state['gitBranchesParams'].repositoryPath}")`, - `${base}branchesResponse.branches.forEach(branch => {`, - `${ind}console.log(\`Branch: \${branch}\`)`, - `${base}})`, - ].join('\n'), - ) - } - - return joinGroupedSections(sections) - }, - - buildFullSnippet(p) { - const imports = this.getImports(p) - const config = this.getConfig(p) - const client = this.getClientInit(p) - const create = this.getSandboxCreate(p) - const codeRun = this.getCodeRun(p) - const shell = this.getShellRun(p) - const fsOps = this.getFileSystemOps(p) - const gitOps = this.getGitOps(p) - - return `${imports}${config} -async function main() { -${client} -\ttry { -${create}${fsOps}${gitOps}${codeRun}${shell} -\t} catch (error) { -\t\tconsole.error("Sandbox flow error:", error) -\t} -} -main().catch(console.error)` - }, -} diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/utils.ts b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/utils.ts deleted file mode 100644 index 15ebd387f..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippets/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Daytona Platforms Inc. - * SPDX-License-Identifier: AGPL-3.0 - */ - -/** - * Joins grouped code snippet sections with consistent spacing. - * Each non-empty section gets a `\n\n` prefix, producing a blank line between sections. - */ -export function joinGroupedSections(sections: string[]): string { - const nonEmpty = sections.filter(Boolean) - if (nonEmpty.length === 0) return '' - return nonEmpty.map((section) => '\n\n' + section).join('') -} diff --git a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippetsResponse.tsx b/apps/dashboard/src/components/Playground/Sandbox/CodeSnippetsResponse.tsx deleted file mode 100644 index 1663c88e7..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/CodeSnippetsResponse.tsx +++ /dev/null @@ -1,452 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import PythonIcon from '@/assets/python.svg' -import TypescriptIcon from '@/assets/typescript.svg' -import CodeBlock from '@/components/CodeBlock' -import { CopyButton } from '@/components/CopyButton' -import TooltipButton from '@/components/TooltipButton' -import { Button } from '@/components/ui/button' -import { ScrollArea } from '@/components/ui/scroll-area' -import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' -import { - FileSystemActions, - GitOperationsActions, - ProcessCodeExecutionActions, - SandboxParametersSections, -} from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import { usePlaygroundSandbox } from '@/hooks/usePlaygroundSandbox' -import { createErrorMessageOutput } from '@/lib/playground' -import { cn } from '@/lib/utils' -import { CodeLanguage, Sandbox } from '@boxlite-ai/sdk' -import { ChevronUpIcon, Loader2, PanelBottom, Play, XIcon } from 'lucide-react' -import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Group, Panel, usePanelRef } from 'react-resizable-panels' -import ResponseCard from '../ResponseCard' -import { Window, WindowContent, WindowTitleBar } from '../Window' -import { codeSnippetGenerators, CodeSnippetParams } from './CodeSnippets' - -const codeSnippetSupportedLanguages = [ - { value: CodeLanguage.PYTHON, label: 'Python', icon: PythonIcon }, - { value: CodeLanguage.TYPESCRIPT, label: 'TypeScript', icon: TypescriptIcon }, -] as const - -const SECTION_SCROLL_MARKERS: Partial> = { - [SandboxParametersSections.FILE_SYSTEM]: [ - '# Create folder', - '# List files', - '# Delete', - '// Create folder', - '// List files', - '// Delete', - ], - [SandboxParametersSections.GIT_OPERATIONS]: [ - '# Clone git', - '# Get repository', - '# List branches', - '// Clone git', - '// Get repository', - '// List branches', - ], - [SandboxParametersSections.PROCESS_CODE_EXECUTION]: [ - '# Run code securely', - '# Execute shell', - '// Run code securely', - '// Execute shell', - ], -} - -const SandboxCodeSnippetsResponse = ({ className }: { className?: string }) => { - const [codeSnippetLanguage, setCodeSnippetLanguage] = useState(CodeLanguage.PYTHON) - const [codeSnippetOutput, setCodeSnippetOutput] = useState('') - const [isCodeSnippetRunning, setIsCodeSnippetRunning] = useState(false) - - const { - sandboxParametersState, - actionRuntimeError, - getSandboxParametersInfo, - enabledSections, - pendingScrollSection, - clearPendingScrollSection, - } = usePlayground() - const { - sandbox: { create: createSandbox }, - } = usePlaygroundSandbox() - - const useConfigObject = false // Currently not needed, we use jwtToken for client config - - const fsOn = enabledSections.includes(SandboxParametersSections.FILE_SYSTEM) - const gitOn = enabledSections.includes(SandboxParametersSections.GIT_OPERATIONS) - const procOn = enabledSections.includes(SandboxParametersSections.PROCESS_CODE_EXECUTION) - - const fileSystemListFilesLocationSet = fsOn && !actionRuntimeError[FileSystemActions.LIST_FILES] - const fileSystemCreateFolderParamsSet = fsOn && !actionRuntimeError[FileSystemActions.CREATE_FOLDER] - const fileSystemDeleteFileRequiredParamsSet = fsOn && !actionRuntimeError[FileSystemActions.DELETE_FILE] - const useFileSystemDeleteFileRecursive = - fileSystemDeleteFileRequiredParamsSet && sandboxParametersState['deleteFileParams'].recursive === true - const shellCommandExists = procOn && !actionRuntimeError[ProcessCodeExecutionActions.SHELL_COMMANDS_RUN] - const codeToRunExists = procOn && !actionRuntimeError[ProcessCodeExecutionActions.CODE_RUN] - const gitCloneOperationRequiredParamsSet = gitOn && !actionRuntimeError[GitOperationsActions.GIT_CLONE] - const useGitCloneBranch = !!sandboxParametersState['gitCloneParams'].branchToClone - const useGitCloneCommitId = !!sandboxParametersState['gitCloneParams'].commitToClone - const useGitCloneUsername = !!sandboxParametersState['gitCloneParams'].authUsername - const useGitClonePassword = !!sandboxParametersState['gitCloneParams'].authPassword - const gitStatusOperationLocationSet = gitOn && !actionRuntimeError[GitOperationsActions.GIT_STATUS] - const gitBranchesOperationLocationSet = gitOn && !actionRuntimeError[GitOperationsActions.GIT_BRANCHES_LIST] - - const codeScrollRef = useRef(null) - const highlightTimersRef = useRef[]>([]) - - const scrollToSection = useCallback((section: SandboxParametersSections) => { - const viewport = codeScrollRef.current?.querySelector('[data-slot=scroll-area-viewport]') - if (!viewport) return - - const markers = SECTION_SCROLL_MARKERS[section] - if (!markers?.length) return - - const walker = document.createTreeWalker(viewport, NodeFilter.SHOW_TEXT) - let node: Text | null - while ((node = walker.nextNode() as Text | null)) { - const text = node.textContent?.trim() ?? '' - if (!markers.some((m) => text.startsWith(m))) continue - - const span = node.parentElement - if (!span) continue - - const el = (span.closest('[class*="line"]') as HTMLElement | null) ?? span - const viewportRect = viewport.getBoundingClientRect() - viewport.scrollTo({ - top: viewport.scrollTop + el.getBoundingClientRect().top - viewportRect.top - 32, - behavior: 'smooth', - }) - - highlightTimersRef.current.forEach(clearTimeout) - el.style.backgroundColor = 'rgba(34, 197, 94, 0.2)' - el.style.borderRadius = '3px' - highlightTimersRef.current = [ - setTimeout(() => { - el.style.transition = 'background-color 1.5s ease-out' - el.style.backgroundColor = 'rgba(34, 197, 94, 0)' - }, 500), - setTimeout(() => { - el.style.backgroundColor = '' - el.style.transition = '' - el.style.borderRadius = '' - }, 2100), - ] - return - } - }, []) - - useEffect(() => { - if (!pendingScrollSection) return - requestAnimationFrame(() => { - scrollToSection(pendingScrollSection) - clearPendingScrollSection() - }) - }, [pendingScrollSection, scrollToSection, clearPendingScrollSection]) - - const codeSnippetParams = useMemo( - () => ({ - state: sandboxParametersState, - config: getSandboxParametersInfo(), - actions: { - useConfigObject, - fileSystemListFilesLocationSet, - fileSystemCreateFolderParamsSet, - fileSystemDeleteFileRequiredParamsSet, - useFileSystemDeleteFileRecursive, - shellCommandExists, - codeToRunExists, - gitCloneOperationRequiredParamsSet, - useGitCloneBranch, - useGitCloneCommitId, - useGitCloneUsername, - useGitClonePassword, - gitStatusOperationLocationSet, - gitBranchesOperationLocationSet, - }, - }), - [ - sandboxParametersState, - getSandboxParametersInfo, - useConfigObject, - fileSystemListFilesLocationSet, - fileSystemCreateFolderParamsSet, - fileSystemDeleteFileRequiredParamsSet, - useFileSystemDeleteFileRecursive, - shellCommandExists, - codeToRunExists, - gitCloneOperationRequiredParamsSet, - useGitCloneBranch, - useGitCloneCommitId, - useGitCloneUsername, - useGitClonePassword, - gitStatusOperationLocationSet, - gitBranchesOperationLocationSet, - ], - ) - - const sandboxCodeSnippetsData = useMemo( - () => ({ - [CodeLanguage.PYTHON]: { code: codeSnippetGenerators[CodeLanguage.PYTHON].buildFullSnippet(codeSnippetParams) }, - [CodeLanguage.TYPESCRIPT]: { - code: codeSnippetGenerators[CodeLanguage.TYPESCRIPT].buildFullSnippet(codeSnippetParams), - }, - }), - [codeSnippetParams], - ) - - const runCodeSnippet = async () => { - setIsCodeSnippetRunning(true) - let codeSnippetOutput = 'Creating sandbox...\n' - setCodeSnippetOutput(codeSnippetOutput) - let sandbox: Sandbox | undefined - - try { - sandbox = await createSandbox() - codeSnippetOutput = `Sandbox successfully created: ${sandbox.id}\n` - setCodeSnippetOutput(codeSnippetOutput) - if (codeToRunExists) { - setCodeSnippetOutput(codeSnippetOutput + '\nRunning code...') - const codeRunResponse = await sandbox.process.codeRun( - sandboxParametersState['codeRunParams'].languageCode as string, - ) // codeToRunExists guarantees that value isn't undefined so we put as string to silence TS compiler - codeSnippetOutput += `\nCode run result: ${codeRunResponse.result}` - setCodeSnippetOutput(codeSnippetOutput) - } - if (shellCommandExists) { - setCodeSnippetOutput(codeSnippetOutput + '\nRunning shell command...') - const shellCommandResponse = await sandbox.process.executeCommand( - sandboxParametersState['shellCommandRunParams'].shellCommand as string, // shellCommandExists guarantees that value isn't undefined so we put as string to silence TS compiler - ) - codeSnippetOutput += `\nShell command result: ${shellCommandResponse.result}` - setCodeSnippetOutput(codeSnippetOutput) - } - let codeRunShellCommandFinishedMessage = '\n' - if (codeToRunExists && shellCommandExists) { - codeRunShellCommandFinishedMessage += '🎉 Code and shell command executed successfully.' - } else if (codeToRunExists) { - codeRunShellCommandFinishedMessage += '🎉 Code executed successfully.' - } else if (shellCommandExists) { - codeRunShellCommandFinishedMessage += '🎉 Shell command executed successfully.' - } - codeSnippetOutput += codeRunShellCommandFinishedMessage + '\n' - setCodeSnippetOutput(codeSnippetOutput) - if (fileSystemCreateFolderParamsSet) { - setCodeSnippetOutput(codeSnippetOutput + '\nCreating directory...') - await sandbox.fs.createFolder( - sandboxParametersState['createFolderParams'].folderDestinationPath, - sandboxParametersState['createFolderParams'].permissions, - ) - codeSnippetOutput += '\n🎉 Directory created successfully.\n' - setCodeSnippetOutput(codeSnippetOutput) - } - if (fileSystemListFilesLocationSet) { - setCodeSnippetOutput(codeSnippetOutput + '\nListing directory files...') - const files = await sandbox.fs.listFiles(sandboxParametersState['listFilesParams'].directoryPath) - codeSnippetOutput += '\nDirectory content:' - codeSnippetOutput += '\n' - files.forEach((file) => { - codeSnippetOutput += `Name: ${file.name}\n` - codeSnippetOutput += `Is directory: ${file.isDir}\n` - codeSnippetOutput += `Size: ${file.size}\n` - codeSnippetOutput += `Modified: ${file.modTime}\n` - }) - setCodeSnippetOutput(codeSnippetOutput) - } - if (fileSystemDeleteFileRequiredParamsSet) { - setCodeSnippetOutput( - codeSnippetOutput + `\nDeleting ${useFileSystemDeleteFileRecursive ? 'directory' : 'file'}...`, - ) - await sandbox.fs.deleteFile( - sandboxParametersState['deleteFileParams'].filePath, - useFileSystemDeleteFileRecursive || false, - ) - codeSnippetOutput += `\n🎉 ${useFileSystemDeleteFileRecursive ? 'Directory' : 'File'} deleted successfully.\n` - setCodeSnippetOutput(codeSnippetOutput) - } - if (gitCloneOperationRequiredParamsSet) { - setCodeSnippetOutput(codeSnippetOutput + '\nCloning repo...') - await sandbox.git.clone( - sandboxParametersState['gitCloneParams'].repositoryURL, - sandboxParametersState['gitCloneParams'].cloneDestinationPath, - useGitCloneBranch ? sandboxParametersState['gitCloneParams'].branchToClone : undefined, - useGitCloneCommitId ? sandboxParametersState['gitCloneParams'].commitToClone : undefined, - useGitCloneUsername ? sandboxParametersState['gitCloneParams'].authUsername : undefined, - useGitClonePassword ? sandboxParametersState['gitCloneParams'].authPassword : undefined, - ) - codeSnippetOutput += '\n🎉 Repository cloned successfully.\n' - setCodeSnippetOutput(codeSnippetOutput) - } - if (gitStatusOperationLocationSet) { - setCodeSnippetOutput(codeSnippetOutput + '\nFetching repository status...') - const status = await sandbox.git.status(sandboxParametersState['gitStatusParams'].repositoryPath) - codeSnippetOutput += `\nCurrent branch: ${status.currentBranch}\n` - codeSnippetOutput += `Commits ahead: ${status.ahead}\n` - codeSnippetOutput += `Commits behind: ${status.behind}\n` - status.fileStatus.forEach((file) => (codeSnippetOutput += `File: ${file.name}\n`)) - setCodeSnippetOutput(codeSnippetOutput) - } - if (gitBranchesOperationLocationSet) { - setCodeSnippetOutput(codeSnippetOutput + '\nFetching repository branches...') - const response = await sandbox.git.branches(sandboxParametersState['gitBranchesParams'].repositoryPath) - codeSnippetOutput += '\n' - response.branches.forEach((branch) => (codeSnippetOutput += `Branch: ${branch}\n`)) - setCodeSnippetOutput(codeSnippetOutput) - } - setCodeSnippetOutput(codeSnippetOutput + '\nSandbox session finished.') - } catch (error) { - console.error(error) - setCodeSnippetOutput( - <> - {codeSnippetOutput} -
- {createErrorMessageOutput(error)} - , - ) - } finally { - setIsCodeSnippetRunning(false) - } - } - - const resultPanelRef = usePanelRef() - - return ( - - Sandbox Code - - setCodeSnippetLanguage(languageValue as CodeLanguage)} - > -
- - {codeSnippetSupportedLanguages.map((language) => ( - -
- {`${language.label} - {language.label} -
-
- ))} -
-
- - { - if (resultPanelRef.current?.isCollapsed()) { - resultPanelRef.current.resize('20%') - } else { - resultPanelRef.current?.collapse() - } - }} - > - - -
-
- - -
- {codeSnippetSupportedLanguages.map((language) => ( - - - - - - - ))} -
-
- - -
-
-
Result
-
- resultPanelRef.current?.resize('80%')} - tooltipText="Maximize" - className="h-6 w-6" - size="sm" - variant="ghost" - > - - - resultPanelRef.current?.collapse()} - > - - -
-
-
- Code output will be shown here...
- ) - } - /> -
-
- - - - - - ) -} - -export default SandboxCodeSnippetsResponse diff --git a/apps/dashboard/src/components/Playground/Sandbox/Parameters/FileSystem.tsx b/apps/dashboard/src/components/Playground/Sandbox/Parameters/FileSystem.tsx deleted file mode 100644 index 05404fa11..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/Parameters/FileSystem.tsx +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - CreateFolderParams, - DeleteFileParams, - FileSystemActionFormData, - ListFilesParams, - ParameterFormData, - ParameterFormItem, -} from '@/contexts/PlaygroundContext' -import { FileSystemActions } from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import PlaygroundActionForm from '../../ActionForm' -import FormCheckboxInput from '../../Inputs/CheckboxInput' -import InlineInputFormControl from '../../Inputs/InlineInputFormControl' -import FormTextInput from '../../Inputs/TextInput' - -const SandboxFileSystem: React.FC = () => { - const { sandboxParametersState, playgroundActionParamValueSetter } = usePlayground() - const createFolderParams = sandboxParametersState['createFolderParams'] - const listFilesParams = sandboxParametersState['listFilesParams'] - const deleteFileParams = sandboxParametersState['deleteFileParams'] - - const listFilesDirectoryFormData: ParameterFormItem & { key: 'directoryPath' } = { - label: 'Directory location', - key: 'directoryPath', - placeholder: 'Directory path to list', - required: true, - } - - const createFolderParamsFormData: ParameterFormData = [ - { - label: 'Folder location', - key: 'folderDestinationPath', - placeholder: 'Path where the directory should be created', - required: true, - }, - { - label: 'Permissions', - key: 'permissions', - placeholder: 'Directory permissions in octal format (e.g. "755")', - required: true, - }, - ] - - const deleteFileLocationFormData: ParameterFormItem & { key: 'filePath' } = { - label: 'File location', - key: 'filePath', - placeholder: 'Path to the file or directory to delete', - required: true, - } - const deleteFileRecursiveFormData: ParameterFormItem & { key: 'recursive' } = { - label: 'Delete directory', - key: 'recursive', - placeholder: 'If the file is a directory, this must be true to delete it.', - } - - const fileSystemActionsFormData: FileSystemActionFormData[] = - [ - { - methodName: FileSystemActions.CREATE_FOLDER, - label: 'createFolder()', - description: 'Creates a new directory in the Sandbox at the specified path with the given permissions', - parametersFormItems: createFolderParamsFormData, - parametersState: createFolderParams, - }, - { - methodName: FileSystemActions.LIST_FILES, - label: 'listFiles()', - description: 'Lists files and directories in a given path and returns their information', - parametersFormItems: [listFilesDirectoryFormData], - parametersState: listFilesParams, - }, - { - methodName: FileSystemActions.DELETE_FILE, - label: 'deleteFile()', - description: 'Deletes a file from the Sandbox', - parametersFormItems: [deleteFileLocationFormData, deleteFileRecursiveFormData], - parametersState: deleteFileParams, - }, - ] - - return ( -
- {fileSystemActionsFormData.map((fileSystemAction) => ( -
- actionFormItem={fileSystemAction} hideRunActionButton /> -
- {fileSystemAction.methodName === FileSystemActions.LIST_FILES && ( - - - playgroundActionParamValueSetter( - fileSystemAction, - listFilesDirectoryFormData, - 'listFilesParams', - value, - ) - } - /> - - )} - {fileSystemAction.methodName === FileSystemActions.CREATE_FOLDER && ( - <> - {createFolderParamsFormData.map((createFolderParamFormItem) => ( - - - playgroundActionParamValueSetter( - fileSystemAction, - createFolderParamFormItem, - 'createFolderParams', - value, - ) - } - /> - - ))} - - )} - {fileSystemAction.methodName === FileSystemActions.DELETE_FILE && ( - <> - - - playgroundActionParamValueSetter( - fileSystemAction, - deleteFileLocationFormData, - 'deleteFileParams', - value, - ) - } - /> - - - - playgroundActionParamValueSetter( - fileSystemAction, - deleteFileRecursiveFormData, - 'deleteFileParams', - checked, - ) - } - /> - - - )} -
-
- ))} -
- ) -} - -export default SandboxFileSystem diff --git a/apps/dashboard/src/components/Playground/Sandbox/Parameters/GitOperations.tsx b/apps/dashboard/src/components/Playground/Sandbox/Parameters/GitOperations.tsx deleted file mode 100644 index 4a3fb21f5..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/Parameters/GitOperations.tsx +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - GitBranchesParams, - GitCloneParams, - GitOperationsActionFormData, - GitStatusParams, - ParameterFormData, - ParameterFormItem, -} from '@/contexts/PlaygroundContext' -import { GitOperationsActions } from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import PlaygroundActionForm from '../../ActionForm' -import InlineInputFormControl from '../../Inputs/InlineInputFormControl' -import FormTextInput from '../../Inputs/TextInput' - -const SandboxGitOperations: React.FC = () => { - const { sandboxParametersState, playgroundActionParamValueSetter } = usePlayground() - const gitCloneParams = sandboxParametersState['gitCloneParams'] - const gitStatusParams = sandboxParametersState['gitStatusParams'] - const gitBranchesParams = sandboxParametersState['gitBranchesParams'] - - const gitCloneParamsFormData: ParameterFormData = [ - { label: 'URL', key: 'repositoryURL', placeholder: 'Repository URL to clone from', required: true }, - { - label: 'Destination', - key: 'cloneDestinationPath', - placeholder: 'Path where the repository should be cloned', - required: true, - }, - { label: 'Branch', key: 'branchToClone', placeholder: 'Specific branch to clone' }, - { label: 'Commit', key: 'commitToClone', placeholder: 'Specific commit to clone' }, - { label: 'Username', key: 'authUsername', placeholder: 'Git username for authentication' }, - { label: 'Password', key: 'authPassword', placeholder: 'Git password or token for authentication' }, - ] - - const gitRepoLocationFormData: ParameterFormItem & { key: 'repositoryPath' } = { - label: 'Repo location', - key: 'repositoryPath', - placeholder: 'Path to the Git repository root', - required: true, - } - - const gitOperationsActionsFormData: GitOperationsActionFormData< - GitCloneParams | GitStatusParams | GitBranchesParams - >[] = [ - { - methodName: GitOperationsActions.GIT_CLONE, - label: 'clone()', - description: 'Clones a Git repository into the specified path', - parametersFormItems: gitCloneParamsFormData, - parametersState: gitCloneParams, - }, - { - methodName: GitOperationsActions.GIT_STATUS, - label: 'status()', - description: 'Gets the current Git repository status', - parametersFormItems: [gitRepoLocationFormData], - parametersState: gitStatusParams, - }, - { - methodName: GitOperationsActions.GIT_BRANCHES_LIST, - label: 'branches()', - description: 'Lists branches in the repository', - parametersFormItems: [gitRepoLocationFormData], - parametersState: gitBranchesParams, - }, - ] - - return ( -
- {gitOperationsActionsFormData.map((gitOperationsAction) => ( -
- actionFormItem={gitOperationsAction} hideRunActionButton /> -
- {gitOperationsAction.methodName === GitOperationsActions.GIT_CLONE && ( - <> - {gitCloneParamsFormData.map((gitCloneParamFormItem) => ( - - - playgroundActionParamValueSetter( - gitOperationsAction, - gitCloneParamFormItem, - 'gitCloneParams', - value, - ) - } - /> - - ))} - - )} - {gitOperationsAction.methodName === GitOperationsActions.GIT_STATUS && ( - - - playgroundActionParamValueSetter( - gitOperationsAction, - gitRepoLocationFormData, - 'gitStatusParams', - value, - ) - } - /> - - )} - {gitOperationsAction.methodName === GitOperationsActions.GIT_BRANCHES_LIST && ( - - - playgroundActionParamValueSetter( - gitOperationsAction, - gitRepoLocationFormData, - 'gitBranchesParams', - value, - ) - } - /> - - )} -
-
- ))} -
- ) -} - -export default SandboxGitOperations diff --git a/apps/dashboard/src/components/Playground/Sandbox/Parameters/Management.tsx b/apps/dashboard/src/components/Playground/Sandbox/Parameters/Management.tsx deleted file mode 100644 index a664923dd..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/Parameters/Management.tsx +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Tooltip } from '@/components/Tooltip' -import { Label } from '@/components/ui/label' -import { SANDBOX_SNAPSHOT_DEFAULT_VALUE } from '@/constants/Playground' -import { NumberParameterFormItem, ParameterFormItem } from '@/contexts/PlaygroundContext' -import { usePlayground } from '@/hooks/usePlayground' -import { getLanguageCodeToRun } from '@/lib/playground' -import { SnapshotDto } from '@boxlite-ai/api-client' -import { CodeLanguage, Resources } from '@boxlite-ai/sdk' -import { HelpCircleIcon } from 'lucide-react' -import InlineInputFormControl from '../../Inputs/InlineInputFormControl' -import FormNumberInput from '../../Inputs/NumberInput' -import FormSelectInput from '../../Inputs/SelectInput' -import StackedInputFormControl from '../../Inputs/StackedInputFormControl' -import { useEffect } from 'react' - -// TODO - Currently, snapshot selection is not supported in the Playground, so props are hardcoded to an empty array and false for loading. We keep snapshot parts commented to enable it in future if requested by users. Also, sandbox creation and code snippet generation suppoort snapshot selection, so they will work when snapshot selection is enabled in the UI without requiring any additional changes. Currently, the snapshot value is fixed to 'Default' -type SandboxManagementParametersProps = { - snapshotsData: Array - snapshotsLoading: boolean -} - -const SandboxManagementParameters: React.FC = ({ - snapshotsData, - snapshotsLoading, -}) => { - const { sandboxParametersState, setSandboxParameterValue } = usePlayground() - const sandboxLanguage = sandboxParametersState['language'] - const sandboxSnapshotName = sandboxParametersState['snapshotName'] - const resources = sandboxParametersState['resources'] - const sandboxFromImageParams = sandboxParametersState['createSandboxBaseParams'] - - const languageFormData: ParameterFormItem = { - label: 'Language', - key: 'language', - placeholder: 'Select sandbox language', - } - - // const sandboxSnapshotFormData: ParameterFormItem = { - // label: 'Snapshot', - // key: 'snapshotName', - // placeholder: 'Select sandbox snapshot', - // } - - // Available languages - const languageOptions = [ - { - value: CodeLanguage.PYTHON, - label: 'Python (default)', - }, - { - value: CodeLanguage.TYPESCRIPT, - label: 'TypeScript', - }, - { - value: CodeLanguage.JAVASCRIPT, - label: 'JavaScript', - }, - ] - const resourcesFormData: (NumberParameterFormItem & { key: keyof Resources })[] = [ - { label: 'Compute (vCPU)', key: 'cpu', min: 1, max: Infinity, placeholder: '1' }, - { label: 'Memory (GiB)', key: 'memory', min: 1, max: Infinity, placeholder: '1' }, - { label: 'Storage (GiB)', key: 'disk', min: 1, max: Infinity, placeholder: '3' }, - ] - - const lifecycleParamsFormData: (NumberParameterFormItem & { - key: 'autoStopInterval' | 'autoArchiveInterval' | 'autoDeleteInterval' - })[] = [ - { label: 'Stop (min)', key: 'autoStopInterval', min: 0, max: Infinity, placeholder: '15' }, - { label: 'Archive (min)', key: 'autoArchiveInterval', min: 0, max: Infinity, placeholder: '7' }, - { label: 'Delete (min)', key: 'autoDeleteInterval', min: -1, max: Infinity, placeholder: '' }, - ] - - // Change code to run based on selected sandbox language - useEffect(() => { - setSandboxParameterValue('codeRunParams', { - languageCode: getLanguageCodeToRun(sandboxParametersState.language), - }) - }, [sandboxParametersState.language, setSandboxParameterValue]) - - const nonDefaultSnapshotSelected = sandboxSnapshotName && sandboxSnapshotName !== SANDBOX_SNAPSHOT_DEFAULT_VALUE - - return ( - <> - - { - setSandboxParameterValue(languageFormData.key as 'language', value as CodeLanguage) - }} - /> - - {/* - ({ - value: snapshot.name, - label: snapshot.name, - })), - ]} - loading={snapshotsLoading} - selectValue={sandboxSnapshotName} - formItem={sandboxSnapshotFormData} - onChangeHandler={(snapshotName) => { - setSandboxParameterValue(sandboxSnapshotFormData.key as 'snapshotName', snapshotName) - }} - /> - */} -
-
- - {nonDefaultSnapshotSelected && ( - - Resources cannot be modified when a non-default snapshot is selected. -
- } - label={ - - } - /> - )} -
-
- {resourcesFormData.map((resourceParamFormItem) => ( - - { - setSandboxParameterValue('resources', { ...resources, [resourceParamFormItem.key]: value }) - }} - /> - - ))} -
-
-
- -
- {lifecycleParamsFormData.map((lifecycleParamFormItem) => ( - - { - setSandboxParameterValue('createSandboxBaseParams', { - ...sandboxFromImageParams, - [lifecycleParamFormItem.key]: value, - }) - }} - /> - - ))} -
-
- - ) -} - -export default SandboxManagementParameters diff --git a/apps/dashboard/src/components/Playground/Sandbox/Parameters/ProcessCodeExecution.tsx b/apps/dashboard/src/components/Playground/Sandbox/Parameters/ProcessCodeExecution.tsx deleted file mode 100644 index 5e3c1c9e9..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/Parameters/ProcessCodeExecution.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import CodeBlock from '@/components/CodeBlock' -import { - CodeRunParams, - ParameterFormItem, - ProcessCodeExecutionOperationsActionFormData, - ShellCommandRunParams, -} from '@/contexts/PlaygroundContext' -import { ProcessCodeExecutionActions } from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import { CodeLanguage } from '@boxlite-ai/sdk' -import PlaygroundActionForm from '../../ActionForm' -import StackedInputFormControl from '../../Inputs/StackedInputFormControl' - -const SandboxProcessCodeExecution: React.FC = () => { - const { sandboxParametersState, setSandboxParameterValue } = usePlayground() - const codeRunParams = sandboxParametersState['codeRunParams'] - const shellCommandRunParams = sandboxParametersState['shellCommandRunParams'] - - const codeRunLanguageCodeFormData: ParameterFormItem & { key: 'languageCode' } = { - label: 'Code to execute', - key: 'languageCode', - placeholder: 'Write the code you want to execute inside the sandbox', - required: true, - } - - const shellCommandFormData: ParameterFormItem & { key: 'shellCommand' } = { - label: 'Shell command', - key: 'shellCommand', - placeholder: 'Enter a shell command to run inside the sandbox', - required: true, - } - - const processCodeExecutionActionsFormData: ProcessCodeExecutionOperationsActionFormData< - CodeRunParams | ShellCommandRunParams - >[] = [ - { - methodName: ProcessCodeExecutionActions.CODE_RUN, - label: 'codeRun()', - description: 'Executes code in the Sandbox using the appropriate language runtime', - parametersFormItems: [codeRunLanguageCodeFormData], - parametersState: codeRunParams, - }, - { - methodName: ProcessCodeExecutionActions.SHELL_COMMANDS_RUN, - label: 'executeCommand()', - description: 'Executes a shell command in the Sandbox', - parametersFormItems: [shellCommandFormData], - parametersState: shellCommandRunParams, - }, - ] - - //TODO -> Currently codeRun and executeCommand values are fixed -> when we enable user to define them implement onChange handlers with validatePlaygroundActionWithParams logic - return ( -
- {processCodeExecutionActionsFormData.map((processCodeExecutionAction) => ( -
- - actionFormItem={processCodeExecutionAction} - hideRunActionButton - /> -
- {processCodeExecutionAction.methodName === ProcessCodeExecutionActions.CODE_RUN && ( - - - - )} - {processCodeExecutionAction.methodName === ProcessCodeExecutionActions.SHELL_COMMANDS_RUN && ( - - - - )} -
-
- ))} -
- ) -} - -export default SandboxProcessCodeExecution diff --git a/apps/dashboard/src/components/Playground/Sandbox/Parameters/index.tsx b/apps/dashboard/src/components/Playground/Sandbox/Parameters/index.tsx deleted file mode 100644 index 291176023..000000000 --- a/apps/dashboard/src/components/Playground/Sandbox/Parameters/index.tsx +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion' -import { Switch } from '@/components/ui/switch' -import { SandboxParametersSections } from '@/enums/Playground' -import { usePlayground } from '@/hooks/usePlayground' -import { cn } from '@/lib/utils' -import { BoltIcon, FolderIcon, GitBranchIcon, SquareTerminalIcon } from 'lucide-react' -import SandboxFileSystem from './FileSystem' -import SandboxGitOperations from './GitOperations' -import SandboxManagementParameters from './Management' -import SandboxProcessCodeExecution from './ProcessCodeExecution' - -const sandboxParametersSectionsData = [ - { value: SandboxParametersSections.SANDBOX_MANAGEMENT, label: 'Management' }, - { value: SandboxParametersSections.FILE_SYSTEM, label: 'File System' }, - { value: SandboxParametersSections.GIT_OPERATIONS, label: 'Git Operations' }, - { value: SandboxParametersSections.PROCESS_CODE_EXECUTION, label: 'Process & Code Execution' }, -] - -const sectionIcons = { - [SandboxParametersSections.SANDBOX_MANAGEMENT]: , - [SandboxParametersSections.GIT_OPERATIONS]: , - [SandboxParametersSections.FILE_SYSTEM]: , - [SandboxParametersSections.PROCESS_CODE_EXECUTION]: , -} - -const SandboxParameters = ({ className }: { className?: string }) => { - const { openedParametersSections, setOpenedParametersSections, enabledSections, enableSection, disableSection } = - usePlayground() - - // TODO - Currently, snapshot selection is not supported in the Playground, so we are using empty array and false for loading. We keep to code commented to enable it in future if requested by users. - // const { snapshotApi } = useApi() - // const { selectedOrganization } = useSelectedOrganization() - - // const { data: snapshotsData = [], isLoading: snapshotsLoading } = useQuery({ - // queryKey: ['snapshots', selectedOrganization?.id, 'all'], - // queryFn: async () => { - // if (!selectedOrganization) return [] - // const response = await snapshotApi.getAllSnapshots(selectedOrganization.id) - // return response.data.items - // }, - // enabled: !!selectedOrganization, - // }) - - return ( -
-
-

Sandbox Configuration

-

Manage resources, lifecycle policies, and file systems.

-
- setOpenedParametersSections(sections as SandboxParametersSections[])} - > - {sandboxParametersSectionsData.map((section) => { - const isManagement = section.value === SandboxParametersSections.SANDBOX_MANAGEMENT - const isEnabled = enabledSections.includes(section.value as SandboxParametersSections) - const isExpanded = openedParametersSections.includes(section.value as SandboxParametersSections) - return ( - - - checked - ? enableSection(section.value as SandboxParametersSections) - : disableSection(section.value as SandboxParametersSections) - } - size="sm" - className="ml-3" - /> - ) : undefined - } - > -
- {sectionIcons[section.value]} {section.label} -
-
- - {isExpanded && ( -
- {section.value === SandboxParametersSections.FILE_SYSTEM && } - {section.value === SandboxParametersSections.GIT_OPERATIONS && } - {section.value === SandboxParametersSections.SANDBOX_MANAGEMENT && ( - - )} - {section.value === SandboxParametersSections.PROCESS_CODE_EXECUTION && ( - - )} -
- )} -
-
- ) - })} -
-
- ) -} - -export default SandboxParameters diff --git a/apps/dashboard/src/components/Playground/Terminal/Description.tsx b/apps/dashboard/src/components/Playground/Terminal/Description.tsx deleted file mode 100644 index b99ebc7a2..000000000 --- a/apps/dashboard/src/components/Playground/Terminal/Description.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -'use client' - -import { CopyButton } from '@/components/CopyButton' -import { TerminalIcon } from 'lucide-react' - -const sampleCommands = ['ls -la', 'top', 'ps aux', 'df -h'] - -const TerminalCommand = ({ value }: { value: string }) => { - return ( -
- - $ - {value} - - -
- ) -} - -const TerminalDescription: React.FC = () => { - return ( -
-
-

Web Terminal

-

- Run commands, view files, and debug directly in the browser. -

-
-
-

- - Common Commands -

-
    - {sampleCommands.map((cmd) => ( -
  • - -
  • - ))} -
-
-
- ) -} - -export default TerminalDescription diff --git a/apps/dashboard/src/components/Playground/Terminal/WebTerminal.tsx b/apps/dashboard/src/components/Playground/Terminal/WebTerminal.tsx deleted file mode 100644 index 09fa0f92e..000000000 --- a/apps/dashboard/src/components/Playground/Terminal/WebTerminal.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Button } from '@/components/ui/button' -import { Spinner } from '@/components/ui/spinner' -import { usePlaygroundSandbox } from '@/hooks/usePlaygroundSandbox' -import { AnimatePresence, motion } from 'framer-motion' -import { RefreshCcw } from 'lucide-react' -import { Window, WindowContent, WindowTitleBar } from '../Window' - -const motionLoadingProps = { - initial: { opacity: 0, y: 10 }, - animate: { opacity: 1, y: 0 }, - exit: { opacity: 0, y: -10 }, - transition: { duration: 0.175 }, -} - -const WebTerminal: React.FC<{ className?: string }> = ({ className }) => { - const { sandbox, terminal } = usePlaygroundSandbox() - const loadingTerminalUrl = terminal.loading || (!sandbox.instance && !sandbox.error) - - return ( - - Sandbox Terminal - -
- {loadingTerminalUrl || !terminal.url ? ( -
- - {loadingTerminalUrl ? ( - - Loading terminal... - - ) : ( - - There was an error loading the terminal. - {sandbox.instance ? ( - - ) : ( - sandbox.error && {sandbox.error} - )} - - )} - -
- ) : ( - - - - - - - - - - - - - - - - {spendingTabAvailable && ( - - - - )} - - - - ) -} - -export default SandboxDetailsSheet diff --git a/apps/dashboard/src/components/SandboxTable/BulkActionAlertDialog.tsx b/apps/dashboard/src/components/SandboxTable/BulkActionAlertDialog.tsx deleted file mode 100644 index 94875b4ed..000000000 --- a/apps/dashboard/src/components/SandboxTable/BulkActionAlertDialog.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '../ui/alert-dialog' - -export enum BulkAction { - Delete = 'delete', - Start = 'start', - Stop = 'stop', - Archive = 'archive', -} - -interface BulkActionData { - title: string - description: string - buttonLabel: string - buttonVariant?: 'destructive' -} - -function getBulkActionData(action: BulkAction, count: number): BulkActionData { - const countText = count === 1 ? 'this sandbox' : `these ${count} selected sandboxes` - - switch (action) { - case BulkAction.Delete: - return { - title: 'Delete Sandboxes', - description: `Are you sure you want to delete ${countText}? This action cannot be undone.`, - buttonLabel: 'Delete', - buttonVariant: 'destructive', - } - case BulkAction.Start: - return { - title: 'Start Sandboxes', - description: `Are you sure you want to start ${countText}?`, - buttonLabel: 'Start', - } - case BulkAction.Stop: - return { - title: 'Stop Sandboxes', - description: `Are you sure you want to stop ${countText}?`, - buttonLabel: 'Stop', - } - case BulkAction.Archive: - return { - title: 'Archive Sandboxes', - description: `Are you sure you want to archive ${countText}? Archived sandboxes can be restored later.`, - buttonLabel: 'Archive', - } - } -} - -interface BulkActionAlertDialogProps { - action: BulkAction | null - count: number - onConfirm: () => void - onCancel: () => void -} - -export function BulkActionAlertDialog({ action, count, onConfirm, onCancel }: BulkActionAlertDialogProps) { - const data = action ? getBulkActionData(action, count) : null - - if (!data) return null - - return ( - !open && onCancel()}> - - <> - - {data.title} - {data.description} - - - Cancel - - {data.buttonLabel} - - - - - - ) -} diff --git a/apps/dashboard/src/components/SandboxTable/SandboxState.tsx b/apps/dashboard/src/components/SandboxTable/SandboxState.tsx deleted file mode 100644 index 543c997ab..000000000 --- a/apps/dashboard/src/components/SandboxTable/SandboxState.tsx +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { cn } from '@/lib/utils' -import { SandboxState as SandboxStateType } from '@boxlite-ai/api-client' -import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' -import { getStateLabel } from './constants' -import { STATE_ICONS } from './state-icons' - -interface SandboxStateProps { - state?: SandboxStateType - errorReason?: string - recoverable?: boolean - className?: string -} - -export function SandboxState({ state, errorReason, recoverable, className }: SandboxStateProps) { - if (!state) return null - const stateIcon = recoverable ? STATE_ICONS['RECOVERY'] : STATE_ICONS[state] || STATE_ICONS[SandboxStateType.UNKNOWN] - const label = getStateLabel(state) - - if (state === SandboxStateType.ERROR || state === SandboxStateType.BUILD_FAILED) { - const errorColor = recoverable ? 'text-yellow-600 dark:text-yellow-400' : 'text-red-600 dark:text-red-400' - - const errorContent = ( -
-
{stateIcon}
- {label} -
- ) - - if (!errorReason) { - return errorContent - } - - return ( - - {errorContent} - -

{errorReason}

-
-
- ) - } - - return ( -
-
{stateIcon}
- {label} -
- ) -} diff --git a/apps/dashboard/src/components/SandboxTable/SandboxTableActions.tsx b/apps/dashboard/src/components/SandboxTable/SandboxTableActions.tsx deleted file mode 100644 index 87eb3c62a..000000000 --- a/apps/dashboard/src/components/SandboxTable/SandboxTableActions.tsx +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { RoutePath } from '@/enums/RoutePath' -import { SandboxState } from '@boxlite-ai/api-client' -import { Terminal, MoreVertical, Play, Square, Loader2, Wrench } from 'lucide-react' -import { generatePath, useNavigate } from 'react-router-dom' -import { useMemo } from 'react' -import { Button } from '../ui/button' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from '../ui/dropdown-menu' -import { SandboxTableActionsProps } from './types' - -export function SandboxTableActions({ - sandbox, - layout = 'table', - writePermitted, - deletePermitted, - isLoading, - onStart, - onStop, - onDelete, - onArchive, - onVnc, - onOpenWebTerminal, - onCreateSshAccess, - onRevokeSshAccess, - onRecover, - onScreenRecordings, -}: SandboxTableActionsProps) { - const navigate = useNavigate() - const isTransitioning = sandbox.state === SandboxState.STARTING || sandbox.state === SandboxState.STOPPING - - const primaryAction = useMemo(() => { - if (sandbox.state === SandboxState.STARTED) { - return { - label: 'Stop', - icon: , - onClick: () => onStop(sandbox.id), - } - } - - if (isTransitioning) { - return { - label: 'Working', - icon: , - onClick: undefined, - } - } - - if (sandbox.state === SandboxState.ERROR && sandbox.recoverable) { - return { - label: 'Recover', - icon: , - onClick: () => onRecover(sandbox.id), - } - } - - return { - label: 'Start', - icon: , - onClick: () => onStart(sandbox.id), - } - }, [isTransitioning, onRecover, onStart, onStop, sandbox.id, sandbox.recoverable, sandbox.state]) - - const menuItems = useMemo(() => { - const items = [] - - items.push({ - key: 'open', - label: 'Open', - onClick: () => navigate(generatePath(RoutePath.SANDBOX_DETAILS, { sandboxId: sandbox.id })), - disabled: isLoading, - }) - - if (writePermitted) { - if (sandbox.state === SandboxState.STARTED) { - items.push({ - key: 'terminal', - label: 'Terminal', - onClick: () => onOpenWebTerminal(sandbox.id), - disabled: isLoading, - }) - items.push({ - key: 'vnc', - label: 'VNC', - onClick: () => onVnc(sandbox.id), - disabled: isLoading, - }) - items.push({ - key: 'screen-recordings', - label: 'Screen Recordings', - onClick: () => onScreenRecordings(sandbox.id), - disabled: isLoading, - }) - items.push({ - key: 'stop', - label: 'Stop', - onClick: () => onStop(sandbox.id), - disabled: isLoading, - }) - } else if (sandbox.state === SandboxState.STOPPED || sandbox.state === SandboxState.ARCHIVED) { - items.push({ - key: 'start', - label: 'Start', - onClick: () => onStart(sandbox.id), - disabled: isLoading, - }) - } else if (sandbox.state === SandboxState.ERROR && sandbox.recoverable) { - items.push({ - key: 'recover', - label: 'Recover', - onClick: () => onRecover(sandbox.id), - disabled: isLoading, - }) - } - - if (sandbox.state === SandboxState.STOPPED) { - items.push({ - key: 'archive', - label: 'Archive', - onClick: () => onArchive(sandbox.id), - disabled: isLoading, - }) - } - - // Add SSH access options - items.push({ - key: 'create-ssh', - label: 'Create SSH Access', - onClick: () => onCreateSshAccess(sandbox.id), - disabled: isLoading, - }) - items.push({ - key: 'revoke-ssh', - label: 'Revoke SSH Access', - onClick: () => onRevokeSshAccess(sandbox.id), - disabled: isLoading, - }) - } - - if (deletePermitted) { - if (items.length > 0 && (sandbox.state === SandboxState.STOPPED || sandbox.state === SandboxState.STARTED)) { - items.push({ key: 'separator', type: 'separator' }) - } - - items.push({ - key: 'delete', - label: 'Delete', - onClick: () => onDelete(sandbox.id), - disabled: isLoading, - className: 'text-red-600 dark:text-red-400', - }) - } - - return items - }, [ - writePermitted, - deletePermitted, - sandbox.state, - sandbox.id, - isLoading, - sandbox.recoverable, - onStart, - onStop, - onDelete, - onArchive, - onVnc, - onOpenWebTerminal, - onCreateSshAccess, - onRevokeSshAccess, - onRecover, - onScreenRecordings, - navigate, - ]) - - if (!writePermitted && !deletePermitted) { - return null - } - - if (layout === 'mobile') { - return ( -
- {writePermitted && ( - - )} - - - - - - - {menuItems.map((item) => { - if (item.type === 'separator') { - return - } - - return ( - { - e.stopPropagation() - item.onClick?.() - }} - className={`cursor-pointer ${item.className || ''}`} - disabled={item.disabled} - > - {item.label} - - ) - })} - - -
- ) - } - - return ( -
- - - {sandbox.state === SandboxState.STARTED ? ( - - ) : ( - - )} - - - - - - - {menuItems.map((item) => { - if (item.type === 'separator') { - return - } - - return ( - { - e.stopPropagation() - item.onClick?.() - }} - className={`cursor-pointer ${item.className || ''}`} - disabled={item.disabled} - > - {item.label} - - ) - })} - - -
- ) -} diff --git a/apps/dashboard/src/components/SandboxTable/SandboxTableHeader.tsx b/apps/dashboard/src/components/SandboxTable/SandboxTableHeader.tsx deleted file mode 100644 index 2b4d3536b..000000000 --- a/apps/dashboard/src/components/SandboxTable/SandboxTableHeader.tsx +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { useIsCompactScreen, useIsMobile } from '@/hooks/use-mobile' -import { cn } from '@/lib/utils' -import { - ArrowUpDown, - Calendar, - Camera, - Check, - Columns, - Cpu, - Globe, - HardDrive, - ListFilter, - MemoryStick, - RefreshCw, - Square, - Tag, -} from 'lucide-react' -import * as React from 'react' -import { DebouncedInput } from '../DebouncedInput' -import { TableColumnVisibilityToggle } from '../TableColumnVisibilityToggle' -import { Button } from '../ui/button' -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandInputButton, - CommandItem, - CommandList, -} from '../ui/command' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuPortal, - DropdownMenuSub, - DropdownMenuSubContent, - DropdownMenuSubTrigger, - DropdownMenuTrigger, -} from '../ui/dropdown-menu' -import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover' -import { LabelFilter, LabelFilterIndicator } from './filters/LabelFilter' -import { LastEventFilter, LastEventFilterIndicator } from './filters/LastEventFilter' -import { RegionFilter, RegionFilterIndicator } from './filters/RegionFilter' -import { ResourceFilter, ResourceFilterIndicator, ResourceFilterValue } from './filters/ResourceFilter' -import { SnapshotFilter, SnapshotFilterIndicator } from './filters/SnapshotFilter' -import { StateFilter, StateFilterIndicator } from './filters/StateFilter' -import { SandboxTableHeaderProps } from './types' - -const RESOURCE_FILTERS = [ - { type: 'cpu' as const, label: 'CPU', icon: Cpu }, - { type: 'memory' as const, label: 'Memory', icon: MemoryStick }, - { type: 'disk' as const, label: 'Disk', icon: HardDrive }, -] - -export function SandboxTableHeader({ - table, - regionOptions, - regionsDataIsLoading, - snapshots, - snapshotsDataIsLoading, - snapshotsDataHasMore, - onChangeSnapshotSearchValue, - onRefresh, - isRefreshing = false, -}: SandboxTableHeaderProps) { - const isMobile = useIsMobile() - const isCompactScreen = useIsCompactScreen() - const [open, setOpen] = React.useState(false) - const currentSort = table.getState().sorting[0]?.id || '' - - const sortableColumns = [ - { id: 'name', label: 'Name' }, - { id: 'state', label: 'State' }, - { id: 'snapshot', label: 'Snapshot' }, - { id: 'region', label: 'Region' }, - { id: 'lastEvent', label: 'Last Event' }, - ] - - const stateFilterValue = (table.getColumn('state')?.getFilterValue() as string[]) || [] - const snapshotFilterValue = (table.getColumn('snapshot')?.getFilterValue() as string[]) || [] - const regionFilterValue = (table.getColumn('region')?.getFilterValue() as string[]) || [] - const resourceFilterValue = (table.getColumn('resources')?.getFilterValue() as ResourceFilterValue) || {} - const labelFilterValue = (table.getColumn('labels')?.getFilterValue() as string[]) || [] - const lastEventFilterValue = (table.getColumn('lastEvent')?.getFilterValue() as Date[]) || [] - - const hasActiveFilters = - stateFilterValue.length > 0 || - snapshotFilterValue.length > 0 || - regionFilterValue.length > 0 || - RESOURCE_FILTERS.some((filter) => Boolean(resourceFilterValue[filter.type])) || - labelFilterValue.length > 0 || - lastEventFilterValue.length > 0 - - return ( -
-
- table.getColumn('name')?.setFilterValue(value)} - placeholder="Search by Name or UUID" - className={cn('min-w-0', { - 'w-full': isMobile, - 'min-w-[16rem] flex-1': !isMobile && isCompactScreen, - 'w-[360px]': !isMobile && !isCompactScreen, - })} - /> - - - - {!isCompactScreen && ( - - - - - - ['name', 'id', 'labels'].includes(column.id))} - getColumnLabel={(id: string) => { - switch (id) { - case 'name': - return 'Name' - case 'id': - return 'UUID' - case 'labels': - return 'Labels' - default: - return id - } - }} - /> - - - )} - - - - - - - - - { - table.resetSorting() - setOpen(false) - }} - > - Reset - - - - No column found. - - {sortableColumns.map((column) => ( - { - const col = table.getColumn(currentValue) - if (col) { - col.toggleSorting(false) - } - setOpen(false) - }} - > - - {column.label} - - ))} - - - - - - - - - - - - - - - State - - - - table.getColumn('state')?.setFilterValue(value)} - /> - - - - - - - Snapshot - - - - table.getColumn('snapshot')?.setFilterValue(value)} - snapshots={snapshots} - isLoading={snapshotsDataIsLoading} - hasMore={snapshotsDataHasMore} - onChangeSnapshotSearchValue={onChangeSnapshotSearchValue} - /> - - - - - - - Region - - - - table.getColumn('region')?.setFilterValue(value)} - options={regionOptions} - isLoading={regionsDataIsLoading} - /> - - - - {RESOURCE_FILTERS.map(({ type, label, icon: Icon }) => ( - - - - {label} - - - - table.getColumn('resources')?.setFilterValue(value)} - resourceType={type} - /> - - - - ))} - - - - Labels - - - - table.getColumn('labels')?.setFilterValue(value)} - /> - - - - - - - Last Event - - - - table.getColumn('lastEvent')?.setFilterValue(value)} - value={lastEventFilterValue} - /> - - - - - -
- - {hasActiveFilters && ( -
- {stateFilterValue.length > 0 && ( - table.getColumn('state')?.setFilterValue(value)} - /> - )} - - {snapshotFilterValue.length > 0 && ( - table.getColumn('snapshot')?.setFilterValue(value)} - snapshots={snapshots} - isLoading={snapshotsDataIsLoading} - hasMore={snapshotsDataHasMore} - onChangeSnapshotSearchValue={onChangeSnapshotSearchValue} - /> - )} - - {regionFilterValue.length > 0 && ( - table.getColumn('region')?.setFilterValue(value)} - options={regionOptions} - isLoading={regionsDataIsLoading} - /> - )} - - {RESOURCE_FILTERS.map(({ type }) => { - const resourceValue = resourceFilterValue[type] - return resourceValue ? ( - table.getColumn('resources')?.setFilterValue(value)} - resourceType={type} - /> - ) : null - })} - - {labelFilterValue.length > 0 && ( - table.getColumn('labels')?.setFilterValue(value)} - /> - )} - - {lastEventFilterValue.length > 0 && ( - table.getColumn('lastEvent')?.setFilterValue(value)} - /> - )} -
- )} -
- ) -} diff --git a/apps/dashboard/src/components/SandboxTable/columns.tsx b/apps/dashboard/src/components/SandboxTable/columns.tsx deleted file mode 100644 index 709cd99b5..000000000 --- a/apps/dashboard/src/components/SandboxTable/columns.tsx +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { formatTimestamp, getRelativeTimeString } from '@/lib/utils' -import { Sandbox, SandboxDesiredState, SandboxState } from '@boxlite-ai/api-client' -import { ColumnDef } from '@tanstack/react-table' -import { ArrowDown, ArrowUp } from 'lucide-react' -import React from 'react' -import { EllipsisWithTooltip } from '../EllipsisWithTooltip' -import { Checkbox } from '../ui/checkbox' -import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip' -import { SandboxState as SandboxStateComponent } from './SandboxState' -import { SandboxTableActions } from './SandboxTableActions' - -interface SortableHeaderProps { - column: any - label: string - dataState?: string -} - -const SortableHeader: React.FC = ({ column, label, dataState }) => { - return ( -
column.toggleSorting(column.getIsSorted() === 'asc')} - className="flex items-center" - {...(dataState && { 'data-state': dataState })} - > - {label} - {column.getIsSorted() === 'asc' ? ( - - ) : column.getIsSorted() === 'desc' ? ( - - ) : ( -
- )} -
- ) -} - -interface GetColumnsProps { - handleStart: (id: string) => void - handleStop: (id: string) => void - handleDelete: (id: string) => void - handleArchive: (id: string) => void - handleVnc: (id: string) => void - getWebTerminalUrl: (id: string) => Promise - sandboxIsLoading: Record - writePermitted: boolean - deletePermitted: boolean - handleCreateSshAccess: (id: string) => void - handleRevokeSshAccess: (id: string) => void - handleRecover: (id: string) => void - getRegionName: (regionId: string) => string | undefined - handleScreenRecordings: (id: string) => void -} - -export function getColumns({ - handleStart, - handleStop, - handleDelete, - handleArchive, - handleVnc, - getWebTerminalUrl, - sandboxIsLoading, - writePermitted, - deletePermitted, - handleCreateSshAccess, - handleRevokeSshAccess, - handleRecover, - getRegionName, - handleScreenRecordings, -}: GetColumnsProps): ColumnDef[] { - const handleOpenWebTerminal = async (sandboxId: string) => { - const url = await getWebTerminalUrl(sandboxId) - if (url) { - window.open(url, '_blank') - } - } - - const columns: ColumnDef[] = [ - { - id: 'select', - size: 30, - header: ({ table }) => ( - { - for (const row of table.getRowModel().rows) { - if (sandboxIsLoading[row.original.id] || row.original.state === SandboxState.DESTROYED) { - row.toggleSelected(false) - } else { - row.toggleSelected(!!value) - } - } - }} - aria-label="Select all" - className="translate-y-[2px]" - /> - ), - cell: ({ row }) => { - return ( -
- row.toggleSelected(!!value)} - aria-label="Select row" - onClick={(e) => e.stopPropagation()} - className="translate-y-[1px]" - /> -
- ) - }, - - enableSorting: false, - enableHiding: false, - }, - { - id: 'name', - size: 320, - enableSorting: true, - enableHiding: true, - header: ({ column }) => { - return - }, - accessorKey: 'name', - cell: ({ row }) => { - const displayName = getSandboxDisplayName(row.original) - return ( -
- {displayName} -
- ) - }, - }, - { - id: 'id', - size: 320, - enableSorting: false, - enableHiding: true, - header: () => { - return UUID - }, - accessorKey: 'id', - cell: ({ row }) => { - return ( -
- {row.original.id} -
- ) - }, - }, - { - id: 'state', - size: 140, - enableSorting: true, - enableHiding: false, - header: ({ column }) => { - return - }, - cell: ({ row }) => ( -
- -
- ), - accessorKey: 'state', - }, - { - id: 'snapshot', - size: 150, - enableSorting: true, - enableHiding: false, - header: ({ column }) => { - return - }, - cell: ({ row }) => { - return ( -
- {row.original.snapshot ? ( - {row.original.snapshot} - ) : ( -
-
- )} -
- ) - }, - accessorKey: 'snapshot', - }, - { - id: 'region', - size: 100, - enableSorting: true, - enableHiding: false, - header: ({ column }) => { - return - }, - cell: ({ row }) => { - return ( -
- {getRegionName(row.original.target) ?? row.original.target} -
- ) - }, - accessorKey: 'target', - }, - { - id: 'resources', - size: 190, - enableSorting: false, - enableHiding: false, - header: () => { - return Resources - }, - cell: ({ row }) => { - return ( -
-
- {row.original.cpu} vCPU -
-
-
- {row.original.memory} GiB -
-
-
- {row.original.disk} GiB -
-
- ) - }, - }, - { - id: 'labels', - size: 110, - enableSorting: false, - enableHiding: true, - header: () => { - return Labels - }, - cell: ({ row }) => { - const labels = Object.entries(row.original.labels ?? {}) - .map(([key, value]) => `${key}: ${value}`) - .join(', ') - - const labelCount = Object.keys(row.original.labels ?? {}).length - return ( - - - {labelCount > 0 ? ( -
- {labelCount > 0 ? (labelCount === 1 ? '1 label' : `${labelCount} labels`) : '/'} -
- ) : ( -
-
- )} -
- {labels && ( - -

{labels}

-
- )} -
- ) - }, - accessorFn: (row) => Object.entries(row.labels ?? {}).map(([key, value]) => `${key}: ${value}`), - }, - { - id: 'lastEvent', - size: 120, - enableSorting: true, - enableHiding: false, - header: ({ column }) => { - return - }, - accessorFn: (row) => getSandboxLastEvent(row).date, - cell: ({ row }) => { - const lastEvent = getSandboxLastEvent(row.original) - return ( -
- {lastEvent.relativeTimeString} -
- ) - }, - }, - { - id: 'createdAt', - size: 200, - enableSorting: true, - enableHiding: false, - header: ({ column }) => { - return - }, - cell: ({ row }) => { - const timestamp = formatTimestamp(row.original.createdAt) - return ( -
- {timestamp} -
- ) - }, - }, - { - id: 'actions', - size: 100, - enableHiding: false, - cell: ({ row }) => ( -
- -
- ), - }, - ] - - return columns -} - -export function getSandboxDisplayName(sandbox: Sandbox): string { - // If the sandbox is destroying and the name starts with "DESTROYED_", trim the prefix and timestamp - if (sandbox.desiredState === SandboxDesiredState.DESTROYED && sandbox.name.startsWith('DESTROYED_')) { - // Remove "DESTROYED_" prefix and everything after the last underscore (timestamp) - const withoutPrefix = sandbox.name.substring(10) // Remove "DESTROYED_" - const lastUnderscoreIndex = withoutPrefix.lastIndexOf('_') - if (lastUnderscoreIndex !== -1) { - return withoutPrefix.substring(0, lastUnderscoreIndex) - } - return withoutPrefix - } - return sandbox.name -} - -export function getSandboxLastEvent(sandbox: Sandbox): { date: Date; relativeTimeString: string } { - return getRelativeTimeString(sandbox.updatedAt) -} diff --git a/apps/dashboard/src/components/SandboxTable/constants.ts b/apps/dashboard/src/components/SandboxTable/constants.ts deleted file mode 100644 index 1b1634bbb..000000000 --- a/apps/dashboard/src/components/SandboxTable/constants.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxState } from '@boxlite-ai/api-client' -import { CheckCircle, Circle, AlertTriangle, Timer, Archive } from 'lucide-react' -import { FacetedFilterOption } from './types' - -const STATE_LABEL_MAPPING: Record = { - [SandboxState.STARTED]: 'Started', - [SandboxState.STOPPED]: 'Stopped', - [SandboxState.ERROR]: 'Error', - [SandboxState.BUILD_FAILED]: 'Build Failed', - [SandboxState.BUILDING_SNAPSHOT]: 'Building Snapshot', - [SandboxState.PENDING_BUILD]: 'Pending Build', - [SandboxState.RESTORING]: 'Restoring', - [SandboxState.ARCHIVED]: 'Archived', - [SandboxState.CREATING]: 'Creating', - [SandboxState.STARTING]: 'Starting', - [SandboxState.STOPPING]: 'Stopping', - [SandboxState.DESTROYING]: 'Deleting', - [SandboxState.DESTROYED]: 'Deleted', - [SandboxState.PULLING_SNAPSHOT]: 'Pulling Snapshot', - [SandboxState.UNKNOWN]: 'Unknown', - [SandboxState.ARCHIVING]: 'Archiving', - [SandboxState.RESIZING]: 'Resizing', -} - -export const STATUSES: FacetedFilterOption[] = [ - { - label: getStateLabel(SandboxState.STARTED), - value: SandboxState.STARTED, - icon: CheckCircle, - }, - { label: getStateLabel(SandboxState.STOPPED), value: SandboxState.STOPPED, icon: Circle }, - { label: getStateLabel(SandboxState.ERROR), value: SandboxState.ERROR, icon: AlertTriangle }, - { label: getStateLabel(SandboxState.BUILD_FAILED), value: SandboxState.BUILD_FAILED, icon: AlertTriangle }, - { label: getStateLabel(SandboxState.STARTING), value: SandboxState.STARTING, icon: Timer }, - { label: getStateLabel(SandboxState.STOPPING), value: SandboxState.STOPPING, icon: Timer }, - { label: getStateLabel(SandboxState.DESTROYING), value: SandboxState.DESTROYING, icon: Timer }, - { label: getStateLabel(SandboxState.ARCHIVED), value: SandboxState.ARCHIVED, icon: Archive }, - { label: getStateLabel(SandboxState.ARCHIVING), value: SandboxState.ARCHIVING, icon: Timer }, -] - -export function getStateLabel(state?: SandboxState): string { - if (!state) { - return 'Unknown' - } - return STATE_LABEL_MAPPING[state] -} diff --git a/apps/dashboard/src/components/SandboxTable/filters/SnapshotFilter.tsx b/apps/dashboard/src/components/SandboxTable/filters/SnapshotFilter.tsx deleted file mode 100644 index 7d7c8d0c6..000000000 --- a/apps/dashboard/src/components/SandboxTable/filters/SnapshotFilter.tsx +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { - Command, - CommandCheckboxItem, - CommandEmpty, - CommandGroup, - CommandInput, - CommandInputButton, - CommandList, -} from '@/components/ui/command' -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' -import { SnapshotDto } from '@boxlite-ai/api-client' -import { Loader2, X } from 'lucide-react' -import { useState } from 'react' - -interface SnapshotFilterProps { - value: string[] - onFilterChange: (value: string[] | undefined) => void - snapshots: SnapshotDto[] - isLoading: boolean - hasMore?: boolean - onChangeSnapshotSearchValue: (name?: string) => void -} - -export function SnapshotFilterIndicator({ - value, - onFilterChange, - snapshots, - isLoading, - hasMore, - onChangeSnapshotSearchValue, -}: SnapshotFilterProps) { - return ( -
- - - Snapshot: {value.length} selected - - - - - - - - -
- ) -} - -export function SnapshotFilter({ - value, - onFilterChange, - snapshots, - isLoading, - hasMore, - onChangeSnapshotSearchValue, -}: SnapshotFilterProps) { - const [searchValue, setSearchValue] = useState('') - - const handleSelect = (snapshotName: string) => { - const newValue = value.includes(snapshotName) - ? value.filter((name) => name !== snapshotName) - : [...value, snapshotName] - onFilterChange(newValue.length > 0 ? newValue : undefined) - } - - const handleSearchChange = (search: string | number) => { - const searchStr = String(search) - setSearchValue(searchStr) - if (onChangeSnapshotSearchValue) { - onChangeSnapshotSearchValue(searchStr || undefined) - } - } - - return ( - - - { - onFilterChange(undefined) - setSearchValue('') - if (onChangeSnapshotSearchValue) { - onChangeSnapshotSearchValue(undefined) - } - }} - > - Clear - - - {hasMore && ( -
-
- Please refine your search to see more Snapshots. -
-
- )} - - {isLoading ? ( -
- - Loading Snapshots... -
- ) : ( - <> - No Snapshots found. - - {snapshots.map((snapshot) => ( - handleSelect(snapshot.name ?? '')} - value={snapshot.name} - className="cursor-pointer" - checked={value.includes(snapshot.name ?? '')} - > - {snapshot.name} - - ))} - - - )} -
-
- ) -} diff --git a/apps/dashboard/src/components/SandboxTable/index.tsx b/apps/dashboard/src/components/SandboxTable/index.tsx deleted file mode 100644 index b3e6fc21f..000000000 --- a/apps/dashboard/src/components/SandboxTable/index.tsx +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { RoutePath } from '@/enums/RoutePath' -import { useCommandPaletteAnalytics } from '@/hooks/useCommandPaletteAnalytics' -import { useIsCompactScreen } from '@/hooks/use-mobile' -import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' -import { cn } from '@/lib/utils' -import { - filterArchivable, - filterDeletable, - filterStartable, - filterStoppable, - getBulkActionCounts, -} from '@/lib/utils/sandbox' -import { OrganizationRolePermissionsEnum, Sandbox, SandboxState } from '@boxlite-ai/api-client' -import { flexRender } from '@tanstack/react-table' -import { Container } from 'lucide-react' -import { AnimatePresence } from 'motion/react' -import { type ReactNode, useCallback, useMemo, useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { useCommandPaletteActions } from '../CommandPalette' -import { Pagination } from '../Pagination' -import { SelectionToast } from '../SelectionToast' -import { TableEmptyState } from '../TableEmptyState' -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table' -import { BulkAction, BulkActionAlertDialog } from './BulkActionAlertDialog' -import { getSandboxDisplayName, getSandboxLastEvent } from './columns' -import { SandboxState as SandboxStateComponent } from './SandboxState' -import { SandboxTableActions } from './SandboxTableActions' -import { SandboxTableHeader } from './SandboxTableHeader' -import { SandboxTableProps } from './types' -import { useSandboxCommands } from './useSandboxCommands' -import { useSandboxTable } from './useSandboxTable' - -function CompactSandboxMeta({ label, children }: { label: string; children: ReactNode }) { - return ( -
-
{label}
-
{children}
-
- ) -} - -export function SandboxTable({ - data, - sandboxIsLoading, - sandboxStateIsTransitioning, - loading, - snapshots, - snapshotsDataIsLoading, - snapshotsDataHasMore, - onChangeSnapshotSearchValue, - regionsData, - regionsDataIsLoading, - getRegionName, - handleStart, - handleStop, - handleDelete, - handleBulkDelete, - handleBulkStart, - handleBulkStop, - handleBulkArchive, - handleArchive, - handleVnc, - getWebTerminalUrl, - handleCreateSshAccess, - handleRevokeSshAccess, - handleScreenRecordings, - handleRefresh, - isRefreshing, - onRowClick, - pagination, - pageCount, - totalItems, - onPaginationChange, - sorting, - onSortingChange, - filters, - onFiltersChange, - handleRecover, -}: SandboxTableProps) { - const navigate = useNavigate() - const isCompactScreen = useIsCompactScreen() - const useCompactList = isCompactScreen - const { authenticatedUserHasPermission } = useSelectedOrganization() - const writePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.WRITE_SANDBOXES) - const deletePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.DELETE_SANDBOXES) - - const { table, regionOptions } = useSandboxTable({ - data, - sandboxIsLoading, - writePermitted, - deletePermitted, - handleStart, - handleStop, - handleDelete, - handleArchive, - handleVnc, - getWebTerminalUrl, - handleCreateSshAccess, - handleRevokeSshAccess, - handleScreenRecordings, - pagination, - pageCount, - onPaginationChange, - sorting, - onSortingChange, - filters, - onFiltersChange, - regionsData, - handleRecover, - getRegionName, - }) - - const [pendingBulkAction, setPendingBulkAction] = useState(null) - - const selectedRows = table.getRowModel().rows.filter((row) => row.getIsSelected()) - const hasSelection = selectedRows.length > 0 - const selectedCount = selectedRows.length - const totalCount = table.getRowModel().rows.length - const selectedSandboxes: Sandbox[] = selectedRows.map((row) => row.original) - - const bulkActionCounts = useMemo(() => getBulkActionCounts(selectedSandboxes), [selectedSandboxes]) - - const handleBulkActionConfirm = () => { - if (!pendingBulkAction) return - - const handlers: Record void> = { - [BulkAction.Delete]: () => handleBulkDelete(filterDeletable(selectedSandboxes).map((s) => s.id)), - [BulkAction.Start]: () => handleBulkStart(filterStartable(selectedSandboxes).map((s) => s.id)), - [BulkAction.Stop]: () => handleBulkStop(filterStoppable(selectedSandboxes).map((s) => s.id)), - [BulkAction.Archive]: () => handleBulkArchive(filterArchivable(selectedSandboxes).map((s) => s.id)), - } - - handlers[pendingBulkAction]() - setPendingBulkAction(null) - table.toggleAllRowsSelected(false) - } - - const toggleAllRowsSelected = useCallback( - (selected: boolean) => { - if (selected) { - for (const row of table.getRowModel().rows) { - const selectDisabled = sandboxIsLoading[row.original.id] || row.original.state === SandboxState.DESTROYED - if (!selectDisabled) { - row.toggleSelected(true) - } - } - } else { - table.toggleAllRowsSelected(selected) - } - }, - [sandboxIsLoading, table], - ) - - const selectableCount = useMemo(() => { - return data.filter((sandbox) => !sandboxIsLoading[sandbox.id] && sandbox.state !== SandboxState.DESTROYED).length - }, [sandboxIsLoading, data]) - - useSandboxCommands({ - writePermitted, - deletePermitted, - selectedCount, - totalCount, - selectableCount, - toggleAllRowsSelected, - bulkActionCounts, - onDelete: () => setPendingBulkAction(BulkAction.Delete), - onStart: () => setPendingBulkAction(BulkAction.Start), - onStop: () => setPendingBulkAction(BulkAction.Stop), - onArchive: () => setPendingBulkAction(BulkAction.Archive), - }) - - const { setIsOpen } = useCommandPaletteActions() - const { trackOpened } = useCommandPaletteAnalytics() - const handleOpenCommandPalette = () => { - trackOpened('sandbox_selection_toast') - setIsOpen(true) - } - - const handleOpenWebTerminal = useCallback( - async (sandboxId: string) => { - const url = await getWebTerminalUrl(sandboxId) - if (url) { - window.open(url, '_blank') - } - }, - [getWebTerminalUrl], - ) - - const emptyStateDescription = ( -
-

Spin up a Sandbox to run code in an isolated environment.

-

Use the BoxLite SDK or CLI to create one.

-

- {' '} - to learn more. -

-
- ) - - return ( - <> - - - {useCompactList ? ( - loading ? ( -
Loading...
- ) : table.getRowModel().rows?.length ? ( -
- {table.getRowModel().rows.map((row) => { - const sandbox = row.original - const lastEvent = getSandboxLastEvent(sandbox) - const regionName = getRegionName(sandbox.target) ?? sandbox.target - - return ( -
-
onRowClick?.(sandbox)} - onKeyDown={(event) => { - if ((event.key === 'Enter' || event.key === ' ') && onRowClick) { - event.preventDefault() - onRowClick(sandbox) - } - }} - > -
-
-
- {getSandboxDisplayName(sandbox)} -
-
{sandbox.id}
-
- -
- {sandbox.snapshot || '-'} - {regionName} - - {sandbox.cpu} vCPU • {sandbox.memory} GiB • {sandbox.disk} GiB - - {lastEvent.relativeTimeString} -
- -
- - -
-
-
-
- ) - })} -
- ) : ( -
- -
No Sandboxes yet.
-
{emptyStateDescription}
-
- ) - ) : ( - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - header.column.getCanSort() && header.column.toggleSorting(header.column.getIsSorted() === 'asc') - } - className={cn( - 'sticky top-0 z-[3] border-b border-border', - header.column.getCanSort() ? 'hover:bg-muted cursor-pointer' : '', - )} - style={{ - width: `${header.column.getSize()}px`, - }} - > - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - - ) - })} - - ))} - - - {loading ? ( - - - Loading... - - - ) : table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - onRowClick?.(row.original)} - > - {row.getVisibleCells().map((cell) => ( - { - if (cell.column.id === 'select' || cell.column.id === 'actions') { - e.stopPropagation() - } - }} - className={cn('border-b border-border', { - 'group-hover/table-row:underline': cell.column.id === 'name', - })} - style={{ - width: `${cell.column.getSize()}px`, - }} - sticky={cell.column.id === 'actions' ? 'right' : undefined} - > - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - )) - ) : ( - } - description={emptyStateDescription} - /> - )} - -
- )} - -
- - - - {!useCompactList && hasSelection && ( - table.resetRowSelection()} - onActionClick={handleOpenCommandPalette} - /> - )} - -
- - setPendingBulkAction(null)} - /> - - ) -} diff --git a/apps/dashboard/src/components/SandboxTable/state-icons.tsx b/apps/dashboard/src/components/SandboxTable/state-icons.tsx deleted file mode 100644 index edb36eb3d..000000000 --- a/apps/dashboard/src/components/SandboxTable/state-icons.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { SandboxState } from '@boxlite-ai/api-client' -import { Loader2 } from 'lucide-react' - -interface SquareProps { - color: string -} - -function Square({ color }: SquareProps) { - return ( -
-
-
- ) -} - -export const STATE_ICONS: Record = { - [SandboxState.UNKNOWN]: , - [SandboxState.CREATING]: , - [SandboxState.STARTING]: , - [SandboxState.STARTED]: , - [SandboxState.STOPPING]: , - [SandboxState.STOPPED]: , - [SandboxState.DESTROYING]: , - [SandboxState.DESTROYED]: , - [SandboxState.ERROR]: , - [SandboxState.BUILD_FAILED]: , - [SandboxState.BUILDING_SNAPSHOT]: , - [SandboxState.PULLING_SNAPSHOT]: , - [SandboxState.PENDING_BUILD]: , - [SandboxState.ARCHIVED]: , - [SandboxState.ARCHIVING]: , - [SandboxState.RESTORING]: , - [SandboxState.RESIZING]: , - RECOVERY: , -} diff --git a/apps/dashboard/src/components/SandboxTable/types.ts b/apps/dashboard/src/components/SandboxTable/types.ts deleted file mode 100644 index 8b0cfee82..000000000 --- a/apps/dashboard/src/components/SandboxTable/types.ts +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { DEFAULT_SANDBOX_SORTING, SandboxFilters, SandboxSorting } from '@/hooks/useSandboxes' -import { - ListSandboxesPaginatedOrderEnum, - ListSandboxesPaginatedSortEnum, - ListSandboxesPaginatedStatesEnum, - Region, - Sandbox, - SandboxState, - SnapshotDto, -} from '@boxlite-ai/api-client' -import { ColumnFiltersState, SortingState, Table } from '@tanstack/react-table' - -export interface SandboxTableProps { - data: Sandbox[] - sandboxIsLoading: Record - sandboxStateIsTransitioning: Record - loading: boolean - snapshots: SnapshotDto[] - snapshotsDataIsLoading: boolean - snapshotsDataHasMore?: boolean - onChangeSnapshotSearchValue: (name?: string) => void - regionsData: Region[] - regionsDataIsLoading: boolean - getRegionName: (regionId: string) => string | undefined - handleStart: (id: string) => void - handleStop: (id: string) => void - handleDelete: (id: string) => void - handleBulkDelete: (ids: string[]) => void - handleBulkStart: (ids: string[]) => void - handleBulkStop: (ids: string[]) => void - handleBulkArchive: (ids: string[]) => void - handleArchive: (id: string) => void - handleVnc: (id: string) => void - getWebTerminalUrl: (id: string) => Promise - handleCreateSshAccess: (id: string) => void - handleRevokeSshAccess: (id: string) => void - handleRefresh: () => void - isRefreshing?: boolean - onRowClick?: (sandbox: Sandbox) => void - pagination: { - pageIndex: number - pageSize: number - } - pageCount: number - totalItems: number - onPaginationChange: (pagination: { pageIndex: number; pageSize: number }) => void - sorting: SandboxSorting - onSortingChange: (sorting: SandboxSorting) => void - filters: SandboxFilters - onFiltersChange: (filters: SandboxFilters) => void - handleRecover: (id: string) => void - handleScreenRecordings: (id: string) => void -} - -export interface SandboxTableActionsProps { - sandbox: Sandbox - layout?: 'table' | 'mobile' - writePermitted: boolean - deletePermitted: boolean - isLoading: boolean - onStart: (id: string) => void - onStop: (id: string) => void - onDelete: (id: string) => void - onArchive: (id: string) => void - onVnc: (id: string) => void - onOpenWebTerminal: (id: string) => void - onCreateSshAccess: (id: string) => void - onRevokeSshAccess: (id: string) => void - onRecover: (id: string) => void - onScreenRecordings: (id: string) => void -} - -export interface SandboxTableHeaderProps { - table: Table - regionOptions: FacetedFilterOption[] - regionsDataIsLoading: boolean - snapshots: SnapshotDto[] - snapshotsDataIsLoading: boolean - snapshotsDataHasMore?: boolean - onChangeSnapshotSearchValue: (name?: string) => void - onRefresh: () => void - isRefreshing?: boolean -} - -export interface FacetedFilterOption { - label: string - value: string | SandboxState - icon?: any -} - -export const convertTableSortingToApiSorting = (sorting: SortingState): SandboxSorting => { - if (!sorting.length) { - return DEFAULT_SANDBOX_SORTING - } - - const sort = sorting[0] - let field: ListSandboxesPaginatedSortEnum - - switch (sort.id) { - case 'name': - field = ListSandboxesPaginatedSortEnum.NAME - break - case 'state': - field = ListSandboxesPaginatedSortEnum.STATE - break - case 'snapshot': - field = ListSandboxesPaginatedSortEnum.SNAPSHOT - break - case 'region': - case 'target': - field = ListSandboxesPaginatedSortEnum.REGION - break - case 'lastEvent': - case 'updatedAt': - field = ListSandboxesPaginatedSortEnum.UPDATED_AT - break - case 'createdAt': - default: - field = ListSandboxesPaginatedSortEnum.CREATED_AT - break - } - - return { - field, - direction: sort.desc ? ListSandboxesPaginatedOrderEnum.DESC : ListSandboxesPaginatedOrderEnum.ASC, - } -} - -export const convertTableFiltersToApiFilters = (columnFilters: ColumnFiltersState): SandboxFilters => { - const filters: SandboxFilters = {} - - columnFilters.forEach((filter) => { - switch (filter.id) { - case 'name': - if (filter.value && typeof filter.value === 'string') { - filters.idOrName = filter.value - } - break - case 'state': - if (Array.isArray(filter.value) && filter.value.length > 0) { - filters.states = filter.value as ListSandboxesPaginatedStatesEnum[] - } - break - case 'snapshot': - if (Array.isArray(filter.value) && filter.value.length > 0) { - filters.snapshots = filter.value as string[] - } - break - case 'region': - case 'target': - if (Array.isArray(filter.value) && filter.value.length > 0) { - filters.regions = filter.value as string[] - } - break - case 'labels': - if (Array.isArray(filter.value) && filter.value.length > 0) { - const labelObj: Record = {} - filter.value.forEach((label: string) => { - const [key, value] = label.split(': ') - if (key && value) { - labelObj[key] = value - } - }) - if (Object.keys(labelObj).length > 0) { - filters.labels = labelObj - } - } - break - case 'resources': - if (filter.value && typeof filter.value === 'object') { - const resourceValue = filter.value as { - cpu?: { min?: number; max?: number } - memory?: { min?: number; max?: number } - disk?: { min?: number; max?: number } - } - - if (resourceValue.cpu?.min !== undefined) { - filters.minCpu = resourceValue.cpu.min - } - if (resourceValue.cpu?.max !== undefined) { - filters.maxCpu = resourceValue.cpu.max - } - if (resourceValue.memory?.min !== undefined) { - filters.minMemoryGiB = resourceValue.memory.min - } - if (resourceValue.memory?.max !== undefined) { - filters.maxMemoryGiB = resourceValue.memory.max - } - if (resourceValue.disk?.min !== undefined) { - filters.minDiskGiB = resourceValue.disk.min - } - if (resourceValue.disk?.max !== undefined) { - filters.maxDiskGiB = resourceValue.disk.max - } - } - break - case 'lastEvent': - if (Array.isArray(filter.value) && filter.value.length > 0) { - const dateRange = filter.value as (Date | undefined)[] - if (dateRange[0]) { - filters.lastEventAfter = dateRange[0] - } - if (dateRange[1]) { - filters.lastEventBefore = dateRange[1] - } - } - break - } - }) - - return filters -} - -export const convertApiSortingToTableSorting = (sorting: SandboxSorting): SortingState => { - if (!sorting.field || !sorting.direction) { - return [{ id: 'lastEvent', desc: true }] - } - - let id: string - switch (sorting.field) { - case ListSandboxesPaginatedSortEnum.NAME: - id = 'name' - break - case ListSandboxesPaginatedSortEnum.STATE: - id = 'state' - break - case ListSandboxesPaginatedSortEnum.SNAPSHOT: - id = 'snapshot' - break - case ListSandboxesPaginatedSortEnum.REGION: - id = 'region' - break - case ListSandboxesPaginatedSortEnum.UPDATED_AT: - id = 'lastEvent' - break - case ListSandboxesPaginatedSortEnum.CREATED_AT: - default: - id = 'createdAt' - break - } - - return [{ id, desc: sorting.direction === ListSandboxesPaginatedOrderEnum.DESC }] -} - -export const convertApiFiltersToTableFilters = (filters: SandboxFilters): ColumnFiltersState => { - const columnFilters: ColumnFiltersState = [] - - if (filters.idOrName) { - columnFilters.push({ id: 'name', value: filters.idOrName }) - } - - if (filters.states && filters.states.length > 0) { - columnFilters.push({ id: 'state', value: filters.states }) - } - - if (filters.snapshots && filters.snapshots.length > 0) { - columnFilters.push({ id: 'snapshot', value: filters.snapshots }) - } - - if (filters.regions && filters.regions.length > 0) { - columnFilters.push({ id: 'region', value: filters.regions }) - } - - if (filters.labels && Object.keys(filters.labels).length > 0) { - const labelArray = Object.entries(filters.labels).map(([key, value]) => `${key}: ${value}`) - columnFilters.push({ id: 'labels', value: labelArray }) - } - - // Convert resource filters back to table format - const resourceValue: { - cpu?: { min?: number; max?: number } - memory?: { min?: number; max?: number } - disk?: { min?: number; max?: number } - } = {} - - if (filters.minCpu !== undefined || filters.maxCpu !== undefined) { - resourceValue.cpu = {} - if (filters.minCpu !== undefined) resourceValue.cpu.min = filters.minCpu - if (filters.maxCpu !== undefined) resourceValue.cpu.max = filters.maxCpu - } - - if (filters.minMemoryGiB !== undefined || filters.maxMemoryGiB !== undefined) { - resourceValue.memory = {} - if (filters.minMemoryGiB !== undefined) resourceValue.memory.min = filters.minMemoryGiB - if (filters.maxMemoryGiB !== undefined) resourceValue.memory.max = filters.maxMemoryGiB - } - - if (filters.minDiskGiB !== undefined || filters.maxDiskGiB !== undefined) { - resourceValue.disk = {} - if (filters.minDiskGiB !== undefined) resourceValue.disk.min = filters.minDiskGiB - if (filters.maxDiskGiB !== undefined) resourceValue.disk.max = filters.maxDiskGiB - } - - if (Object.keys(resourceValue).length > 0) { - columnFilters.push({ id: 'resources', value: resourceValue }) - } - - // Convert date range filters back to table format - if (filters.lastEventAfter || filters.lastEventBefore) { - const dateRange: (Date | undefined)[] = [undefined, undefined] - if (filters.lastEventAfter) dateRange[0] = filters.lastEventAfter - if (filters.lastEventBefore) dateRange[1] = filters.lastEventBefore - columnFilters.push({ id: 'lastEvent', value: dateRange }) - } - - return columnFilters -} diff --git a/apps/dashboard/src/components/SandboxTable/useSandboxCommands.tsx b/apps/dashboard/src/components/SandboxTable/useSandboxCommands.tsx deleted file mode 100644 index 2e5c810c0..000000000 --- a/apps/dashboard/src/components/SandboxTable/useSandboxCommands.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { pluralize } from '@/lib/utils' -import { BulkActionCounts } from '@/lib/utils/sandbox' -import { ArchiveIcon, CheckSquare2Icon, MinusSquareIcon, PlayIcon, SquareIcon, TrashIcon } from 'lucide-react' -import { useMemo } from 'react' -import { CommandConfig, useRegisterCommands } from '../CommandPalette' - -interface UseSandboxCommandsProps { - writePermitted: boolean - deletePermitted: boolean - selectedCount: number - totalCount: number - selectableCount: number - toggleAllRowsSelected: (selected: boolean) => void - bulkActionCounts: BulkActionCounts - onDelete: () => void - onStart: () => void - onStop: () => void - onArchive: () => void -} - -export function useSandboxCommands({ - writePermitted, - deletePermitted, - selectedCount, - selectableCount, - totalCount, - toggleAllRowsSelected, - bulkActionCounts, - onDelete, - onStart, - onStop, - onArchive, -}: UseSandboxCommandsProps) { - const rootCommands: CommandConfig[] = useMemo(() => { - const commands: CommandConfig[] = [] - - if (selectableCount !== selectedCount) { - commands.push({ - id: 'select-all-sandboxes', - label: 'Select All Sandboxes', - icon: , - onSelect: () => toggleAllRowsSelected(true), - chainable: true, - }) - } - - if (selectedCount > 0) { - commands.push({ - id: 'deselect-all-sandboxes', - label: 'Deselect All Sandboxes', - icon: , - onSelect: () => toggleAllRowsSelected(false), - chainable: true, - }) - } - - if (writePermitted && bulkActionCounts.startable > 0) { - commands.push({ - id: 'start-sandboxes', - label: `Start ${pluralize(bulkActionCounts.startable, 'Sandbox', 'Sandboxes')}`, - icon: , - onSelect: onStart, - }) - } - - if (writePermitted && bulkActionCounts.stoppable > 0) { - commands.push({ - id: 'stop-sandboxes', - label: `Stop ${pluralize(bulkActionCounts.stoppable, 'Sandbox', 'Sandboxes')}`, - icon: , - onSelect: onStop, - }) - } - - if (writePermitted && bulkActionCounts.archivable > 0) { - commands.push({ - id: 'archive-sandboxes', - label: `Archive ${pluralize(bulkActionCounts.archivable, 'Sandbox', 'Sandboxes')}`, - icon: , - onSelect: onArchive, - }) - } - - if (deletePermitted && bulkActionCounts.deletable > 0) { - commands.push({ - id: 'delete-sandboxes', - label: `Delete ${pluralize(bulkActionCounts.deletable, 'Sandbox', 'Sandboxes')}`, - icon: , - onSelect: onDelete, - }) - } - - return commands - }, [ - selectedCount, - selectableCount, - toggleAllRowsSelected, - writePermitted, - deletePermitted, - bulkActionCounts, - onDelete, - onStart, - onStop, - onArchive, - ]) - - useRegisterCommands(rootCommands, { groupId: 'sandbox-actions', groupLabel: 'Sandbox actions', groupOrder: 0 }) -} diff --git a/apps/dashboard/src/components/SandboxTable/useSandboxTable.ts b/apps/dashboard/src/components/SandboxTable/useSandboxTable.ts deleted file mode 100644 index 3ac5ecf4b..000000000 --- a/apps/dashboard/src/components/SandboxTable/useSandboxTable.ts +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { Sandbox, Region } from '@boxlite-ai/api-client' -import { - useReactTable, - getCoreRowModel, - getFacetedRowModel, - getFacetedUniqueValues, - getPaginationRowModel, - VisibilityState, -} from '@tanstack/react-table' -import { useMemo, useState, useEffect } from 'react' -import { FacetedFilterOption } from './types' -import { getColumns } from './columns' -import { - convertApiSortingToTableSorting, - convertApiFiltersToTableFilters, - convertTableSortingToApiSorting, - convertTableFiltersToApiFilters, -} from './types' -import { SandboxFilters, SandboxSorting } from '@/hooks/useSandboxes' -import { LocalStorageKey } from '@/enums/LocalStorageKey' -import { getLocalStorageItem, setLocalStorageItem } from '@/lib/local-storage' -import { getRegionFullDisplayName } from '@/lib/utils' - -interface UseSandboxTableProps { - data: Sandbox[] - sandboxIsLoading: Record - writePermitted: boolean - deletePermitted: boolean - handleStart: (id: string) => void - handleStop: (id: string) => void - handleDelete: (id: string) => void - handleArchive: (id: string) => void - handleVnc: (id: string) => void - getWebTerminalUrl: (id: string) => Promise - handleCreateSshAccess: (id: string) => void - handleRevokeSshAccess: (id: string) => void - handleScreenRecordings: (id: string) => void - pagination: { - pageIndex: number - pageSize: number - } - pageCount: number - onPaginationChange: (pagination: { pageIndex: number; pageSize: number }) => void - sorting: SandboxSorting - onSortingChange: (sorting: SandboxSorting) => void - filters: SandboxFilters - onFiltersChange: (filters: SandboxFilters) => void - regionsData: Region[] - handleRecover: (id: string) => void - getRegionName: (regionId: string) => string | undefined -} - -export function useSandboxTable({ - data, - sandboxIsLoading, - writePermitted, - deletePermitted, - handleStart, - handleStop, - handleDelete, - handleArchive, - handleVnc, - getWebTerminalUrl, - handleCreateSshAccess, - handleRevokeSshAccess, - handleScreenRecordings, - pagination, - pageCount, - onPaginationChange, - sorting, - onSortingChange, - filters, - onFiltersChange, - regionsData, - handleRecover, - getRegionName, -}: UseSandboxTableProps) { - // Column visibility state management with persistence - const [columnVisibility, setColumnVisibility] = useState(() => { - const saved = getLocalStorageItem(LocalStorageKey.SandboxTableColumnVisibility) - if (saved) { - try { - return JSON.parse(saved) - } catch { - return { id: false, labels: false } - } - } - return { id: false, labels: false } - }) - - useEffect(() => { - setLocalStorageItem(LocalStorageKey.SandboxTableColumnVisibility, JSON.stringify(columnVisibility)) - }, [columnVisibility]) - - // Convert API sorting and filters to table format for internal use - const tableSorting = useMemo(() => convertApiSortingToTableSorting(sorting), [sorting]) - const tableFilters = useMemo(() => convertApiFiltersToTableFilters(filters), [filters]) - - const regionOptions: FacetedFilterOption[] = useMemo(() => { - return regionsData.map((region) => ({ - label: getRegionFullDisplayName(region), - value: region.id, - })) - }, [regionsData]) - - const columns = useMemo( - () => - getColumns({ - handleStart, - handleStop, - handleDelete, - handleArchive, - handleVnc, - getWebTerminalUrl, - sandboxIsLoading, - writePermitted, - deletePermitted, - handleCreateSshAccess, - handleRevokeSshAccess, - handleRecover, - getRegionName, - handleScreenRecordings, - }), - [ - handleStart, - handleStop, - handleDelete, - handleArchive, - handleVnc, - getWebTerminalUrl, - sandboxIsLoading, - writePermitted, - deletePermitted, - handleCreateSshAccess, - handleRevokeSshAccess, - handleRecover, - getRegionName, - handleScreenRecordings, - ], - ) - - const table = useReactTable({ - data, - columns, - manualFiltering: true, - onColumnFiltersChange: (updater) => { - const newTableFilters = typeof updater === 'function' ? updater(table.getState().columnFilters) : updater - const newApiFilters = convertTableFiltersToApiFilters(newTableFilters) - onFiltersChange(newApiFilters) - }, - getCoreRowModel: getCoreRowModel(), - manualSorting: true, - onSortingChange: (updater) => { - const newTableSorting = typeof updater === 'function' ? updater(table.getState().sorting) : updater - const newApiSorting = convertTableSortingToApiSorting(newTableSorting) - onSortingChange(newApiSorting) - }, - getFacetedRowModel: getFacetedRowModel(), - getFacetedUniqueValues: getFacetedUniqueValues(), - manualPagination: true, - pageCount: pageCount, - onPaginationChange: (updater) => { - const newPagination = typeof updater === 'function' ? updater(table.getState().pagination) : updater - onPaginationChange(newPagination) - }, - getPaginationRowModel: getPaginationRowModel(), - state: { - sorting: tableSorting, - columnFilters: tableFilters, - columnVisibility, - pagination: { - pageIndex: pagination.pageIndex, - pageSize: pagination.pageSize, - }, - }, - onColumnVisibilityChange: setColumnVisibility, - defaultColumn: { - size: 100, - }, - enableRowSelection: deletePermitted, - getRowId: (row) => row.id, - }) - - return { - table, - regionOptions, - } -} diff --git a/apps/dashboard/src/components/SelectionToast.tsx b/apps/dashboard/src/components/SelectionToast.tsx index 14fba4e35..a979b3870 100644 --- a/apps/dashboard/src/components/SelectionToast.tsx +++ b/apps/dashboard/src/components/SelectionToast.tsx @@ -7,7 +7,7 @@ import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' import { cn, pluralize } from '@/lib/utils' -import { CommandIcon, XIcon } from 'lucide-react' +import { CommandIcon, XIcon } from '@/components/ui/icon' import { motion } from 'motion/react' export function SelectionToast({ diff --git a/apps/dashboard/src/components/Sidebar.tsx b/apps/dashboard/src/components/Sidebar.tsx index de31c5583..f9045cebe 100644 --- a/apps/dashboard/src/components/Sidebar.tsx +++ b/apps/dashboard/src/components/Sidebar.tsx @@ -5,9 +5,7 @@ */ import { LogoText } from '@/assets/Logo' -import { OrganizationPicker } from '@/components/Organizations/OrganizationPicker' -import { Button } from '@/components/ui/button' -import { Kbd } from '@/components/ui/kbd' +import { BoxSearchCommands } from '@/components/BoxSearchCommands' import { DropdownMenu, DropdownMenuContent, @@ -16,73 +14,93 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' +import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { BOXLITE_DOCS_URL, BOXLITE_SLACK_URL } from '@/constants/ExternalLinks' -import { useTheme } from '@/contexts/ThemeContext' -import { FeatureFlags } from '@/enums/FeatureFlags' +import { Theme, useTheme } from '@/contexts/ThemeContext' import { RoutePath } from '@/enums/RoutePath' -import { useIsCompactScreen } from '@/hooks/use-mobile' -import { useWebhookAppPortalAccessQuery } from '@/hooks/queries/useWebhookAppPortalAccessQuery' +import { useApi } from '@/hooks/useApi' import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' -import { useUserOrganizationInvitations } from '@/hooks/useUserOrganizationInvitations' -import { useWebhooks } from '@/hooks/useWebhooks' +import { useCopyToClipboard } from 'usehooks-ts' +import { toast } from 'sonner' +import { markJustLoggedOut } from '@/lib/auth-session' +import { ONBOARDING_OPEN_EVENT } from '@/lib/onboarding-progress' import { cn, getMetaKey } from '@/lib/utils' -import { usePylon, usePylonCommands } from '@/vendor/pylon' -import { OrganizationRolePermissionsEnum, OrganizationUserRoleEnum } from '@boxlite-ai/api-client' import { ArrowRightIcon, BookOpen, - Box, - ChartColumn, - Container, - CreditCard, - FlaskConical, - HardDrive, - Joystick, + Building2, + ChevronDown, + Copy, KeyRound, - LifeBuoyIcon, ListChecks, - LockKeyhole, LogOut, - Mail, - MapPinned, - Menu, MessageCircle, - MoreHorizontal, + Monitor, MoonIcon, - PackageOpen, + MoreHorizontal, SearchIcon, - Server, - Settings, - SquareUserRound, + ShieldCheck, SunIcon, - TextSearch, - Users, -} from 'lucide-react' -import { useFeatureFlagEnabled, usePostHog } from 'posthog-js/react' -import React, { useMemo } from 'react' +} from '@/components/ui/icon' +import { usePostHog } from 'posthog-js/react' +import { useQuery } from '@tanstack/react-query' +import { useCallback, useMemo } from 'react' import { useAuth } from 'react-oidc-context' import { Link, useLocation, useNavigate } from 'react-router-dom' import { CommandConfig, useCommandPaletteActions, useRegisterCommands } from './CommandPalette' +const ADMIN_UI_HEADERS = { 'X-BoxLite-Source': 'ui' } as const + interface SidebarProps { isBannerVisible: boolean billingEnabled: boolean version: string } -interface SidebarItem { - icon: React.ReactElement +interface NavItem { + icon?: React.ReactElement label: string path: RoutePath | string onClick?: () => void } -interface SidebarGroup { - label: string - items: SidebarItem[] +const themeOptions: { value: Theme; label: string; icon: React.ReactElement }[] = [ + { value: 'system', label: 'System', icon: }, + { value: 'light', label: 'Light', icon: }, + { value: 'dark', label: 'Dark', icon: }, +] + +function ThemeMenuItems({ theme, setTheme }: { theme: Theme; setTheme: (theme: Theme) => void }) { + return ( +
+
Theme
+ { + if (value) setTheme(value as Theme) + }} + variant="outline" + size="sm" + className="grid w-full grid-cols-3 gap-0 border border-border" + > + {themeOptions.map((option) => ( + + {option.icon} + {option.label} + + ))} + +
+ ) } -const useNavCommands = (items: { label: string; path: RoutePath | string; onClick?: () => void }[]) => { +const useNavCommands = (items: NavItem[]) => { const { pathname } = useLocation() const navigate = useNavigate() @@ -102,232 +120,102 @@ const useNavCommands = (items: { label: string; path: RoutePath | string; onClic useRegisterCommands(navCommands, { groupId: 'navigation', groupLabel: 'Navigation', groupOrder: 1 }) } -export function Sidebar({ isBannerVisible, billingEnabled, version: _version }: SidebarProps) { - const isCompactScreen = useIsCompactScreen() +/** + * Top navigation bar (ASCII/terminal restyle). Named `Sidebar` for import compatibility; + * it has always rendered as a top header. All data/command wiring is preserved — only the + * presentation matches the new design (Nav.dc): a single 60px monospace bar with bordered + * segments, a standalone organization switcher, and a profile menu. + */ +export function Sidebar({ isBannerVisible }: SidebarProps) { const posthog = usePostHog() + const { axiosInstance } = useApi() const { theme, setTheme } = useTheme() const { user, signoutRedirect } = useAuth() const { pathname } = useLocation() - const { selectedOrganization, authenticatedUserOrganizationMember, authenticatedUserHasPermission } = - useSelectedOrganization() - const { count: organizationInvitationsCount } = useUserOrganizationInvitations() - const { isInitialized: webhooksInitialized } = useWebhooks() - const webhooksAccess = useWebhookAppPortalAccessQuery(selectedOrganization?.id) - const orgInfraEnabled = useFeatureFlagEnabled(FeatureFlags.ORGANIZATION_INFRASTRUCTURE) - const organizationExperimentsEnabled = useFeatureFlagEnabled(FeatureFlags.ORGANIZATION_EXPERIMENTS) - const playgroundEnabled = useFeatureFlagEnabled(FeatureFlags.DASHBOARD_PLAYGROUND) - const webhooksEnabled = useFeatureFlagEnabled(FeatureFlags.DASHBOARD_WEBHOOKS) - - const primaryItems = useMemo(() => { - const arr: SidebarItem[] = [ - { - icon: , - label: 'Sandboxes', - path: RoutePath.SANDBOXES, - }, - { - icon: , - label: 'Snapshots', - path: RoutePath.SNAPSHOTS, - }, - { - icon: , - label: 'Registries', - path: RoutePath.REGISTRIES, - }, - ] - - if (authenticatedUserHasPermission(OrganizationRolePermissionsEnum.READ_VOLUMES)) { - arr.push({ - icon: , - label: 'Volumes', - path: RoutePath.VOLUMES, - }) - } - - if (authenticatedUserHasPermission(OrganizationRolePermissionsEnum.READ_AUDIT_LOGS)) { - arr.push({ - icon: , - label: 'Audit Logs', - path: RoutePath.AUDIT_LOGS, - }) - } - - return arr - }, [authenticatedUserHasPermission]) - - const settingsItems = useMemo(() => { - const arr: SidebarItem[] = [ - { - icon: , - label: 'Settings', - path: RoutePath.SETTINGS, - }, - { icon: , label: 'API Keys', path: RoutePath.KEYS }, - ] - - if (webhooksInitialized) { - if (webhooksEnabled) { - arr.push({ - icon: , - label: 'Webhooks', - path: RoutePath.WEBHOOKS, - }) - } else { - arr.push({ - icon: , - label: 'Webhooks', - path: '#webhooks' as RoutePath, - onClick: () => { - window.open(webhooksAccess.data?.url, '_blank', 'noopener,noreferrer') - }, - }) - } - } - - if (authenticatedUserOrganizationMember?.role === OrganizationUserRoleEnum.OWNER) { - arr.push({ - icon: , - label: 'Limits', - path: RoutePath.LIMITS, - }) - } - - if (!selectedOrganization?.personal) { - arr.push({ - icon: , - label: 'Members', - path: RoutePath.MEMBERS, - }) - } - - return arr - }, [ - authenticatedUserOrganizationMember?.role, - selectedOrganization?.personal, - webhooksAccess.data?.url, - webhooksEnabled, - webhooksInitialized, - ]) - - const billingItems = useMemo(() => { - if (!billingEnabled || authenticatedUserOrganizationMember?.role !== OrganizationUserRoleEnum.OWNER) { - return [] - } - - return [ - { - icon: , - label: 'Spending', - path: RoutePath.BILLING_SPENDING, - }, - { - icon: , - label: 'Wallet', - path: RoutePath.BILLING_WALLET, - }, - ] - }, [authenticatedUserOrganizationMember?.role, billingEnabled]) - - const infrastructureItems = useMemo(() => { - if (!orgInfraEnabled) { - return [] - } - - const arr: SidebarItem[] = [ - { - icon: , - label: 'Regions', - path: RoutePath.REGIONS, - }, - ] - - if (authenticatedUserHasPermission(OrganizationRolePermissionsEnum.READ_RUNNERS)) { - arr.push({ - icon: , - label: 'Runners', - path: RoutePath.RUNNERS, - }) - } + const navigate = useNavigate() + const { selectedOrganization } = useSelectedOrganization() + const [, copyToClipboard] = useCopyToClipboard() - return arr - }, [authenticatedUserHasPermission, orgInfraEnabled]) + const copyOrgId = useCallback(() => { + if (!selectedOrganization) return + copyToClipboard(selectedOrganization.id) + toast.success('Organization ID copied to clipboard') + }, [copyToClipboard, selectedOrganization]) - const experimentalItems = useMemo(() => { - if ( - !organizationExperimentsEnabled || - authenticatedUserOrganizationMember?.role !== OrganizationUserRoleEnum.OWNER - ) { - return [] - } + const orgCommands = useMemo( + () => + selectedOrganization + ? [ + { + id: 'copy-org-id', + label: 'Copy Organization ID', + icon: , + onSelect: copyOrgId, + }, + ] + : [], + [selectedOrganization, copyOrgId], + ) + useRegisterCommands(orgCommands, { groupId: 'organization', groupLabel: 'Organization', groupOrder: 5 }) + + const adminAccessQuery = useQuery({ + queryKey: ['admin', 'sidebar-access'], + queryFn: async () => { + await axiosInstance.get('/admin/overview', { headers: ADMIN_UI_HEADERS }) + return true + }, + enabled: !!user, + retry: false, + // Admin status doesn't change within a session. Cache it for the whole + // session so this /admin/overview probe (which 403s for non-admins and + // competes with the first-paint API burst) fires once, not on every + // sidebar mount / navigation. Login/logout go through a full OIDC page + // redirect, which discards the in-memory queryClient — so a same-tab user + // switch can't carry a stale admin flag across. + staleTime: Infinity, + gcTime: Infinity, + refetchOnWindowFocus: false, + }) + const canViewAdmin = adminAccessQuery.data === true + + const primaryItems = useMemo( + () => [ + { label: 'Boxes', path: RoutePath.BOXES }, + { label: 'Billing', path: RoutePath.BILLING }, + ...(canViewAdmin ? [{ label: 'Admin', path: RoutePath.ADMIN }] : []), + ], + [canViewAdmin], + ) - return [ - { - icon: , - label: 'Experimental', - path: RoutePath.EXPERIMENTAL, - }, - ] - }, [authenticatedUserOrganizationMember?.role, organizationExperimentsEnabled]) + const openOnboardingGuide = useCallback(() => { + const event = new Event(ONBOARDING_OPEN_EVENT, { cancelable: true }) + window.dispatchEvent(event) - const miscItems = useMemo(() => { - if (!playgroundEnabled) { - return [] + if (!event.defaultPrevented) { + navigate(`${RoutePath.BOXES}?onboarding=1`) } + }, [navigate]) - return [ + const commandItems = useMemo( + () => [ + ...primaryItems, + { path: RoutePath.KEYS, label: 'API Keys', icon: }, { - icon: , - label: 'Playground', - path: RoutePath.PLAYGROUND, + path: RoutePath.ONBOARDING, + label: 'Onboarding', + icon: , + onClick: openOnboardingGuide, }, - ] - }, [playgroundEnabled]) - - const secondaryGroups: SidebarGroup[] = useMemo( - () => - [ - { label: 'Misc', items: miscItems }, - { label: 'Settings', items: settingsItems }, - { label: 'Billing', items: billingItems }, - { label: 'Infrastructure', items: infrastructureItems }, - { label: 'Experimental', items: experimentalItems }, - ].filter((group) => group.items.length > 0), - [billingItems, experimentalItems, infrastructureItems, miscItems, settingsItems], - ) - - const commandItems = useMemo( - () => - primaryItems - .concat(secondaryGroups.flatMap((group) => group.items)) - .concat( - { - path: RoutePath.ACCOUNT_SETTINGS, - label: 'Account Settings', - icon: , - }, - { - path: RoutePath.USER_INVITATIONS, - label: 'Invitations', - icon: , - }, - { - path: RoutePath.ONBOARDING, - label: 'Onboarding', - icon: , - }, - ), - [primaryItems, secondaryGroups], + ], + [openOnboardingGuide, primaryItems], ) const handleSignOut = () => { posthog?.reset() + markJustLoggedOut() signoutRedirect() } - const { unreadCount: pylonUnreadCount, toggle: togglePylon, isEnabled: pylonEnabled } = usePylon() - usePylonCommands() - const commandPaletteActions = useCommandPaletteActions() useNavCommands(commandItems) @@ -338,229 +226,183 @@ export function Sidebar({ isBannerVisible, billingEnabled, version: _version }: commandPaletteActions.setIsOpen(true) } - const renderMenuItem = (item: SidebarItem) => { - if (item.onClick) { - return ( - item.onClick?.()} className="cursor-pointer"> - {item.icon} - {item.label} - - ) - } - - return ( - - - {item.icon} - {item.label} - - + const userName = user?.profile.name || user?.profile.email || 'Profile' + const initials = + userName + .split(/\s+/) + .map((w) => w[0]) + .slice(0, 2) + .join('') + .toUpperCase() || 'U' + + // One full-height nav cell. Selection is conveyed purely by value/grayscale — no hue: + // active = neutral grey fill + brightest (foreground) label; inactive = dimmer muted text. + const navCellClass = (active: boolean, extra?: string) => + cn( + 'relative h-full items-center gap-2 px-[18px] text-[13px] font-medium transition-colors', + active ? 'bg-accent text-foreground' : 'text-muted-foreground hover:bg-card hover:text-foreground', + extra, ) - } return (
-
-
- - + + + {/* brand */} + + + + + {/* primary nav */} + {primaryItems.map((item) => { + const active = pathname.startsWith(item.path) + return ( + + {item.label} - - {!isCompactScreen && ( - - )} -
- -
- {!isCompactScreen && ( - + ) + })} + +
+ + {/* search → command palette */} + + + {/* api keys */} + + + API Keys + + + {/* onboarding guide */} + + + {/* profile menu (organization switcher tucked inside) */} + + + + {user?.profile.picture ? ( + {userName} + ) : ( + initials + )} + + {userName} + + + + + {userName} + {user?.profile.email && user.profile.email !== userName && ( + + {user.profile.email} + + )} + + + + + {selectedOrganization && ( + + + + Organization + + )} - -
- -
- - {!isCompactScreen && secondaryGroups.length > 0 && ( - - - - - - {secondaryGroups.map((group, index) => ( - - {index > 0 && } - - {group.label} - - {group.items.map(renderMenuItem)} - - ))} - - - )} - - - - - - - - - - Account Settings - - - - - - Invitations - {organizationInvitationsCount > 0 && ( - {organizationInvitationsCount} - )} - - - - - - Onboarding - - - {pylonEnabled && ( - togglePylon()}> - - Support - {pylonUnreadCount > 0 && new} - - )} - - -
- - Docs - - - - - - Discord - - - setTheme(theme === 'dark' ? 'light' : 'dark')}> - {theme === 'dark' ? : } - {theme === 'dark' ? 'Light Mode' : 'Dark Mode'} - - - - - Sign out - - - - - {isCompactScreen && ( - - - - - - openCommandPalette('dashboard_mobile_menu')}> - - Search - - - {primaryItems.map(renderMenuItem)} - {secondaryGroups.map((group) => ( - - - - {group.label} - - {group.items.map(renderMenuItem)} - - ))} - - + + + + Docs + + + + + + Discord + + + {canViewAdmin && ( + + + + Admin + + )} -
-
+ + + + Sign out + + + + + {/* mobile: search + nav */} + + + + + + openCommandPalette('dashboard_mobile_menu')}> + + Search + + + {primaryItems.map((item) => ( + + {item.label} + + ))} + + + + API Keys + + + + + Quickstart + + +
) } diff --git a/apps/dashboard/src/components/SortIcon.tsx b/apps/dashboard/src/components/SortIcon.tsx index 4ee070baa..71adbf893 100644 --- a/apps/dashboard/src/components/SortIcon.tsx +++ b/apps/dashboard/src/components/SortIcon.tsx @@ -6,7 +6,7 @@ import { cn } from '@/lib/utils' import { AnimatePresence, motion } from 'framer-motion' -import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from 'lucide-react' +import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from '@/components/ui/icon' interface Props { sort: 'asc' | 'desc' | null diff --git a/apps/dashboard/src/components/TierComparisonTable.tsx b/apps/dashboard/src/components/TierComparisonTable.tsx index fbb74b54f..e9dcb9cdb 100644 --- a/apps/dashboard/src/components/TierComparisonTable.tsx +++ b/apps/dashboard/src/components/TierComparisonTable.tsx @@ -38,8 +38,8 @@ export function TierComparisonTable({ 'Memory (GiB)', 'Storage (GiB)', 'API Requests/min', - 'Sandbox Creation/min', - 'Sandbox Lifecycle/min', + 'Box Creation/min', + 'Box Lifecycle/min', ]} currentRow={(currentTier?.tier || 1) - 1} data={buildTierComparisonTableData(tiers || [])} @@ -61,8 +61,8 @@ function buildTierComparisonTableData(tiers: Tier[]): ComparisonSection[] { `${tier.tierLimit.concurrentRAMGiB}`, `${tier.tierLimit.concurrentDiskGiB}`, `${TIER_RATE_LIMITS[tier.tier]?.authenticatedRateLimit.toLocaleString() || '-'}`, - `${TIER_RATE_LIMITS[tier.tier]?.sandboxCreateRateLimit.toLocaleString() || '-'}`, - `${TIER_RATE_LIMITS[tier.tier]?.sandboxLifecycleRateLimit.toLocaleString() || '-'}`, + `${TIER_RATE_LIMITS[tier.tier]?.boxCreateRateLimit.toLocaleString() || '-'}`, + `${TIER_RATE_LIMITS[tier.tier]?.boxLifecycleRateLimit.toLocaleString() || '-'}`, ], } }) diff --git a/apps/dashboard/src/components/TierUpgradeCard.tsx b/apps/dashboard/src/components/TierUpgradeCard.tsx index e88cd0351..2f367a37c 100644 --- a/apps/dashboard/src/components/TierUpgradeCard.tsx +++ b/apps/dashboard/src/components/TierUpgradeCard.tsx @@ -13,7 +13,7 @@ import { useUpgradeTierMutation } from '@/hooks/mutations/useUpgradeTierMutation import { handleApiError } from '@/lib/error-handling' import { cn } from '@/lib/utils' import { Organization } from '@boxlite-ai/api-client/src' -import { CheckIcon, ExternalLinkIcon, Loader2 } from 'lucide-react' +import { CheckIcon, ExternalLinkIcon, Loader2 } from '@/components/ui/icon' import { useMemo } from 'react' import { Link } from 'react-router-dom' import { toast } from 'sonner' @@ -129,7 +129,7 @@ export function TierUpgradeCard({ tiers, organizationTier, requirementsState, or
@@ -223,7 +223,6 @@ function getTierRequirementItems( items.push({ label: 'Email verification', isChecked: requirementsState.emailVerified, - link: RoutePath.ACCOUNT_SETTINGS, }) } if (tier.tier === 2) { diff --git a/apps/dashboard/src/components/UpdateRegionDialog.tsx b/apps/dashboard/src/components/UpdateRegionDialog.tsx index bc232ba42..57b9b51db 100644 --- a/apps/dashboard/src/components/UpdateRegionDialog.tsx +++ b/apps/dashboard/src/components/UpdateRegionDialog.tsx @@ -37,7 +37,6 @@ export const UpdateRegionDialog: React.FC = ({ const [formData, setFormData] = useState({ proxyUrl: region.proxyUrl || '', sshGatewayUrl: region.sshGatewayUrl || '', - snapshotManagerUrl: region.snapshotManagerUrl || '', }) // Reset form when dialog opens with new region @@ -46,7 +45,6 @@ export const UpdateRegionDialog: React.FC = ({ setFormData({ proxyUrl: region.proxyUrl || '', sshGatewayUrl: region.sshGatewayUrl || '', - snapshotManagerUrl: region.snapshotManagerUrl || '', }) } }, [open, region]) @@ -54,8 +52,7 @@ export const UpdateRegionDialog: React.FC = ({ const hasChanges = useMemo(() => { const proxyChanged = (formData.proxyUrl.trim() || null) !== (region.proxyUrl || null) const sshGatewayChanged = (formData.sshGatewayUrl.trim() || null) !== (region.sshGatewayUrl || null) - const snapshotManagerChanged = (formData.snapshotManagerUrl.trim() || null) !== (region.snapshotManagerUrl || null) - return proxyChanged || sshGatewayChanged || snapshotManagerChanged + return proxyChanged || sshGatewayChanged }, [formData, region]) const handleUpdate = async () => { @@ -64,7 +61,6 @@ export const UpdateRegionDialog: React.FC = ({ const proxyUrlValue = formData.proxyUrl.trim() || null const sshGatewayUrlValue = formData.sshGatewayUrl.trim() || null - const snapshotManagerUrlValue = formData.snapshotManagerUrl.trim() || null if (proxyUrlValue !== (region.proxyUrl || null)) { updateData.proxyUrl = proxyUrlValue @@ -72,9 +68,6 @@ export const UpdateRegionDialog: React.FC = ({ if (sshGatewayUrlValue !== (region.sshGatewayUrl || null)) { updateData.sshGatewayUrl = sshGatewayUrlValue } - if (snapshotManagerUrlValue !== (region.snapshotManagerUrl || null)) { - updateData.snapshotManagerUrl = snapshotManagerUrlValue - } const success = await onUpdateRegion(region.id, updateData) if (success) { @@ -127,22 +120,6 @@ export const UpdateRegionDialog: React.FC = ({ (Optional) URL of the custom SSH gateway for this region

- -
- - { - setFormData((prev) => ({ ...prev, snapshotManagerUrl: e.target.value })) - }} - placeholder="https://snapshot-manager.example.com" - /> -

- (Optional) URL of the custom snapshot manager for this region. Cannot be changed if snapshots exist in - this region. -

-
diff --git a/apps/dashboard/src/components/UsageOverview.tsx b/apps/dashboard/src/components/UsageOverview.tsx deleted file mode 100644 index 27e1821e8..000000000 --- a/apps/dashboard/src/components/UsageOverview.tsx +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { cn } from '@/lib/utils' -import { RegionUsageOverview } from '@boxlite-ai/api-client' -import QuotaLine from './QuotaLine' -import { Skeleton } from './ui/skeleton' - -export function UsageOverview({ - usageOverview, - className, -}: { - usageOverview: RegionUsageOverview - className?: string -}) { - return ( -
*]:flex-1 flex-col lg:flex-row', className)}> -
-
-
Compute
- -
- -
-
-
-
Memory
- -
- -
-
-
-
Storage
- -
- -
-
- ) -} - -export function UsageOverviewSkeleton() { - return ( -
- - - -
- ) -} - -const UsageLabel = ({ current, total, unit }: { current: number; total: number; unit: string }) => { - const percentage = (current / total) * 100 - const isHighUsage = percentage > 90 - - return ( - - {current} / {total} {unit} - - ) -} diff --git a/apps/dashboard/src/components/UsageOverviewIndicator.tsx b/apps/dashboard/src/components/UsageOverviewIndicator.tsx deleted file mode 100644 index 4f3b51184..000000000 --- a/apps/dashboard/src/components/UsageOverviewIndicator.tsx +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2025 Daytona Platforms Inc. - * Modified by BoxLite AI, 2025-2026 - * SPDX-License-Identifier: AGPL-3.0 - */ - -import { cn } from '@/lib/utils' -import { RegionUsageOverview } from '@boxlite-ai/api-client/src' - -export function UsageOverviewIndicator({ - usage, - className, - isLive, -}: { - usage: RegionUsageOverview - className?: string - isLive?: boolean -}) { - return ( -
- {isLive && } - - - -
- ) -} - -function ResourceLabel({ value, total, unit, name }: { value: number; total: number; unit?: string; name?: string }) { - return ( - - {name} - {value}/{total} - {unit} - - ) -} - -function LiveIndicatorDot({ className }: { className?: string }) { - return ( -
-
-
-
- ) -} diff --git a/apps/dashboard/src/components/UserOrganizationInvitations/UserOrganizationInvitationTable.tsx b/apps/dashboard/src/components/UserOrganizationInvitations/UserOrganizationInvitationTable.tsx index 133ff8785..ff8ad8c4f 100644 --- a/apps/dashboard/src/components/UserOrganizationInvitations/UserOrganizationInvitationTable.tsx +++ b/apps/dashboard/src/components/UserOrganizationInvitations/UserOrganizationInvitationTable.tsx @@ -5,7 +5,7 @@ */ import { useState } from 'react' -import { Check, X } from 'lucide-react' +import { Check, X } from '@/components/ui/icon' import { ColumnDef, flexRender, diff --git a/apps/dashboard/src/components/VerifyEmailDialog.tsx b/apps/dashboard/src/components/VerifyEmailDialog.tsx index 2f5f27049..443dc2ca5 100644 --- a/apps/dashboard/src/components/VerifyEmailDialog.tsx +++ b/apps/dashboard/src/components/VerifyEmailDialog.tsx @@ -29,7 +29,7 @@ export const VerifyEmailDialog: React.FC = ({ open, onOp Verify Your Account A verification email was sent to your registered email address. Please note that you must verify your email - before you can create sandboxes or new organizations. + before you can create boxes or new organizations. diff --git a/apps/dashboard/src/components/VolumeTable.tsx b/apps/dashboard/src/components/VolumeTable.tsx index 88d5261fe..bd410a7da 100644 --- a/apps/dashboard/src/components/VolumeTable.tsx +++ b/apps/dashboard/src/components/VolumeTable.tsx @@ -31,7 +31,7 @@ import { SortingState, useReactTable, } from '@tanstack/react-table' -import { AlertTriangle, CheckCircle, HardDrive, Loader2, MoreHorizontal, Timer } from 'lucide-react' +import { AlertTriangle, CheckCircle, HardDrive, Loader2, MoreHorizontal, Timer } from '@/components/ui/icon' import { AnimatePresence } from 'motion/react' import { useCallback, useMemo, useState } from 'react' import { TableEmptyState } from './TableEmptyState' @@ -210,7 +210,7 @@ export function VolumeTable({

Volumes are shared, persistent directories backed by S3-compatible storage, perfect for reusing - datasets, caching dependencies, or passing files across sandboxes. + datasets, caching dependencies, or passing files across boxes.

Create one via the SDK or CLI.
diff --git a/apps/dashboard/src/components/VolumeTable/useVolumeCommands.tsx b/apps/dashboard/src/components/VolumeTable/useVolumeCommands.tsx index 01792c716..349d39973 100644 --- a/apps/dashboard/src/components/VolumeTable/useVolumeCommands.tsx +++ b/apps/dashboard/src/components/VolumeTable/useVolumeCommands.tsx @@ -5,7 +5,7 @@ import { pluralize } from '@/lib/utils' import { VolumeDto, VolumeState } from '@boxlite-ai/api-client' -import { CheckSquare2Icon, MinusSquareIcon, PlusIcon, TrashIcon } from 'lucide-react' +import { CheckSquare2Icon, MinusSquareIcon, PlusIcon, TrashIcon } from '@/components/ui/icon' import { useMemo } from 'react' import { CommandConfig, useRegisterCommands } from '../CommandPalette' diff --git a/apps/dashboard/src/components/Webhooks/CreateEndpointDialog.tsx b/apps/dashboard/src/components/Webhooks/CreateEndpointDialog.tsx index b491e9c28..e684d0450 100644 --- a/apps/dashboard/src/components/Webhooks/CreateEndpointDialog.tsx +++ b/apps/dashboard/src/components/Webhooks/CreateEndpointDialog.tsx @@ -31,7 +31,7 @@ import { WEBHOOK_EVENT_CATEGORIES, WEBHOOK_EVENTS } from '@/constants/webhook-ev import { handleApiError } from '@/lib/error-handling' import { cn } from '@/lib/utils' import { useForm } from '@tanstack/react-form' -import { ChevronsUpDown, Plus } from 'lucide-react' +import { ChevronsUpDown, Plus } from '@/components/ui/icon' import React, { useCallback, useEffect, useState } from 'react' import { toast } from 'sonner' import { useSvix } from 'svix-react' diff --git a/apps/dashboard/src/components/Webhooks/EditEndpointDialog.tsx b/apps/dashboard/src/components/Webhooks/EditEndpointDialog.tsx index cb2502652..bbc0d15fb 100644 --- a/apps/dashboard/src/components/Webhooks/EditEndpointDialog.tsx +++ b/apps/dashboard/src/components/Webhooks/EditEndpointDialog.tsx @@ -30,7 +30,7 @@ import { WEBHOOK_EVENT_CATEGORIES, WEBHOOK_EVENTS } from '@/constants/webhook-ev import { useUpdateWebhookEndpointMutation } from '@/hooks/mutations/useUpdateWebhookEndpointMutation' import { handleApiError } from '@/lib/error-handling' import { useForm } from '@tanstack/react-form' -import { ChevronsUpDown } from 'lucide-react' +import { ChevronsUpDown } from '@/components/ui/icon' import React, { useEffect, useState } from 'react' import { toast } from 'sonner' import { EndpointOut } from 'svix' diff --git a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EndpointEventsTable.tsx b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EndpointEventsTable.tsx index 105bfcc4b..3bb73ad0d 100644 --- a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EndpointEventsTable.tsx +++ b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EndpointEventsTable.tsx @@ -22,7 +22,7 @@ import { SortingState, useReactTable, } from '@tanstack/react-table' -import { Mail } from 'lucide-react' +import { Mail } from '@/components/ui/icon' import { useCallback, useState } from 'react' import { EndpointMessageOut } from 'svix' import { columns, eventTypeOptions, statusOptions } from './columns' diff --git a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EventDetailsSheet.tsx b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EventDetailsSheet.tsx index 9e5633c59..ca13a581e 100644 --- a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EventDetailsSheet.tsx +++ b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/EventDetailsSheet.tsx @@ -11,7 +11,7 @@ import { ScrollArea } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet' import { getRelativeTimeString } from '@/lib/utils' -import { ChevronDown, ChevronUp, RefreshCw, X } from 'lucide-react' +import { ChevronDown, ChevronUp, RefreshCw, X } from '@/components/ui/icon' import { useCallback, useState } from 'react' import { EndpointMessageOut } from 'svix' import { MessageAttemptsTable } from '../MessageAttemptsTable' diff --git a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/columns.tsx b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/columns.tsx index 3f4fb6e4a..0e55000c9 100644 --- a/apps/dashboard/src/components/Webhooks/EndpointEventsTable/columns.tsx +++ b/apps/dashboard/src/components/Webhooks/EndpointEventsTable/columns.tsx @@ -11,7 +11,7 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge import { WEBHOOK_EVENTS } from '@/constants/webhook-events' import { getRelativeTimeString } from '@/lib/utils' import { ColumnDef, RowData, Table } from '@tanstack/react-table' -import { CheckCircle, Clock, MoreHorizontal, XCircle } from 'lucide-react' +import { CheckCircle, Clock, MoreHorizontal, XCircle } from '@/components/ui/icon' import { EndpointMessageOut } from 'svix' import { CopyButton } from '../../CopyButton' diff --git a/apps/dashboard/src/components/Webhooks/MessageAttemptsTable.tsx b/apps/dashboard/src/components/Webhooks/MessageAttemptsTable.tsx index ada725cfc..42f6390ac 100644 --- a/apps/dashboard/src/components/Webhooks/MessageAttemptsTable.tsx +++ b/apps/dashboard/src/components/Webhooks/MessageAttemptsTable.tsx @@ -10,7 +10,7 @@ import { Skeleton } from '@/components/ui/skeleton' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { getRelativeTimeString } from '@/lib/utils' import { AnimatePresence, motion } from 'framer-motion' -import { ChevronDown, ChevronRight, LoaderCircle } from 'lucide-react' +import { ChevronDown, ChevronRight, LoaderCircle } from '@/components/ui/icon' import { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { MessageAttemptOut } from 'svix' import { useMessageAttempts } from 'svix-react' diff --git a/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/WebhooksEndpointTable.tsx b/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/WebhooksEndpointTable.tsx index baca396f1..f01f6d1ca 100644 --- a/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/WebhooksEndpointTable.tsx +++ b/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/WebhooksEndpointTable.tsx @@ -29,7 +29,7 @@ import { SortingState, useReactTable, } from '@tanstack/react-table' -import { Mail } from 'lucide-react' +import { Mail } from '@/components/ui/icon' import { useState } from 'react' import { useNavigate } from 'react-router-dom' import { EndpointOut } from 'svix' diff --git a/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/columns.tsx b/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/columns.tsx index 5582eba97..4c7ca7938 100644 --- a/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/columns.tsx +++ b/apps/dashboard/src/components/Webhooks/WebhooksEndpointTable/columns.tsx @@ -9,7 +9,7 @@ import { Button } from '@/components/ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { getRelativeTimeString } from '@/lib/utils' import { ColumnDef, RowData, Table } from '@tanstack/react-table' -import { MoreHorizontal } from 'lucide-react' +import { MoreHorizontal } from '@/components/ui/icon' import { EndpointOut } from 'svix' import { CopyButton } from '../../CopyButton' diff --git a/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/MessageDetailsSheet.tsx b/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/MessageDetailsSheet.tsx index cb41ed988..8d0c1e759 100644 --- a/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/MessageDetailsSheet.tsx +++ b/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/MessageDetailsSheet.tsx @@ -10,7 +10,7 @@ import { Button } from '@/components/ui/button' import { Separator } from '@/components/ui/separator' import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet' import { getRelativeTimeString } from '@/lib/utils' -import { ChevronDown, ChevronUp, X } from 'lucide-react' +import { ChevronDown, ChevronUp, X } from '@/components/ui/icon' import { MessageOut } from 'svix' import { MessageAttemptsTable } from '../MessageAttemptsTable' diff --git a/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/WebhooksMessagesTable.tsx b/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/WebhooksMessagesTable.tsx index 19818cb53..c080c614c 100644 --- a/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/WebhooksMessagesTable.tsx +++ b/apps/dashboard/src/components/Webhooks/WebhooksMessagesTable/WebhooksMessagesTable.tsx @@ -22,7 +22,7 @@ import { SortingState, useReactTable, } from '@tanstack/react-table' -import { Mail, RefreshCcw } from 'lucide-react' +import { Mail, RefreshCcw } from '@/components/ui/icon' import { useCallback, useState } from 'react' import { Button } from '@/components/ui/button' import { useMessages } from 'svix-react' diff --git a/apps/dashboard/src/components/admin/AdminFleetView.tsx b/apps/dashboard/src/components/admin/AdminFleetView.tsx new file mode 100644 index 000000000..6243d6a47 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminFleetView.tsx @@ -0,0 +1,321 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Skeleton } from '@/components/ui/skeleton' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import { cn } from '@/lib/utils' +import React, { useEffect, useMemo, useState } from 'react' +import { type AdminMachine, type AdminRunner, isOnlineRunner, runnerCpuPercent } from './adminHelpers' +import { AdminSectionFrame, AdminStateBadge } from './AdminPrimitives' +import { useAdminMachines, useAdminRunners, useAdminActions } from './useAdminData' + +interface AdminFleetViewProps { + query: string + highlightRunnerId: string | null + onShowRunnerBoxes: (runnerId: string) => void + onDiagnoseRunner: (runner: AdminRunner) => void + onDiagnoseMachine: (machine: AdminMachine) => void +} + +interface ConfirmState { + title: string + description: string + confirmLabel: string + onConfirm: () => void +} + +const AdminFleetView: React.FC = ({ + query, + highlightRunnerId, + onShowRunnerBoxes, + onDiagnoseRunner, + onDiagnoseMachine, +}) => { + const runnersQuery = useAdminRunners() + const machinesQuery = useAdminMachines() + const { cordon, drain } = useAdminActions() + const [confirm, setConfirm] = useState(null) + + const normalizedQuery = query.trim().toLowerCase() + const runners = useMemo(() => { + const allRunners = runnersQuery.data ?? [] + if (!normalizedQuery) return allRunners + return allRunners.filter((runner) => { + return ( + runner.id.toLowerCase().includes(normalizedQuery) || + runner.state.toLowerCase().includes(normalizedQuery) || + String(runner.currentStartedBoxes).includes(normalizedQuery) + ) + }) + }, [runnersQuery.data, normalizedQuery]) + const online = useMemo(() => runners.filter(isOnlineRunner), [runners]) + const stale = useMemo(() => runners.filter((r) => !isOnlineRunner(r)), [runners]) + const machines = useMemo(() => { + const allMachines = machinesQuery.data ?? [] + if (!normalizedQuery) return allMachines + return allMachines.filter((machine) => { + return ( + machine.host.toLowerCase().includes(normalizedQuery) || machine.region.toLowerCase().includes(normalizedQuery) + ) + }) + }, [machinesQuery.data, normalizedQuery]) + + useEffect(() => { + if (!highlightRunnerId) return + const el = document.getElementById(`admin-runner-${highlightRunnerId}`) + el?.scrollIntoView({ block: 'center', behavior: 'smooth' }) + }, [highlightRunnerId, runners]) + + const renderRunnerRows = (rows: AdminRunner[], emptyText: string) => + rows.length > 0 ? ( + rows.map((r) => { + const pct = Math.round(runnerCpuPercent(r) * 100) + return ( + + {r.id} + +

+ + {r.draining && ( + + draining + + )} + {r.unschedulable && ( + + cordoned + + )} +
+ + + + + {r.currentAllocatedCpu}/{r.cpu} + + = 80 ? 'text-destructive' : 'text-muted-foreground')}>{pct}% + + + + {r.currentAllocatedMemoryGiB.toFixed(1)}/{r.memory.toFixed(1)} GiB + + + {r.currentStartedBoxes > 0 ? ( + + ) : ( + 0 + )} + + {r.availabilityScore?.toFixed(2) ?? '—'} + +
+ + + {!r.draining && ( + + )} +
+
+ + ) + }) + ) : ( + + + {emptyText} + + + ) + + const runnerHeader = ( + + + Runner + State + CPU alloc + Mem alloc + Boxes + Score + Actions + + + ) + + return ( +
+ {online.length} online} + > + {runnersQuery.isPending ? ( + + ) : ( + <> +
+ + {runnerHeader} + {renderRunnerRows(online, 'No online runners.')} +
+
+ + {stale.length > 0 && ( + + + +
+ Unresponsive & stale + + {stale.length} runners outside READY state + +
+
+ +
+ + {runnerHeader} + {renderRunnerRows(stale, 'No stale runners.')} +
+
+
+
+
+ )} + + )} +
+ + + {machinesQuery.isPending ? ( + + ) : ( +
+ + + + Host + Region + Oversell CPU + CPU waterline + Mem waterline + Boxes + Diagnose + + + + {machines.length > 0 ? ( + machines.map((m) => ( + + {m.host} + {m.region} + + {m.oversellCpu.toFixed(1)}× + {m.oversellCpu > 1 && ( + + over + + )} + + {m.cpuWaterline.toFixed(1)}% + {m.memWaterline.toFixed(1)}% + {m.boxes} + + + + + )) + ) : ( + + + {normalizedQuery ? 'No machines match the current search.' : 'No machines found.'} + + + )} + +
+
+ )} +
+ + !open && setConfirm(null)}> + + + {confirm?.title} + {confirm?.description} + + + Cancel + { + confirm?.onConfirm() + setConfirm(null) + }} + > + {confirm?.confirmLabel ?? 'Confirm'} + + + + +
+ ) +} + +export default AdminFleetView diff --git a/apps/dashboard/src/components/admin/AdminOverviewView.tsx b/apps/dashboard/src/components/admin/AdminOverviewView.tsx new file mode 100644 index 000000000..826690d32 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminOverviewView.tsx @@ -0,0 +1,207 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Skeleton } from '@/components/ui/skeleton' +import { ChevronRight } from '@/components/ui/icon' +import React, { useMemo } from 'react' +import { groupBoxesByOwner, isOnlineRunner, runnerCpuPercent, selectErroringOwners } from './adminHelpers' +import AdminPlatformTelemetryView from './AdminPlatformTelemetryView' +import { AdminSectionFrame } from './AdminPrimitives' +import { useAdminBoxes, useAdminRunners } from './useAdminData' + +const PLATFORM_TELEMETRY_TARGET = 'boxlite-api' +const PLATFORM_TELEMETRY_PANEL_ID = 'admin-platform-telemetry-panel' + +interface AdminOverviewViewProps { + onJumpToOwner: (ownerName: string) => void + onJumpToRunner: (runnerId: string) => void + onDiagnoseTrace: (traceId: string) => void + onDiagnoseExecution: (executionId: string, traceId?: string) => void + onDiagnoseJob: (jobId: string, traceId?: string) => void + onDiagnoseRequest: (requestId: string, traceId?: string) => void +} + +function AttentionRow({ + color, + title, + subtitle, + action, + onClick, +}: { + color: string + title: React.ReactNode + subtitle: string + action: string + onClick: () => void +}) { + return ( + + ) +} + +function NeedsAttentionPanel({ + isBoxesPending, + erroringOwners, + isRunnersPending, + staleRunners, + hotRunners, + onJumpToOwner, + onJumpToRunner, +}: { + isBoxesPending: boolean + erroringOwners: ReturnType + isRunnersPending: boolean + staleRunners: ReturnType['data'] + hotRunners: ReturnType['data'] + onJumpToOwner: (ownerName: string) => void + onJumpToRunner: (runnerId: string) => void +}) { + const hasRunnerAlerts = Boolean(staleRunners?.length || hotRunners?.length) + const hasAlerts = erroringOwners.length > 0 || hasRunnerAlerts + + return ( + + {isBoxesPending || isRunnersPending ? ( +
+ + +
+ ) : !hasAlerts ? ( +

No active attention items.

+ ) : ( + <> + {erroringOwners.map(({ group, errorBoxes }) => ( + b.id).join(', ')}`} + action="view" + onClick={() => onJumpToOwner(group.owner.name)} + /> + ))} + {staleRunners?.map((r) => ( + {r.id}} + subtitle={`${r.state} · outside READY state`} + action="drain" + onClick={() => onJumpToRunner(r.id)} + /> + ))} + {hotRunners?.map((r) => ( + {r.id}} + subtitle={`${Math.round(runnerCpuPercent(r) * 100)}% CPU · nearly full`} + action="view" + onClick={() => onJumpToRunner(r.id)} + /> + ))} + + )} +
+ ) +} + +function PlatformTelemetryScopeSection() { + const focusTelemetry = () => { + document.getElementById(PLATFORM_TELEMETRY_PANEL_ID)?.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + + return ( + + ) +} + +const AdminOverviewView: React.FC = ({ + onJumpToOwner, + onJumpToRunner, + onDiagnoseTrace, + onDiagnoseExecution, + onDiagnoseJob, + onDiagnoseRequest, +}) => { + const boxesQuery = useAdminBoxes() + const runnersQuery = useAdminRunners() + + const boxes = boxesQuery.data ?? [] + const erroringOwners = useMemo(() => selectErroringOwners(groupBoxesByOwner(boxes)), [boxes]) + + const runners = runnersQuery.data ?? [] + const staleRunners = useMemo(() => runners.filter((r) => !isOnlineRunner(r)), [runners]) + const hotRunners = useMemo(() => runners.filter((r) => isOnlineRunner(r) && runnerCpuPercent(r) >= 0.8), [runners]) + + return ( +
+ +
+
+ +
+ +
+
+ ) +} + +export default AdminOverviewView diff --git a/apps/dashboard/src/components/admin/AdminPeopleBoxesView.tsx b/apps/dashboard/src/components/admin/AdminPeopleBoxesView.tsx new file mode 100644 index 000000000..6134504b7 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminPeopleBoxesView.tsx @@ -0,0 +1,223 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Skeleton } from '@/components/ui/skeleton' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import { cn } from '@/lib/utils' +import { ChevronsDownUp, ChevronsUpDown, Search, Users, X } from '@/components/ui/icon' +import React, { useEffect, useMemo, useState } from 'react' +import { + type AdminBox, + type OwnerGroup, + filterOwnerGroups, + getBoxRollupText, + groupBoxesByOwner, + isErrorState, +} from './adminHelpers' +import { AdminSectionFrame, AdminStateBadge, BreakdownBar } from './AdminPrimitives' +import { useAdminBoxes } from './useAdminData' + +interface AdminPeopleBoxesViewProps { + query: string + runnerFilter: string | null + onClearRunnerFilter: () => void + onOpenBox: (box: AdminBox) => void + onOpenOwnerGroup: (group: OwnerGroup) => void +} + +const AdminPeopleBoxesView: React.FC = ({ + query, + runnerFilter, + onClearRunnerFilter, + onOpenBox, + onOpenOwnerGroup, +}) => { + const boxesQuery = useAdminBoxes() + const [expandedOwnerIds, setExpandedOwnerIds] = useState([]) + + const groups = useMemo(() => { + const boxes = boxesQuery.data ?? [] + const scoped = runnerFilter ? boxes.filter((b) => b.runnerId === runnerFilter) : boxes + return filterOwnerGroups(groupBoxesByOwner(scoped), query) + }, [boxesQuery.data, runnerFilter, query]) + + const groupIds = useMemo(() => groups.map((g) => g.organizationId), [groups]) + const hasActiveFilter = Boolean(query.trim() || runnerFilter) + + useEffect(() => { + setExpandedOwnerIds(hasActiveFilter ? groupIds : []) + }, [groupIds, hasActiveFilter]) + + if (boxesQuery.isPending) { + return ( +
+ {Array.from({ length: 3 }).map((_, i) => ( + + ))} +
+ ) + } + + const action = ( +
+ + +
+ ) + + return ( + + {runnerFilter && ( +
+ + Boxes on runner {runnerFilter} + + +
+ )} + + {groups.length === 0 ? ( +
+ + + {query || runnerFilter ? 'No user or box matches the current filter.' : 'No boxes found.'} + +
+ ) : ( + + {groups.map((group) => ( + +
+ +
+
+
+ {group.owner.name} + + {group.owner.personal ? 'personal' : 'team'} + +
+

+ {group.owner.email || group.owner.orgName} +

+
+
+

+ {getBoxRollupText(group.boxes)} · {group.boxes.length} total +

+ +
+
+
+ +
+ +
+ + + + Box + State + CPU + Mem + Runner + Created + Diagnose + + + + {group.boxes.map((box) => ( + onOpenBox(box)} + > + {box.id} + + + + {box.cpu} + {box.memoryGiB} GiB + + {box.runnerId ?? '—'} + + + {new Date(box.createdAt).toLocaleString()} + + + + + + ))} + +
+
+
+
+ ))} +
+ )} +
+ ) +} + +export default AdminPeopleBoxesView diff --git a/apps/dashboard/src/components/admin/AdminPlatformTelemetryView.tsx b/apps/dashboard/src/components/admin/AdminPlatformTelemetryView.tsx new file mode 100644 index 000000000..a929ddc40 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminPlatformTelemetryView.tsx @@ -0,0 +1,50 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import React from 'react' +import { type AdminBox } from './adminHelpers' +import AdminTelemetryPanel from './AdminTelemetryPanel' + +interface AdminPlatformTelemetryViewProps { + contextBox?: AdminBox | null + onDiagnoseTrace?: (traceId: string) => void + onDiagnoseExecution?: (executionId: string, traceId?: string) => void + onDiagnoseJob?: (jobId: string, traceId?: string) => void + onDiagnoseRequest?: (requestId: string, traceId?: string) => void +} + +const AdminPlatformTelemetryView: React.FC = ({ + contextBox, + onDiagnoseTrace, + onDiagnoseExecution, + onDiagnoseJob, + onDiagnoseRequest, +}) => { + const preset = contextBox + ? { + boxId: contextBox.id, + runnerId: contextBox.runnerId ?? undefined, + } + : undefined + + return ( + + ) +} + +export default AdminPlatformTelemetryView diff --git a/apps/dashboard/src/components/admin/AdminPrimitives.tsx b/apps/dashboard/src/components/admin/AdminPrimitives.tsx new file mode 100644 index 000000000..d82a0a168 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminPrimitives.tsx @@ -0,0 +1,82 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Badge } from '@/components/ui/badge' +import { cn } from '@/lib/utils' +import React from 'react' +import { type BreakdownSegment, stateBadgeVariant } from './adminHelpers' + +export function AdminSectionFrame({ + title, + description, + action, + children, + className, + contentClassName, +}: { + title: string + description?: string + action?: React.ReactNode + children: React.ReactNode + className?: string + contentClassName?: string +}) { + return ( +
+
+
+

{title}

+ {description &&

{description}

} +
+ {action} +
+
{children}
+
+ ) +} + +export function AdminStateBadge({ state, className }: { state: string; className?: string }) { + return ( + + {state?.replace(/_/g, ' ')} + + ) +} + +export function BreakdownBar({ + segments, + total, + className, +}: { + segments: BreakdownSegment[] + total: number + className?: string +}) { + if (total <= 0) return null + return ( +
+ {segments.map((s) => ( +
+ ))} +
+ ) +} + +export function BreakdownLegend({ segments, className }: { segments: BreakdownSegment[]; className?: string }) { + return ( +
+ {segments.map((s) => ( + + + {s.count} {s.label} + + ))} +
+ ) +} diff --git a/apps/dashboard/src/components/admin/AdminStatusStrip.tsx b/apps/dashboard/src/components/admin/AdminStatusStrip.tsx new file mode 100644 index 000000000..2668edd10 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminStatusStrip.tsx @@ -0,0 +1,118 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Skeleton } from '@/components/ui/skeleton' +import React, { useMemo } from 'react' +import { getBoxBreakdown } from './adminHelpers' +import { BreakdownBar, BreakdownLegend } from './AdminPrimitives' +import { useAdminBoxes, useAdminOverview } from './useAdminData' + +function KpiCard({ children }: { children: React.ReactNode }) { + return {children} +} + +function formatCpuPercent(cpuUtil: number): string { + const percent = Math.max(cpuUtil * 100, 0) + if (percent === 0) return '0%' + if (percent > 0 && percent < 0.1) return '<0.1%' + return `${percent.toFixed(1)}%` +} + +function cpuBarWidthPercent(cpuUtil: number): number { + const percent = Math.min(Math.max(cpuUtil * 100, 0), 100) + if (percent === 0) return 0 + return Math.max(percent, 3) +} + +const AdminStatusStrip: React.FC = () => { + const overviewQuery = useAdminOverview() + const boxesQuery = useAdminBoxes() + const boxes = boxesQuery.data ?? [] + const breakdown = useMemo(() => getBoxBreakdown(boxes), [boxes]) + const overview = overviewQuery.data + const clusterCpuBarWidth = overview ? cpuBarWidthPercent(overview.cluster.cpuUtil) : 0 + + if (overviewQuery.isPending || !overview) { + return ( +
+ {Array.from({ length: 4 }).map((_, i) => ( + + ))} +
+ ) + } + + return ( +
+ + + Users + + +

{overview.users}

+

across all orgs

+
+
+ + + + Boxes + + +

+ {overview.activeBoxes}{' '} + active · {overview.boxes.total} total +

+ {boxes.length > 0 && ( + <> + + + + )} +
+
+ + + + Runners + + +

+ {overview.runners.online}{' '} + / {overview.runners.total} online +

+

+ {overview.runners.draining > 0 ? `${overview.runners.draining} draining` : 'none draining'} +

+
+
+ + + + Cluster CPU + + +

{formatCpuPercent(overview.cluster.cpuUtil)}

+

+ {overview.cluster.oversell.toFixed(1)}x oversell · online runners +

+
+
+
+
+ 0 + 100% +
+ + +
+ ) +} + +export default AdminStatusStrip diff --git a/apps/dashboard/src/components/admin/AdminTelemetryDrawer.spec.tsx b/apps/dashboard/src/components/admin/AdminTelemetryDrawer.spec.tsx new file mode 100644 index 000000000..0d261c488 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminTelemetryDrawer.spec.tsx @@ -0,0 +1,219 @@ +// @vitest-environment jsdom +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { act } from 'react' +import { createRoot, type Root } from 'react-dom/client' +import { MemoryRouter } from 'react-router-dom' +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest' +import { type AdminBox } from './adminHelpers' +import { createBoxDiagnoseTarget } from './adminDiagnoseTarget' +import AdminTelemetryDrawer from './AdminTelemetryDrawer' + +const investigateMock = vi.hoisted(() => { + const createData = () => ({ + resource: { type: 'box', title: 'Box box-test-001', subtitle: 'Brian Luo · org-test' }, + sources: [ + { source: 'clickhouse', state: 'available', count: 2 }, + { source: 'cloudwatch', state: 'missing', message: 'No stdout logs matched this box.' }, + ], + externalLinks: { + clickstack: { + configured: true, + dashboardUrl: 'https://hyperdx.clickhouse.cloud/dashboards/dashboard-1', + logsUrl: 'https://hyperdx.clickhouse.cloud/search', + tracesUrl: 'https://hyperdx.clickhouse.cloud/search', + metricsUrl: 'https://hyperdx.clickhouse.cloud/chart', + missingSources: ['logs'], + message: 'ClickStack is reachable, but logs source id needs to be configured for one-click queries', + sourceSetup: [ + { + kind: 'logs', + envVar: 'ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID', + name: 'BoxLite Logs', + dataType: 'Log', + database: 'otel', + table: 'otel_logs', + timestampColumn: 'Timestamp', + }, + ], + }, + }, + commands: { + api: '/admin/observability/investigate?boxId=box-test-001', + aiAgentPrompt: 'Use the BoxLite Admin API with X-BoxLite-Source=agent.', + }, + operations: [], + timeline: [], + }) + + return { + createData, + data: createData(), + refetch: vi.fn(), + } +}) + +vi.mock('@/hooks/useAdminObservability', () => ({ + useAdminObservabilityInvestigate: () => ({ + data: investigateMock.data, + isLoading: false, + isError: false, + refetch: investigateMock.refetch, + }), +})) + +const box: AdminBox = { + id: 'box-test-001', + organizationId: 'org-test', + state: 'started', + runnerId: 'runner-test-001', + cpu: 2, + memoryGiB: 4, + createdAt: '2026-05-24T09:10:00.000Z', + owner: { + name: 'Brian Luo', + email: 'brian@example.com', + orgName: 'personal', + personal: true, + }, +} + +describe('AdminTelemetryDrawer', () => { + let root: Root | null = null + + beforeAll(() => { + ;(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true + }) + + beforeEach(() => { + investigateMock.data = investigateMock.createData() + investigateMock.refetch.mockClear() + }) + + afterEach(() => { + act(() => { + root?.unmount() + }) + root = null + document.body.innerHTML = '' + }) + + it('shows box diagnosis evidence and agent entry points', () => { + const host = document.createElement('div') + document.body.appendChild(host) + + act(() => { + root = createRoot(host) + root.render( + + + , + ) + }) + + const text = document.body.textContent ?? '' + + expect(text).toContain('Diagnose box') + expect(text).toContain('Evidence sources') + expect(text).toContain('Human deep links') + expect(text).toContain('Dashboard') + expect(text).toContain('ClickStack is reachable, but logs source id needs to be configured') + expect(text).toContain('ClickStack source setup') + expect(text).toContain('BoxLite Logs') + expect(text).toContain('ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID') + expect(text).toContain('Admin API and AI Agent') + expect(text).toContain('Operations') + expect(text).toContain('Timeline') + }) + + it('makes the diagnose query window explicit to avoid empty-equals-no-data ambiguity', () => { + const host = document.createElement('div') + document.body.appendChild(host) + + act(() => { + root = createRoot(host) + root.render( + + + , + ) + }) + + const note = document.querySelector('[data-testid="diagnose-window-note"]') + expect(note?.textContent).toContain('Window:') + expect(note?.textContent).toContain('last 1h') + }) + + it('renders the ClickStack dashboard as a first-class human link', () => { + const host = document.createElement('div') + document.body.appendChild(host) + + act(() => { + root = createRoot(host) + root.render( + + + , + ) + }) + + const dashboardAnchor = Array.from(document.querySelectorAll('a')).find((anchor) => + anchor.textContent?.includes('Dashboard'), + ) + + expect(dashboardAnchor?.getAttribute('href')).toBe('https://hyperdx.clickhouse.cloud/dashboards/dashboard-1') + }) + + it('disables ClickStack links when a source URL is missing', () => { + investigateMock.data.externalLinks.clickstack.logsUrl = undefined + const host = document.createElement('div') + document.body.appendChild(host) + + act(() => { + root = createRoot(host) + root.render( + + + , + ) + }) + + const logsButton = Array.from(document.querySelectorAll('button')).find((button) => + button.textContent?.includes('Logs'), + ) + const logAnchors = Array.from(document.querySelectorAll('a')).filter((anchor) => + anchor.textContent?.includes('Logs'), + ) + + expect(logsButton?.hasAttribute('disabled')).toBe(true) + expect(logAnchors).toHaveLength(0) + expect(document.body.textContent ?? '').toContain('ADMIN_OBSERVABILITY_CLICKSTACK_LOG_SOURCE_ID') + }) +}) diff --git a/apps/dashboard/src/components/admin/AdminTelemetryDrawer.tsx b/apps/dashboard/src/components/admin/AdminTelemetryDrawer.tsx new file mode 100644 index 000000000..7f67cdea3 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminTelemetryDrawer.tsx @@ -0,0 +1,477 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from '@/components/ui/sheet' +import { + useAdminObservabilityInvestigate, + type AdminObservabilityClickStackSourceSetup, + type AdminObservabilityOperation, +} from '@/hooks/useAdminObservability' +import { + ArrowUpRight, + Bot, + CheckCircle2, + CircleDashed, + Clipboard, + ExternalLink, + Loader2, + Search, + TriangleAlert, + Wrench, + X, +} from '@/components/ui/icon' +import React, { useMemo, useState } from 'react' +import { toast } from 'sonner' +import { isErrorState } from './adminHelpers' +import type { AdminDiagnoseTarget } from './adminDiagnoseTarget' +import { AdminStateBadge } from './AdminPrimitives' + +interface AdminTelemetryDrawerProps { + target: AdminDiagnoseTarget | null + open: boolean + onOpenChange: (open: boolean) => void + onRecover: (boxId: string) => void + onCordonRunner?: (runnerId: string) => void + onDrainRunner?: (runnerId: string) => void + onJumpToRunner?: (runnerId: string) => void +} + +function MetaRow({ k, children }: { k: string; children: React.ReactNode }) { + return ( +
+
{k}
+
{children}
+
+ ) +} + +function SourceBadge({ state }: { state: string }) { + const icon = + state === 'available' ? ( + + ) : state === 'error' ? ( + + ) : ( + + ) + const variant = state === 'available' ? 'success' : state === 'error' ? 'destructive' : 'secondary' + + return ( + + {icon} + {state.replace(/_/g, ' ')} + + ) +} + +function CodeCopy({ label, value, icon }: { label: string; value: string; icon: React.ReactNode }) { + const [copied, setCopied] = useState(false) + + const copy = async () => { + await navigator.clipboard.writeText(value) + setCopied(true) + toast.success(`${label} copied`) + window.setTimeout(() => setCopied(false), 1200) + } + + return ( +
+
+ {icon} + {label} +
+
+ {value} + +
+
+ ) +} + +function ClickStackSourceSetupList({ setup }: { setup: AdminObservabilityClickStackSourceSetup[] }) { + return ( +
+
ClickStack source setup
+
+ {setup.map((source) => { + const tableSummary = + source.table ?? + Object.values(source.metricTables ?? {}) + .filter(Boolean) + .join(', ') + + return ( +
+
+
{source.name}
+ + {source.dataType} + +
+
+
+ DB/Table: {source.database} + {tableSummary ? ( + <> + {' / '} + {tableSummary} + + ) : null} +
+
+ Timestamp: {source.timestampColumn} +
+
+ After saving, put the created source id into {source.envVar}. +
+
+
+ ) + })} +
+
+ ) +} + +function ClickStackLinkButton({ href, label }: { href?: string; label: string }) { + if (!href) { + return ( + + ) + } + + return ( + + ) +} + +function operationButtonLabel(operation: AdminObservabilityOperation) { + if (operation.state === 'request_only') return 'Request' + if (operation.state === 'disabled') return 'Unavailable' + return operation.label +} + +function formatWindow(window: { from: Date; to: Date }) { + const day = window.from.toISOString().slice(0, 10) + const fromTime = window.from.toISOString().slice(11, 16) + const toTime = window.to.toISOString().slice(11, 16) + return `${day} ${fromTime}–${toTime} UTC` +} + +function buildFallbackCommands(target: AdminDiagnoseTarget, window: { from: Date; to: Date }) { + const params = new URLSearchParams() + params.set('from', window.from.toISOString()) + params.set('to', window.to.toISOString()) + params.set('limit', '100') + for (const [key, value] of Object.entries(target.params)) { + if (value !== undefined && value !== null && value !== '') { + params.set(key, String(value)) + } + } + const path = `/admin/observability/investigate?${params.toString()}` + return { + api: `GET ${path}`, + aiAgentPrompt: + `Use BoxLite Admin API only. Query GET ${path} with header X-BoxLite-Source=agent, ` + + 'then summarize resource, sources, missing reasons, timeline, xLog, audit, and next operations.', + } +} + +const AdminTelemetryDrawer: React.FC = ({ + target, + open, + onOpenChange, + onRecover, + onCordonRunner, + onDrainRunner, + onJumpToRunner, +}) => { + const queryWindow = useMemo(() => { + const to = new Date() + const from = new Date(to.getTime() - 60 * 60 * 1000) + return { from, to } + }, [target?.kind, target?.subtitle, open]) + + const investigateQuery = useAdminObservabilityInvestigate( + { + from: queryWindow.from, + to: queryWindow.to, + limit: 100, + ...target?.params, + }, + { enabled: open && !!target, retry: false }, + ) + + if (!target) return null + + const evidence = investigateQuery.data + const sources = evidence?.sources ?? [] + const operations = evidence?.operations ?? [] + const timeline = evidence?.timeline ?? [] + const clickstack = evidence?.externalLinks?.clickstack + const commands = evidence?.commands ?? buildFallbackCommands(target, queryWindow) + const recoverOperation = operations.find((operation) => operation.id.startsWith('recover:')) + const cordonOperation = operations.find((operation) => operation.id.startsWith('cordon:')) + const drainOperation = operations.find((operation) => operation.id.startsWith('drain:')) + const requestOnlyOperations = operations.filter((operation) => operation.state === 'request_only') + const hasPartialContract = Boolean( + evidence && + (!evidence.resource || !evidence.externalLinks || !evidence.commands || !evidence.operations || !evidence.timeline), + ) + const displayTitle = evidence?.resource?.title ?? target.title + const displaySubtitle = + evidence?.resource?.subtitle ?? + (hasPartialContract + ? 'Using Admin inventory context until the Phase B API contract is deployed.' + : investigateQuery.isLoading + ? 'Loading diagnosis evidence' + : target.subtitle) + + const runOperation = (operation: AdminObservabilityOperation) => { + if (operation.id.startsWith('recover:') && target.box) { + onRecover(operation.targetId ?? target.box.id) + return + } + if (operation.id.startsWith('cordon:') && operation.targetId) { + onCordonRunner?.(operation.targetId) + return + } + if (operation.id.startsWith('drain:') && operation.targetId) { + onDrainRunner?.(operation.targetId) + } + } + + return ( + + + +
+ + {target.title} + {target.state && } + + +
+ {target.subtitle} +
+ +
+
+
+
+

{displayTitle}

+

{displaySubtitle}

+
+ {investigateQuery.isLoading && ( + + + loading + + )} +
+ +
+ {target.details.map((detail) => { + const action = detail.action + return ( + + {action?.type === 'runner' ? ( + + ) : ( + {detail.value} + )} + + ) + })} + {target.kind !== 'runner' && + target.params.runnerId && + !target.details.some((detail) => detail.label === 'runner') && ( + + + + )} +
+
+ +
+
+
+

Evidence sources

+

What is available, missing, or not configured.

+

+ Window: {formatWindow(queryWindow)} (last 1h). Signals outside it (e.g. CloudWatch retains longer) are + not shown — an empty source here may mean “outside this window”, not “no data”. +

+
+ +
+ + {investigateQuery.isError && ( +
+ Failed to load diagnosis evidence. +
+ )} + {hasPartialContract && ( +
+ Admin Diagnose is using an older backend response. Deploy the Phase B API contract to enable ClickStack + links, operations, commands, and timeline evidence. +
+ )} + +
+ {sources.map((source) => ( +
+
+
{source.source}
+
+ {source.message ?? `${source.count ?? 0} related item${source.count === 1 ? '' : 's'}`} +
+
+ +
+ ))} + {!evidence && !investigateQuery.isLoading && ( +
+ Open this drawer from a real box to resolve telemetry, platform state, audit, and xLog evidence. +
+ )} +
+
+ +
+

Human deep links

+

+ BoxLite keeps context, permission, and audit here. ClickStack is for detailed logs, traces, and metrics. +

+
+ {clickstack?.configured ? ( + <> + + + + + {clickstack.message && ( +
+ {clickstack.message} +
+ )} + {clickstack.sourceSetup && clickstack.sourceSetup.length > 0 && ( + + )} + + ) : ( +
+ {clickstack?.message ?? 'ClickStack links are not available from the current backend response yet.'} +
+ )} +
+
+ +
+

Admin API and AI Agent

+ } /> + } /> +
+ +
+

Operations

+
+ {[recoverOperation, cordonOperation, drainOperation] + .filter((operation): operation is AdminObservabilityOperation => Boolean(operation)) + .map((operation) => ( + + ))} +
+ {operations.length === 0 && ( +
+ No operations are available for this context yet. +
+ )} + {requestOnlyOperations.length > 0 && ( +
+ {requestOnlyOperations.map((operation) => ( +
+
{operation.label}
+
{operation.reason}
+
+ ))} +
+ )} +
+ +
+
+

Timeline

+ {timeline.length} events +
+
+ {timeline.slice(0, 12).map((event, index) => ( +
+
+
{event.title}
+
{new Date(event.timestamp).toLocaleString()}
+
+
{event.detail ?? event.source}
+
+ ))} + {evidence && timeline.length === 0 && ( +
+ No timeline events were found for this context. Check source status above for missing data reasons. +
+ )} +
+
+
+
+
+ ) +} + +export default AdminTelemetryDrawer diff --git a/apps/dashboard/src/components/admin/AdminTelemetryPanel.tsx b/apps/dashboard/src/components/admin/AdminTelemetryPanel.tsx new file mode 100644 index 000000000..e98933c72 --- /dev/null +++ b/apps/dashboard/src/components/admin/AdminTelemetryPanel.tsx @@ -0,0 +1,750 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CopyButton } from '@/components/CopyButton' +import { SeverityBadge } from '@/components/telemetry/SeverityBadge' +import { buildTraceWaterfallRows } from '@/components/telemetry/traceWaterfall' +import { Badge, BadgeProps } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import { DateRangePicker, QuickRangesConfig } from '@/components/ui/date-range-picker' +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' +import { Input } from '@/components/ui/input' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Spinner } from '@/components/ui/spinner' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { + AdminObservabilityBaseParams, + AdminObservabilityLogEntry, + AdminObservabilityMetricSeries, + AdminObservabilitySourceStatus, + AdminObservabilityStatus, + AdminObservabilityTraceSummary, + OBSERVABILITY_LAYERS, + ObservabilityLayer, + ObservabilityState, + useAdminObservabilityInvestigate, + useAdminObservabilityLogs, + useAdminObservabilityMetrics, + useAdminObservabilityStatus, + useAdminObservabilityTraceSpans, + useAdminObservabilityTraces, +} from '@/hooks/useAdminObservability' +import { cn } from '@/lib/utils' +import { format, subHours } from 'date-fns' +import { Activity, AlertCircle, BarChart3, FileText, RefreshCw, Search } from '@/components/ui/icon' +import React, { useMemo, useState } from 'react' +import { DateRange } from 'react-day-picker' +import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip as RechartsTooltip, XAxis, YAxis } from 'recharts' + +const QUICK_RANGES: QuickRangesConfig = { + minutes: [15, 30], + hours: [1, 3, 6, 12, 24], + days: [3, 7], +} + +const PAGE_LIMIT = 30 +const CHART_COLORS = [ + 'hsl(var(--chart-1))', + 'hsl(var(--chart-2))', + 'hsl(var(--chart-3))', + 'hsl(var(--chart-4))', + 'hsl(var(--chart-5))', +] + +const LAYER_LABELS: Record = { + api: 'API', + runner: 'Runner', + ec2_host: 'EC2 Host', + box: 'Box', +} + +const STATE_VARIANTS: Record = { + missing: 'outline', + configured: 'secondary', + receiving: 'success', + stale: 'warning', + error: 'destructive', +} + +const SOURCE_STATE_VARIANTS: Record = { + available: 'success', + missing: 'outline', + stale: 'warning', + not_configured: 'secondary', + error: 'destructive', +} + +type TelemetryPreset = Partial> +type CompleteDateRange = { from: Date; to: Date } + +interface AdminTelemetryPanelProps { + title?: string + description?: string + preset?: TelemetryPreset + compact?: boolean + className?: string + onDiagnoseTrace?: (traceId: string) => void + onDiagnoseExecution?: (executionId: string, traceId?: string) => void + onDiagnoseJob?: (jobId: string, traceId?: string) => void + onDiagnoseRequest?: (requestId: string, traceId?: string) => void +} + +function createDefaultRange(): CompleteDateRange { + const now = new Date() + return { from: subHours(now, 1), to: now } +} + +function getCompleteRange(range: DateRange): CompleteDateRange { + const fallback = createDefaultRange() + return { + from: range.from ?? fallback.from, + to: range.to ?? fallback.to, + } +} + +function formatTimestamp(timestamp?: string) { + if (!timestamp) return '-' + try { + return format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss.SSS') + } catch { + return timestamp + } +} + +function formatDuration(durationMs: number) { + if (durationMs < 1) return `${(durationMs * 1000).toFixed(2)}us` + if (durationMs < 1000) return `${durationMs.toFixed(2)}ms` + return `${(durationMs / 1000).toFixed(2)}s` +} + +function truncateMiddle(value: string, head = 8, tail = 8) { + if (value.length <= head + tail + 3) return value + return `${value.slice(0, head)}...${value.slice(-tail)}` +} + +function readAttribute(attributes: Record | undefined, keys: string[]) { + for (const key of keys) { + const value = attributes?.[key] + if (value) return value + } + return undefined +} + +function getLayerFromAttributes(resourceAttributes?: Record) { + const layer = resourceAttributes?.['boxlite.layer'] + return OBSERVABILITY_LAYERS.includes(layer as ObservabilityLayer) ? LAYER_LABELS[layer as ObservabilityLayer] : '-' +} + +function getErrorDescription(error: unknown) { + if (error && typeof error === 'object' && 'response' in error) { + const response = (error as { response?: { status?: number; data?: { message?: string } } }).response + if (response?.status === 403) return 'System Admin access required.' + if (response?.data?.message) return response.data.message + } + if (error instanceof Error) return error.message + return 'Request failed.' +} + +function StateBadge({ state }: { state: ObservabilityState }) { + return ( + + {state} + + ) +} + +function QueryError({ error }: { error: unknown }) { + return ( + + + + + + Unable to load telemetry + {getErrorDescription(error)} + + + ) +} + +function EmptyTelemetry({ icon: Icon, title }: { icon: React.ElementType; title: string }) { + return ( + + + + + + {title} + + + ) +} + +function StatusStrip({ + status, + isLoading, + compact, +}: { + status?: AdminObservabilityStatus + isLoading: boolean + compact?: boolean +}) { + const layerStatuses = + status?.layers ?? + OBSERVABILITY_LAYERS.map((layer) => ({ + layer, + state: 'missing' as ObservabilityState, + signals: { + logs: 'missing' as ObservabilityState, + traces: 'missing' as ObservabilityState, + metrics: 'missing' as ObservabilityState, + }, + })) + + return ( +
+
+
+ Backend + {isLoading ? : } +
+ {status?.backend.message && ( +

{status.backend.message}

+ )} +
+ + {layerStatuses.map((layerStatus) => ( +
+
+ {LAYER_LABELS[layerStatus.layer]} + +
+
+ logs {layerStatus.signals.logs} · traces {layerStatus.signals.traces} · metrics{' '} + {layerStatus.signals.metrics} +
+
+ ))} +
+ ) +} + +function buildChartData(series: AdminObservabilityMetricSeries[]) { + const timestamps = Array.from( + new Set(series.flatMap((item) => item.dataPoints.map((point) => point.timestamp))), + ).sort() + return timestamps.map((timestamp) => { + const row: Record = { timestamp } + for (const [index, item] of series.entries()) { + row[`series_${index}`] = item.dataPoints.find((point) => point.timestamp === timestamp)?.value ?? null + } + return row + }) +} + +function MetricsView({ + series, + isLoading, + error, +}: { + series?: AdminObservabilityMetricSeries[] + isLoading: boolean + error: unknown +}) { + const visibleSeries = useMemo(() => (series ?? []).slice(0, 5), [series]) + const chartData = useMemo(() => buildChartData(visibleSeries), [visibleSeries]) + + if (error) return + if (isLoading) return + if (!series?.length) return + + return ( +
+
+ + + + formatTimestamp(String(value)).slice(11, 19)} + tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 11 }} + /> + + + {visibleSeries.map((item, index) => { + return ( + + ) + })} + + +
+
+ {series.slice(0, 8).map((item) => ( +
+
{item.metricName}
+
+ {item.layer ? LAYER_LABELS[item.layer] : 'Unknown layer'} · {item.dataPoints.length} points +
+
+ ))} +
+
+ ) +} + +function LogsView({ + logs, + total, + isLoading, + error, + onDiagnoseTrace, + onDiagnoseExecution, + onDiagnoseJob, + onDiagnoseRequest, +}: { + logs?: AdminObservabilityLogEntry[] + total?: number + isLoading: boolean + error: unknown + onDiagnoseTrace?: (traceId: string) => void + onDiagnoseExecution?: (executionId: string, traceId?: string) => void + onDiagnoseJob?: (jobId: string, traceId?: string) => void + onDiagnoseRequest?: (requestId: string, traceId?: string) => void +}) { + if (error) return + if (isLoading) return + if (!logs?.length) return + + return ( +
+ + + + Time + Layer + Severity + Service + Message + Trace + Diagnose + + + + {logs.map((log, index) => { + const executionId = readAttribute(log.logAttributes, [ + 'boxlite.execution_id', + 'execution_id', + 'execution.id', + ]) + const jobId = readAttribute(log.logAttributes, ['boxlite.job_id', 'job_id', 'job.id']) + const requestId = readAttribute(log.logAttributes, ['boxlite.request_id', 'request_id', 'request.id']) + const diagnose = executionId + ? () => onDiagnoseExecution?.(executionId, log.traceId) + : jobId + ? () => onDiagnoseJob?.(jobId, log.traceId) + : requestId + ? () => onDiagnoseRequest?.(requestId, log.traceId) + : log.traceId + ? () => onDiagnoseTrace?.(log.traceId as string) + : undefined + + return ( + + {formatTimestamp(log.timestamp)} + {getLayerFromAttributes(log.resourceAttributes)} + + + + {log.serviceName} + {log.body} + + {log.traceId && } + + + + + + ) + })} + +
+ {total !== undefined && ( +
{total} total logs
+ )} +
+ ) +} + +function TracesView({ + traces, + total, + spans, + selectedTraceId, + onSelectTrace, + onDiagnoseTrace, + isLoading, + spansLoading, + error, +}: { + traces?: AdminObservabilityTraceSummary[] + total?: number + spans?: ReturnType + selectedTraceId: string | null + onSelectTrace: (traceId: string) => void + onDiagnoseTrace?: (traceId: string) => void + isLoading: boolean + spansLoading: boolean + error: unknown +}) { + if (error) return + if (isLoading) return + if (!traces?.length) return + + return ( +
+
+ + + + Trace + Root span + Start + Duration + Spans + Diagnose + + + + {traces.map((trace) => ( + onSelectTrace(trace.traceId)} + > + + {truncateMiddle(trace.traceId)} + + + {trace.rootSpanName} + {formatTimestamp(trace.startTime)} + {formatDuration(trace.durationMs)} + {trace.spanCount} + + + + + ))} + +
+ {total !== undefined && ( +
{total} total traces
+ )} +
+ +
+
Trace spans
+ {!selectedTraceId ? ( +
Select a trace to inspect its waterfall.
+ ) : spansLoading ? ( + + ) : !spans?.length ? ( +
No spans found for this trace.
+ ) : ( + +
+ {spans.map((span) => ( +
+
+ {span.spanName} + {formatDuration(span.durationMs)} +
+
+
+
+
+ ))} +
+ + )} +
+
+ ) +} + +function InvestigateView({ + data, + isLoading, + error, +}: { + data: ReturnType['data'] + isLoading: boolean + error: unknown +}) { + if (error) return + if (isLoading) return + if (!data) return + + const correlationEntries = Object.entries(data.correlation).filter( + ([, values]) => Array.isArray(values) && values.length > 0, + ) + + return ( +
+
+ {data.sources.map((source) => ( +
+
+ {source.source} + {source.state.replace('_', ' ')} +
+
+ {source.count ?? 0} item(s) + {source.message ? ` · ${source.message}` : ''} +
+
+ ))} +
+ +
+
+
Trace spans
+
{data.traceSpans.length}
+
+
+
Logs
+
{data.logs.length}
+
+
+
Audit
+
{data.auditLogs.length}
+
+
+
xLog
+
{data.xlogs.length}
+
+
+
S3 objects
+
{data.s3Objects.length}
+
+
+ +
+
Correlation IDs
+ {correlationEntries.length === 0 ? ( +

No correlated platform identifiers discovered yet.

+ ) : ( +
+ {correlationEntries.map(([key, values]) => ( +
+ {key} + + {(values as string[]).map((value) => truncateMiddle(value)).join(', ')} + +
+ ))} +
+ )} +
+
+ ) +} + +const AdminTelemetryPanel: React.FC = ({ + title = 'Telemetry', + description, + preset = {}, + compact, + className, + onDiagnoseTrace, + onDiagnoseExecution, + onDiagnoseJob, + onDiagnoseRequest, +}) => { + const [activeTab, setActiveTab] = useState('metrics') + const [dateRange, setDateRange] = useState(() => createDefaultRange()) + const [searchInput, setSearchInput] = useState('') + const [search, setSearch] = useState('') + const [selectedTraceId, setSelectedTraceId] = useState(null) + + const range = getCompleteRange(dateRange) + const baseParams: AdminObservabilityBaseParams = { + from: range.from, + to: range.to, + page: 1, + limit: PAGE_LIMIT, + layer: 'all', + ...preset, + } + + const statusQuery = useAdminObservabilityStatus() + const logsQuery = useAdminObservabilityLogs( + { ...baseParams, search: search || undefined }, + { enabled: activeTab === 'logs' }, + ) + const tracesQuery = useAdminObservabilityTraces(baseParams, { enabled: activeTab === 'traces' }) + const metricsQuery = useAdminObservabilityMetrics(baseParams, { enabled: activeTab === 'metrics' }) + const traceSpansQuery = useAdminObservabilityTraceSpans(selectedTraceId ?? undefined, baseParams, { + enabled: activeTab === 'traces' && !!selectedTraceId, + }) + const investigateQuery = useAdminObservabilityInvestigate( + { ...baseParams, traceId: selectedTraceId ?? preset.traceId }, + { enabled: activeTab === 'investigate' }, + ) + + const waterfallRows = useMemo(() => buildTraceWaterfallRows(traceSpansQuery.data), [traceSpansQuery.data]) + + const refreshActive = () => { + statusQuery.refetch() + if (activeTab === 'logs') logsQuery.refetch() + if (activeTab === 'traces') { + tracesQuery.refetch() + if (selectedTraceId) traceSpansQuery.refetch() + } + if (activeTab === 'metrics') metricsQuery.refetch() + if (activeTab === 'investigate') investigateQuery.refetch() + } + + const runLogSearch = () => { + setSearch(searchInput.trim()) + } + + return ( +
+
+
+
+

{title}

+ {description &&

{description}

} +
+
+ + +
+
+ +
+ + + + + Metrics + + + Logs + + + Traces + + + Investigate + + + + + + + + +
+ setSearchInput(event.target.value)} + onKeyDown={(event) => event.key === 'Enter' && runLogSearch()} + placeholder="Search logs" + className="w-full sm:w-64" + /> + +
+ +
+ + + + + + + + +
+
+ ) +} + +export default AdminTelemetryPanel diff --git a/apps/dashboard/src/components/admin/adminDiagnoseTarget.spec.ts b/apps/dashboard/src/components/admin/adminDiagnoseTarget.spec.ts new file mode 100644 index 000000000..a513fd78b --- /dev/null +++ b/apps/dashboard/src/components/admin/adminDiagnoseTarget.spec.ts @@ -0,0 +1,65 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { describe, expect, it } from 'vitest' +import type { OwnerGroup } from './adminHelpers' +import { createOwnerGroupDiagnoseTarget } from './adminDiagnoseTarget' + +function ownerGroup(partial: Partial): OwnerGroup { + return { + organizationId: 'org-1', + owner: { + userId: 'user-1', + name: 'Brian Luo', + email: 'brian@example.com', + orgName: 'Brian Personal', + personal: true, + }, + boxes: [], + breakdown: [], + ...partial, + } +} + +describe('createOwnerGroupDiagnoseTarget', () => { + it('creates a user diagnosis target for personal owner groups with a user id', () => { + const target = createOwnerGroupDiagnoseTarget(ownerGroup({ boxes: [{ id: 'box-1' } as never] })) + + expect(target).toMatchObject({ + kind: 'user', + title: 'Diagnose user', + params: { + orgId: 'org-1', + userId: 'user-1', + }, + }) + expect(target.details).toEqual(expect.arrayContaining([expect.objectContaining({ label: 'user' })])) + }) + + it('creates an org diagnosis target for team owner groups', () => { + const target = createOwnerGroupDiagnoseTarget( + ownerGroup({ + organizationId: 'org-team', + owner: { + userId: 'owner-user-1', + name: 'Platform Team', + email: 'owner@example.com', + orgName: 'Platform Team', + personal: false, + }, + }), + ) + + expect(target).toMatchObject({ + kind: 'org', + title: 'Diagnose org', + params: { + orgId: 'org-team', + }, + }) + expect(target.params).not.toHaveProperty('userId') + expect(target.details).toEqual(expect.arrayContaining([expect.objectContaining({ label: 'org' })])) + }) +}) diff --git a/apps/dashboard/src/components/admin/adminDiagnoseTarget.ts b/apps/dashboard/src/components/admin/adminDiagnoseTarget.ts new file mode 100644 index 000000000..ccf717623 --- /dev/null +++ b/apps/dashboard/src/components/admin/adminDiagnoseTarget.ts @@ -0,0 +1,166 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import type { AdminObservabilityBaseParams } from '@/hooks/useAdminObservability' +import type { AdminBox, AdminMachine, AdminRunner, OwnerGroup } from './adminHelpers' + +export type AdminDiagnoseTargetKind = + | 'box' + | 'runner' + | 'machine' + | 'trace' + | 'execution' + | 'job' + | 'request' + | 'org' + | 'user' + +export interface AdminDiagnoseDetail { + label: string + value: string + action?: { type: 'runner'; id: string } +} + +export interface AdminDiagnoseTarget { + kind: AdminDiagnoseTargetKind + title: string + subtitle: string + state?: string + details: AdminDiagnoseDetail[] + params: Partial> + box?: AdminBox + runner?: AdminRunner + machine?: AdminMachine +} + +export function createBoxDiagnoseTarget(box: AdminBox): AdminDiagnoseTarget { + return { + kind: 'box', + title: 'Diagnose box', + subtitle: box.id, + state: box.state, + box, + params: { + orgId: box.organizationId, + boxId: box.id, + runnerId: box.runnerId ?? undefined, + }, + details: [ + { label: 'owner', value: box.owner.name }, + { label: 'org', value: box.owner.personal ? 'personal' : box.owner.orgName }, + box.runnerId + ? { label: 'runner', value: box.runnerId, action: { type: 'runner', id: box.runnerId } } + : { label: 'runner', value: 'none' }, + { label: 'specs', value: `${box.cpu}c / ${box.memoryGiB}G` }, + { label: 'created', value: new Date(box.createdAt).toLocaleString() }, + ], + } +} + +export function createOwnerGroupDiagnoseTarget(group: OwnerGroup): AdminDiagnoseTarget { + const isUserTarget = group.owner.personal && Boolean(group.owner.userId) + const targetKind: AdminDiagnoseTargetKind = isUserTarget ? 'user' : 'org' + const params: AdminDiagnoseTarget['params'] = { + orgId: group.organizationId, + ...(isUserTarget ? { userId: group.owner.userId } : {}), + } + + return { + kind: targetKind, + title: isUserTarget ? 'Diagnose user' : 'Diagnose org', + subtitle: isUserTarget ? `${group.owner.name} · ${group.organizationId}` : group.owner.orgName, + state: `${group.boxes.length} box${group.boxes.length === 1 ? '' : 'es'}`, + params, + details: [ + { label: isUserTarget ? 'user' : 'org', value: group.owner.name }, + { label: 'email', value: group.owner.email || 'none' }, + { label: 'orgId', value: group.organizationId }, + { label: 'boxes', value: String(group.boxes.length) }, + ], + } +} + +export function createRunnerDiagnoseTarget(runner: AdminRunner): AdminDiagnoseTarget { + return { + kind: 'runner', + title: 'Diagnose runner', + subtitle: runner.id, + state: runner.draining ? 'draining' : runner.unschedulable ? 'cordoned' : runner.state, + runner, + params: { + runnerId: runner.id, + machineId: runner.id, + }, + details: [ + { label: 'runner', value: runner.id }, + { label: 'state', value: runner.state }, + { label: 'scheduling', value: runner.unschedulable ? 'cordoned' : runner.draining ? 'draining' : 'accepting' }, + { label: 'boxes', value: String(runner.currentStartedBoxes) }, + { label: 'cpu alloc', value: `${runner.currentAllocatedCpu}/${runner.cpu}` }, + { label: 'mem alloc', value: `${runner.currentAllocatedMemoryGiB.toFixed(1)}/${runner.memory.toFixed(1)} GiB` }, + ], + } +} + +export function createMachineDiagnoseTarget(machine: AdminMachine): AdminDiagnoseTarget { + return { + kind: 'machine', + title: 'Diagnose machine', + subtitle: machine.host, + machine, + params: { + machineId: machine.host, + runnerId: machine.host, + }, + details: [ + { label: 'host', value: machine.host }, + { label: 'region', value: machine.region }, + { label: 'boxes', value: String(machine.boxes) }, + { label: 'oversell cpu', value: `${machine.oversellCpu.toFixed(1)}x` }, + { label: 'cpu waterline', value: `${machine.cpuWaterline.toFixed(1)}%` }, + { label: 'mem waterline', value: `${machine.memWaterline.toFixed(1)}%` }, + ], + } +} + +export function createTraceDiagnoseTarget(traceId: string): AdminDiagnoseTarget { + return { + kind: 'trace', + title: 'Diagnose trace', + subtitle: traceId, + params: { traceId }, + details: [{ label: 'traceId', value: traceId }], + } +} + +export function createExecutionDiagnoseTarget(executionId: string, traceId?: string): AdminDiagnoseTarget { + return { + kind: 'execution', + title: 'Diagnose execution', + subtitle: executionId, + params: { executionId, traceId }, + details: [{ label: 'executionId', value: executionId }, ...(traceId ? [{ label: 'traceId', value: traceId }] : [])], + } +} + +export function createJobDiagnoseTarget(jobId: string, traceId?: string): AdminDiagnoseTarget { + return { + kind: 'job', + title: 'Diagnose job', + subtitle: jobId, + params: { jobId, traceId }, + details: [{ label: 'jobId', value: jobId }, ...(traceId ? [{ label: 'traceId', value: traceId }] : [])], + } +} + +export function createRequestDiagnoseTarget(requestId: string, traceId?: string): AdminDiagnoseTarget { + return { + kind: 'request', + title: 'Diagnose request', + subtitle: requestId, + params: { requestId, traceId }, + details: [{ label: 'requestId', value: requestId }, ...(traceId ? [{ label: 'traceId', value: traceId }] : [])], + } +} diff --git a/apps/dashboard/src/components/admin/adminHelpers.spec.ts b/apps/dashboard/src/components/admin/adminHelpers.spec.ts new file mode 100644 index 000000000..aee22feeb --- /dev/null +++ b/apps/dashboard/src/components/admin/adminHelpers.spec.ts @@ -0,0 +1,220 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { describe, expect, it } from 'vitest' +import { + type AdminBox, + type AdminRunner, + filterOwnerGroups, + getBoxBreakdown, + groupBoxesByOwner, + isErrorState, + isOnlineRunner, + runnerCpuPercent, + selectErroringOwners, + stateBadgeVariant, + findBoxById, +} from './adminHelpers' + +function box(partial: Partial & Pick): AdminBox { + return { + runnerId: 'rnr-1', + cpu: 1, + memoryGiB: 1, + createdAt: '2026-05-20T00:00:00.000Z', + owner: { name: 'Owner', email: 'owner@x.io', orgName: 'Org', personal: true }, + ...partial, + } +} + +function runner(partial: Partial & Pick): AdminRunner { + return { + cpu: 8, + memory: 8, + currentAllocatedCpu: 0, + currentAllocatedMemoryGiB: 0, + currentStartedBoxes: 0, + availabilityScore: 1, + draining: false, + unschedulable: false, + ...partial, + } +} + +describe('stateBadgeVariant', () => { + it('maps healthy states to success', () => { + expect(stateBadgeVariant('started')).toBe('success') + expect(stateBadgeVariant('RUNNING')).toBe('success') + expect(stateBadgeVariant('ready')).toBe('success') + }) + it('maps failure states to destructive', () => { + expect(stateBadgeVariant('error')).toBe('destructive') + expect(stateBadgeVariant('build_failed')).toBe('destructive') + }) + it('maps transitional states to warning', () => { + expect(stateBadgeVariant('draining')).toBe('warning') + expect(stateBadgeVariant('stopping')).toBe('warning') + }) + it('falls back to secondary for unknown / idle states', () => { + expect(stateBadgeVariant('stopped')).toBe('secondary') + expect(stateBadgeVariant('whatever')).toBe('secondary') + }) +}) + +describe('isErrorState', () => { + it('treats error and build_failed as error', () => { + expect(isErrorState('error')).toBe(true) + expect(isErrorState('build_failed')).toBe(true) + expect(isErrorState('started')).toBe(false) + }) +}) + +describe('getBoxBreakdown', () => { + it('counts by state in a stable order and folds the remainder into "other"', () => { + const boxes = [ + box({ id: 'a', organizationId: 'o', state: 'started' }), + box({ id: 'b', organizationId: 'o', state: 'started' }), + box({ id: 'c', organizationId: 'o', state: 'error' }), + box({ id: 'd', organizationId: 'o', state: 'build_failed' }), + box({ id: 'e', organizationId: 'o', state: 'archived' }), // unknown → other + ] + const breakdown = getBoxBreakdown(boxes) + expect(breakdown.map((s) => [s.key, s.count])).toEqual([ + ['started', 2], + ['error', 1], + ['build_failed', 1], + ['other', 1], + ]) + }) + it('omits zero-count segments', () => { + const breakdown = getBoxBreakdown([box({ id: 'a', organizationId: 'o', state: 'started' })]) + expect(breakdown.every((s) => s.count > 0)).toBe(true) + expect(breakdown.find((s) => s.key === 'error')).toBeUndefined() + }) +}) + +describe('groupBoxesByOwner', () => { + const boxes = [ + box({ id: 'b1', organizationId: 'org-z', state: 'started', owner: ownerOf('Zoe') }), + box({ id: 'b2', organizationId: 'org-a', state: 'error', owner: ownerOf('Adam') }), + box({ id: 'b3', organizationId: 'org-a', state: 'started', owner: ownerOf('Adam') }), + ] + function ownerOf(name: string): AdminBox['owner'] { + return { name, email: `${name.toLowerCase()}@x.io`, orgName: name, personal: true } + } + + it('groups boxes by organizationId', () => { + const groups = groupBoxesByOwner(boxes) + const adam = groups.find((g) => g.organizationId === 'org-a') + expect(adam?.boxes.map((b) => b.id).sort()).toEqual(['b2', 'b3']) + }) + it('sorts groups by owner name', () => { + const groups = groupBoxesByOwner(boxes) + expect(groups.map((g) => g.owner.name)).toEqual(['Adam', 'Zoe']) + }) + it('attaches a per-owner breakdown', () => { + const groups = groupBoxesByOwner(boxes) + const adam = groups.find((g) => g.organizationId === 'org-a') + expect(adam?.breakdown.find((s) => s.key === 'error')?.count).toBe(1) + }) +}) + +describe('isOnlineRunner / runnerCpuPercent', () => { + it('only READY runners are online', () => { + expect(isOnlineRunner(runner({ id: 'r', state: 'ready' }))).toBe(true) + expect(isOnlineRunner(runner({ id: 'r', state: 'unresponsive' }))).toBe(false) + }) + it('computes allocated CPU ratio', () => { + expect(runnerCpuPercent(runner({ id: 'r', state: 'ready', cpu: 8, currentAllocatedCpu: 6 }))).toBeCloseTo(0.75) + }) + it('guards divide-by-zero capacity', () => { + expect(runnerCpuPercent(runner({ id: 'r', state: 'error', cpu: 0, currentAllocatedCpu: 0 }))).toBe(0) + }) +}) + +describe('filterOwnerGroups', () => { + const groups = groupBoxesByOwner([ + box({ + id: 'box-aaa', + organizationId: 'org-brian', + state: 'started', + owner: { name: 'Brian Luo', email: 'brian@x.io', orgName: 'Brian', personal: true }, + }), + box({ + id: 'box-bbb', + organizationId: 'org-hao', + state: 'error', + owner: { name: 'Hao Luo', email: 'hao@x.io', orgName: 'Hao', personal: true }, + }), + ]) + + it('returns all groups for an empty query', () => { + expect(filterOwnerGroups(groups, '').length).toBe(2) + }) + it('matches an owner by name (case-insensitive) and keeps all their boxes', () => { + const res = filterOwnerGroups(groups, 'BRIAN') + expect(res.length).toBe(1) + expect(res[0].owner.name).toBe('Brian Luo') + expect(res[0].boxes.length).toBe(1) + }) + it('matches a box by id and narrows the group to the matching box', () => { + const res = filterOwnerGroups(groups, 'box-bbb') + expect(res.length).toBe(1) + expect(res[0].boxes.map((b) => b.id)).toEqual(['box-bbb']) + }) + it('returns nothing when neither owner nor box matches', () => { + expect(filterOwnerGroups(groups, 'zzz-nope').length).toBe(0) + }) +}) + +describe('selectErroringOwners', () => { + it('keeps only owners with error/build_failed boxes, ranked by error count desc', () => { + const groups = groupBoxesByOwner([ + box({ + id: 'a1', + organizationId: 'org-a', + state: 'error', + owner: { name: 'A', email: 'a@x.io', orgName: 'A', personal: true }, + }), + box({ + id: 'a2', + organizationId: 'org-a', + state: 'build_failed', + owner: { name: 'A', email: 'a@x.io', orgName: 'A', personal: true }, + }), + box({ + id: 'b1', + organizationId: 'org-b', + state: 'error', + owner: { name: 'B', email: 'b@x.io', orgName: 'B', personal: true }, + }), + box({ + id: 'c1', + organizationId: 'org-c', + state: 'started', + owner: { name: 'C', email: 'c@x.io', orgName: 'C', personal: true }, + }), + ]) + const ranked = selectErroringOwners(groups) + expect(ranked.map((r) => r.group.owner.name)).toEqual(['A', 'B']) + expect(ranked[0].errorBoxes.length).toBe(2) + }) +}) + +describe('findBoxById', () => { + it('finds exact box ids so pasted Box IDs can open telemetry', () => { + const groups = groupBoxesByOwner([ + box({ + id: 'aB3cD4eF5gH6', + organizationId: 'org-a', + state: 'error', + owner: { name: 'Brian Luo', email: 'brian@x.io', orgName: 'Brian', personal: true }, + }), + ]) + + expect(findBoxById(groups, ' aB3cD4eF5gH6 ')?.box.state).toBe('error') + expect(findBoxById(groups, 'ab3cd4ef5gh6')).toBeUndefined() + }) +}) diff --git a/apps/dashboard/src/components/admin/adminHelpers.ts b/apps/dashboard/src/components/admin/adminHelpers.ts new file mode 100644 index 000000000..439a38fd2 --- /dev/null +++ b/apps/dashboard/src/components/admin/adminHelpers.ts @@ -0,0 +1,228 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +// ─── admin-only API shapes (not in the generated api-client) ────────────────── + +export interface AdminOverview { + users: number + activeBoxes: number + boxes: { total: number; byState: Record } + runners: { online: number; total: number; draining: number } + cluster: { cpuUtil: number; oversell: number } +} + +export interface AdminUser { + id: string + email: string + name: string + role: string +} + +export interface AdminBoxOwner { + userId?: string + name: string + email: string + orgName: string + personal: boolean +} + +export interface AdminBox { + id: string + organizationId: string + state: string + runnerId: string | null + cpu: number + memoryGiB: number + createdAt: string + owner: AdminBoxOwner +} + +export interface AdminRunner { + id: string + state: string + cpu: number + memory: number + currentAllocatedCpu: number + currentAllocatedMemoryGiB: number + currentStartedBoxes: number + availabilityScore: number + draining: boolean + unschedulable: boolean +} + +export interface AdminMachine { + host: string + region: string + oversellCpu: number + cpuWaterline: number + memWaterline: number + boxes: number +} + +// ─── state → visual ─────────────────────────────────────────────────────────── + +export type StateVariant = 'success' | 'destructive' | 'warning' | 'secondary' + +export function stateBadgeVariant(state: string): StateVariant { + const lower = state?.toLowerCase() ?? '' + if (lower === 'running' || lower === 'online' || lower === 'started' || lower === 'ready') { + return 'success' + } + if (lower === 'error' || lower === 'failed' || lower === 'build_failed') { + return 'destructive' + } + if (lower === 'draining' || lower === 'stopping') { + return 'warning' + } + return 'secondary' +} + +export function isErrorState(state: string): boolean { + const lower = state?.toLowerCase() ?? '' + return lower === 'error' || lower === 'failed' || lower === 'build_failed' +} + +// Balanced status palette — shared with the box-breakdown bars (kept in sync with +// the dashboard's existing admin breakdown colors so old and new views match). +export const BOX_STATE_COLORS: Record = { + started: '#5aac7b', + error: '#dd7d70', + build_failed: '#d6a84f', + stopped: '#838b97', + other: '#444a54', +} + +// ─── box breakdown ────────────────────────────────────────────────────────── + +export interface BreakdownSegment { + key: string + label: string + count: number + color: string +} + +const BREAKDOWN_ORDER: { key: string; label: string }[] = [ + { key: 'started', label: 'started' }, + { key: 'error', label: 'error' }, + { key: 'build_failed', label: 'build failed' }, + { key: 'stopped', label: 'stopped' }, +] + +export function getBoxBreakdown(boxes: AdminBox[]): BreakdownSegment[] { + const counts = boxes.reduce>((acc, b) => { + acc[b.state] = (acc[b.state] ?? 0) + 1 + return acc + }, {}) + + const known = BREAKDOWN_ORDER.map(({ key, label }) => ({ + key, + label, + count: counts[key] ?? 0, + color: BOX_STATE_COLORS[key] ?? BOX_STATE_COLORS.other, + })) + const knownTotal = known.reduce((sum, s) => sum + s.count, 0) + const other = Math.max(boxes.length - knownTotal, 0) + + return [...known, { key: 'other', label: 'other', count: other, color: BOX_STATE_COLORS.other }].filter( + (s) => s.count > 0, + ) +} + +// ─── owner grouping ─────────────────────────────────────────────────────────── + +export interface OwnerGroup { + organizationId: string + owner: AdminBoxOwner + boxes: AdminBox[] + breakdown: BreakdownSegment[] +} + +export function groupBoxesByOwner(boxes: AdminBox[]): OwnerGroup[] { + const groups = new Map() + + for (const b of boxes) { + const existing = groups.get(b.organizationId) + if (existing) { + existing.boxes.push(b) + } else { + groups.set(b.organizationId, { owner: b.owner, boxes: [b] }) + } + } + + return Array.from(groups.entries()) + .map(([organizationId, group]) => ({ + organizationId, + owner: group.owner, + boxes: group.boxes, + breakdown: getBoxBreakdown(group.boxes), + })) + .sort((a, b) => a.owner.name.localeCompare(b.owner.name)) +} + +export function getBoxRollupText(boxes: AdminBox[]): string { + const counts = boxes.reduce>((acc, b) => { + acc[b.state] = (acc[b.state] ?? 0) + 1 + return acc + }, {}) + const parts = [ + { label: 'started', count: counts.started ?? 0 }, + { label: 'error', count: counts.error ?? 0 }, + { label: 'build failed', count: counts.build_failed ?? 0 }, + ].filter((p) => p.count > 0) + + return parts.length > 0 ? parts.map((p) => `${p.count} ${p.label}`).join(' · ') : 'idle' +} + +// ─── runners ────────────────────────────────────────────────────────────────── + +export function isOnlineRunner(runner: AdminRunner): boolean { + return runner.state?.toLowerCase() === 'ready' +} + +export function runnerCpuPercent(runner: AdminRunner): number { + return runner.cpu > 0 ? runner.currentAllocatedCpu / runner.cpu : 0 +} + +// ─── search & attention selectors ───────────────────────────────────────────── + +export function filterOwnerGroups(groups: OwnerGroup[], query: string): OwnerGroup[] { + const q = query.trim().toLowerCase() + if (!q) return groups + + const result: OwnerGroup[] = [] + for (const group of groups) { + const ownerMatches = group.owner.name.toLowerCase().includes(q) || group.owner.email.toLowerCase().includes(q) + if (ownerMatches) { + result.push(group) + continue + } + const matchingBoxes = group.boxes.filter((b) => b.id.toLowerCase().includes(q)) + if (matchingBoxes.length > 0) { + result.push({ ...group, boxes: matchingBoxes, breakdown: getBoxBreakdown(matchingBoxes) }) + } + } + return result +} + +export interface ErroringOwner { + group: OwnerGroup + errorBoxes: AdminBox[] +} + +export function selectErroringOwners(groups: OwnerGroup[]): ErroringOwner[] { + return groups + .map((group) => ({ group, errorBoxes: group.boxes.filter((b) => isErrorState(b.state)) })) + .filter((x) => x.errorBoxes.length > 0) + .sort((a, b) => b.errorBoxes.length - a.errorBoxes.length) +} + +export function findBoxById(groups: OwnerGroup[], boxId: string): { box: AdminBox; group: OwnerGroup } | undefined { + const targetBoxId = boxId.trim() + for (const group of groups) { + const box = group.boxes.find((b) => b.id === targetBoxId) + if (box) return { box, group } + } + return undefined +} diff --git a/apps/dashboard/src/components/admin/adminNavigation.spec.ts b/apps/dashboard/src/components/admin/adminNavigation.spec.ts new file mode 100644 index 000000000..7d022e907 --- /dev/null +++ b/apps/dashboard/src/components/admin/adminNavigation.spec.ts @@ -0,0 +1,19 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { describe, expect, it } from 'vitest' +import { ADMIN_VIEWS, adminViewFromParam } from './adminNavigation' + +describe('admin navigation scope', () => { + it('keeps platform telemetry inside overview instead of a top-level admin view', () => { + expect(ADMIN_VIEWS.map((view) => view.label)).toEqual(['Overview', 'People & Boxes', 'Fleet']) + }) + + it('parses admin view query params safely', () => { + expect(adminViewFromParam('platformTelemetry')).toBeNull() + expect(adminViewFromParam('unknown')).toBeNull() + expect(adminViewFromParam(null)).toBeNull() + }) +}) diff --git a/apps/dashboard/src/components/admin/adminNavigation.ts b/apps/dashboard/src/components/admin/adminNavigation.ts new file mode 100644 index 000000000..bd8dac061 --- /dev/null +++ b/apps/dashboard/src/components/admin/adminNavigation.ts @@ -0,0 +1,16 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +export type AdminView = 'overview' | 'people' | 'fleet' + +export const ADMIN_VIEWS: { id: AdminView; label: string }[] = [ + { id: 'overview', label: 'Overview' }, + { id: 'people', label: 'People & Boxes' }, + { id: 'fleet', label: 'Fleet' }, +] + +export function adminViewFromParam(value: string | null): AdminView | null { + return ADMIN_VIEWS.some((view) => view.id === value) ? (value as AdminView) : null +} diff --git a/apps/dashboard/src/components/admin/useAdminData.ts b/apps/dashboard/src/components/admin/useAdminData.ts new file mode 100644 index 000000000..df4186d01 --- /dev/null +++ b/apps/dashboard/src/components/admin/useAdminData.ts @@ -0,0 +1,94 @@ +/* + * Modified by BoxLite AI, 2025-2026 + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { useApi } from '@/hooks/useApi' +import { handleApiError } from '@/lib/error-handling' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' +import type { AdminBox, AdminMachine, AdminOverview, AdminRunner, AdminUser } from './adminHelpers' + +const ADMIN_UI_HEADERS = { 'X-BoxLite-Source': 'ui' } as const + +export function useAdminOverview() { + const { axiosInstance } = useApi() + return useQuery({ + queryKey: ['admin', 'overview'], + queryFn: () => axiosInstance.get('/admin/overview', { headers: ADMIN_UI_HEADERS }).then((r) => r.data), + retry: false, + }) +} + +export function useAdminUsers() { + const { axiosInstance } = useApi() + return useQuery({ + queryKey: ['admin', 'users'], + queryFn: () => axiosInstance.get('/admin/overview/users', { headers: ADMIN_UI_HEADERS }).then((r) => r.data), + }) +} + +export function useAdminBoxes() { + const { axiosInstance } = useApi() + return useQuery({ + queryKey: ['admin', 'boxes'], + queryFn: () => axiosInstance.get('/admin/overview/boxes', { headers: ADMIN_UI_HEADERS }).then((r) => r.data), + }) +} + +export function useAdminRunners() { + const { axiosInstance } = useApi() + return useQuery({ + queryKey: ['admin', 'runners'], + queryFn: () => axiosInstance.get('/admin/overview/runners', { headers: ADMIN_UI_HEADERS }).then((r) => r.data), + }) +} + +export function useAdminMachines() { + const { axiosInstance } = useApi() + return useQuery({ + queryKey: ['admin', 'machines'], + queryFn: () => axiosInstance.get('/admin/overview/machines', { headers: ADMIN_UI_HEADERS }).then((r) => r.data), + }) +} + +export function useAdminActions() { + const { axiosInstance } = useApi() + const queryClient = useQueryClient() + + const cordon = useMutation({ + mutationFn: (runner: AdminRunner) => + axiosInstance.patch( + `/admin/runners/${runner.id}/scheduling`, + { unschedulable: !runner.unschedulable }, + { headers: ADMIN_UI_HEADERS }, + ), + onSuccess: (_data, runner) => { + toast.success(runner.unschedulable ? 'Runner un-cordoned' : 'Runner cordoned') + queryClient.invalidateQueries({ queryKey: ['admin', 'runners'] }) + }, + onError: (error) => handleApiError(error, 'Failed to update runner scheduling'), + }) + + const drain = useMutation({ + mutationFn: (runnerId: string) => + axiosInstance.patch(`/admin/runners/${runnerId}/draining`, { draining: true }, { headers: ADMIN_UI_HEADERS }), + onSuccess: () => { + toast.success('Runner draining') + queryClient.invalidateQueries({ queryKey: ['admin', 'runners'] }) + }, + onError: (error) => handleApiError(error, 'Failed to drain runner'), + }) + + const recover = useMutation({ + mutationFn: (boxId: string) => + axiosInstance.post(`/admin/box/${boxId}/recover`, undefined, { headers: ADMIN_UI_HEADERS }), + onSuccess: () => { + toast.success('Box recovery initiated') + queryClient.invalidateQueries({ queryKey: ['admin', 'boxes'] }) + }, + onError: (error) => handleApiError(error, 'Failed to recover box'), + }) + + return { cordon, drain, recover } +} diff --git a/apps/dashboard/src/components/boxes/BoxContentTabs.tsx b/apps/dashboard/src/components/boxes/BoxContentTabs.tsx new file mode 100644 index 000000000..737ddf95d --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxContentTabs.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Skeleton } from '@/components/ui/skeleton' +import { Spinner } from '@/components/ui/spinner' +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' +import { getBoxContentTabs } from '@/lib/dashboard-features' +import { Box } from '@boxlite-ai/api-client' +import { BoxLogsTab } from './BoxLogsTab' +import { BoxMetricsTab } from './BoxMetricsTab' +import { BoxSpendingTab } from './BoxSpendingTab' +import { BoxTracesTab } from './BoxTracesTab' +import { TabValue } from './SearchParams' + +interface BoxContentTabsProps { + box: Box | undefined + isLoading: boolean + experimentsEnabled: boolean | undefined + tab: TabValue + onTabChange: (tab: TabValue) => void +} + +// Bounded surface so observability tabs render with real height inside the +// detail page's centered scrolling column. +const TAB_SHELL = + 'flex flex-col h-[60vh] min-h-[440px] gap-0 overflow-hidden rounded-xl border border-border/60 bg-card shadow-card' + +export function BoxContentTabs({ box, isLoading, experimentsEnabled, tab, onTabChange }: BoxContentTabsProps) { + const availableTabs = getBoxContentTabs({ experimentsEnabled }) + + if (isLoading) { + return ( +
+
+ + + + + +
+
+ +
+
+ ) + } + + if (!box) return null + + if (!experimentsEnabled) { + return null + } + + return ( + onTabChange(v as TabValue)} className={TAB_SHELL}> + + {experimentsEnabled && + availableTabs.some((value) => ['logs', 'traces', 'metrics', 'spending'].includes(value)) && ( + <> + Logs + Traces + Metrics + Spending + + )} + + + {experimentsEnabled && ( + <> + + + + + + + + + + + + + + )} + + ) +} diff --git a/apps/dashboard/src/components/boxes/BoxDetails.tsx b/apps/dashboard/src/components/boxes/BoxDetails.tsx new file mode 100644 index 000000000..4584c9848 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxDetails.tsx @@ -0,0 +1,485 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { OrganizationSuspendedError } from '@/api/errors' +import { OnboardingGuideDialog } from '@/components/OnboardingGuideDialog' +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog' +import { Button } from '@/components/ui/button' +import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' +import { LocalStorageKey } from '@/enums/LocalStorageKey' +import { RoutePath } from '@/enums/RoutePath' +import { useDeleteBoxMutation } from '@/hooks/mutations/useDeleteBoxMutation' +import { useRecoverBoxMutation } from '@/hooks/mutations/useRecoverBoxMutation' +import { useStartBoxMutation } from '@/hooks/mutations/useStartBoxMutation' +import { useStopBoxMutation } from '@/hooks/mutations/useStopBoxMutation' +import { useBoxQuery } from '@/hooks/queries/useBoxQuery' +import { useConfig } from '@/hooks/useConfig' +import { useRegions } from '@/hooks/useRegions' +import { useBoxWsSync } from '@/hooks/useBoxWsSync' +import { useSelectedOrganization } from '@/hooks/useSelectedOrganization' +import { getBoxPublicId, getBoxPublicIdLabel } from '@/lib/box-identity' +import { handleApiError } from '@/lib/error-handling' +import { setLocalStorageItem } from '@/lib/local-storage' +import { + ONBOARDING_ENTRY_HIGHLIGHT_EVENT, + mergeOnboardingProgress, + ONBOARDING_PROGRESS_EVENT, + readOnboardingProgress, + type OnboardingProgress, +} from '@/lib/onboarding-progress' +import { getRelativeTimeString } from '@/lib/utils' +import { isRecoverable, isStartable, isStoppable, isTransitioning } from '@/lib/utils/box' +import { Box, BoxState, OrganizationRolePermissionsEnum, OrganizationUserRoleEnum } from '@boxlite-ai/api-client' +import { isAxiosError } from 'axios' +import { Check, Container, Copy, MoreVertical, Pause, Play, RefreshCw, RotateCcw, Trash2 } from '@/components/ui/icon' +import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react' +import { useAuth } from 'react-oidc-context' +import { useNavigate, useParams, useSearchParams } from 'react-router-dom' +import { toast } from 'sonner' +import { BoxTerminalTab } from './BoxTerminalTab' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' + +const STATUS = { running: '#5ad67d', idle: '#e0b341', stopped: '#8C919C', error: '#e0564a', dim: '#5b616e' } as const + +function statusOf(box: Box): { label: string; color: string } { + switch (box.state) { + case BoxState.STARTED: + return { label: 'Running', color: STATUS.running } + case BoxState.STOPPED: + return { label: 'Stopped', color: STATUS.dim } + case BoxState.ERROR: + return { label: 'Error', color: STATUS.error } + case BoxState.CREATING: + case BoxState.STARTING: + case BoxState.RESTORING: + return { label: 'Starting', color: STATUS.idle } + case BoxState.STOPPING: + return { label: 'Stopping', color: STATUS.idle } + case BoxState.DESTROYING: + return { label: 'Deleting', color: STATUS.idle } + default: + return { label: (box.state ?? 'Unknown').replace(/^\w/, (c) => c.toUpperCase()), color: STATUS.dim } + } +} + +function SectionHeader({ title, right }: { title: string; right?: ReactNode }) { + return ( +
+ + {title} + + {right} +
+ ) +} + +function SpecRow({ label, children, title }: { label: string; children: ReactNode; title?: string }) { + return ( +
+ {label} + + {title ? ( + + + {children} + + {title} + + ) : ( + {children} + )} +
+ ) +} + +export default function BoxDetails() { + const { boxId } = useParams<{ boxId: string }>() + const navigate = useNavigate() + const [searchParams, setSearchParams] = useSearchParams() + const config = useConfig() + const { user } = useAuth() + const userId = user?.profile.sub + const { authenticatedUserOrganizationMember, selectedOrganization, authenticatedUserHasPermission } = + useSelectedOrganization() + const { getRegionName } = useRegions() + + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + const [showOnboardingDialog, setShowOnboardingDialog] = useState(false) + const [onboardingProgress, setOnboardingProgress] = useState(() => readOnboardingProgress(userId)) + const [copied, setCopied] = useState(false) + const refreshRef = useRef(null) + + const updateOnboardingProgress = useCallback( + (progress: OnboardingProgress) => { + setOnboardingProgress(mergeOnboardingProgress(userId, progress)) + }, + [userId], + ) + + useEffect(() => { + setOnboardingProgress(readOnboardingProgress(userId)) + }, [userId]) + + useEffect(() => { + const handleOnboardingProgress = (event: Event) => { + const progress = (event as CustomEvent).detail + setOnboardingProgress(progress ?? readOnboardingProgress(userId)) + } + window.addEventListener(ONBOARDING_PROGRESS_EVENT, handleOnboardingProgress) + return () => window.removeEventListener(ONBOARDING_PROGRESS_EVENT, handleOnboardingProgress) + }, [userId]) + + useEffect(() => { + if (!selectedOrganization || !user?.profile.sub) return + if (searchParams.get('onboarding') === '1') setShowOnboardingDialog(true) + }, [searchParams, selectedOrganization, user?.profile.sub]) + + const clearOnboardingUrlParam = useCallback(() => { + if (searchParams.get('onboarding') !== '1') return + const nextParams = new URLSearchParams(searchParams) + nextParams.delete('onboarding') + setSearchParams(nextParams, { replace: true }) + }, [searchParams, setSearchParams]) + + const closeOnboardingDialog = useCallback(() => { + if (userId) setLocalStorageItem(`${LocalStorageKey.SkipOnboardingPrefix}${userId}`, 'true') + setShowOnboardingDialog(false) + window.setTimeout(() => { + window.dispatchEvent(new Event(ONBOARDING_ENTRY_HIGHLIGHT_EVENT)) + clearOnboardingUrlParam() + }, 220) + }, [clearOnboardingUrlParam, userId]) + + const { data: box, isLoading, isError, error, refetch } = useBoxQuery(boxId ?? '') + const isNotFound = isError && isAxiosError(error.cause) && error.cause?.status === 404 + useBoxWsSync({ boxId }) + + useEffect(() => { + if (box && !onboardingProgress.boxCreated) { + updateOnboardingProgress({ boxCreated: true }) + } + }, [onboardingProgress.boxCreated, box, updateOnboardingProgress]) + + const startMutation = useStartBoxMutation() + const stopMutation = useStopBoxMutation() + const recoverMutation = useRecoverBoxMutation() + const deleteMutation = useDeleteBoxMutation() + + const writePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.WRITE_BOXES) + const deletePermitted = authenticatedUserHasPermission(OrganizationRolePermissionsEnum.DELETE_BOXES) + const transitioning = box ? isTransitioning(box) : false + const anyMutating = + startMutation.isPending || stopMutation.isPending || recoverMutation.isPending || deleteMutation.isPending + const actionsDisabled = anyMutating || transitioning + + const handleStart = async () => { + if (!box) return + try { + await startMutation.mutateAsync({ boxId: box.id, detailRef: boxId }) + toast.success('Box started') + } catch (error) { + handleApiError(error, 'Failed to start box', { + action: + error instanceof OrganizationSuspendedError && + config.billingApiUrl && + authenticatedUserOrganizationMember?.role === OrganizationUserRoleEnum.OWNER ? ( + + ) : undefined, + }) + } + } + + const handleStop = async () => { + if (!box) return + try { + await stopMutation.mutateAsync({ boxId: box.id, detailRef: boxId }) + toast.success('Box stopped') + } catch (error) { + handleApiError(error, 'Failed to stop box') + } + } + + const handleRecover = async () => { + if (!box) return + try { + await recoverMutation.mutateAsync({ boxId: box.id, detailRef: boxId }) + toast.success('Box recovery started') + } catch (error) { + handleApiError(error, 'Failed to recover box') + } + } + + const handleDelete = async () => { + if (!box) return + try { + await deleteMutation.mutateAsync({ boxId: box.id, detailRef: boxId }) + toast.success('Box deleted') + setDeleteDialogOpen(false) + navigate(RoutePath.BOXES) + } catch (error) { + handleApiError(error, 'Failed to delete box') + } + } + + const handleRefresh = () => { + refetch() + const el = refreshRef.current + if (el) { + el.style.animation = 'none' + void el.offsetWidth + el.style.animation = 'spin .6s ease' + } + } + + const copyId = () => { + const id = box ? getBoxPublicId(box) : null + if (!id) return + try { + navigator.clipboard?.writeText(id) + } catch { + /* clipboard may be unavailable */ + } + setCopied(true) + setTimeout(() => setCopied(false), 1300) + } + + return ( +
+ (isOpen ? setShowOnboardingDialog(true) : closeOnboardingDialog())} + onProgressChange={updateOnboardingProgress} + progress={onboardingProgress} + /> + + {/* breadcrumb */} +
+ + / + {box ? getBoxPublicIdLabel(box).toLowerCase() : boxId?.toLowerCase()} +
+ + {isNotFound ? ( +
+ +
Box not found
+
Are you sure you're in the right organization?
+ +
+ ) : isLoading || !box ? ( +
+ {isError ? ( + + ) : ( + <> + Loading box… + + )} +
+ ) : ( + <> + {/* identity strip */} +
+
+ + {getBoxPublicIdLabel(box)} + + + + {statusOf(box).label} + + {box.image && ( + + {box.image} + + )} +
+ +
+ {writePermitted && isRecoverable(box) && ( + + )} + {writePermitted && isStartable(box) && ( + + )} + {writePermitted && isStoppable(box) && ( + + )} + {writePermitted && isTransitioning(box) && !isRecoverable(box) && !isStartable(box) && !isStoppable(box) && ( + + )} + {deletePermitted && ( + + + + + + setDeleteDialogOpen(true)} + > + Delete + + + + )} + +
+
+ + {/* body */} +
+ {/* spec readout */} +
+ + + + {getBoxPublicIdLabel(box)} + + + + + {box.image ?? '—'} + + {(getRegionName(box.target) ?? box.target ?? '—').toUpperCase()} + + quota} + /> + {box.cpu} vcpu + {box.memory} gib + {box.disk} gib + + + {getRelativeTimeString(box.createdAt).relativeTimeString} + {getRelativeTimeString(box.updatedAt).relativeTimeString} + + {box.errorReason && ( + <> + +

+ {box.errorReason} +

+ + )} +
+ + {/* shell / terminal */} +
+
+ + + shell + + {getBoxPublicIdLabel(box)} + + +
+
+ +
+
+
+ + )} + + + + + Delete Box + + Are you sure you want to delete this box? This action cannot be undone. + + + + Cancel + + {deleteMutation.isPending ? 'Deleting...' : 'Delete'} + + + + +
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxFullscreenShell.tsx b/apps/dashboard/src/components/boxes/BoxFullscreenShell.tsx new file mode 100644 index 000000000..ab81636e6 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxFullscreenShell.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CopyButton } from '@/components/CopyButton' +import { Button } from '@/components/ui/button' +import { RoutePath } from '@/enums/RoutePath' +import { ArrowLeft } from '@/components/ui/icon' +import { type ReactNode } from 'react' +import { useNavigate } from 'react-router-dom' + +interface BoxFullscreenShellProps { + boxId?: string + title?: string + copyValue?: string + children: ReactNode +} + +export function BoxFullscreenShell({ boxId, title, copyValue, children }: BoxFullscreenShellProps) { + const navigate = useNavigate() + + const handleBack = () => { + navigate(boxId ? RoutePath.BOX_DETAILS.replace(':boxId', boxId) : RoutePath.BOXES) + } + + return ( +
+
+ +

{title || boxId || 'Box'}

+ {copyValue && } +
+
{children}
+
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxHeader.tsx b/apps/dashboard/src/components/boxes/BoxHeader.tsx new file mode 100644 index 000000000..8df04cb4f --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxHeader.tsx @@ -0,0 +1,164 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CopyButton } from '@/components/CopyButton' +import { BoxState } from '@/components/BoxTable/BoxState' +import { Button } from '@/components/ui/button' +import { ButtonGroup } from '@/components/ui/button-group' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { Skeleton } from '@/components/ui/skeleton' +import { Spinner } from '@/components/ui/spinner' +import { getBoxDisplayName } from '@/lib/box-identity' +import { isRecoverable, isStartable, isStoppable } from '@/lib/utils/box' +import { Box } from '@boxlite-ai/api-client' +import { ArrowLeft, MoreHorizontal, Play, RefreshCw, Square, Wrench } from '@/components/ui/icon' + +interface BoxHeaderProps { + box: Box | undefined + isLoading: boolean + writePermitted: boolean + deletePermitted: boolean + actionsDisabled: boolean + isFetching: boolean + onStart: () => void + onStop: () => void + onRecover: () => void + onDelete: () => void + onRefresh: () => void + onBack: () => void + mutations: { + start: boolean + stop: boolean + recover: boolean + } +} + +export function BoxHeader({ + box, + isLoading, + writePermitted, + deletePermitted, + actionsDisabled, + isFetching, + onStart, + onStop, + onRecover, + onDelete, + onRefresh, + onBack, + mutations, +}: BoxHeaderProps) { + return ( +
+
+
+ + {isLoading ? ( + + ) : box ? ( + <> +
+

{getBoxDisplayName(box)}

+ +
+ + + ) : null} +
+ +
+ {isLoading ? ( +
+ + + + +
+ ) : box ? ( + <> +
+ {writePermitted && ( + + {isStartable(box) && !box.recoverable && ( + + )} + {isStoppable(box) && ( + + )} + {isRecoverable(box) && ( + + )} + + + + + + + + Refresh + + + + {deletePermitted && ( + <> + + + + Delete + + + + )} + + + + )} + +
+ + ) : null} +
+
+
+ ) +} + +function BoxHeaderSkeleton() { + return ( +
+ + +
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxInfoPanel.tsx b/apps/dashboard/src/components/boxes/BoxInfoPanel.tsx new file mode 100644 index 000000000..2d4de1331 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxInfoPanel.tsx @@ -0,0 +1,159 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CopyButton } from '@/components/CopyButton' +import { ResourceChip } from '@/components/ResourceChip' +import { TimestampTooltip } from '@/components/TimestampTooltip' +import { Alert, AlertDescription } from '@/components/ui/alert' +import { Skeleton } from '@/components/ui/skeleton' +import { getBoxPublicId, getBoxPublicIdLabel } from '@/lib/box-identity' +import { getRelativeTimeString } from '@/lib/utils' +import { Box } from '@boxlite-ai/api-client' +import { AlertCircle } from '@/components/ui/icon' +import React from 'react' + +interface BoxInfoPanelProps { + box: Box + getRegionName: (id: string) => string | undefined +} + +function MetaCell({ label, children }: { label: string; children: React.ReactNode }) { + return ( +
+
{label}
+
{children}
+
+ ) +} + +export function BoxInfoPanel({ box, getRegionName }: BoxInfoPanelProps) { + const publicBoxId = getBoxPublicId(box) + const region = getRegionName(box.target) ?? box.target + const labelEntries = Object.entries(box.labels ?? {}) + + return ( +
+ {box.errorReason && ( +
+ + + {box.errorReason} + +
+ )} + +
+ + {box.image ? ( + + {box.image} + + ) : ( + + )} + + +
+ +
+ {getBoxPublicIdLabel(box)} + {publicBoxId && } +
+
+ + {region ? {region} : } + + +
+ + + +
+
+ + + {getRelativeTimeString(box.createdAt).relativeTimeString} + + + + + {getRelativeTimeString(box.updatedAt).relativeTimeString} + + +
+
+ + {labelEntries.length > 0 && ( +
+

Labels

+
+ {labelEntries.map(([key, value]) => ( + + {key}={value} + + ))} +
+
+ )} +
+ ) +} + +export function InfoPanelSkeleton() { + return ( +
+
+ +
+
+ + +
+
+
+
+ +
+ + + +
+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ +
+
+ + +
+
+ + +
+
+
+
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxLogsTab.tsx b/apps/dashboard/src/components/boxes/BoxLogsTab.tsx new file mode 100644 index 000000000..944b00956 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxLogsTab.tsx @@ -0,0 +1,318 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { CopyButton } from '@/components/CopyButton' +import { SeverityBadge } from '@/components/telemetry/SeverityBadge' +import { TimeRangeSelector } from '@/components/telemetry/TimeRangeSelector' +import { Button } from '@/components/ui/button' +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' +import { Input } from '@/components/ui/input' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' +import { Skeleton } from '@/components/ui/skeleton' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import { BOXLITE_DOCS_URL } from '@/constants/ExternalLinks' +import { LogsQueryParams, useBoxLogs } from '@/hooks/useBoxLogs' +import { cn } from '@/lib/utils' +import { LogEntry } from '@boxlite-ai/api-client' +import { format, subHours } from 'date-fns' +import { ChevronDown, ChevronLeft, ChevronRight, FileText, RefreshCw, Search } from '@/components/ui/icon' +import { useQueryStates } from 'nuqs' +import React, { useCallback, useMemo, useState } from 'react' +import { logsSearchParams, SEVERITY_OPTIONS, timeRangeSearchParams } from './SearchParams' + +function formatTimestamp(timestamp: string) { + try { + return format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss.SSS') + } catch { + return timestamp + } +} + +const getLogKey = (log: LogEntry, index: number) => `${log.timestamp}-${index}` + +function LogsTableSkeleton() { + return ( + + + + + Timestamp + Severity + Message + + + + {Array.from({ length: 10 }).map((_, i) => ( + + + + + + + + + + + + + + + ))} + +
+ ) +} + +function LogsErrorState({ onRetry }: { onRetry: () => void }) { + return ( + + + Failed to load logs + Something went wrong while fetching logs. + + + + ) +} + +function LogsEmptyState() { + return ( + + + + + + No logs found + + Try adjusting your time range or filters.{' '} + + Learn more about observability + + . + + + + ) +} + +export function BoxLogsTab({ boxId }: { boxId: string }) { + const [params, setParams] = useQueryStates(logsSearchParams) + const [timeRange, setTimeRange] = useQueryStates(timeRangeSearchParams) + const [searchInput, setSearchInput] = useState(params.search) + const [expandedRow, setExpandedRow] = useState(null) + const limit = 50 + + const resolvedFrom = useMemo(() => timeRange.from ?? subHours(new Date(), 1), [timeRange.from]) + const resolvedTo = useMemo(() => timeRange.to ?? new Date(), [timeRange.to]) + + const queryParams: LogsQueryParams = useMemo( + () => ({ + from: resolvedFrom, + to: resolvedTo, + page: params.logsPage, + limit, + severities: params.severity.length > 0 ? [...params.severity] : undefined, + search: params.search || undefined, + }), + [resolvedFrom, resolvedTo, params.logsPage, params.severity, params.search], + ) + + const { data, isLoading, isError, refetch } = useBoxLogs(boxId, queryParams) + + const handleTimeRangeChange = useCallback( + (from: Date, to: Date) => { + setTimeRange({ from, to }) + setParams({ logsPage: 1 }) + }, + [setTimeRange, setParams], + ) + + const handleSearch = useCallback(() => { + setParams({ search: searchInput, logsPage: 1 }) + }, [searchInput, setParams]) + + const handleSeverityChange = useCallback( + (value: string) => { + if (value === 'all' || !value) { + setParams({ severity: [], logsPage: 1 }) + } else { + setParams({ severity: [value as (typeof SEVERITY_OPTIONS)[number]], logsPage: 1 }) + } + }, + [setParams], + ) + + return ( +
+
+ + +
+ setSearchInput(e.target.value)} + onKeyDown={(e) => e.key === 'Enter' && handleSearch()} + className="w-48" + /> + +
+ + + + +
+ + {isLoading ? ( +
+ +
+ ) : isError ? ( +
+ refetch()} /> +
+ ) : !data?.items?.length ? ( +
+ +
+ ) : ( + + + + + + Timestamp + Severity + Message + + + + {data.items.map((log: LogEntry, index: number) => ( + + setExpandedRow(expandedRow === index ? null : index)} + > + + + + {formatTimestamp(log.timestamp)} + + + + {log.body} + + {expandedRow === index && ( + + +
+
+

Full Message

+
+                              {log.body}
+                            
+
+ {log.traceId && ( +
+

Trace ID

+ {log.traceId} +
+ )} + {log.spanId && ( +
+

Span ID

+ {log.spanId} +
+ )} + {Object.keys(log.logAttributes || {}).length > 0 && ( +
+

Attributes

+
+ +
+                                  {JSON.stringify(log.logAttributes, null, 2)}
+                                
+
+
+ )} +
+
+
+ )} +
+ ))} +
+
+
+ )} + + {data && data.totalPages > 1 && ( +
+ + Page {params.logsPage} of {data.totalPages} ({data.total} total) + +
+ + +
+
+ )} +
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxMetricsTab.tsx b/apps/dashboard/src/components/boxes/BoxMetricsTab.tsx new file mode 100644 index 000000000..34d5db044 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxMetricsTab.tsx @@ -0,0 +1,325 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import React, { useState, useCallback, useMemo } from 'react' +import { useQueryStates } from 'nuqs' +import { useBoxMetrics, MetricsQueryParams } from '@/hooks/useBoxMetrics' +import { TimeRangeSelector } from '@/components/telemetry/TimeRangeSelector' +import { Button } from '@/components/ui/button' +import { ChartContainer, ChartTooltip, ChartTooltipContent, ChartConfig } from '@/components/ui/chart' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Skeleton } from '@/components/ui/skeleton' +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Legend } from 'recharts' +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' +import { BOXLITE_DOCS_URL } from '@/constants/ExternalLinks' +import { RefreshCw, BarChart3 } from '@/components/ui/icon' +import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' +import { format, subHours } from 'date-fns' +import { MetricSeries } from '@boxlite-ai/api-client' +import { getMetricDisplayName } from '@/constants/metrics' +import { timeRangeSearchParams } from './SearchParams' + +const CHART_COLORS = [ + 'hsl(var(--chart-1))', + 'hsl(var(--chart-2))', + 'hsl(var(--chart-3))', + 'hsl(var(--chart-4))', + 'hsl(var(--chart-5))', +] + +const BYTES_TO_GIB = 1024 * 1024 * 1024 +type ViewMode = '%' | 'GiB' + +const METRIC_GROUPS = [ + { key: 'cpu', title: 'CPU', prefix: '.cpu.', hasToggle: false }, + { key: 'memory', title: 'Memory', prefix: '.memory.', hasToggle: true }, + { key: 'filesystem', title: 'Filesystem', prefix: '.filesystem.', hasToggle: true }, +] + +function isByteMetric(metricName: string): boolean { + return !metricName.endsWith('.utilization') +} + +function buildChartData(series: MetricSeries[], convertToGiB: boolean): Record[] { + const timestampSet = new Set() + const indexed = series.map((s) => { + const byTimestamp = new Map() + for (const p of s.dataPoints) { + timestampSet.add(p.timestamp) + byTimestamp.set(p.timestamp, p.value ?? null) + } + return { metricName: s.metricName, byTimestamp, convertMetric: convertToGiB && isByteMetric(s.metricName) } + }) + const timestamps = Array.from(timestampSet).sort() + + return timestamps.map((timestamp) => { + const point: Record = { timestamp } + for (const { metricName, byTimestamp, convertMetric } of indexed) { + const value = byTimestamp.get(timestamp) + if (value == null) { + point[metricName] = null + } else if (convertMetric) { + point[metricName] = Math.round((value / BYTES_TO_GIB) * 100) / 100 + } else { + point[metricName] = value + } + } + return point + }) +} + +function buildChartConfig(series: MetricSeries[]): ChartConfig { + const config: ChartConfig = {} + series.forEach((s, index) => { + config[s.metricName] = { + label: getMetricDisplayName(s.metricName), + color: CHART_COLORS[index % CHART_COLORS.length], + } + }) + return config +} + +const formatXAxis = (timestamp: string) => { + try { + return format(new Date(timestamp), 'HH:mm') + } catch { + return timestamp + } +} + +function MetricGroupChart({ + title, + series, + convertToGiB, + viewMode, + onViewModeChange, +}: { + title: string + series: MetricSeries[] + convertToGiB: boolean + viewMode?: ViewMode + onViewModeChange?: (mode: ViewMode) => void +}) { + const chartData = React.useMemo(() => buildChartData(series, convertToGiB), [series, convertToGiB]) + const chartConfig = React.useMemo(() => buildChartConfig(series), [series]) + const displayTitle = viewMode ? `${title} (${viewMode})` : title + + return ( +
+
+

{displayTitle}

+ {viewMode && onViewModeChange && ( + { + if (value) onViewModeChange(value as ViewMode) + }} + variant="outline" + size="sm" + > + + % + + + GiB + + + )} +
+
+ + + + + value.toFixed(2) : undefined} + domain={viewMode === '%' ? [0, 100] : undefined} + /> + { + try { + return format(new Date(label as string), 'yyyy-MM-dd HH:mm:ss') + } catch { + return String(label) + } + }} + /> + } + /> + + {series.map((s, index) => { + const isLimit = s.metricName.endsWith('.limit') || s.metricName.endsWith('.total') + return ( + + ) + })} + + +
+
+ ) +} + +function MetricsChartsSkeleton() { + return ( +
+ {['CPU', 'Memory', 'Filesystem'].map((title) => ( +
+
+ +
+ +
+ ))} +
+ ) +} + +function MetricsErrorState({ onRetry }: { onRetry: () => void }) { + return ( + + + Failed to load metrics + Something went wrong while fetching metrics. + + + + ) +} + +function MetricsEmptyState() { + return ( + + + + + + No metrics available + + Metrics may take a moment to appear after the box starts.{' '} + + Learn more about observability + + . + + + + ) +} + +export function BoxMetricsTab({ boxId }: { boxId: string }) { + const [timeRange, setTimeRange] = useQueryStates(timeRangeSearchParams) + const [viewModes, setViewModes] = useState>({ memory: '%', filesystem: '%' }) + + const resolvedFrom = useMemo(() => timeRange.from ?? subHours(new Date(), 1), [timeRange.from]) + const resolvedTo = useMemo(() => timeRange.to ?? new Date(), [timeRange.to]) + + const queryParams: MetricsQueryParams = { from: resolvedFrom, to: resolvedTo } + const { data, isLoading, isError, refetch } = useBoxMetrics(boxId, queryParams) + + const handleTimeRangeChange = useCallback( + (from: Date, to: Date) => { + setTimeRange({ from, to }) + }, + [setTimeRange], + ) + + const handleViewModeChange = useCallback((groupKey: string, mode: ViewMode) => { + setViewModes((prev) => ({ ...prev, [groupKey]: mode })) + }, []) + + const groupedSeries = React.useMemo(() => { + if (!data?.series?.length) return [] + + return METRIC_GROUPS.map((group) => { + const allSeries = data.series.filter((s) => s.metricName.includes(group.prefix)) + const mode = group.hasToggle ? viewModes[group.key] : undefined + + let filteredSeries = allSeries.filter((s) => s.metricName !== 'system.memory.utilization') + if (mode === '%') { + filteredSeries = filteredSeries.filter((s) => s.metricName.endsWith('.utilization')) + } else if (mode === 'GiB') { + filteredSeries = filteredSeries.filter((s) => !s.metricName.endsWith('.utilization')) + } + + return { + key: group.key, + title: group.title, + series: filteredSeries, + convertToGiB: mode === 'GiB', + hasToggle: group.hasToggle, + viewMode: mode, + } + }).filter((group) => group.series.length > 0) + }, [data, viewModes]) + + return ( +
+
+ + +
+ + {isLoading ? ( +
+ +
+ ) : isError ? ( +
+ refetch()} /> +
+ ) : !data?.series?.length ? ( +
+ +
+ ) : ( + +
+ {groupedSeries.map((group) => ( + handleViewModeChange(group.key, mode) : undefined} + /> + ))} +
+
+ )} +
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxSpendingTab.tsx b/apps/dashboard/src/components/boxes/BoxSpendingTab.tsx new file mode 100644 index 000000000..88004f596 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxSpendingTab.tsx @@ -0,0 +1,185 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { TimeRangeSelector } from '@/components/telemetry/TimeRangeSelector' +import { Button } from '@/components/ui/button' +import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty' +import { ScrollArea } from '@/components/ui/scroll-area' +import { Skeleton } from '@/components/ui/skeleton' +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' +import { AnalyticsUsageParams, useBoxUsagePeriods } from '@/hooks/queries/useAnalyticsUsage' +import { formatMoney } from '@/lib/utils' +import { format, subHours } from 'date-fns' +import { DollarSign, RefreshCw } from '@/components/ui/icon' +import { useQueryStates } from 'nuqs' +import { useCallback, useMemo } from 'react' +import { timeRangeSearchParams } from './SearchParams' + +function formatTimestamp(timestamp: string) { + try { + return format(new Date(timestamp), 'yyyy-MM-dd HH:mm:ss') + } catch { + return timestamp + } +} + +function SpendingTableSkeleton() { + return ( + + + + Start + End + CPU + RAM (GB) + Disk (GB) + Price + + + + {Array.from({ length: 8 }).map((_, i) => ( + + + + + + + + + + + + + + + + + + + + + ))} + +
+ ) +} + +function SpendingErrorState({ onRetry }: { onRetry: () => void }) { + return ( + + + Failed to load spending + Something went wrong while fetching usage periods. + + + + ) +} + +function SpendingEmptyState() { + return ( + + + + + + No usage periods found + Try adjusting your time range. + + + ) +} + +export function BoxSpendingTab({ boxId }: { boxId: string }) { + const [timeRange, setTimeRange] = useQueryStates(timeRangeSearchParams) + + const resolvedFrom = useMemo(() => timeRange.from ?? subHours(new Date(), 24), [timeRange.from]) + const resolvedTo = useMemo(() => timeRange.to ?? new Date(), [timeRange.to]) + + const queryParams: AnalyticsUsageParams = { from: resolvedFrom, to: resolvedTo } + const { data, isLoading, isError, refetch } = useBoxUsagePeriods(boxId, queryParams) + + const handleTimeRangeChange = useCallback( + (from: Date, to: Date) => { + setTimeRange({ from, to }) + }, + [setTimeRange], + ) + + return ( +
+
+ + +
+ + {isLoading ? ( +
+ +
+ ) : isError ? ( +
+ refetch()} /> +
+ ) : !data?.length ? ( +
+ +
+ ) : ( + + + + + Start + End + CPU + RAM (GB) + Disk (GB) + Price + + + + {data.map((period) => { + const rowKey = `${period.startAt ?? 'unknown-start'}-${period.endAt ?? 'unknown-end'}` + return ( + + + {period.startAt ? formatTimestamp(period.startAt) : '-'} + + + {period.endAt ? formatTimestamp(period.endAt) : '-'} + + {period.cpu ?? 0} + {period.ramGB ?? 0} + {period.diskGB ?? 0} + + {formatMoney(period.price ?? 0, { + maximumFractionDigits: 8, + })} + + + ) + })} + +
+
+ )} +
+ ) +} diff --git a/apps/dashboard/src/components/boxes/BoxTerminalFrame.tsx b/apps/dashboard/src/components/boxes/BoxTerminalFrame.tsx new file mode 100644 index 000000000..36a1c1285 --- /dev/null +++ b/apps/dashboard/src/components/boxes/BoxTerminalFrame.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Daytona Platforms Inc. + * SPDX-License-Identifier: AGPL-3.0 + */ + +import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import { Maximize2 } from '@/components/ui/icon' +import { useEffect, useRef, type SyntheticEvent } from 'react' +import { Link } from 'react-router-dom' +import { buildTerminalIframeSrc, registerActiveTerminalFrame } from './terminalIframeSrc' + +interface BoxTerminalFrameProps { + sessionUrl: string + fullscreenHref?: string + className?: string +} + +export function BoxTerminalFrame({ sessionUrl, fullscreenHref, className }: BoxTerminalFrameProps) { + const deregisterRef = useRef<(() => void) | null>(null) + const iframeSrc = buildTerminalIframeSrc(sessionUrl) + + const handleLoad = (event: SyntheticEvent) => { + const frame = event.currentTarget.contentWindow + if (!frame) return + deregisterRef.current?.() + deregisterRef.current = registerActiveTerminalFrame(frame, sessionUrl) + } + + useEffect(() => { + return () => { + deregisterRef.current?.() + deregisterRef.current = null + } + }, []) + + return ( +
+