Mise à jour des fichiers de configuration et amélioration du script d'installation

- Ajout de la gestion des fichiers .env dans le script d'installation pour garantir une configuration correcte.
- Révision de la logique de création de l'environnement virtuel et d'installation des dépendances dans run.sh et run_windows.bat.
- Modification de la route API pour la création de clés utilisateur dans admin.py.
- Ajout de la gestion des fichiers .setup_state pour suivre l'état de l'installation.
This commit is contained in:
Saifeddine ALOUI
2025-09-05 23:29:08 +02:00
parent 4c3d1e22c6
commit 5fee479644
6 changed files with 370 additions and 243 deletions

5
.gitignore vendored
View File

@@ -95,4 +95,7 @@ docker-compose.yml
# Database
ollama_proxy.db
*.db-journal
*.db-journal
# setup state
.setup_state

View File

@@ -206,21 +206,41 @@ async def create_user_api_key(
},
)
@router.post("/keys/{key_id}/revoke", name="revoke_user_api_key")
async def revoke_user_api_key(
@router.post("/users/{user_id}/keys", name="create_user_api_key")
async def create_user_api_key(
request: Request,
key_id: int,
user_id: int,
db: AsyncSession = Depends(get_db),
admin_user: User = Depends(require_admin_user),
key_name: str = Form(...),
):
key = await apikey_crud.get_api_key_by_id(db, key_id=key_id)
if not key:
raise HTTPException(status_code=404, detail="API Key not found")
await apikey_crud.revoke_api_key(db, key_id=key_id)
flash(request, f"API Key '{key.key_name}' has been revoked.", "success")
return RedirectResponse(url=request.url_for("get_user_details", user_id=key.user_id), status_code=status.HTTP_303_SEE_OTHER)
"""
Creates a new API key for the given user.
Instead of flashing the key (which would be lost on a redirect),
we render a dedicated page that displays the **full plain key**
exactly once and offers a “Copy to clipboard” button.
"""
plain_key, _ = await apikey_crud.create_api_key(db, user_id=user_id, key_name=key_name)
# --- FIX ---
# The user object in request.state (admin_user) is from a different,
# now-closed session. Accessing its properties in the template would
# cause a lazy-load, which fails in a synchronous context.
# We fetch a fresh user object using the current session ('db') and
# update the request state to ensure the template renders correctly.
fresh_admin_user = await user_crud.get_user_by_id(db, user_id=admin_user.id)
request.state.user = fresh_admin_user
# --- END FIX ---
# Render a page that shows the key and a copy button.
return templates.TemplateResponse(
"admin/key_created.html",
{
"request": request,
"plain_key": plain_key,
"user_id": user_id,
},
)
@router.post("/users/{user_id}/delete", name="delete_user_account")
async def delete_user_account(
request: Request,

View File

@@ -28,7 +28,9 @@ from app.database.session import AsyncSessionLocal
from app.crud import user_crud, server_crud
from app.schema.user import UserCreate
from app.schema.server import ServerCreate
import os
os.environ.setdefault("PASSLIB_DISABLE_WARNINGS", "1")
# ----------------------------------------------------------------------
# Logging
# ----------------------------------------------------------------------

300
run.sh
View File

@@ -1,147 +1,199 @@
#!/bin/bash
set -euo pipefail # fail on error, undefined vars, and pipe failures
# ------------------------------------------------------------
# Ollama Proxy Server installer & runner (macOS / Linux)
# ------------------------------------------------------------
# ====================================================================
#
# Ollama Proxy Fortress - Professional Installer & Runner
# Version: 3.0 (with Linux Service Installer)
# For: macOS & Linux
#
# ====================================================================
# --- Configuration ---
VENV_DIR="venv"
REQUIREMENTS_FILE="requirements.txt"
GUNICORN_CONF="gunicorn_conf.py"
APP_MODULE="app.main:app"
STATE_FILE=".setup_state"
SERVICE_NAME="ollama_proxy"
PROJECT_DIR=$(pwd) # Get the absolute path to the project directory
# ------------------------------------------------------------
# Helper Functions
# ------------------------------------------------------------
print_info() { echo -e "\e[34m[INFO]\e[0m $*"; }
print_success() { echo -e "\e[32m[SUCCESS]\e[0m $*"; }
print_error() { echo -e "\e[31m[ERROR]\e[0m $*" >&2; }
# --- Colors and Styling ---
COLOR_RESET='\e[0m'
COLOR_INFO='\e[1;34m' # Bold Blue
COLOR_SUCCESS='\e[1;32m' # Bold Green
COLOR_ERROR='\e[1;31m' # Bold Red
COLOR_WARN='\e[1;33m' # Bold Yellow
COLOR_HEADER='\e[1;35m' # Bold Magenta
# ------------------------------------------------------------
# 1⃣ Check for Python 3
# ------------------------------------------------------------
print_info "Checking for Python 3 installation..."
if ! command -v python3 &>/dev/null; then
print_error "Python 3 not found. Please install it to continue."
# --- Helper Functions ---
print_header() { echo -e "\n${COLOR_HEADER}=====================================================${COLOR_RESET}"; \
echo -e "${COLOR_HEADER}$1${COLOR_RESET}"; \
echo -e "${COLOR_HEADER}=====================================================${COLOR_RESET}"; }
print_info() { echo -e "${COLOR_INFO}[INFO]${COLOR_RESET} $*"; }
print_success() { echo -e "${COLOR_SUCCESS}[SUCCESS]${COLOR_RESET} $*"; }
print_error() { echo -e "${COLOR_ERROR}[ERROR]${COLOR_RESET} $*" >&2; }
print_warn() { echo -e "${COLOR_WARN}[WARNING]${COLOR_RESET} $*"; }
clear
print_header " Ollama Proxy Fortress Installer & Runner"
# ====================================================================
# 1. PRE-CHECKS
# ====================================================================
print_info "Performing initial system checks..."
if ! command -v python3 &>/dev/null || ! python3 -m pip --version &>/dev/null || ! python3 -m venv -h &>/dev/null; then
print_error "Python 3, pip, or venv is missing. Please ensure a complete Python 3 installation."
exit 1
fi
print_success "Python 3 is available."
print_success "Python 3, pip, and venv are available."
# ------------------------------------------------------------
# 2⃣ Firsttime setup (create venv, install deps, generate .env)
# ------------------------------------------------------------
if [[ ! -d "$VENV_DIR" ]]; then
print_info "Firsttime setup detected configuring the server..."
CURRENT_STATE=0
if [[ -f "$STATE_FILE" ]]; then CURRENT_STATE=$(cat "$STATE_FILE"); fi
# ---- 2.1 Create virtual environment
print_info "Creating Python virtual environment in ./$VENV_DIR ..."
python3 -m venv "$VENV_DIR"
# ---- 2.2 Activate and install dependencies
print_info "Activating environment and installing dependencies..."
source "$VENV_DIR/bin/activate"
if [[ -f "$REQUIREMENTS_FILE" ]]; then
pip install --no-cache-dir -r "$REQUIREMENTS_FILE"
if [[ "$CURRENT_STATE" -ge 4 ]] && [[ ! -f ".env" ]]; then
print_warn "Setup complete, but '.env' file is missing! The server cannot start."
read -p "Do you want to run the setup wizard again to create a new .env file? (y/n): " REBUILD_CHOICE
if [[ "$REBUILD_CHOICE" =~ ^[Yy]$ ]]; then
print_info "Resetting setup state..."
rm -f "$STATE_FILE"
CURRENT_STATE=0
else
print_error "Missing $REQUIREMENTS_FILE aborting."
exit 1
print_info "Aborting."
exit 0
fi
# ---- 2.3 Gather configuration from the user
print_info "Please provide the following configuration (press Enter for defaults):"
read -p "Port for the proxy server to listen on [8080]: " PROXY_PORT
PROXY_PORT=${PROXY_PORT:-8080}
read -p "Backend Ollama server URL(s), commaseparated [http://127.0.0.1:11434]: " OLLAMA_SERVERS
OLLAMA_SERVERS=${OLLAMA_SERVERS:-http://127.0.0.1:11434}
read -p "Redis URL for rate limiting [redis://localhost:6379/0]: " REDIS_URL
REDIS_URL=${REDIS_URL:-redis://localhost:6379/0}
read -p "Username for the admin dashboard [admin]: " ADMIN_USER
ADMIN_USER=${ADMIN_USER:-admin}
# hide password input
read -s -p "Password for the admin user (will be hidden): " ADMIN_PASSWORD
echo
if [[ -z "$ADMIN_PASSWORD" ]]; then
print_error "Admin password cannot be empty."
exit 1
fi
read -p "Allowed IPs, commaseparated (leave empty for all): " ALLOWED_IPS
read -p "Denied IPs, commaseparated (leave empty for none): " DENIED_IPS
# ---- 2.4 Generate .env file
print_info "Generating .env configuration file..."
# Create a random secret key (32byte hex)
SECRET_KEY=$(openssl rand -hex 32)
# Escape any double quotes that might be present in the password
ESCAPED_ADMIN_PASSWORD=${ADMIN_PASSWORD//\"/\\\"}
{
echo "# --------------------------------------------------"
echo "# Application Settings"
echo "# --------------------------------------------------"
echo "APP_NAME=\"Ollama Proxy Server\""
echo "APP_VERSION=\"8.0.0\""
echo "LOG_LEVEL=\"info\""
echo "PROXY_PORT=${PROXY_PORT}"
echo "OLLAMA_SERVERS=\"${OLLAMA_SERVERS}\""
echo "DATABASE_URL=\"sqlite+aiosqlite:///./ollama_proxy.db\""
echo "ADMIN_USER=${ADMIN_USER}"
echo "ADMIN_PASSWORD=\"${ESCAPED_ADMIN_PASSWORD}\""
echo "SECRET_KEY=${SECRET_KEY}"
echo ""
echo "# --------------------------------------------------"
echo "# Advanced Security"
echo "# --------------------------------------------------"
echo "REDIS_URL=\"${REDIS_URL}\""
echo "RATE_LIMIT_REQUESTS=100"
echo "RATE_LIMIT_WINDOW_MINUTES=1"
# Only write the listtype variables when they are nonempty.
if [[ -n "$ALLOWED_IPS" ]]; then
echo "ALLOWED_IPS=${ALLOWED_IPS}"
fi
if [[ -n "$DENIED_IPS" ]]; then
echo "DENIED_IPS=${DENIED_IPS}"
fi
} > .env
print_success ".env file created."
# ---- 2.5 Initialise the database with Alembic
print_info "Running database migrations (Alembic)..."
alembic upgrade head
print_success "Database is uptodate."
print_success "Firsttime setup complete!"
echo
fi
# ------------------------------------------------------------
# 3⃣ Start the server
# ------------------------------------------------------------
print_info "Activating virtual environment..."
source "$VENV_DIR/bin/activate"
# ====================================================================
# 2. SETUP WIZARD (Resumable)
# ====================================================================
if [[ "$CURRENT_STATE" -lt 4 ]]; then
print_info "Setup state is ${CURRENT_STATE}/4. Starting or resuming installation..."
print_info "Setting PYTHONPATH to project root..."
export PYTHONPATH=.
if [[ "$CURRENT_STATE" -lt 1 ]]; then
print_header "--- [Step 1/4] Creating Python Virtual Environment ---"
python3 -m venv "$VENV_DIR"
echo "1" > "$STATE_FILE"
print_success "Virtual environment created in './${VENV_DIR}'."
fi
source "$VENV_DIR/bin/activate"
if [[ "$CURRENT_STATE" -lt 2 ]]; then
print_header "--- [Step 2/4] Installing Python Dependencies ---"
pip install --no-cache-dir -r "$REQUIREMENTS_FILE"
echo "2" > "$STATE_FILE"
print_success "All dependencies installed."
fi
if [[ "$CURRENT_STATE" -lt 3 ]]; then
print_header "--- [Step 3/4] Server Configuration ---"
read -p " -> Port for the proxy server [8080]: " PROXY_PORT
read -p " -> Backend Ollama server(s) [http://127.0.0.1:11434]: " OLLAMA_SERVERS
read -p " -> Redis URL [redis://localhost:6379/0]: " REDIS_URL
read -p " -> Admin username [admin]: " ADMIN_USER
ADMIN_PASSWORD=""
while [[ -z "$ADMIN_PASSWORD" ]]; do
read -s -p " -> Admin password (cannot be empty): " ADMIN_PASSWORD; echo
if [[ -z "$ADMIN_PASSWORD" ]]; then print_error " Password cannot be empty."; fi
done
read -p " -> Allowed IPs (comma-separated, leave empty for all): " ALLOWED_IPS
read -p " -> Denied IPs (comma-separated, leave empty for none): " DENIED_IPS
print_info "Generating .env configuration file..."
SECRET_KEY=$(openssl rand -hex 32)
(
echo "APP_NAME=\"Ollama Proxy Fortress\""; echo "APP_VERSION=\"8.0.0\""; echo "LOG_LEVEL=\"info\""
echo "PROXY_PORT=\"${PROXY_PORT:-8080}\""
echo "OLLAMA_SERVERS=\"${OLLAMA_SERVERS:-http://127.0.0.1:11434}\""
echo "DATABASE_URL=\"sqlite+aiosqlite:///./ollama_proxy.db\""
echo "ADMIN_USER=\"${ADMIN_USER:-admin}\""; echo "ADMIN_PASSWORD=\"${ADMIN_PASSWORD}\""
echo "SECRET_KEY=\"${SECRET_KEY}\""
echo "REDIS_URL=\"${REDIS_URL:-redis://localhost:6379/0}\""
echo "RATE_LIMIT_REQUESTS=\"100\""; echo "RATE_LIMIT_WINDOW_MINUTES=\"1\""
echo "ALLOWED_IPS=\"${ALLOWED_IPS}\""; echo "DENIED_IPS=\"${DENIED_IPS}\""
) > .env
echo "3" > "$STATE_FILE"
print_success ".env file created."
fi
if [[ "$CURRENT_STATE" -lt 4 ]]; then
print_header "--- [Step 4/4] Initializing Database ---"
alembic upgrade head
echo "4" > "$STATE_FILE"
print_success "Database migrated to the latest version."
fi
# Determine the port from .env (fallback to 8080)
DEFAULT_PORT=8080
if [[ -f .env && $(grep -E '^PROXY_PORT=' .env) ]]; then
# Strip quotes if present
PORT_TO_USE=$(grep -E '^PROXY_PORT=' .env | cut -d '=' -f2 | tr -d '"' | tr -d "'")
else
PORT_TO_USE=$DEFAULT_PORT
print_header "--- Setup Complete! ---"
ADMIN_USER_FINAL=$(grep -E '^ADMIN_USER=' .env | cut -d '=' -f2 | tr -d '"')
PORT_FINAL=$(grep -E '^PROXY_PORT=' .env | cut -d '=' -f2 | tr -d '"')
print_success "Your Ollama Proxy Fortress is ready."
print_info "Admin Dashboard: http://127.0.0.1:${PORT_FINAL}/admin"
print_info "Admin Username: ${ADMIN_USER_FINAL}"
fi
print_info "Starting Ollama Proxy Server on port ${PORT_TO_USE}..."
print_info "Press Ctrl+C to stop the server."
# ====================================================================
# 3. OPTIONAL: CREATE LINUX SYSTEMD SERVICE
# ====================================================================
SERVICE_CREATED=false
if [[ "$(uname)" == "Linux" ]] && command -v systemctl &>/dev/null; then
print_header "--- Optional: Create a Systemd Service ---"
print_info "A service will automatically start the proxy on boot and restart it if it fails."
read -p "Do you want to create and enable a systemd service for this application? (y/n): " CREATE_SERVICE
if [[ "$CREATE_SERVICE" =~ ^[Yy]$ ]]; then
SERVICE_FILE_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
print_info "Creating systemd service file..."
# Run via Gunicorn the config file is expected to exist in the repo.
exec gunicorn -c "$GUNICORN_CONF" "$APP_MODULE" --bind "0.0.0.0:${PORT_TO_USE}"
# Using a 'here document' to create the service file content
SERVICE_FILE_CONTENT=$(cat << EOF
[Unit]
Description=Ollama Proxy Fortress Service
After=network.target
[Service]
User=${USER}
Group=$(id -gn ${USER})
WorkingDirectory=${PROJECT_DIR}
Environment="PYTHONPATH=${PROJECT_DIR}"
ExecStart=${PROJECT_DIR}/${VENV_DIR}/bin/gunicorn -c ${PROJECT_DIR}/${GUNICORN_CONF} ${APP_MODULE} --bind 0.0.0.0:$(grep -E '^PROXY_PORT=' .env | cut -d '=' -f2 | tr -d '"')
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
)
print_warn "Root privileges are required to install the service."
echo "$SERVICE_FILE_CONTENT" | sudo tee "$SERVICE_FILE_PATH" > /dev/null
print_info "Reloading systemd daemon..."
sudo systemctl daemon-reload
print_info "Enabling the service to start on boot..."
sudo systemctl enable "${SERVICE_NAME}.service"
print_info "Starting the service now..."
sudo systemctl start "${SERVICE_NAME}.service"
print_header "--- Service Management ---"
print_success "Service '${SERVICE_NAME}' is now running."
print_info "Check status: sudo systemctl status ${SERVICE_NAME}"
print_info "View logs: sudo journalctl -u ${SERVICE_NAME} -f"
print_info "Stop service: sudo systemctl stop ${SERVICE_NAME}"
SERVICE_CREATED=true
fi
fi
# ====================================================================
# 4. START THE SERVER (if service was not created)
# ====================================================================
if [ "$SERVICE_CREATED" = false ]; then
print_header "--- Starting Ollama Proxy Fortress (Foreground Mode) ---"
print_info "Activating virtual environment..."
source "$VENV_DIR/bin/activate"
print_info "Setting PYTHONPATH to project root..."
export PYTHONPATH=.
PORT_TO_USE=$(grep -E '^PROXY_PORT=' .env | cut -d '=' -f2 | tr -d '"' | tr -d "'" || echo "8080")
print_info "Starting Gunicorn server on http://0.0.0.0:${PORT_TO_USE}"
print_info "Press Ctrl+C to stop the server."
echo
exec gunicorn -c "$GUNICORN_CONF" "$APP_MODULE" --bind "0.0.0.0:${PORT_TO_USE}"
fi

View File

@@ -1,155 +1,129 @@
@echo off
setlocal enabledelayedexpansion
:: This script sets up and runs the Ollama Proxy Server on Windows.
:: It has been rewritten to be more robust.
:: On first run, it will guide you through setup.
:: Afterwards, it will just start the server.
:: ==================================================================
:: Ollama Proxy Server - Python-Powered Installer for Windows
:: ==================================================================
:: This script now delegates the fragile .env creation to a Python
:: script (`setup_wizard.py`) to guarantee a correct setup.
set VENV_DIR=venv
set REQUIREMENTS_FILE=requirements.txt
set STATE_FILE=.setup_state
set SETUP_WIZARD_SCRIPT=setup_wizard.py
:: --- Check for Python ---
:: ------------------------------------------------------------------
:: 1. PRE-CHECKS
:: ------------------------------------------------------------------
echo [INFO] Checking for Python installation...
where python >nul 2>nul
if %errorlevel% neq 0 (
echo [ERROR] Python not found in your PATH.
echo Please install Python 3.11+ from https://python.org or the Microsoft Store.
echo Make sure to check "Add Python to PATH" during installation.
echo [ERROR] Python not found in your PATH. Please install Python 3.11+.
pause
exit /b 1
)
echo [SUCCESS] Python found.
:: --- Check if setup is needed ---
if exist "%VENV_DIR%\Scripts\activate.bat" goto :start_server
set "CURRENT_STATE=0"
if exist "%STATE_FILE%" (
set /p CURRENT_STATE=<%STATE_FILE%
)
if %CURRENT_STATE% GEQ 4 (
if not exist ".env" (
echo.
echo *****************************************************************
echo * [WARNING] The setup is complete, but the '.env' file is missing!
echo *****************************************************************
echo.
set /p REBUILD_CHOICE="Do you want to run the setup wizard again? (y/n): "
if /i "!REBUILD_CHOICE!"=="y" (
echo [INFO] Resetting setup state...
del /f "%STATE_FILE%" >nul 2>nul
set "CURRENT_STATE=0"
echo.
) else (
echo [INFO] Aborting.
pause
exit /b 0
)
)
)
:: ##################################################################
:: # FIRST-TIME SETUP SCRIPT #
:: ##################################################################
echo.
echo [INFO] First-time setup detected. Configuring the server...
if %CURRENT_STATE% GEQ 4 goto start_server
:: 1. Create Virtual Environment
echo [INFO] Creating Python virtual environment...
:: ==================================================================
:: SETUP WIZARD (RESUMABLE)
:: ==================================================================
echo [INFO] Setup state is !CURRENT_STATE!/4. Starting or resuming installation...
if %CURRENT_STATE% GEQ 1 goto setup_step_2
echo [INFO] [1/4] Creating Python virtual environment...
python -m venv %VENV_DIR%
if %errorlevel% neq 0 ( echo [ERROR] Failed to create virtual environment. & pause & exit /b 1 )
(echo 1) > %STATE_FILE%
echo [SUCCESS] Virtual environment created.
:setup_step_2
call .\%VENV_DIR%\Scripts\activate.bat
if %CURRENT_STATE% GEQ 2 goto setup_step_3
echo [INFO] [2/4] Installing dependencies...
pip install --no-cache-dir -r %REQUIREMENTS_FILE%
if %errorlevel% neq 0 ( echo [ERROR] Failed to install Python packages. & pause & exit /b 1 )
(echo 2) > %STATE_FILE%
echo [SUCCESS] Dependencies installed.
:setup_step_3
if %CURRENT_STATE% GEQ 4 goto setup_step_4
echo [INFO] [3/4] Launching Python setup wizard for configuration...
python %SETUP_WIZARD_SCRIPT%
if %errorlevel% neq 0 (
echo [ERROR] Failed to create virtual environment.
echo [ERROR] The Python setup wizard failed to create the .env file.
pause
exit /b 1
)
(echo 3) > %STATE_FILE%
echo [SUCCESS] .env file created successfully by Python wizard.
:: 2. Install Dependencies
echo [INFO] Activating environment and installing dependencies (this may take a moment)...
call %VENV_DIR%\Scripts\activate.bat
pip install --no-cache-dir -r requirements.txt
if %errorlevel% neq 0 (
echo [ERROR] Failed to install Python packages.
pause
exit /b 1
)
:: 3. Gather Configuration
echo [INFO] Please provide the following configuration:
set /p PROXY_PORT="Enter the port for the proxy server to listen on [8080]: "
if not defined PROXY_PORT set PROXY_PORT=8080
set /p OLLAMA_SERVERS="Enter the backend Ollama server URL(s), comma-separated [http://127.0.0.1:11434]: "
if not defined OLLAMA_SERVERS set OLLAMA_SERVERS=http://127.0.0.1:11434
set /p REDIS_URL="Enter the Redis URL for rate limiting [redis://localhost:6379/0]: "
if not defined REDIS_URL set REDIS_URL=redis://localhost:6379/0
set /p ADMIN_USER="Enter a username for the admin dashboard [admin]: "
if not defined ADMIN_USER set ADMIN_USER=admin
set /p ADMIN_PASSWORD="Enter a password for the admin user: "
if not defined ADMIN_PASSWORD (
echo [ERROR] Admin password cannot be empty.
pause
exit /b 1
)
set /p ALLOWED_IPS_INPUT="Enter allowed IPs, comma-separated (e.g., 127.0.0.1,localhost) [*]: "
if not defined ALLOWED_IPS_INPUT set ALLOWED_IPS_INPUT=*
set /p DENIED_IPS_INPUT="Enter denied IPs, comma-separated (leave empty for none): "
:: 4. Generate .env file
echo [INFO] Generating .env configuration file...
(
echo # Application Settings
REM --- FIX START ---
REM Removed quotes from simple key-value pairs
echo APP_NAME=Ollama Proxy Server
echo APP_VERSION=8.0.0
echo LOG_LEVEL=info
echo PROXY_PORT=!PROXY_PORT!
echo OLLAMA_SERVERS=!OLLAMA_SERVERS!
echo DATABASE_URL=sqlite+aiosqlite:///./ollama_proxy.db
echo ADMIN_USER=!ADMIN_USER!
echo ADMIN_PASSWORD=!ADMIN_PASSWORD!
echo SECRET_KEY=!RANDOM!!RANDOM!!RANDOM!!RANDOM!
echo.
echo # --- Advanced Security ---
echo REDIS_URL=!REDIS_URL!
echo RATE_LIMIT_REQUESTS=100
echo RATE_LIMIT_WINDOW_MINUTES=1
REM --- FIX END ---
REM Correctly formats the ALLOWED_IPS value. These need special formatting.
if "!ALLOWED_IPS_INPUT!"=="*" (
echo ALLOWED_IPS='["*"]'
) else (
set formatted_allowed_ips=!ALLOWED_IPS_INPUT:,=","!
echo ALLOWED_IPS='["!formatted_allowed_ips!"]'
)
REM Correctly formats the DENIED_IPS value. These need special formatting.
if not defined DENIED_IPS_INPUT (
echo DENIED_IPS='[]'
) else (
set formatted_denied_ips=!DENIED_IPS_INPUT:,=","!
echo DENIED_IPS='["!formatted_denied_ips!"]'
)
) > .env
:: 5. Initialize Database
echo [INFO] Initializing the database...
:setup_step_4
if %CURRENT_STATE% GEQ 4 goto all_setup_done
echo [INFO] [4/4] Initializing the database with Alembic...
alembic upgrade head
if %errorlevel% neq 0 (
echo [ERROR] Failed to initialize the database.
echo [ERROR] Failed to initialize the database. The app configuration might be invalid.
pause
exit /b 1
)
(echo 4) > %STATE_FILE%
echo [SUCCESS] Database is up-to-date.
echo [SUCCESS] First-time setup complete!
:all_setup_done
echo.
echo [SUCCESS] Setup complete!
echo.
goto :start_server
:: ##################################################################
:: # SERVER START SCRIPT #
:: ##################################################################
:: ==================================================================
:: START THE SERVER
:: ==================================================================
:start_server
echo [INFO] Activating virtual environment...
call %VENV_DIR%\Scripts\activate.bat
call .\%VENV_DIR%\Scripts\activate.bat
echo [INFO] Setting Python Path...
set PYTHONPATH=.
:: Read the port from the .env file for the status message
set PORT_TO_USE=8080
for /f "usebackq tokens=1,* delims==" %%a in (".env") do (
if "%%a"=="PROXY_PORT" set PORT_TO_USE=%%b
if /i "%%a"=="PROXY_PORT" set "PORT_TO_USE=%%~b"
)
echo [INFO] Starting Ollama Proxy Server on port !PORT_TO_USE!...
echo To stop the server, simply close this window or press Ctrl+C.
echo.
:: Uvicorn is used directly as Gunicorn is not available on Windows.
uvicorn app.main:app --host 0.0.0.0 --port !PORT_TO_USE!
echo [INFO] Server has been stopped.
pause
pause
exit /b

76
setup_wizard.py Normal file
View File

@@ -0,0 +1,76 @@
import sys
import secrets
def print_info(message):
print(f"[INFO] {message}")
def print_error(message):
print(f"[ERROR] {message}", file=sys.stderr)
def get_user_input(prompt, default=None):
"""Gets user input with a default value."""
default_text = f"[{default}]" if default is not None else ""
prompt_text = f" -> {prompt} {default_text}: "
user_value = input(prompt_text)
return user_value.strip() if user_value.strip() else default
def create_env_file():
"""
Guides the user through creating a .env file and writes it.
This version omits empty optional fields to prevent parsing errors.
"""
print_info("Please provide the following configuration (press Enter for defaults):")
config = {
"PROXY_PORT": get_user_input("Port for the proxy server", "8080"),
"OLLAMA_SERVERS": get_user_input("Backend Ollama server(s)", "http://127.0.0.1:11434"),
"REDIS_URL": get_user_input("Redis URL for rate limiting", "redis://localhost:6379/0"),
"ADMIN_USER": get_user_input("Username for the admin dashboard", "admin"),
"ALLOWED_IPS": get_user_input("Allowed IPs (comma-separated, leave empty for all)", ""),
"DENIED_IPS": get_user_input("Denied IPs (comma-separated, leave empty for none)", ""),
}
# --- Password Loop ---
admin_password = ""
while not admin_password:
admin_password = input(" -> Password for the admin user (cannot be empty): ").strip()
if not admin_password:
print_error(" Password cannot be empty. Please try again.")
config["ADMIN_PASSWORD"] = admin_password
# --- Generate Secret Key ---
config["SECRET_KEY"] = secrets.token_hex(32)
# --- Write the .env file ---
print_info("Generating .env configuration file...")
try:
with open(".env", "w", encoding="utf-8") as f:
f.write('APP_NAME="Ollama Proxy Server"\n')
f.write('APP_VERSION="8.0.0"\n')
f.write('LOG_LEVEL="info"\n')
f.write(f'PROXY_PORT="{config["PROXY_PORT"]}"\n')
f.write(f'OLLAMA_SERVERS="{config["OLLAMA_SERVERS"]}"\n')
f.write('DATABASE_URL="sqlite+aiosqlite:///./ollama_proxy.db"\n')
f.write(f'ADMIN_USER="{config["ADMIN_USER"]}"\n')
f.write(f'ADMIN_PASSWORD="{config["ADMIN_PASSWORD"]}"\n')
f.write(f'SECRET_KEY="{config["SECRET_KEY"]}"\n')
f.write(f'REDIS_URL="{config["REDIS_URL"]}"\n')
f.write('RATE_LIMIT_REQUESTS="100"\n')
f.write('RATE_LIMIT_WINDOW_MINUTES="1"\n')
# --- CRITICAL FIX: Only write these lines if they have a value ---
if config["ALLOWED_IPS"]:
f.write(f'ALLOWED_IPS="{config["ALLOWED_IPS"]}"\n')
if config["DENIED_IPS"]:
f.write(f'DENIED_IPS="{config["DENIED_IPS"]}"\n')
print_info(".env file created successfully.")
return True
except IOError as e:
print_error(f"Failed to write to .env file: {e}")
return False
if __name__ == "__main__":
if not create_env_file():
# Exit with a non-zero code to signal failure to the batch script
sys.exit(1)