Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,528 changes: 1,528 additions & 0 deletions .cursor/rules/NEW_PROJECT_REFERENCE.md

Large diffs are not rendered by default.

Empty file modified .dockerignore
100644 → 100755
Empty file.
Empty file modified .envrc
100644 → 100755
Empty file.
Empty file modified .gemini/config.yml
100644 → 100755
Empty file.
Empty file modified .github/actions/setup/action.yml
100644 → 100755
Empty file.
Empty file modified .github/dependabot.yml
100644 → 100755
Empty file.
Empty file modified .github/workflows/ci.yml
100644 → 100755
Empty file.
8 changes: 7 additions & 1 deletion .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ deploy/rustfs-operator/Chart.lock

# Operator
operator.log
operator.pid
operator.pid

# Console Web Frontend
console-web/.next/
console-web/out/
console-web/node_modules/
.cursor/
Empty file modified .script-test.sh
100644 → 100755
Empty file.
Empty file modified CHANGELOG.md
100644 → 100755
Empty file.
Empty file modified CLA.md
100644 → 100755
Empty file.
Empty file modified CLAUDE.md
100644 → 100755
Empty file.
Empty file modified CODE_OF_CONDUCT.md
100644 → 100755
Empty file.
Empty file modified CONSOLE-DEVELOPMENT-PLAN.md
100644 → 100755
Empty file.
Empty file modified CONSOLE-INTEGRATION-SUMMARY.md
100644 → 100755
Empty file.
Empty file modified CONTRIBUTING.md
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions Cargo.lock
100644 → 100755

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ chrono = { version = "0.4", features = ["serde"] }
const-str = "1.0.0"
serde = { version = "1.0.228", features = ["derive"] }
tokio = { version = "1.49.0", features = ["rt", "rt-multi-thread", "macros", "fs", "io-std", "io-util"] }
tokio-util = { version = "0.7", features = ["io", "compat"] }
futures = "0.3.31"
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
Expand Down
Empty file modified Dockerfile
100644 → 100755
Empty file.
Empty file modified Justfile
100644 → 100755
Empty file.
Empty file modified LICENSE
100644 → 100755
Empty file.
Empty file modified README.md
100644 → 100755
Empty file.
Empty file modified ROADMAP.md
100644 → 100755
Empty file.
Empty file modified SCRIPTS-UPDATE.md
100644 → 100755
Empty file.
Empty file modified build.rs
100644 → 100755
Empty file.
58 changes: 20 additions & 38 deletions check-rustfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,30 @@ echo " Access RustFS"
echo "========================================="
echo ""

# Check if Console is running locally
if pgrep -f "target/release/operator.*console" >/dev/null; then
echo "✅ Operator Console (local):"
echo " Running at: http://localhost:9090"
# Operator Console backend (deployed in K8s)
if kubectl get deployment rustfs-operator-console -n rustfs-system >/dev/null 2>&1; then
echo "✅ Operator Console API (K8s Deployment):"
echo " Port forward: kubectl port-forward -n rustfs-system svc/rustfs-operator-console 9090:9090"
echo " Health check: curl http://localhost:9090/healthz"
echo " API docs: deploy/console/README.md"
echo ""
echo " Create K8s token: kubectl create token default --duration=24h"
echo " Login: POST http://localhost:9090/api/v1/login"
echo ""
else
echo "⚠️ Operator Console not running locally"
echo " Start with: cargo run -- console --port 9090"
echo "⚠️ Operator Console Deployment not found in rustfs-system"
echo " Deploy with: ./deploy-rustfs.sh"
echo ""
fi

# Operator Console Web (frontend)
if kubectl get deployment rustfs-operator-console-frontend -n rustfs-system >/dev/null 2>&1; then
echo "✅ Operator Console Web UI (K8s Deployment):"
echo " Port forward (UI): kubectl port-forward -n rustfs-system svc/rustfs-operator-console-frontend 8080:80"
echo " Then open: http://localhost:8080"
echo " (Also port-forward Console API to 9090 so login works)"
echo ""
else
echo "⚠️ Operator Console Web (frontend) Deployment not found in rustfs-system"
echo " Deploy with: ./deploy-rustfs.sh"
echo ""
fi

Expand Down Expand Up @@ -199,37 +210,8 @@ else
echo ""
fi

# Dynamically get credentials
echo "Credentials:"
CREDS_SECRET=$(kubectl get tenant "$TENANT_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.credsSecret.name}' 2>/dev/null || echo "")

if [ -n "$CREDS_SECRET" ]; then
# Read credentials from Secret
ACCESS_KEY=$(kubectl get secret "$CREDS_SECRET" -n "$NAMESPACE" -o jsonpath='{.data.accesskey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")
SECRET_KEY=$(kubectl get secret "$CREDS_SECRET" -n "$NAMESPACE" -o jsonpath='{.data.secretkey}' 2>/dev/null | base64 -d 2>/dev/null || echo "")

if [ -n "$ACCESS_KEY" ] && [ -n "$SECRET_KEY" ]; then
echo " Source: Secret '$CREDS_SECRET'"
echo " Access Key: $ACCESS_KEY"
echo " Secret Key: [hidden]"
else
echo " ⚠️ Unable to read credentials from Secret '$CREDS_SECRET'"
fi
else
# Try to read from environment variables
ROOT_USER=$(kubectl get tenant "$TENANT_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.env[?(@.name=="RUSTFS_ROOT_USER")].value}' 2>/dev/null || echo "")
ROOT_PASSWORD=$(kubectl get tenant "$TENANT_NAME" -n "$NAMESPACE" -o jsonpath='{.spec.env[?(@.name=="RUSTFS_ROOT_PASSWORD")].value}' 2>/dev/null || echo "")

if [ -n "$ROOT_USER" ] && [ -n "$ROOT_PASSWORD" ]; then
echo " Source: Environment variables"
echo " Username: $ROOT_USER"
echo " Password: $ROOT_PASSWORD"
else
echo " ⚠️ Credentials not configured"
echo " Note: RustFS may use built-in default credentials, please refer to RustFS documentation"
fi
fi
echo ""
echo "longin with kubectl create token default --duration=24h"

# Show cluster configuration
echo "========================================="
Expand Down
95 changes: 2 additions & 93 deletions cleanup-rustfs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@ confirm_cleanup() {
echo ""
log_warning "This operation will delete all RustFS resources:"
echo " - Tenant: example-tenant"
echo " - Namespace: rustfs-system (including all Pods, PVCs, Services)"
echo " - Namespace: rustfs-system (including Operator, Console, Console Web, Pods, PVCs, Services)"
echo " - CRD: tenants.rustfs.com"
echo " - Operator process"
echo ""
read -p "Confirm deletion? (yes/no): " confirm

Expand Down Expand Up @@ -87,76 +86,6 @@ delete_tenant() {
fi
}

# Stop Operator
stop_operator() {
log_info "Stopping Operator process..."

# Method 1: Read from PID file
if [ -f operator.pid ]; then
local pid=$(cat operator.pid)
if ps -p $pid > /dev/null 2>&1; then
log_info "Stopping Operator (PID: $pid)..."
kill $pid 2>/dev/null || true
sleep 2

# If process still exists, force kill
if ps -p $pid > /dev/null 2>&1; then
log_warning "Process did not exit normally, forcing termination..."
kill -9 $pid 2>/dev/null || true
fi
fi
rm -f operator.pid
fi

# Method 2: Find all operator processes
local operator_pids=$(pgrep -f "target/release/operator.*server" 2>/dev/null || true)
if [ -n "$operator_pids" ]; then
log_info "Found Operator processes: $operator_pids"
pkill -f "target/release/operator.*server" || true
sleep 2

# Force kill remaining processes
pkill -9 -f "target/release/operator.*server" 2>/dev/null || true
fi

log_success "Operator stopped"
}

# Stop Console
stop_console() {
log_info "Stopping Console process..."

# Method 1: Read from PID file
if [ -f console.pid ]; then
local pid=$(cat console.pid)
if ps -p $pid > /dev/null 2>&1; then
log_info "Stopping Console (PID: $pid)..."
kill $pid 2>/dev/null || true
sleep 2

# If process still exists, force kill
if ps -p $pid > /dev/null 2>&1; then
log_warning "Process did not exit normally, forcing termination..."
kill -9 $pid 2>/dev/null || true
fi
fi
rm -f console.pid
fi

# Method 2: Find all console processes
local console_pids=$(pgrep -f "target/release/operator.*console" 2>/dev/null || true)
if [ -n "$console_pids" ]; then
log_info "Found Console processes: $console_pids"
pkill -f "target/release/operator.*console" || true
sleep 2

# Force kill remaining processes
pkill -9 -f "target/release/operator.*console" 2>/dev/null || true
fi

log_success "Console stopped"
}

# Delete Namespace
delete_namespace() {
log_info "Deleting Namespace: rustfs-system..."
Expand Down Expand Up @@ -223,10 +152,6 @@ cleanup_local_files() {
log_info "Cleaning up local files..."

local files_to_clean=(
"operator.log"
"operator.pid"
"console.log"
"console.pid"
"deploy/rustfs-operator/crds/tenant-crd.yaml"
)

Expand Down Expand Up @@ -271,21 +196,7 @@ verify_cleanup() {
log_success "✓ CRD cleaned"
fi

# Check Operator process
if pgrep -f "target/release/operator.*server" >/dev/null; then
log_error "Operator process still running"
issues=$((issues + 1))
else
log_success "✓ Operator stopped"
fi

# Check Console process
if pgrep -f "target/release/operator.*console" >/dev/null; then
log_error "Console process still running"
issues=$((issues + 1))
else
log_success "✓ Console stopped"
fi
# Operator and Console are deleted with namespace (no local process check)

echo ""
if [ $issues -eq 0 ]; then
Expand Down Expand Up @@ -331,8 +242,6 @@ main() {
echo ""

delete_tenant
stop_console
stop_operator
delete_namespace
delete_crd
cleanup_local_files
Expand Down
41 changes: 41 additions & 0 deletions console-web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
13 changes: 13 additions & 0 deletions console-web/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"semi": false,
"singleQuote": false,
"jsxSingleQuote": false,
"trailingComma": "all",
"printWidth": 120,
"tabWidth": 2,
"arrowParens": "always",
"bracketSpacing": true,
"quoteProps": "as-needed",
"bracketSameLine": false,
"endOfLine": "lf"
}
28 changes: 28 additions & 0 deletions console-web/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Build stage: produce static export (out/)
FROM node:22-alpine AS builder

WORKDIR /app

RUN corepack enable && corepack prepare pnpm@latest --activate

COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml* ./
RUN pnpm install --frozen-lockfile

COPY . .
# Same-origin: default /api/v1. For local port-forward dev use: --build-arg NEXT_PUBLIC_API_BASE_URL=http://localhost:9090/api/v1
ARG NEXT_PUBLIC_API_BASE_URL=
ENV NEXT_PUBLIC_API_BASE_URL=${NEXT_PUBLIC_API_BASE_URL}
RUN pnpm build

# Run stage: nginx serves static files
FROM nginx:alpine

# SPA: try_files for client-side routes; Next export emits per-route index.html
COPY --from=builder /app/out /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

RUN chown -R nginx:nginx /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
56 changes: 56 additions & 0 deletions console-web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# RustFS Operator Console Web

Frontend for the RustFS Operator Console (login, dashboard, tenant management). Built with Next.js and designed to run in Kubernetes next to the console backend.

## Development

```bash
pnpm install
pnpm dev
```

Open [http://localhost:3000](http://localhost:3000). The app calls the console API at `http://localhost:9090` in dev only if you set the env below; by default it uses relative `/api/v1` (see Deployment).

### Local dev with backend

Run the operator console backend (e.g. `cargo run -- server` or another port). Then either:

- Use same-origin: e.g. put frontend and backend behind one dev server that proxies `/api/v1` to the backend, and run the frontend with `NEXT_PUBLIC_API_BASE_URL=` (empty or `/api/v1`), or
- Use different ports: run frontend on 3000, backend on 9090, and set `NEXT_PUBLIC_API_BASE_URL=http://localhost:9090/api/v1`. The backend allows `http://localhost:3000` by default (CORS).

## Build

```bash
pnpm build
```

Static output is in `out/`. The default API base URL is **`/api/v1`** (relative), so the same build works when the app is served under the same host as the API (e.g. Ingress with `/` → frontend and `/api` → backend).

## Deployment (Kubernetes)

When frontend and backend are deployed in the same cluster and exposed under **one host** (recommended):

1. Build the Docker image (from repo root):

```bash
docker build -t your-registry/console-web:latest console-web/
```

2. Enable the console frontend in the Helm chart and Ingress (see [deploy/rustfs-operator/README.md](../deploy/rustfs-operator/README.md#console-ui-frontend--backend-in-k8s)). The Ingress will serve `/` from this app and `/api` from the backend.

3. Do **not** set `NEXT_PUBLIC_API_BASE_URL` (or set it to `/api/v1`). The browser will send requests to the same origin, so cookies and CORS work without extra config.

If the frontend is served from a **different host** than the API, set at build time:

```bash
NEXT_PUBLIC_API_BASE_URL=https://api.example.com/api/v1 pnpm build
```

Then configure the backend with `CORS_ALLOWED_ORIGINS` (see deploy README).

## Environment variables

| Variable | Description | Default |
|----------|-------------|--------|
| `NEXT_PUBLIC_BASE_PATH` | Base path for the app (e.g. `/console`) | `""` |
| `NEXT_PUBLIC_API_BASE_URL` | API base URL (relative or absolute) | `"/api/v1"` |
Loading