diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..ea8af9d --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,68 @@ +name: Rust Docker Build + +on: + push: + branches: + - 'RUST' + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Extract version from Cargo.toml + id: extract_version + run: | + VERSION=$(grep '^version = ' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/') + echo "cargo_version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + + - name: Set outputs + id: vars + run: | + echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + echo "sha_full=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "build_date=$(date -u +'%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_OUTPUT + echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT + echo "rust_version=rs-${{ steps.extract_version.outputs.cargo_version }}" >> $GITHUB_OUTPUT + + - name: Check outputs + run: | + echo "Short SHA: ${{ steps.vars.outputs.sha_short }}" + echo "Full SHA: ${{ steps.vars.outputs.sha_full }}" + echo "Build Date: ${{ steps.vars.outputs.build_date }}" + echo "Branch: ${{ steps.vars.outputs.branch_name }}" + echo "Cargo Version: ${{ steps.extract_version.outputs.cargo_version }}" + echo "Rust Version Tag: ${{ steps.vars.outputs.rust_version }}" + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + cache-from: type=registry,ref=ultradesu/outfleet:rust-buildcache + cache-to: type=registry,ref=ultradesu/outfleet:rust-buildcache,mode=max + build-args: | + GIT_COMMIT=${{ steps.vars.outputs.sha_full }} + GIT_COMMIT_SHORT=${{ steps.vars.outputs.sha_short }} + BUILD_DATE=${{ steps.vars.outputs.build_date }} + BRANCH_NAME=${{ steps.vars.outputs.branch_name }} + CARGO_VERSION=${{ steps.extract_version.outputs.cargo_version }} + tags: | + ultradesu/outfleet:${{ steps.vars.outputs.rust_version }} + ultradesu/outfleet:${{ steps.vars.outputs.rust_version }}-${{ steps.vars.outputs.sha_short }} + ultradesu/outfleet:rust-latest \ No newline at end of file diff --git a/API.md b/API.md index 84fa9d5..3ab6476 100644 --- a/API.md +++ b/API.md @@ -29,12 +29,17 @@ Complete API documentation for OutFleet - a web admin panel for managing xray-co **Response:** - **Content-Type:** `text/plain; charset=utf-8` -- **Success (200):** Plain text with configuration URIs, one per line +- **Success (200):** Base64 encoded string containing configuration URIs (one per line when decoded) - **Not Found (404):** User doesn't exist -- **No Content:** Returns comment if no configurations available +- **No Content:** Returns base64 encoded comment if no configurations available **Example Response:** ``` +dm1lc3M6Ly9leUoySWpvaU1pSXNJbkJ6SWpvaVUyVnlkbVZ5TVNJc0ltRmtaQ0k2SWpFeU55NHdMakF1TVM0eElpd2ljRzl5ZENJNklqUTBNeUlzSWxsa0lqb2lNVEl6TkRVMk56Z3RNVEl6TkMwMU5qYzRMVGxoWW1NdE1USXpORFUyTnpnNVlXSmpJaXdpWVdsa0lqb2lNQ0lzSW5Oamj0SWpvaVlYVjBieUlzSW01bGRDSTZJblJqY0NJc0luUjVjR1VpT2lKdWIyNWxJaXdpYUc5emRDSTZJaUlzSW5CaGRHZ2lPaUlpTEhKMGJITWlPaUowYkhNaUxGTnVhU0k2SWlKOQ0Kdmxlc3M6Ly91dWlkQGhvc3RuYW1lOnBvcnQ/ZW5jcnlwdGlvbj1ub25lJnNlY3VyaXR5PXRscyZ0eXBlPXRjcCZoZWFkZXJUeXBlPW5vbmUjU2VydmVyTmFtZQ0Kc3M6Ly9ZV1Z6TFRJMk5TMW5ZMjFBY0dGemMzZHZjbVE2TVRJNExqQXVNQzR5T2pnd09EQT0jU2VydmVyMg0K +``` + +**Decoded Example:** +``` vmess://eyJ2IjoiMiIsInBzIjoiU2VydmVyMSIsImFkZCI6IjEyNy4wLjAuMSIsInBvcnQiOiI0NDMiLCJpZCI6IjEyMzQ1Njc4LTEyMzQtNTY3OC05YWJjLTEyMzQ1Njc4OWFiYyIsImFpZCI6IjAiLCJzY3kiOiJhdXRvIiwibmV0IjoidGNwIiwidHlwZSI6Im5vbmUiLCJob3N0IjoiIiwicGF0aCI6IiIsInRscyI6InRscyIsInNuaSI6IiJ9 vless://uuid@hostname:port?encryption=none&security=tls&type=tcp&headerType=none#ServerName ss://YWVzLTI1Ni1nY21AcGFzc3dvcmQ6MTI3LjAuMC4xOjgwODA=#Server2 diff --git a/Dockerfile b/Dockerfile index 591fd9c..d5ead1e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,20 @@ # Build stage FROM rust:latest as builder +# Build arguments +ARG GIT_COMMIT="development" +ARG GIT_COMMIT_SHORT="dev" +ARG BUILD_DATE="unknown" +ARG BRANCH_NAME="unknown" +ARG CARGO_VERSION="0.1.0" + +# Environment variables from build args +ENV GIT_COMMIT=${GIT_COMMIT} +ENV GIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} +ENV BUILD_DATE=${BUILD_DATE} +ENV BRANCH_NAME=${BRANCH_NAME} +ENV CARGO_VERSION=${CARGO_VERSION} + WORKDIR /app # Install system dependencies @@ -23,6 +37,20 @@ RUN cargo build --release # Runtime stage FROM ubuntu:24.04 +# Build arguments (needed for runtime stage) +ARG GIT_COMMIT="development" +ARG GIT_COMMIT_SHORT="dev" +ARG BUILD_DATE="unknown" +ARG BRANCH_NAME="unknown" +ARG CARGO_VERSION="0.1.0" + +# Environment variables from build args +ENV GIT_COMMIT=${GIT_COMMIT} +ENV GIT_COMMIT_SHORT=${GIT_COMMIT_SHORT} +ENV BUILD_DATE=${BUILD_DATE} +ENV BRANCH_NAME=${BRANCH_NAME} +ENV CARGO_VERSION=${CARGO_VERSION} + WORKDIR /app # Install runtime dependencies diff --git a/src/web/handlers/subscription.rs b/src/web/handlers/subscription.rs index c72a221..bff2186 100644 --- a/src/web/handlers/subscription.rs +++ b/src/web/handlers/subscription.rs @@ -3,6 +3,7 @@ use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; +use base64::{Engine, engine::general_purpose}; use uuid::Uuid; use crate::{ @@ -35,11 +36,12 @@ pub async fn get_user_subscription( }; if user_inbounds.is_empty() { - let response = "# No configurations available\n".to_string(); + let response_text = "# No configurations available\n".to_string(); + let response_base64 = general_purpose::STANDARD.encode(response_text); return Ok(( StatusCode::OK, [("content-type", "text/plain; charset=utf-8")], - response, + response_base64, ).into_response()); } @@ -73,20 +75,24 @@ pub async fn get_user_subscription( } if config_lines.is_empty() { - let response = "# No valid configurations available\n".to_string(); + let response_text = "# No valid configurations available\n".to_string(); + let response_base64 = general_purpose::STANDARD.encode(response_text); return Ok(( StatusCode::OK, [("content-type", "text/plain; charset=utf-8")], - response, + response_base64, ).into_response()); } // Join all URIs with newlines - let response = config_lines.join("\n") + "\n"; + let response_text = config_lines.join("\n") + "\n"; + + // Encode the entire response in base64 + let response_base64 = general_purpose::STANDARD.encode(response_text); Ok(( StatusCode::OK, [("content-type", "text/plain; charset=utf-8")], - response, + response_base64, ).into_response()) } \ No newline at end of file