diff --git a/.github/workflows/build-and-deploy-fallback.yml b/.github/workflows/build-and-deploy-fallback.yml deleted file mode 100644 index eaa92e4..0000000 --- a/.github/workflows/build-and-deploy-fallback.yml +++ /dev/null @@ -1,101 +0,0 @@ -# .github/workflows/build-and-deploy.yml - -name: Build and Deploy Fallback - -on: - push: - branches: ["gemini"] - -jobs: - build-and-deploy: - permissions: - contents: read - packages: write - - name: Build Images and Deploy to Server - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set repo name to lowercase - id: repo_name - run: echo "name=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Create web-app .env file - run: echo 'GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }}' > web-app/.env - - - name: Build and push web-app image πŸš€ - uses: docker/build-push-action@v6 - with: - context: ./web-app - push: true - tags: ghcr.io/${{ steps.repo_name.outputs.name }}/web-app:${{ github.sha }} - cache-from: type=gha,scope=web-app - cache-to: type=gha,mode=max,scope=web-app - - - name: Ensure remote deploy directory exists - uses: appleboy/ssh-action@v1.0.3 - with: - host: ${{ secrets.SERVER_HOST }} - username: ${{ secrets.SERVER_USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - script: | - mkdir -p /home/github-actions/codered-astra - - - name: Upload compose files to server - uses: appleboy/scp-action@v0.1.7 - with: - host: ${{ secrets.SERVER_HOST }} - username: ${{ secrets.SERVER_USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - source: "docker-compose.yml,docker-compose.prod.yml" - target: "/home/github-actions/codered-astra/" - - - name: Deploy to server via SSH ☁️ - uses: appleboy/ssh-action@v1.0.3 - env: - RUNNER_GH_ACTOR: ${{ github.actor }} - RUNNER_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - host: ${{ secrets.SERVER_HOST }} - username: ${{ secrets.SERVER_USERNAME }} - key: ${{ secrets.SSH_PRIVATE_KEY }} - # pass selected env vars to the remote shell so docker login works - envs: RUNNER_GITHUB_TOKEN,RUNNER_GH_ACTOR - debug: true - script: | - cd /home/github-actions/codered-astra - chmod -R o+rX rust-engine/demo-data - # wrapper to support both Docker Compose v2 and legacy v1 - compose() { docker compose "$@" || docker-compose "$@"; } - # Log in to GHCR using the run's GITHUB_TOKEN so compose can pull images. - if [ -n "$RUNNER_GITHUB_TOKEN" ] && [ -n "$RUNNER_GH_ACTOR" ]; then - echo "$RUNNER_GITHUB_TOKEN" | docker login ghcr.io -u "$RUNNER_GH_ACTOR" --password-stdin || true - fi - export REPO_NAME_LOWER='${{ steps.repo_name.outputs.name }}' - export GEMINI_API_KEY='${{ secrets.GEMINI_API_KEY }}' - export MYSQL_DATABASE='${{ secrets.MYSQL_DATABASE }}' - export MYSQL_USER='${{ secrets.MYSQL_USER }}' - export MYSQL_PASSWORD='${{ secrets.MYSQL_PASSWORD }}' - export MYSQL_ROOT_PASSWORD='${{ secrets.MYSQL_ROOT_PASSWORD }}' - export IMAGE_TAG=${{ github.sha }} - # Stop and remove old containers before pulling new images - compose -f docker-compose.prod.yml down - # Clear previous logs for a clean deployment log - : > ~/astra-logs/astra-errors.log || true - compose -f docker-compose.prod.yml pull - compose -f docker-compose.prod.yml up -d - # Security hygiene: remove GHCR credentials after pulling - docker logout ghcr.io || true \ No newline at end of file diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index b61318a..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,268 +0,0 @@ -# CodeRED-Astra Architecture - -## Overview - -CodeRED-Astra is a Retrieval-Augmented Generation (RAG) system for querying ISS technical documentation using vector search, MySQL metadata storage, and Gemini AI for analysis and response generation. - -## System Components - -### 1. **Rust Backend** (`rust-engine/`) -High-performance Rust backend using Warp for HTTP, SQLx for MySQL, and Reqwest for external API calls. - -#### Modules - -**`main.rs`** - Entry point -- Initializes tracing, database, storage -- Spawns FileWorker and QueryWorker background tasks -- Serves API routes on port 8000 - -**`db.rs`** - Database initialization -- Connects to MySQL -- Creates `files` table (id, filename, path, description, pending_analysis, analysis_status) -- Creates `queries` table (id, status, payload, result, timestamps) - -**`api.rs`** - HTTP endpoints -- `POST /api/files` - Upload file (multipart/form-data) -- `POST /api/files/import-demo` - Bulk import from demo-data directory -- `GET /api/files/list` - List all files with status -- `GET /api/files/delete?id=` - Delete file and remove from Qdrant -- `POST /api/query/create` - Create new query (returns query ID) -- `GET /api/query/status?id=` - Check query status -- `GET /api/query/result?id=` - Get query result -- `GET /api/query/cancel?id=` - Cancel in-progress query - -**`file_worker.rs`** - File analysis pipeline -- **Background worker** that processes files with `pending_analysis = TRUE` -- Claims stale/queued files (requeues if stuck >10 min) -- **Stage 1**: Call Gemini 1.5 Flash for initial description -- **Stage 2**: Call Gemini 1.5 Pro for deep vector graph data (keywords, relationships) -- **Stage 3**: Generate embedding and upsert to Qdrant -- **Stage 4**: Mark file as ready (`pending_analysis = FALSE`, `analysis_status = 'Completed'`) -- Resumable: Can recover from crashes/restarts - -**`worker.rs`** - Query processing pipeline -- **Background worker** that processes queries with `status = 'Queued'` -- Requeues stale InProgress jobs (>10 min) -- **Stage 1**: Embed query text -- **Stage 2**: Search top-K similar vectors in Qdrant -- **Stage 3**: Fetch file metadata from MySQL (only completed files) -- **Stage 4**: Call Gemini to analyze relationships between files -- **Stage 5**: Call Gemini for final answer synthesis (strict: no speculation) -- **Stage 6**: Save results to database -- Supports cancellation checks between stages - -**`gemini_client.rs`** - Gemini API integration -- `generate_text(prompt)` - Text generation with model switching via GEMINI_MODEL env var -- `demo_text_embedding(text)` - Demo 64-dim embeddings (replace with real Gemini embeddings) -- Falls back to demo responses if GEMINI_API_KEY not set - -**`vector_db.rs`** - Qdrant client -- `ensure_files_collection(dim)` - Create 'files' collection with Cosine distance -- `upsert_point(id, vector)` - Store file embedding -- `search_top_k(vector, k)` - Find k nearest neighbors -- `delete_point(id)` - Remove file from index - -**`storage.rs`** - File storage utilities -- `storage_dir()` - Get storage path from ASTRA_STORAGE env or default `/app/storage` -- `ensure_storage_dir()` - Create storage directory if missing -- `save_file(filename, contents)` - Save file to storage -- `delete_file(path)` - Remove file from storage - -**`models.rs`** - Data structures -- `FileRecord` - File metadata (mirrors files table) -- `QueryRecord` - Query metadata (mirrors queries table) -- `QueryStatus` enum - Queued, InProgress, Completed, Cancelled, Failed - -### 2. **Web App** (`web-app/`) -React + Vite frontend with Express backend for API proxying. - -#### Backend (`server.mjs`) -- Express server that proxies API calls to rust-engine:8000 -- Serves React static build from `/dist` -- **Why needed**: Docker networking - React can't call rust-engine directly from browser - -#### Frontend (`src/`) -- `App.jsx` - Main chat interface component -- `components/ui/chat/chat-header.jsx` - Header with debug-only "Seed Demo Data" button (visible with `?debug=1`) -- Calls `/api/files/import-demo` endpoint to bulk-load ISS PDFs - -### 3. **MySQL Database** -Two tables for metadata storage: - -**`files` table** -```sql -id VARCHAR(36) PRIMARY KEY -filename TEXT NOT NULL -path TEXT NOT NULL -description TEXT -created_at DATETIME DEFAULT CURRENT_TIMESTAMP -pending_analysis BOOLEAN DEFAULT TRUE -analysis_status VARCHAR(32) DEFAULT 'Queued' -``` - -**`queries` table** -```sql -id VARCHAR(36) PRIMARY KEY -status VARCHAR(32) NOT NULL -payload JSON -result JSON -created_at DATETIME DEFAULT CURRENT_TIMESTAMP -updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -``` - -### 4. **Qdrant Vector Database** -- Collection: `files` -- Dimension: 64 (demo) - replace with real Gemini embedding dimension -- Distance: Cosine similarity -- Stores file embeddings for semantic search - -### 5. **Demo Data** (`rust-engine/demo-data/`) -~20 ISS technical PDFs organized by subsystem: -- Electrical Power System (EPS) -- Environmental Control & Life Support (ECLSS) -- Command & Data Handling (C&DH) -- Structures & Mechanisms - -## Data Flow - -### File Upload & Analysis -``` -1. User uploads PDF β†’ POST /api/files -2. API saves file to storage, inserts DB record (pending_analysis=true) -3. FileWorker claims pending file -4. Gemini 1.5 Flash generates description -5. Gemini 1.5 Pro generates vector graph data -6. Embed text β†’ upsert to Qdrant -7. Mark file as ready (pending_analysis=false) -``` - -### Query Processing -``` -1. User submits query β†’ POST /api/query/create -2. API inserts query record (status='Queued') -3. QueryWorker claims queued query -4. Embed query text -5. Search Qdrant for top-K similar files -6. Fetch file metadata from MySQL -7. Gemini analyzes relationships between files -8. Gemini synthesizes final answer (no speculation) -9. Save results to database -``` - -## Deployment - -### Development (`docker-compose.yml`) -- Local testing with hot-reload -- Bind mounts for code - -### Production (`docker-compose.prod.yml`) -- Used by GitHub Actions for deployment -- Runs rust-engine as user "1004" (github-actions) -- Docker volume: `rust-storage` β†’ `/app/storage` -- Bind mount: `/var/www/codered-astra/rust-engine/demo-data` β†’ `/app/demo-data:ro` -- Environment variables: - - `ASTRA_STORAGE=/app/storage` - - `DEMO_DATA_DIR=/app/demo-data` - - `QDRANT_URL=http://qdrant:6333` - - `GEMINI_API_KEY=` - - `DATABASE_URL=mysql://astraadmin:password@mysql:3306/astra` - -## Key Design Decisions - -### 1. **Two-Stage Analysis (Flash β†’ Pro)** -- Flash is faster/cheaper for initial description -- Pro is better for deep analysis and relationship extraction -- Enables cost-effective scaling - -### 2. **Resumable Workers** -- Workers requeue stale jobs (>10 min in InProgress) -- Survives container restarts without data loss -- Atomic state transitions via SQL - -### 3. **Separation of Concerns** -- FileWorker: Makes files searchable -- QueryWorker: Answers user queries -- Independent scaling and failure isolation - -### 4. **Strict Answer Generation** -- Gemini prompted to not speculate -- Must state uncertainty when info is insufficient -- Prevents hallucination in critical ISS documentation - -### 5. **Demo Embeddings** -- Current: 64-dim deterministic embeddings from text hash -- Production: Replace with real Gemini text embeddings API -- Allows development/testing without embedding API credits - -## API Usage Examples - -### Upload File -```bash -curl -F "file=@document.pdf" http://localhost:3001/api/files -``` - -### Import Demo Data -```bash -curl -X POST http://localhost:3001/api/files/import-demo -``` - -### Create Query -```bash -curl -X POST http://localhost:3001/api/query/create \ - -H "Content-Type: application/json" \ - -d '{"q": "What is the voltage of the ISS main bus?", "top_k": 5}' -``` - -### Check Status -```bash -curl http://localhost:3001/api/query/status?id= -``` - -### Get Result -```bash -curl http://localhost:3001/api/query/result?id= -``` - -## Future Enhancements - -### High Priority -1. Real Gemini text embeddings (replace demo embeddings) -2. File status UI panel (show processing progress) -3. Health check endpoint (`/health`) -4. Data purge endpoint (clear all files/queries) - -### Medium Priority -1. Streaming query responses (SSE/WebSocket) -2. Query result caching -3. File chunking for large PDFs -4. User authentication - -### Low Priority -1. Multi-collection support (different document types) -2. Query history UI -3. File preview in chat -4. Export results to PDF - -## Troubleshooting - -### Storage Permission Errors -- Ensure `/app/storage` is owned by container user -- Docker volume must be writable by user 1004 in production - -### SQL Syntax Errors -- MySQL requires separate `CREATE TABLE` statements -- Cannot combine multiple DDL statements in one `sqlx::query()` - -### Qdrant Connection Issues -- Check QDRANT_URL environment variable -- Ensure qdrant service is running and healthy -- Verify network connectivity between containers - -### Worker Not Processing -- Check logs: `docker logs rust-engine` -- Verify database connectivity -- Look for stale InProgress jobs in queries/files tables - -## Demo Presentation (3 minutes) - -See `rust-engine/DEMODETAILS.md` for curated demo script with example queries. diff --git a/QUICK_REFERENCE.md b/QUICK_REFERENCE.md deleted file mode 100644 index c694cfa..0000000 --- a/QUICK_REFERENCE.md +++ /dev/null @@ -1,219 +0,0 @@ -# CodeRED-Astra Quick Reference - -## System Overview - -**Two-worker architecture for ISS document RAG:** - -1. **FileWorker**: Analyzes uploaded files (Flash β†’ Pro β†’ Embed β†’ Qdrant) -2. **QueryWorker**: Answers queries (Embed β†’ Search β†’ Relationships β†’ Answer) - -Both workers are **resumable** and automatically recover from crashes. - -## Core Data Flow - -``` -Upload PDF β†’ Storage β†’ MySQL (pending) β†’ FileWorker β†’ Qdrant β†’ MySQL (ready) - ↓ -User Query β†’ MySQL (queued) β†’ QueryWorker β†’ Search Qdrant β†’ Gemini β†’ Result -``` - -## Module Map - -| Module | Purpose | Key Functions | -|--------|---------|---------------| -| `main.rs` | Entry point | Spawns workers, serves API | -| `db.rs` | Database init | Creates files/queries tables | -| `api.rs` | HTTP endpoints | Upload, list, delete, query CRUD | -| `file_worker.rs` | File analysis | Flashβ†’Proβ†’embedβ†’upsert | -| `worker.rs` | Query processing | Searchβ†’relationshipsβ†’answer | -| `gemini_client.rs` | AI integration | Text generation, embeddings | -| `vector_db.rs` | Qdrant client | Upsert, search, delete | -| `storage.rs` | File management | Save/delete files | -| `models.rs` | Data structures | FileRecord, QueryRecord | - -## API Endpoints - -### Files -- `POST /api/files` - Upload file -- `POST /api/files/import-demo?force=1` - Bulk import demo PDFs -- `GET /api/files/list` - List all files with status -- `GET /api/files/delete?id=` - Delete file - -### Queries -- `POST /api/query/create` - Create query -- `GET /api/query/status?id=` - Check status -- `GET /api/query/result?id=` - Get result -- `GET /api/query/cancel?id=` - Cancel query - -## Database Schema - -### files -- `id` - UUID primary key -- `filename` - Original filename -- `path` - Storage path -- `description` - Gemini Flash description -- `pending_analysis` - FALSE when ready for search -- `analysis_status` - Queued/InProgress/Completed/Failed - -### queries -- `id` - UUID primary key -- `status` - Queued/InProgress/Completed/Cancelled/Failed -- `payload` - JSON query params `{"q": "...", "top_k": 5}` -- `result` - JSON result `{"summary": "...", "related_files": [...], "relationships": "...", "final_answer": "..."}` - -## Environment Variables - -### Required -- `GEMINI_API_KEY` - Gemini API key -- `DATABASE_URL` - MySQL connection string -- `QDRANT_URL` - Qdrant URL (default: http://qdrant:6333) - -### Optional -- `ASTRA_STORAGE` - Storage directory (default: /app/storage) -- `DEMO_DATA_DIR` - Demo data directory (default: /app/demo-data) -- `GEMINI_MODEL` - Override Gemini model (default: gemini-1.5-pro) - -## Worker States - -### FileWorker -1. **Queued** - File uploaded, awaiting processing -2. **InProgress** - Currently being analyzed -3. **Completed** - Ready for search (pending_analysis=FALSE) -4. **Failed** - Error during processing - -### QueryWorker -1. **Queued** - Query created, awaiting processing -2. **InProgress** - Currently searching/analyzing -3. **Completed** - Result available -4. **Cancelled** - User cancelled -5. **Failed** - Error during processing - -## Gemini Prompts - -### FileWorker Stage 1 (Flash) -``` -Describe the file '{filename}' and extract all key components, keywords, -and details for later vectorization. Be comprehensive and factual. -``` - -### FileWorker Stage 2 (Pro) -``` -Given the file '{filename}' and its description: {desc} -Generate a set of vector graph data (keywords, use cases, relationships) -that can be used for broad and precise search. Only include what is -directly supported by the file. -``` - -### QueryWorker Stage 4 (Relationships) -``` -You are an assistant analyzing relationships STRICTLY within the provided files. -Query: {query} -Files: {file_list} -Tasks: -1) Summarize key details from the files relevant to the query. -2) Describe relationships and linkages strictly supported by these files. -3) List important follow-up questions that could be answered only using the provided files. -Rules: Do NOT guess or invent. If information is insufficient in the files, explicitly state that. -``` - -### QueryWorker Stage 5 (Final Answer) -``` -You are to compose a final answer to the user query using only the information from the files. -Query: {query} -Files considered: {file_list} -Relationship analysis: {relationships} -Requirements: -- Use only information present in the files and analysis above. -- If the answer is uncertain or cannot be determined from the files, clearly state that limitation. -- Avoid speculation or assumptions. -Provide a concise, structured answer. -``` - -## Docker Architecture - -### Services -- **rust-engine** - Warp API + workers (port 8000) -- **web-app** - Express + React (port 3001) -- **mysql** - MySQL 9.1 (port 3306) -- **qdrant** - Qdrant vector DB (port 6333) -- **phpmyadmin** - DB admin UI (port 8080) - -### Volumes (Production) -- `rust-storage:/app/storage` - File storage (writable) -- `/var/www/codered-astra/rust-engine/demo-data:/app/demo-data:ro` - Demo PDFs (read-only) -- `~/astra-logs:/var/log` - Log files - -## Common Issues - -### 1. SQL Syntax Error -**Problem**: `error near 'CREATE TABLE'` -**Cause**: Multiple CREATE TABLE in one query -**Fix**: Split into separate `sqlx::query()` calls - -### 2. Permission Denied -**Problem**: `Permission denied (os error 13)` -**Cause**: Container user can't write to storage -**Fix**: Use Docker volume, ensure ownership matches container user - -### 3. Worker Not Processing -**Problem**: Files/queries stuck in Queued -**Cause**: Worker crashed or not started -**Fix**: Check logs, ensure workers spawned in main.rs - -### 4. Qdrant Connection Failed -**Problem**: `qdrant upsert/search failed` -**Cause**: Qdrant not running or wrong URL -**Fix**: Verify QDRANT_URL, check qdrant container health - -## Development Commands - -```bash -# Build and run locally -cd rust-engine -cargo build -cargo run - -# Check code -cargo check - -# Run with logs -RUST_LOG=info cargo run - -# Docker compose (dev) -docker-compose up --build - -# Docker compose (production) -docker-compose -f docker-compose.prod.yml up -d - -# View logs -docker logs rust-engine -f - -# Rebuild single service -docker-compose build rust-engine -docker-compose up -d rust-engine -``` - -## Testing Flow - -1. Start services: `docker-compose up -d` -2. Import demo data: `curl -X POST http://localhost:3001/api/files/import-demo` -3. Wait for FileWorker to complete (~30 seconds for 20 files) -4. Check file status: `curl http://localhost:3001/api/files/list` -5. Create query: `curl -X POST http://localhost:3001/api/query/create -H "Content-Type: application/json" -d '{"q": "ISS main bus voltage", "top_k": 5}'` -6. Check status: `curl http://localhost:3001/api/query/status?id=` -7. Get result: `curl http://localhost:3001/api/query/result?id=` - -## Performance Notes - -- FileWorker: ~1-2 sec per file (demo embeddings) -- QueryWorker: ~3-5 sec per query (search + 2 Gemini calls) -- Qdrant search: <100ms for 1000s of vectors -- MySQL queries: <10ms for simple selects - -## Security Considerations - -- Store GEMINI_API_KEY in GitHub Secrets (production) -- Use environment variables for all credentials -- Don't commit `.env` files -- Restrict phpmyadmin to internal network only -- Use HTTPS in production deployment diff --git a/README.md b/README.md deleted file mode 100644 index b0b6190..0000000 --- a/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# CodeRED-Astra πŸš€ - -A hackathon-ready project with React frontend and Rust backend engine. - -## Quick Start - -```bash -# 1. Setup environment -cp .env.example .env -# Edit .env with your credentials - -# 2. Start everything with Docker -docker-compose up --build - -# 3. Access your app -# Frontend: http://localhost -# API: http://localhost:8000 -# Database Admin: http://127.0.0.1:8080 -``` - -## Development - -**Frontend (React + Vite)**: -```bash -cd web-app -npm install -npm run dev # http://localhost:5173 -``` - -**Backend (Rust)**: -```bash -cd rust-engine -cargo run # http://localhost:8000 -``` - -## Architecture - -- **Frontend**: React 18 + Vite + Tailwind CSS -- **Backend**: Rust + Warp + SQLx -- **Database**: MySQL 8.0 + phpMyAdmin -- **API**: RESTful endpoints with CORS enabled -- **Docker**: Full containerization for easy deployment - -## Project Structure - -``` -β”œβ”€β”€ web-app/ # React frontend -β”‚ β”œβ”€β”€ src/ -β”‚ β”‚ β”œβ”€β”€ App.jsx # Main component -β”‚ β”‚ └── main.jsx # Entry point -β”‚ └── Dockerfile -β”œβ”€β”€ rust-engine/ # Rust backend -β”‚ β”œβ”€β”€ src/ -β”‚ β”‚ └── main.rs # API server -β”‚ └── Dockerfile -β”œβ”€β”€ docker-compose.yml # Full stack orchestration -└── .env.example # Environment template -``` - -## Team Workflow - -- **Frontend devs**: Work in `web-app/src/`, use `/api/*` for backend calls -- **Backend devs**: Work in `rust-engine/src/`, add endpoints to main.rs -- **Database**: Access phpMyAdmin at http://127.0.0.1:8080 - -## Features - -βœ… Hot reload for both frontend and backend -βœ… Automatic API proxying from React to Rust -βœ… Database connection with graceful fallback -βœ… CORS configured for cross-origin requests -βœ… Production-ready Docker containers -βœ… Health monitoring and status dashboard - -Ready for your hackathon! See `DEVELOPMENT.md` for detailed setup instructions. diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml deleted file mode 100644 index 8e4c091..0000000 --- a/docker-compose.prod.yml +++ /dev/null @@ -1,72 +0,0 @@ -services: - web-app: - image: ghcr.io/${REPO_NAME_LOWER}/web-app:${IMAGE_TAG} - restart: always - ports: - - "127.0.0.1:3033:3000" - environment: - - DATABASE_URL=mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@mysql:3306/${MYSQL_DATABASE} - - RUST_ENGINE_URL=http://rust-engine:8000 - - GEMINI_API_KEY=${GEMINI_API_KEY} - volumes: - - /home/github-actions/codered-astra/rust-engine/demo-data:/app/storage:ro - depends_on: - - mysql - - rust-engine - - rust-engine: - image: ghcr.io/${REPO_NAME_LOWER}/rust-engine:${IMAGE_TAG} - restart: always - environment: - - DATABASE_URL=mysql://${MYSQL_USER}:${MYSQL_PASSWORD}@mysql:3306/${MYSQL_DATABASE} - - ASTRA_STORAGE=/app/storage - - DEMO_DATA_DIR=/app/demo-data - - QDRANT_URL=http://qdrant:6333 - - GEMINI_API_KEY=${GEMINI_API_KEY} - depends_on: - - mysql - - qdrant - user: "1004" - volumes: - - ~/astra-logs:/var/log - - rust-storage:/app/storage - - /home/github-actions/codered-astra/rust-engine/demo-data:/app/demo-data:ro - - mysql: - image: mysql:8.0 - restart: always - ports: - - "45.43.2.25:3306:3306" - environment: - - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - - MYSQL_DATABASE=${MYSQL_DATABASE} - - MYSQL_USER=${MYSQL_USER} - - MYSQL_PASSWORD=${MYSQL_PASSWORD} - volumes: - - mysql-data:/var/lib/mysql - - phpmyadmin: - image: phpmyadmin/phpmyadmin - restart: always - ports: - - "127.0.0.1:8080:80" - environment: - - PMA_HOST=mysql - depends_on: - - mysql - - qdrant: - image: qdrant/qdrant:latest - restart: unless-stopped - ports: - - "127.0.0.1:6333:6333" - volumes: - - qdrant-data:/qdrant/storage - environment: - - QDRANT__SERVICE__GRPC_PORT=6334 - # expose to rust-engine via service name 'qdrant' - -volumes: - mysql-data: - qdrant-data: - rust-storage: diff --git a/web-app/Dockerfile b/frontend/Dockerfile similarity index 100% rename from web-app/Dockerfile rename to frontend/Dockerfile diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1 @@ + diff --git a/web-app/eslint.config.js b/frontend/eslint.config.js similarity index 100% rename from web-app/eslint.config.js rename to frontend/eslint.config.js diff --git a/web-app/index.html b/frontend/index.html similarity index 100% rename from web-app/index.html rename to frontend/index.html diff --git a/web-app/package-lock.json b/frontend/package-lock.json similarity index 100% rename from web-app/package-lock.json rename to frontend/package-lock.json diff --git a/web-app/package.json b/frontend/package.json similarity index 100% rename from web-app/package.json rename to frontend/package.json diff --git a/web-app/src/app/index.jsx b/frontend/src/app/index.jsx similarity index 100% rename from web-app/src/app/index.jsx rename to frontend/src/app/index.jsx diff --git a/web-app/src/components/layouts/chat-layout.jsx b/frontend/src/components/layouts/chat-layout.jsx similarity index 100% rename from web-app/src/components/layouts/chat-layout.jsx rename to frontend/src/components/layouts/chat-layout.jsx diff --git a/web-app/src/components/ui/button/delete-button.jsx b/frontend/src/components/ui/button/delete-button.jsx similarity index 100% rename from web-app/src/components/ui/button/delete-button.jsx rename to frontend/src/components/ui/button/delete-button.jsx diff --git a/web-app/src/components/ui/button/down-button.jsx b/frontend/src/components/ui/button/down-button.jsx similarity index 100% rename from web-app/src/components/ui/button/down-button.jsx rename to frontend/src/components/ui/button/down-button.jsx diff --git a/web-app/src/components/ui/button/schematic-button.jsx b/frontend/src/components/ui/button/schematic-button.jsx similarity index 100% rename from web-app/src/components/ui/button/schematic-button.jsx rename to frontend/src/components/ui/button/schematic-button.jsx diff --git a/web-app/src/components/ui/chat/chat-header.jsx b/frontend/src/components/ui/chat/chat-header.jsx similarity index 100% rename from web-app/src/components/ui/chat/chat-header.jsx rename to frontend/src/components/ui/chat/chat-header.jsx diff --git a/web-app/src/components/ui/chat/chat-window.jsx b/frontend/src/components/ui/chat/chat-window.jsx similarity index 100% rename from web-app/src/components/ui/chat/chat-window.jsx rename to frontend/src/components/ui/chat/chat-window.jsx diff --git a/web-app/src/components/ui/chat/message-input.jsx b/frontend/src/components/ui/chat/message-input.jsx similarity index 100% rename from web-app/src/components/ui/chat/message-input.jsx rename to frontend/src/components/ui/chat/message-input.jsx diff --git a/web-app/src/components/ui/file/file-list.jsx b/frontend/src/components/ui/file/file-list.jsx similarity index 100% rename from web-app/src/components/ui/file/file-list.jsx rename to frontend/src/components/ui/file/file-list.jsx diff --git a/web-app/src/config/markdown.jsx b/frontend/src/config/markdown.jsx similarity index 100% rename from web-app/src/config/markdown.jsx rename to frontend/src/config/markdown.jsx diff --git a/web-app/src/features/gemini/gemini.js b/frontend/src/features/gemini/gemini.js similarity index 100% rename from web-app/src/features/gemini/gemini.js rename to frontend/src/features/gemini/gemini.js diff --git a/web-app/src/index.css b/frontend/src/index.css similarity index 100% rename from web-app/src/index.css rename to frontend/src/index.css diff --git a/web-app/src/main.jsx b/frontend/src/main.jsx similarity index 100% rename from web-app/src/main.jsx rename to frontend/src/main.jsx diff --git a/web-app/tailwind.config.ts b/frontend/tailwind.config.ts similarity index 100% rename from web-app/tailwind.config.ts rename to frontend/tailwind.config.ts diff --git a/web-app/vite.config.js b/frontend/vite.config.js similarity index 95% rename from web-app/vite.config.js rename to frontend/vite.config.js index a0cbe7b..8e11c92 100644 --- a/web-app/vite.config.js +++ b/frontend/vite.config.js @@ -9,7 +9,6 @@ try { console.log("Env file not found!\n" + error) } -// https://vite.dev/config/ export default defineConfig({ plugins: [tailwindcss(), react(), jsconfigPaths()], resolve: { diff --git a/rust-engine/Cargo.lock b/rust-engine/Cargo.lock deleted file mode 100644 index 3da1251..0000000 --- a/rust-engine/Cargo.lock +++ /dev/null @@ -1,3440 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "adobe-cmap-parser" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d3da9d617508ab8102c22f05bd772fc225ecb4fde431e38a45284e5c129a4bc" -dependencies = [ - "pom 1.1.0", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[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.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "bitflags" -version = "2.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" -dependencies = [ - "serde", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - -[[package]] -name = "cc" -version = "1.2.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link 0.2.1", -] - -[[package]] -name = "concurrent-queue" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[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 = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -dependencies = [ - "serde", -] - -[[package]] -name = "encoding" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" -dependencies = [ - "encoding-index-japanese", - "encoding-index-korean", - "encoding-index-simpchinese", - "encoding-index-singlebyte", - "encoding-index-tradchinese", -] - -[[package]] -name = "encoding-index-japanese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-korean" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-simpchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-singlebyte" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding-index-tradchinese" -version = "1.20141219.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" -dependencies = [ - "encoding_index_tests", -] - -[[package]] -name = "encoding_index_tests" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "etcetera" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" -dependencies = [ - "cfg-if", - "home", - "windows-sys 0.48.0", -] - -[[package]] -name = "euclid" -version = "0.20.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb7ef65b3777a325d1eeefefab5b6d4959da54747e33bd6258e789640f307ad" -dependencies = [ - "num-traits", -] - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "find-msvc-tools" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" - -[[package]] -name = "flate2" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "flume" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" -dependencies = [ - "futures-core", - "futures-sink", - "spin", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" - -[[package]] -name = "futures-executor" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-intrusive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" -dependencies = [ - "futures-core", - "lock_api", - "parking_lot", -] - -[[package]] -name = "futures-io" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - -[[package]] -name = "futures-task" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" - -[[package]] -name = "futures-util" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" -dependencies = [ - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", -] - -[[package]] -name = "hashbrown" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" - -[[package]] -name = "hashlink" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1 0.10.6", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "home" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" -dependencies = [ - "windows-sys 0.59.0", -] - -[[package]] -name = "http" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots 1.0.3", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" - -[[package]] -name = "icu_properties" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "potential_utf", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" - -[[package]] -name = "icu_provider" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" -dependencies = [ - "displaydoc", - "icu_locale_core", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" -dependencies = [ - "equivalent", - "hashbrown 0.16.0", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" - -[[package]] -name = "js-sys" -version = "0.3.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "libc" -version = "0.2.177" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" - -[[package]] -name = "libm" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" - -[[package]] -name = "libredox" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" -dependencies = [ - "bitflags", - "libc", - "redox_syscall", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" -dependencies = [ - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" - -[[package]] -name = "linux-raw-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "lopdf" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0f69c40d6dbc68ebac4bf5aec3d9978e094e22e29fcabd045acd9cec74a9dc" -dependencies = [ - "encoding", - "flate2", - "itoa", - "linked-hash-map", - "log", - "pom 3.4.0", - "time", - "weezl", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "memchr" -version = "2.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" -dependencies = [ - "bytes", - "encoding_rs", - "futures-util", - "http", - "httparse", - "memchr", - "mime", - "spin", - "version_check", -] - -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand 0.8.5", - "smallvec", - "zeroize", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl" -version = "0.10.74" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ad14dd45412269e1a30f52ad8f0664f0f4f4a89ee8fe28c3b3527021ebb654" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.110" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a9f0075ba3c21b09f8e8b2026584b1d18d49388648f2fbbf3c97ea8deced8e2" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link 0.2.1", -] - -[[package]] -name = "pdf-extract" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f21fc45e1b40af7e6c7ca32af35464c1ea7a92e5d2e1465d08c8389e033240" -dependencies = [ - "adobe-cmap-parser", - "encoding", - "euclid", - "linked-hash-map", - "lopdf", - "postscript", - "type1-encoding-parser", - "unicode-normalization", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -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.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "pom" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6ce597ecdcc9a098e7fddacb1065093a3d66446fa16c675e7e71d1b5c28e6" - -[[package]] -name = "pom" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c972d8f86e943ad532d0b04e8965a749ad1d18bb981a9c7b3ae72fe7fd7744b" -dependencies = [ - "bstr", -] - -[[package]] -name = "postscript" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78451badbdaebaf17f053fd9152b3ffb33b516104eacb45e7864aaa9c712f306" - -[[package]] -name = "potential_utf" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.2", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.3", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.16", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex-automata" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" - -[[package]] -name = "reqwest" -version = "0.12.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 1.0.3", -] - -[[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.16", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rsa" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" -dependencies = [ - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core 0.6.4", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rust-engine" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "base64", - "bytes", - "chrono", - "dotenvy", - "futures-util", - "lazy_static", - "pdf-extract", - "reqwest", - "serde", - "serde_json", - "sqlx", - "tokio", - "tokio-util", - "tracing", - "tracing-subscriber", - "uuid", - "warp", -] - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "serde_json" -version = "1.0.145" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" -dependencies = [ - "itoa", - "memchr", - "ryu", - "serde", - "serde_core", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[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 = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" -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.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" - -[[package]] -name = "slab" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "sqlx" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" -dependencies = [ - "sqlx-core", - "sqlx-macros", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", -] - -[[package]] -name = "sqlx-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" -dependencies = [ - "base64", - "bytes", - "chrono", - "crc", - "crossbeam-queue", - "either", - "event-listener", - "futures-core", - "futures-intrusive", - "futures-io", - "futures-util", - "hashbrown 0.15.5", - "hashlink", - "indexmap", - "log", - "memchr", - "once_cell", - "percent-encoding", - "rustls", - "serde", - "serde_json", - "sha2", - "smallvec", - "thiserror", - "tokio", - "tokio-stream", - "tracing", - "url", - "uuid", - "webpki-roots 0.26.11", -] - -[[package]] -name = "sqlx-macros" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" -dependencies = [ - "proc-macro2", - "quote", - "sqlx-core", - "sqlx-macros-core", - "syn 2.0.107", -] - -[[package]] -name = "sqlx-macros-core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" -dependencies = [ - "dotenvy", - "either", - "heck", - "hex", - "once_cell", - "proc-macro2", - "quote", - "serde", - "serde_json", - "sha2", - "sqlx-core", - "sqlx-mysql", - "sqlx-postgres", - "sqlx-sqlite", - "syn 2.0.107", - "tokio", - "url", -] - -[[package]] -name = "sqlx-mysql" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" -dependencies = [ - "atoi", - "base64", - "bitflags", - "byteorder", - "bytes", - "chrono", - "crc", - "digest", - "dotenvy", - "either", - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "generic-array", - "hex", - "hkdf", - "hmac", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "percent-encoding", - "rand 0.8.5", - "rsa", - "serde", - "sha1 0.10.6", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-postgres" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" -dependencies = [ - "atoi", - "base64", - "bitflags", - "byteorder", - "chrono", - "crc", - "dotenvy", - "etcetera", - "futures-channel", - "futures-core", - "futures-util", - "hex", - "hkdf", - "hmac", - "home", - "itoa", - "log", - "md-5", - "memchr", - "once_cell", - "rand 0.8.5", - "serde", - "serde_json", - "sha2", - "smallvec", - "sqlx-core", - "stringprep", - "thiserror", - "tracing", - "uuid", - "whoami", -] - -[[package]] -name = "sqlx-sqlite" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" -dependencies = [ - "atoi", - "chrono", - "flume", - "futures-channel", - "futures-core", - "futures-executor", - "futures-intrusive", - "futures-util", - "libsqlite3-sys", - "log", - "percent-encoding", - "serde", - "serde_urlencoded", - "sqlx-core", - "thiserror", - "tracing", - "url", - "uuid", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "stringprep" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", - "unicode-properties", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - -[[package]] -name = "tinystr" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tower" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "tracing-core" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" -dependencies = [ - "nu-ansi-term", - "sharded-slab", - "smallvec", - "thread_local", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "type1-encoding-parser" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d6cc09e1a99c7e01f2afe4953789311a1c50baebbdac5b477ecf78e2e92a5b" -dependencies = [ - "pom 1.1.0", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicase" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" - -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - -[[package]] -name = "unicode-ident" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" - -[[package]] -name = "unicode-normalization" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-properties" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" -dependencies = [ - "getrandom 0.3.4", - "js-sys", - "serde", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "warp" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d06d9202adc1f15d709c4f4a2069be5428aa912cc025d6f268ac441ab066b0" -dependencies = [ - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "log", - "mime", - "mime_guess", - "multer", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-util", - "tower-service", - "tracing", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasite" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.107", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" -dependencies = [ - "cfg-if", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "web-sys" -version = "0.3.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[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.3", -] - -[[package]] -name = "webpki-roots" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "weezl" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" - -[[package]] -name = "whoami" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" -dependencies = [ - "libredox", - "wasite", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" -dependencies = [ - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link 0.2.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link 0.2.1", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "wit-bindgen" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "writeable" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" - -[[package]] -name = "yoke" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] - -[[package]] -name = "zerofrom" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" - -[[package]] -name = "zerotrie" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.107", -] diff --git a/rust-engine/Cargo.toml b/rust-engine/Cargo.toml deleted file mode 100644 index 816cab3..0000000 --- a/rust-engine/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -# rust-engine/Cargo.toml - -[package] -name = "rust-engine" -version = "0.1.0" -edition = "2021" - -[dependencies] -tokio = { version = "1.38.0", features = ["full"] } -warp = { version = "0.4.2", features = ["server", "multipart"] } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "mysql", "chrono", "uuid", "macros"] } -chrono = { version = "0.4", features = ["serde"] } -tracing = "0.1" -tracing-subscriber = "0.3" -dotenvy = "0.15.7" # Switched from unmaintained 'dotenv' -anyhow = "1.0" -uuid = { version = "1", features = ["serde", "v4"] } -reqwest = { version = "0.12.24", features = ["json", "rustls-tls"] } -async-trait = "0.1" -tokio-util = "0.7" -futures-util = "0.3" -lazy_static = "1.4" -bytes = "1.4" -pdf-extract = "0.6" -base64 = "0.22" diff --git a/rust-engine/DEMODETAILS.md b/rust-engine/DEMODETAILS.md deleted file mode 100644 index a8a2572..0000000 --- a/rust-engine/DEMODETAILS.md +++ /dev/null @@ -1,48 +0,0 @@ -## Demo Runbook: ISS Systems (3-minute showcase) - -This demo uses ~20 public NASA PDFs covering ISS Electrical Power, ECLSS, Avionics, and Structures. They live in `rust-engine/demo-data` and are automatically ingested via the server. - -### 1) Seed demo data (one-click) - -- Trigger ingestion (cloud): POST `/api/files/import-demo` (UI button available when `?debug=1` is present) -- The backend copies PDFs into storage, inserts DB rows with `pending_analysis = true`, and the FileWorker processes them. -- Processing pipeline per file: - - Gemini Flash β†’ comprehensive description (facts/keywords/components) - - Gemini Pro β†’ deep vector graph data (keywords/use cases/relationships) - - Embed + upsert to Qdrant, mark file ready (`pending_analysis = false`) - -Tip: You can list files at `GET /api/files/list`. Ready files will start to appear as analysis completes. - -### 2) Showcase flow (suggested script) - -1. β€œWe ingested real ISS technical PDFs. The worker analyzes each file with Gemini and builds vector graph data for robust retrieval.” -2. Show the files list. Point out a couple of recognizable titles. -3. Run two queries (examples below) and open their results (the app calls `POST /api/query/create` then polls `/api/query/result`). -4. Highlight the grounded answer: β€˜related_files’, β€˜relationships’, and β€˜final_answer’ fields. -5. Call out that if info isn’t present in the PDFs, the system explicitly states uncertainty (no guessing). - -### 3) Demo queries (pick 2–3) - -- Electrical Power System (EPS) - - β€œTrace the power path from the P6 solar array to the BCDU. Where are likely ground fault points?” - - β€œWhat is the role of the DC Switching Unit in array power management?” -- ECLSS - - β€œWhich modules are part of water recovery, and how does the Oxygen Generator Assembly interface?” - - β€œSummarize the CDRA cycle and downstream subsystems it impacts.” -- C&DH / Avionics - - β€œIn the US Lab, a blue/white wire connects to MDM β€˜LAB1’. What are possible data pathways?” - - β€œDescribe the onboard LAN segments and links to MDMs.” -- Structures / Robotics - - β€œWhere does the Latching End Effector connect on S1 truss?” - - β€œWhat is the Mobile Transporter’s role in SSRMS operations?” - -### 4) Reset/refresh (optional) - -- POST `/api/files/import-demo?force=1` to overwrite by filename and re-queue analysis. - -### Appendix: Example sources - -- EPS: 20110014867, 20040171627, 19900007297, 20120002931, 20100029672 -- ECLSS: 20170008316, 20070019910, 20080039691, 20100029191, 20070019929 -- C&DH: 20000012543, 20100029690, 19950014639, 20010023477, 19980227289 -- Structures/Robotics: 20020054238, 20010035542, 20140001008, Destiny fact sheet, 20020088289 \ No newline at end of file diff --git a/rust-engine/Dockerfile b/rust-engine/Dockerfile deleted file mode 100644 index cf22a44..0000000 --- a/rust-engine/Dockerfile +++ /dev/null @@ -1,75 +0,0 @@ -# syntax=docker/dockerfile:1.7 -# rust-engine/Dockerfile - -# --- Stage 1: Builder --- -# (No changes needed in this stage) -FROM rust:slim AS builder -WORKDIR /usr/src/app - -RUN apt-get update && apt-get install -y --no-install-recommends \ - pkg-config \ - libssl-dev \ - curl \ - build-essential \ - ca-certificates \ - && rm -rf /var/lib/apt/lists/* - -ARG RUSTUP_TOOLCHAIN= -ENV PATH="/usr/local/cargo/bin:${PATH}" - -COPY Cargo.toml Cargo.lock rust-toolchain.toml ./ - -RUN set -eux; \ - if [ -n "${RUSTUP_TOOLCHAIN}" ]; then \ - if ! rustup toolchain list | grep -q "^${RUSTUP_TOOLCHAIN}"; then \ - rustup toolchain install "${RUSTUP_TOOLCHAIN}"; \ - fi; \ - rustup default "${RUSTUP_TOOLCHAIN}"; \ - else \ - if [ -f rust-toolchain.toml ]; then \ - TOOLCHAIN=$(sed -n 's/^channel *= *"\(.*\)"/\1/p' rust-toolchain.toml | head -n1); \ - if [ -n "$TOOLCHAIN" ]; then \ - if ! rustup toolchain list | grep -q "^$TOOLCHAIN"; then \ - rustup toolchain install "$TOOLCHAIN"; \ - fi; \ - rustup default "$TOOLCHAIN"; \ - fi; \ - fi; \ - fi; \ - rustup show active-toolchain || true - -RUN mkdir -p src && echo "fn main() { println!(\"cargo cache build\"); }" > src/main.rs - -RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \ - --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \ - cargo fetch - -RUN rm -f src/main.rs -COPY src ./src -RUN cargo build --release --locked - -# --- Stage 2: Final, small image --- - -FROM debian:bookworm-slim -RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && rm -rf /var/lib/apt/lists/* - -RUN useradd --system --uid 1004 --no-create-home --shell /usr/sbin/nologin appuser - -# Copy the compiled binary from the builder stage -COPY --from=builder /usr/src/app/target/release/rust-engine /usr/local/bin/rust-engine - -# --- THIS IS THE FIX --- -# **1. Copy the demo data files from your local machine into the image.** -COPY demo-data /app/demo-data - -# **2. Create other directories and set permissions on everything.** -RUN chown appuser:appuser /usr/local/bin/rust-engine \ - && mkdir -p /var/log /app/storage \ - && touch /var/log/astra-errors.log \ - && chown -R appuser:appuser /var/log /app - -WORKDIR /app -USER appuser - -EXPOSE 8000 -ENTRYPOINT ["/bin/sh", "-c", "/usr/local/bin/rust-engine >> /var/log/astra-errors.log 2>&1"] \ No newline at end of file diff --git a/rust-engine/README.md b/rust-engine/README.md deleted file mode 100644 index 35e0edc..0000000 --- a/rust-engine/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# Rust Engine API and Worker - -## Overview - -- HTTP API (warp) under /api for file management and query lifecycle -- MySQL for metadata, Qdrant for vector similarity -- Background worker resumes queued work and re-queues stale InProgress jobs at startup - -## Environment variables - -- DATABASE_URL: mysql://USER:PASS@HOST:3306/DB -- QDRANT_URL: default -- GEMINI_API_KEY: used for Gemini content generation (optional in demo) -- DEMO_DATA_DIR: path to the folder containing PDF demo data (default resolves to `demo-data` under the repo or `/app/demo-data` in containers) -- ASTRA_STORAGE: directory for uploaded file blobs (default `/app/storage`) -- AUTO_IMPORT_DEMO: set to `false`, `0`, `off`, or `no` to disable automatic demo import at startup (defaults to `true`) - -## Endpoints (JSON) - -- POST /api/files (multipart) - - Form: file=@path - - Response: {"success": true} - -- GET /api/files/list - - Response: {"files": [{"id","filename","path","storage_url","description"}]} - -- POST /api/files/import-demo[?force=1] - - Copies PDFs from the demo directory into storage and queues them for analysis. - - Response: {"imported": N, "skipped": M, "files_found": K, "source_dir": "...", "attempted_paths": [...], "force": bool} - - `force=1` deletes prior records with the same filename before re-importing. - -- GET /api/files/delete?id= - - Response: {"deleted": true|false} - -- POST /api/query/create - - Body: {"q": "text", "top_k": 5} - - Response: {"id": "uuid"} - -- GET /api/query/status?id= - - Response: {"status": "Queued"|"InProgress"|"Completed"|"Cancelled"|"Failed"|"not_found"} - -- GET /api/query/result?id= - - Response (Completed): - { - "result": { - "summary": "Found N related files", - "related_files": [ - {"id","filename","path","description","score"} - ], - "relationships": "...", - "final_answer": "..." - } - } - -- GET /api/query/cancel?id= - - Response: {"cancelled": true} - -## Worker behavior - -- Ensures Qdrant collection exists (dim 64, cosine) -- Re-queues InProgress older than 10 minutes -- Processing stages: - 1) Set InProgress - 2) Embed query text (demo now; pluggable Gemini later) - 3) Search Qdrant top_k (default 5) - 4) Join file metadata (MySQL) - 5) Gemini step: relationship analysis (strictly from provided files) - 6) Gemini step: final answer (no speculation; say unknown if insufficient) - 7) Persist result (JSON) and set Completed - - Checks for cancellation between stages - -## Local quickstart - -1. docker compose up -d mysql qdrant -2. set env DATABASE_URL and QDRANT_URL -3. cargo run -4. (optional) import demo PDFs - - Populate a folder with PDFs under `rust-engine/demo-data` (or point `DEMO_DATA_DIR` to a custom path). The server auto-resolves common locations such as the repo root, `/app/demo-data`, and the working directory when running in Docker. When the engine boots it automatically attempts this import (can be disabled by setting `AUTO_IMPORT_DEMO=false`). - - Call the endpoint: - - POST - - Optional query `?force=1` to overwrite existing by filename. The JSON response also echoes where the engine looked (`source_dir`, `attempted_paths`) and how many PDFs were detected (`files_found`) so misconfigurations are easy to spot. Imported files are written to the shared `/app/storage` volume; the web-app container mounts this volume read-only and serves the contents at `/storage/`. - - Or run the PowerShell helper: - - `./scripts/import_demo.ps1` (adds all PDFs in demo-data) - - `./scripts/import_demo.ps1 -Force` (overwrite existing) - -## Notes - -- Replace demo embeddings with real Gemini calls for production -- Add auth to endpoints if needed (API key/JWT) diff --git a/rust-engine/demo-data/132-1-Final.pdf b/rust-engine/demo-data/132-1-Final.pdf deleted file mode 100644 index ac0ce14..0000000 Binary files a/rust-engine/demo-data/132-1-Final.pdf and /dev/null differ diff --git a/rust-engine/demo-data/179225main_iss_poster_back.pdf b/rust-engine/demo-data/179225main_iss_poster_back.pdf deleted file mode 100644 index 9386c43..0000000 Binary files a/rust-engine/demo-data/179225main_iss_poster_back.pdf and /dev/null differ diff --git a/rust-engine/demo-data/19790004570.pdf b/rust-engine/demo-data/19790004570.pdf deleted file mode 100644 index a0d22b5..0000000 Binary files a/rust-engine/demo-data/19790004570.pdf and /dev/null differ diff --git a/rust-engine/demo-data/19880012104.pdf b/rust-engine/demo-data/19880012104.pdf deleted file mode 100644 index d7f48ea..0000000 Binary files a/rust-engine/demo-data/19880012104.pdf and /dev/null differ diff --git a/rust-engine/demo-data/19890016674.pdf b/rust-engine/demo-data/19890016674.pdf deleted file mode 100644 index 0e315d6..0000000 Binary files a/rust-engine/demo-data/19890016674.pdf and /dev/null differ diff --git a/rust-engine/demo-data/19920015843.pdf b/rust-engine/demo-data/19920015843.pdf deleted file mode 100644 index 53065cf..0000000 Binary files a/rust-engine/demo-data/19920015843.pdf and /dev/null differ diff --git a/rust-engine/demo-data/19950014639.pdf b/rust-engine/demo-data/19950014639.pdf deleted file mode 100644 index 5b200c2..0000000 Binary files a/rust-engine/demo-data/19950014639.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20040171627.pdf b/rust-engine/demo-data/20040171627.pdf deleted file mode 100644 index fe3eb42..0000000 Binary files a/rust-engine/demo-data/20040171627.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20050207388.pdf b/rust-engine/demo-data/20050207388.pdf deleted file mode 100644 index 23ba263..0000000 Binary files a/rust-engine/demo-data/20050207388.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20050210002.pdf b/rust-engine/demo-data/20050210002.pdf deleted file mode 100644 index c7d6e7e..0000000 Binary files a/rust-engine/demo-data/20050210002.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20080014096.pdf b/rust-engine/demo-data/20080014096.pdf deleted file mode 100644 index 2a23fcf..0000000 Binary files a/rust-engine/demo-data/20080014096.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20100029672.pdf b/rust-engine/demo-data/20100029672.pdf deleted file mode 100644 index d639021..0000000 Binary files a/rust-engine/demo-data/20100029672.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20110014867.pdf b/rust-engine/demo-data/20110014867.pdf deleted file mode 100644 index 609a6df..0000000 Binary files a/rust-engine/demo-data/20110014867.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20120002931.pdf b/rust-engine/demo-data/20120002931.pdf deleted file mode 100644 index deb27bf..0000000 Binary files a/rust-engine/demo-data/20120002931.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20190028718.pdf b/rust-engine/demo-data/20190028718.pdf deleted file mode 100644 index d5211e8..0000000 Binary files a/rust-engine/demo-data/20190028718.pdf and /dev/null differ diff --git a/rust-engine/demo-data/20200003149.pdf b/rust-engine/demo-data/20200003149.pdf deleted file mode 100644 index 59ef117..0000000 Binary files a/rust-engine/demo-data/20200003149.pdf and /dev/null differ diff --git a/rust-engine/demo-data/473486main_iss_atcs_overview.pdf b/rust-engine/demo-data/473486main_iss_atcs_overview.pdf deleted file mode 100644 index d742279..0000000 Binary files a/rust-engine/demo-data/473486main_iss_atcs_overview.pdf and /dev/null differ diff --git a/rust-engine/demo-data/8Mod6Prob1.pdf b/rust-engine/demo-data/8Mod6Prob1.pdf deleted file mode 100644 index d719e74..0000000 Binary files a/rust-engine/demo-data/8Mod6Prob1.pdf and /dev/null differ diff --git a/rust-engine/demo-data/ICES_2023_311 final 5 15 23.pdf b/rust-engine/demo-data/ICES_2023_311 final 5 15 23.pdf deleted file mode 100644 index e10f860..0000000 Binary files a/rust-engine/demo-data/ICES_2023_311 final 5 15 23.pdf and /dev/null differ diff --git a/rust-engine/demo-data/ISwSIS Software Standard_NASA-102020_Draft.docx.pdf b/rust-engine/demo-data/ISwSIS Software Standard_NASA-102020_Draft.docx.pdf deleted file mode 100644 index 62f2eec..0000000 Binary files a/rust-engine/demo-data/ISwSIS Software Standard_NASA-102020_Draft.docx.pdf and /dev/null differ diff --git a/rust-engine/rust-toolchain.toml b/rust-engine/rust-toolchain.toml deleted file mode 100644 index 4386630..0000000 --- a/rust-engine/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "1.88.0" -# components = ["rustfmt", "clippy"] -# targets = ["x86_64-unknown-linux-gnu"] diff --git a/rust-engine/src/api.rs b/rust-engine/src/api.rs deleted file mode 100644 index 585ad40..0000000 --- a/rust-engine/src/api.rs +++ /dev/null @@ -1,455 +0,0 @@ -use crate::storage; -use crate::vector_db::QdrantClient; -use anyhow::Result; -use bytes::Buf; -use futures_util::TryStreamExt; -use serde::{Deserialize, Serialize}; -use sqlx::{MySqlPool, Row}; -use tracing::{debug, info, warn}; -use warp::{multipart::FormData, Filter, Rejection, Reply}; - -#[derive(Debug, Deserialize)] -struct DeleteQuery { - id: String, -} - -#[derive(Debug, Serialize)] -pub struct DemoImportSummary { - pub imported: usize, - pub skipped: usize, - pub files_found: usize, - #[serde(skip_serializing_if = "Option::is_none")] - pub source_dir: Option, - pub attempted_paths: Vec, - pub force: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -pub async fn perform_demo_import(force: bool, pool: &MySqlPool) -> Result { - use anyhow::Context; - use std::fs; - use std::path::PathBuf; - - let demo_dir_setting = - std::env::var("DEMO_DATA_DIR").unwrap_or_else(|_| "demo-data".to_string()); - info!(force, requested_dir = %demo_dir_setting, "demo import requested"); - - let base = std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); - - // Build a list of plausible demo-data locations so local runs and containers both work. - let mut candidates: Vec = Vec::new(); - let configured = PathBuf::from(&demo_dir_setting); - let mut push_candidate = |path: PathBuf| { - if !candidates.iter().any(|existing| existing == &path) { - candidates.push(path); - } - }; - - push_candidate(base.join(&configured)); - push_candidate(PathBuf::from(&demo_dir_setting)); - push_candidate(base.join("rust-engine").join(&configured)); - push_candidate(base.join("rust-engine").join("demo-data")); - push_candidate(base.join("demo-data")); - if let Ok(exe_path) = std::env::current_exe() { - if let Some(exe_dir) = exe_path.parent() { - push_candidate(exe_dir.join(&configured)); - push_candidate(exe_dir.join("demo-data")); - push_candidate(exe_dir.join("rust-engine").join(&configured)); - } - } - - let mut attempted: Vec = Vec::new(); - let mut resolved_dir: Option = None; - for candidate in candidates { - debug!(candidate = %candidate.display(), "evaluating demo import path candidate"); - if candidate.exists() && candidate.is_dir() { - resolved_dir = Some(candidate.clone()); - break; - } - attempted.push(candidate); - } - - let attempted_paths: Vec = attempted.iter().map(|p| p.display().to_string()).collect(); - let mut summary = DemoImportSummary { - imported: 0, - skipped: 0, - files_found: 0, - source_dir: resolved_dir.as_ref().map(|p| p.display().to_string()), - attempted_paths, - force, - error: None, - }; - - let src_dir = match resolved_dir { - Some(path) => path, - None => { - summary.error = - Some("demo dir not found; set DEMO_DATA_DIR or bind mount demo PDFs".to_string()); - warn!( - attempted_paths = ?summary.attempted_paths, - "demo import skipped; source directory not found" - ); - return Ok(summary); - } - }; - - summary.source_dir = Some(src_dir.display().to_string()); - info!( - source = %summary.source_dir.as_deref().unwrap_or_default(), - attempted_paths = ?summary.attempted_paths, - "demo import source resolved" - ); - - for entry in fs::read_dir(&src_dir).with_context(|| format!("reading {:?}", src_dir))? { - let entry = entry.with_context(|| format!("reading entry in {:?}", src_dir))?; - let path = entry.path(); - if path - .extension() - .and_then(|e| e.to_str()) - .map(|e| e.eq_ignore_ascii_case("pdf")) - .unwrap_or(false) - { - summary.files_found += 1; - let filename = path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or("unknown.pdf") - .to_string(); - - if !force { - if let Some(_) = sqlx::query("SELECT id FROM files WHERE filename = ?") - .bind(&filename) - .fetch_optional(pool) - .await? - { - summary.skipped += 1; - info!(%filename, "skipping demo import; already present"); - continue; - } - } - - let data = fs::read(&path).with_context(|| format!("reading {:?}", path))?; - let stored_path = storage::save_file(&filename, &data)?; - info!(%filename, dest = %stored_path.to_string_lossy(), "demo file copied to storage"); - - let id = uuid::Uuid::new_v4().to_string(); - - if force { - sqlx::query("DELETE FROM files WHERE filename = ?") - .bind(&filename) - .execute(pool) - .await?; - info!(%filename, "existing file records removed due to force import"); - } - - sqlx::query("INSERT INTO files (id, filename, path, description, pending_analysis, analysis_status) VALUES (?, ?, ?, ?, ?, 'Queued')") - .bind(&id) - .bind(&filename) - .bind(stored_path.to_string_lossy().to_string()) - .bind(Option::::None) - .bind(true) - .execute(pool) - .await?; - info!(%filename, file_id = %id, "demo file inserted into database"); - - info!(%filename, file_id = %id, "demo file queued for analysis by worker"); - - summary.imported += 1; - } else { - debug!(path = %path.display(), "skipping non-PDF entry during demo import"); - } - } - - let source_label = summary.source_dir.as_deref().unwrap_or("unknown"); - - if summary.files_found == 0 { - warn!(source = %source_label, "demo import located zero PDFs"); - } - - info!( - source = %source_label, - files_found = summary.files_found, - attempted_paths = ?summary.attempted_paths, - imported = summary.imported, - skipped = summary.skipped, - force, - "demo import completed" - ); - - Ok(summary) -} - -pub fn routes(pool: MySqlPool) -> impl Filter + Clone { - let pool_filter = warp::any().map(move || pool.clone()); - - // Import demo files from demo-data directory - let import_demo = warp::path!("files" / "import-demo") - .and(warp::post()) - .and( - warp::query::>() - .or(warp::any().map(|| std::collections::HashMap::new())) - .unify(), - ) - .and(pool_filter.clone()) - .and_then(handle_import_demo); - - // Upload file - let upload = warp::path("files") - .and(warp::post()) - .and(warp::multipart::form().max_length(50_000_000)) // 50MB per part default; storage is filesystem-backed - .and(pool_filter.clone()) - .and_then(handle_upload); - - // Delete file - let delete = warp::path!("files" / "delete") - .and(warp::get()) - .and(warp::query::()) - .and(pool_filter.clone()) - .and_then(handle_delete); - - // List files - let list = warp::path!("files" / "list") - .and(warp::get()) - .and(pool_filter.clone()) - .and_then(handle_list); - - // Create query - let create_q = warp::path!("query" / "create") - .and(warp::post()) - .and(warp::body::json()) - .and(pool_filter.clone()) - .and_then(handle_create_query); - - // Query status - let status = warp::path!("query" / "status") - .and(warp::get()) - .and(warp::query::()) - .and(pool_filter.clone()) - .and_then(handle_query_status); - - // Query result - let result = warp::path!("query" / "result") - .and(warp::get()) - .and(warp::query::()) - .and(pool_filter.clone()) - .and_then(handle_query_result); - - // Cancel - let cancel = warp::path!("query" / "cancel") - .and(warp::get()) - .and(warp::query::()) - .and(pool_filter.clone()) - .and_then(handle_cancel_query); - - let api = upload - .or(import_demo) - .or(delete) - .or(list) - .or(create_q) - .or(status) - .or(result) - .or(cancel); - warp::path("api").and(api) -} - -async fn handle_upload(mut form: FormData, pool: MySqlPool) -> Result { - let mut created_files = Vec::new(); - while let Some(field) = form.try_next().await.map_err(|_| warp::reject())? { - let _name = field.name().to_string(); - let filename = field - .filename() - .map(|s| s.to_string()) - .unwrap_or_else(|| format!("upload-{}", uuid::Uuid::new_v4())); - - // Read stream of Buf into a Vec - let data = field - .stream() - .map_ok(|mut buf| { - let mut v = Vec::new(); - while buf.has_remaining() { - let chunk = buf.chunk(); - v.extend_from_slice(chunk); - let n = chunk.len(); - buf.advance(n); - } - v - }) - .try_fold(Vec::new(), |mut acc, chunk_vec| async move { - acc.extend_from_slice(&chunk_vec); - Ok(acc) - }) - .await - .map_err(|_| warp::reject())?; - - // Save file - let path = storage::save_file(&filename, &data).map_err(|_| warp::reject())?; - - // Insert file record with pending_analysis = true, description = NULL - let id = uuid::Uuid::new_v4().to_string(); - sqlx::query("INSERT INTO files (id, filename, path, description, pending_analysis, analysis_status) VALUES (?, ?, ?, ?, ?, 'Queued')") - .bind(&id) - .bind(&filename) - .bind(path.to_str().unwrap()) - .bind(Option::::None) - .bind(true) - .execute(&pool) - .await - .map_err(|e| { - tracing::error!("DB insert error: {}", e); - warp::reject() - })?; - created_files.push(serde_json::json!({ - "id": id, - "filename": filename, - "pending_analysis": true, - "analysis_status": "Queued" - })); - } - - Ok(warp::reply::json(&serde_json::json!({ - "uploaded": created_files.len(), - "files": created_files - }))) -} - -async fn handle_import_demo( - params: std::collections::HashMap, - pool: MySqlPool, -) -> Result { - let force = params - .get("force") - .map(|v| v == "1" || v.eq_ignore_ascii_case("true")) - .unwrap_or(false); - - match perform_demo_import(force, &pool).await { - Ok(summary) => Ok(warp::reply::json(&summary)), - Err(err) => { - tracing::error!(error = %err, "demo import failed"); - let fallback = DemoImportSummary { - imported: 0, - skipped: 0, - files_found: 0, - source_dir: None, - attempted_paths: Vec::new(), - force, - error: Some(err.to_string()), - }; - Ok(warp::reply::json(&fallback)) - } - } -} - -async fn handle_delete(q: DeleteQuery, pool: MySqlPool) -> Result { - if let Some(row) = sqlx::query("SELECT path FROM files WHERE id = ?") - .bind(&q.id) - .fetch_optional(&pool) - .await - .map_err(|_| warp::reject())? - { - let path: String = row.get("path"); - let _ = storage::delete_file(std::path::Path::new(&path)); - // Remove from Qdrant - let qdrant_url = - std::env::var("QDRANT_URL").unwrap_or_else(|_| "http://qdrant:6333".to_string()); - let qdrant = QdrantClient::new(&qdrant_url); - let _ = qdrant.delete_point(&q.id).await; - let _ = sqlx::query("DELETE FROM files WHERE id = ?") - .bind(&q.id) - .execute(&pool) - .await; - return Ok(warp::reply::json(&serde_json::json!({"deleted": true}))); - } - Ok(warp::reply::json(&serde_json::json!({"deleted": false}))) -} - -async fn handle_list(pool: MySqlPool) -> Result { - let rows = sqlx::query("SELECT id, filename, path, description, pending_analysis, analysis_status FROM files ORDER BY created_at DESC LIMIT 500") - .fetch_all(&pool) - .await - .map_err(|e| { - tracing::error!("DB list error: {}", e); - warp::reject() - })?; - let files: Vec = rows - .into_iter() - .map(|r| { - let id: String = r.get("id"); - let filename: String = r.get("filename"); - let path: String = r.get("path"); - let description: Option = r.get("description"); - let pending: bool = r.get("pending_analysis"); - let status: Option = r.try_get("analysis_status").ok(); - let storage_url = format!("/storage/{}", filename); - serde_json::json!({ - "id": id, - "filename": filename, - "path": path, - "storage_url": storage_url, - "description": description, - "pending_analysis": pending, - "analysis_status": status - }) - }) - .collect(); - - Ok(warp::reply::json(&serde_json::json!({"files": files}))) -} - -async fn handle_create_query( - body: serde_json::Value, - pool: MySqlPool, -) -> Result { - // Insert query as queued, worker will pick it up - let id = uuid::Uuid::new_v4().to_string(); - let payload = body; - sqlx::query("INSERT INTO queries (id, status, payload) VALUES (?, 'Queued', ?)") - .bind(&id) - .bind(payload) - .execute(&pool) - .await - .map_err(|e| { - tracing::error!("DB insert query error: {}", e); - warp::reject() - })?; - - Ok(warp::reply::json(&serde_json::json!({"id": id}))) -} - -async fn handle_query_status(q: DeleteQuery, pool: MySqlPool) -> Result { - if let Some(row) = sqlx::query("SELECT status FROM queries WHERE id = ?") - .bind(&q.id) - .fetch_optional(&pool) - .await - .map_err(|_| warp::reject())? - { - let status: String = row.get("status"); - return Ok(warp::reply::json(&serde_json::json!({"status": status}))); - } - Ok(warp::reply::json( - &serde_json::json!({"status": "not_found"}), - )) -} - -async fn handle_query_result(q: DeleteQuery, pool: MySqlPool) -> Result { - if let Some(row) = sqlx::query("SELECT result FROM queries WHERE id = ?") - .bind(&q.id) - .fetch_optional(&pool) - .await - .map_err(|_| warp::reject())? - { - let result: Option = row.get("result"); - return Ok(warp::reply::json(&serde_json::json!({"result": result}))); - } - Ok(warp::reply::json(&serde_json::json!({"result": null}))) -} - -async fn handle_cancel_query(q: DeleteQuery, pool: MySqlPool) -> Result { - // Mark as cancelled; worker must check status before heavy steps - sqlx::query("UPDATE queries SET status = 'Cancelled' WHERE id = ?") - .bind(&q.id) - .execute(&pool) - .await - .map_err(|_| warp::reject())?; - Ok(warp::reply::json(&serde_json::json!({"cancelled": true}))) -} diff --git a/rust-engine/src/db.rs b/rust-engine/src/db.rs deleted file mode 100644 index 3245814..0000000 --- a/rust-engine/src/db.rs +++ /dev/null @@ -1,42 +0,0 @@ -use sqlx::MySqlPool; -use tracing::info; - -pub async fn init_db(database_url: &str) -> Result { - let pool = MySqlPool::connect(database_url).await?; - - // Create tables if they don't exist. Simple schema for demo/hackathon use. - // Note: MySQL requires separate statements for each CREATE TABLE - sqlx::query( - r#" - CREATE TABLE IF NOT EXISTS files ( - id VARCHAR(36) PRIMARY KEY, - filename TEXT NOT NULL, - path TEXT NOT NULL, - description TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - pending_analysis BOOLEAN DEFAULT TRUE, - analysis_status VARCHAR(32) DEFAULT 'Queued' - ) - "#, - ) - .execute(&pool) - .await?; - - sqlx::query( - r#" - CREATE TABLE IF NOT EXISTS queries ( - id VARCHAR(36) PRIMARY KEY, - status VARCHAR(32) NOT NULL, - payload JSON, - result JSON, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP - ) - "#, - ) - .execute(&pool) - .await?; - - info!("Database initialized"); - Ok(pool) -} diff --git a/rust-engine/src/file_worker.rs b/rust-engine/src/file_worker.rs deleted file mode 100644 index 339edc0..0000000 --- a/rust-engine/src/file_worker.rs +++ /dev/null @@ -1,269 +0,0 @@ -use crate::gemini_client::{demo_text_embedding, generate_text_with_model, DEMO_EMBED_DIM}; -use crate::vector; -use crate::vector_db::QdrantClient; -use anyhow::{anyhow, Context, Result}; -use base64::{engine::general_purpose::STANDARD as BASE64_STANDARD, Engine as _}; -use pdf_extract::extract_text; -use sqlx::MySqlPool; -use std::path::PathBuf; -use tracing::{error, info, warn}; - -pub struct FileWorker { - pool: MySqlPool, - qdrant: QdrantClient, -} - -impl FileWorker { - pub fn new(pool: MySqlPool) -> Self { - let qdrant_url = - std::env::var("QDRANT_URL").unwrap_or_else(|_| "http://qdrant:6333".to_string()); - let qdrant = QdrantClient::new(&qdrant_url); - Self { pool, qdrant } - } - - pub async fn run(&self) { - info!("FileWorker starting"); - if let Err(e) = self.qdrant.ensure_files_collection(DEMO_EMBED_DIM).await { - error!("Failed to ensure Qdrant collection: {}", e); - } - loop { - match self.fetch_and_claim().await { - Ok(Some(fid)) => { - info!("Processing file {}", fid); - if let Err(e) = self.process_file(&fid).await { - error!("Error processing file {}: {}", fid, e); - if let Err(mark_err) = self.mark_failed(&fid, &format!("{}", e)).await { - error!("Failed to mark file {} as failed: {}", fid, mark_err); - } - } - } - Ok(None) => { - tokio::time::sleep(std::time::Duration::from_secs(2)).await; - } - Err(e) => { - error!("FileWorker fetch error: {}", e); - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - } - } - } - } - - async fn fetch_and_claim(&self) -> Result> { - // Claim files that are queued or stuck in progress for >10min - if let Some(row) = sqlx::query( - "SELECT id FROM files WHERE (analysis_status = 'Queued' OR (analysis_status = 'InProgress' AND created_at < (NOW() - INTERVAL 10 MINUTE))) AND pending_analysis = TRUE LIMIT 1" - ) - .fetch_optional(&self.pool) - .await? { - use sqlx::Row; - let id: String = row.get("id"); - // Mark as in-progress - let _ = sqlx::query("UPDATE files SET analysis_status = 'InProgress' WHERE id = ?") - .bind(&id) - .execute(&self.pool) - .await?; - Ok(Some(id)) - } else { - Ok(None) - } - } - - async fn process_file(&self, file_id: &str) -> Result<()> { - use sqlx::Row; - let row = sqlx::query("SELECT filename, path FROM files WHERE id = ?") - .bind(file_id) - .fetch_one(&self.pool) - .await?; - let filename: String = row.get("filename"); - let path: String = row.get("path"); - - let (file_excerpt, truncated) = match extract_file_excerpt(&path).await { - Ok(res) => res, - Err(err) => { - error!(file_id, %filename, %path, error = ?err, "failed to extract text from file; continuing with filename only"); - (String::new(), false) - } - }; - if file_excerpt.is_empty() { - warn!(file_id, %filename, %path, "extracted excerpt is empty; prompts may lack context"); - } - - let (raw_base64, raw_truncated) = match read_file_base64(&path).await { - Ok(tuple) => tuple, - Err(err) => { - warn!(file_id, %filename, %path, error = ?err, "failed to read raw file bytes for prompt"); - (String::new(), false) - } - }; - - let excerpt_note = if truncated { - "(excerpt truncated for prompt size)" - } else { - "" - }; - - let raw_note = if raw_truncated { - "(base64 truncated to first 512KB)" - } else { - "(base64)" - }; - - // Stage 1: Gemini 2.5 Flash for description - let mut desc_prompt = format!( - "You are reviewing the PDF file '{filename}'. Use the following extracted text {excerpt_note} to produce a concise, factual description and key highlights that will help downstream search and reasoning.\n\n--- BEGIN EXCERPT ---\n{}\n--- END EXCERPT ---", - file_excerpt - ); - if !raw_base64.is_empty() { - desc_prompt.push_str(&format!( - "\n\n--- BEGIN RAW FILE {raw_note} ---\n{}\n--- END RAW FILE ---", - raw_base64 - )); - } - let desc = generate_text_with_model("gemini-2.5-flash", &desc_prompt) - .await - .unwrap_or_else(|e| format!("[desc error: {}]", e)); - sqlx::query( - "UPDATE files SET description = ?, analysis_status = 'InProgress' WHERE id = ?", - ) - .bind(&desc) - .bind(file_id) - .execute(&self.pool) - .await?; - - // Stage 2: Gemini 2.5 Pro for deep vector graph data - let mut vector_prompt = format!( - "You are constructing vector search metadata for the PDF file '{filename}'.\nCurrent description: {desc}\nUse the extracted text {excerpt_note} below to derive precise keywords, thematic clusters, and relationships that are explicitly supported by the content. Provide richly structured bullet points grouped by themes.\n\n--- BEGIN EXCERPT ---\n{}\n--- END EXCERPT ---", - file_excerpt - ); - if !raw_base64.is_empty() { - vector_prompt.push_str(&format!( - "\n\n--- BEGIN RAW FILE {raw_note} ---\n{}\n--- END RAW FILE ---", - raw_base64 - )); - } - let vector_graph = generate_text_with_model("gemini-2.5-pro", &vector_prompt) - .await - .unwrap_or_else(|e| format!("[vector error: {}]", e)); - - // Stage 3: Embed and upsert to Qdrant - let emb = demo_text_embedding(&vector_graph).await?; - match self.qdrant.upsert_point(file_id, emb.clone()).await { - Ok(_) => { - let _ = vector::store_embedding(file_id, emb.clone()); - } - Err(err) => { - error!("Qdrant upsert failed for {}: {}", file_id, err); - let _ = vector::store_embedding(file_id, emb); - } - } - - // Mark file as ready - sqlx::query( - "UPDATE files SET pending_analysis = FALSE, analysis_status = 'Completed' WHERE id = ?", - ) - .bind(file_id) - .execute(&self.pool) - .await?; - Ok(()) - } - - async fn mark_failed(&self, file_id: &str, reason: &str) -> Result<()> { - sqlx::query( - "UPDATE files SET analysis_status = 'Failed', pending_analysis = TRUE WHERE id = ?", - ) - .bind(file_id) - .execute(&self.pool) - .await?; - sqlx::query("UPDATE files SET description = COALESCE(description, ?) WHERE id = ?") - .bind(format!("[analysis failed: {}]", reason)) - .bind(file_id) - .execute(&self.pool) - .await?; - Ok(()) - } -} - -// Maximum number of characters from the extracted text to include in prompts. -const MAX_EXCERPT_CHARS: usize = 4000; -const MAX_RAW_BYTES: usize = 512 * 1024; // limit base64 payload fed into prompts - -async fn extract_file_excerpt(path: &str) -> Result<(String, bool)> { - let path_buf = PathBuf::from(path); - let extension = path_buf - .extension() - .and_then(|e| e.to_str()) - .map(|s| s.to_ascii_lowercase()) - .unwrap_or_default(); - - let raw_text = if extension == "pdf" { - let pdf_path = path_buf.clone(); - tokio::task::spawn_blocking(move || extract_text(&pdf_path)) - .await - .map_err(|e| anyhow!("pdf text extraction task panicked: {e}"))?? - } else { - let bytes = tokio::fs::read(&path_buf) - .await - .with_context(|| format!("reading file bytes from {path}"))?; - String::from_utf8_lossy(&bytes).into_owned() - }; - - let cleaned = raw_text.replace('\r', ""); - let condensed = collapse_whitespace(&cleaned); - let (excerpt, truncated) = truncate_to_chars(&condensed, MAX_EXCERPT_CHARS); - - Ok((excerpt, truncated)) -} - -fn truncate_to_chars(text: &str, max_chars: usize) -> (String, bool) { - if max_chars == 0 { - return (String::new(), !text.is_empty()); - } - - let mut result = String::new(); - let mut chars = text.chars(); - for _ in 0..max_chars { - match chars.next() { - Some(ch) => result.push(ch), - None => return (result, false), - } - } - - if chars.next().is_some() { - result.push('…'); - (result, true) - } else { - (result, false) - } -} - -fn collapse_whitespace(input: &str) -> String { - let mut output = String::with_capacity(input.len()); - let mut prev_was_ws = false; - for ch in input.chars() { - if ch.is_whitespace() { - if !prev_was_ws { - output.push(' '); - } - prev_was_ws = true; - } else { - prev_was_ws = false; - output.push(ch); - } - } - output.trim().to_string() -} - -async fn read_file_base64(path: &str) -> Result<(String, bool)> { - let bytes = tokio::fs::read(path).await?; - if bytes.is_empty() { - return Ok((String::new(), false)); - } - let truncated = bytes.len() > MAX_RAW_BYTES; - let slice = if truncated { - &bytes[..MAX_RAW_BYTES] - } else { - &bytes[..] - }; - let encoded = BASE64_STANDARD.encode(slice); - Ok((encoded, truncated)) -} diff --git a/rust-engine/src/gemini_client.rs b/rust-engine/src/gemini_client.rs deleted file mode 100644 index a74a1a1..0000000 --- a/rust-engine/src/gemini_client.rs +++ /dev/null @@ -1,97 +0,0 @@ -use anyhow::Result; -use reqwest::Client; -use serde::Deserialize; -use serde_json::json; - -// NOTE: This file provides lightweight helpers around the Gemini API. For the -// hackathon demo we fall back to deterministic strings when the API key is not -// configured so the flows still work end-to-end. - -pub const DEMO_EMBED_DIM: usize = 64; - -/// Demo text embedding (replace with real Gemini text embedding API) -pub async fn demo_text_embedding(text: &str) -> Result> { - let mut v = vec![0f32; DEMO_EMBED_DIM]; - for (i, b) in text.as_bytes().iter().enumerate() { - let idx = i % v.len(); - v[idx] += (*b as f32) / 255.0; - } - Ok(v) -} - -/// Generate text using the default model (GEMINI_MODEL or gemini-2.5-pro). -#[allow(dead_code)] -pub async fn generate_text(prompt: &str) -> Result { - let model = std::env::var("GEMINI_MODEL").unwrap_or_else(|_| "gemini-2.5-pro".to_string()); - generate_text_with_model(&model, prompt).await -} - -/// Generate text with an explicit Gemini model. Falls back to a deterministic -/// response when the API key is not set so the demo still runs. -pub async fn generate_text_with_model(model: &str, prompt: &str) -> Result { - let api_key = match std::env::var("GEMINI_API_KEY") { - Ok(k) if !k.is_empty() => k, - _ => { - return Ok(format!( - "[demo] Gemini ({}) not configured. Prompt preview: {}", - model, - truncate(prompt, 240) - )); - } - }; - - let url = format!( - "https://generativelanguage.googleapis.com/v1beta/models/{}:generateContent?key={}", - model, api_key - ); - - let body = json!({ - "contents": [ { "parts": [ { "text": prompt } ] } ] - }); - - let client = Client::new(); - let resp = client.post(&url).json(&body).send().await?; - let status = resp.status(); - let txt = resp.text().await?; - if !status.is_success() { - return Ok(format!( - "[demo] Gemini ({}) error {}: {}", - model, - status, - truncate(&txt, 240) - )); - } - - #[derive(Deserialize)] - struct Part { - text: Option, - } - #[derive(Deserialize)] - struct Content { - parts: Vec, - } - #[derive(Deserialize)] - struct Candidate { - content: Content, - } - #[derive(Deserialize)] - struct Response { - candidates: Option>, - } - - let data: Response = serde_json::from_str(&txt).unwrap_or(Response { candidates: None }); - let out = data - .candidates - .and_then(|mut v| v.pop()) - .and_then(|c| c.content.parts.into_iter().find_map(|p| p.text)) - .unwrap_or_else(|| "[demo] Gemini returned empty response".to_string()); - Ok(out) -} - -fn truncate(s: &str, max: usize) -> String { - if s.len() <= max { - s.to_string() - } else { - format!("{}…", &s[..max]) - } -} diff --git a/rust-engine/src/main.rs b/rust-engine/src/main.rs deleted file mode 100644 index 006e4ce..0000000 --- a/rust-engine/src/main.rs +++ /dev/null @@ -1,87 +0,0 @@ -mod api; -mod db; -mod file_worker; -mod gemini_client; -mod models; -mod storage; -mod vector; -mod vector_db; -mod worker; - -use std::env; -use std::error::Error; -use tracing::{error, info, warn}; -use warp::Filter; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // Initialize tracing - tracing_subscriber::fmt::init(); - - // Load environment variables - dotenvy::dotenv().ok(); - - let database_url = env::var("DATABASE_URL") - .unwrap_or_else(|_| "mysql://astraadmin:password@mysql:3306/astra".to_string()); - - info!("Starting Rust Engine..."); - - // Ensure storage dir - storage::ensure_storage_dir().expect("storage dir"); - - // Initialize DB - let pool = db::init_db(&database_url) - .await - .map_err(|e| -> Box { Box::new(e) })?; - - let auto_import_setting = env::var("AUTO_IMPORT_DEMO").unwrap_or_else(|_| "true".to_string()); - let auto_import = !matches!( - auto_import_setting.trim().to_ascii_lowercase().as_str(), - "0" | "false" | "off" | "no" - ); - if auto_import { - match api::perform_demo_import(false, &pool).await { - Ok(summary) => { - if let Some(err_msg) = summary.error.as_ref() { - warn!(error = %err_msg, "startup demo import completed with warnings"); - } - info!( - imported = summary.imported, - skipped = summary.skipped, - files_found = summary.files_found, - source = summary.source_dir.as_deref().unwrap_or("unknown"), - "startup demo import completed" - ); - } - Err(err) => { - error!(error = %err, "startup demo import failed"); - } - } - } else { - info!("AUTO_IMPORT_DEMO disabled; skipping startup demo import"); - } - - // Spawn query worker - let worker = worker::Worker::new(pool.clone()); - tokio::spawn(async move { worker.run().await }); - - // Spawn file analysis worker - let file_worker = file_worker::FileWorker::new(pool.clone()); - tokio::spawn(async move { file_worker.run().await }); - - // API routes - let api_routes = api::routes(pool.clone()) - .with( - warp::cors() - .allow_any_origin() - .allow_headers(vec!["content-type", "authorization"]) - .allow_methods(vec!["GET", "POST", "PUT", "DELETE", "OPTIONS"]), - ) - .with(warp::log("rust_engine")); - - info!("Rust Engine started on http://0.0.0.0:8000"); - - warp::serve(api_routes).run(([0, 0, 0, 0], 8000)).await; - - Ok(()) -} diff --git a/rust-engine/src/models.rs b/rust-engine/src/models.rs deleted file mode 100644 index 401e883..0000000 --- a/rust-engine/src/models.rs +++ /dev/null @@ -1,67 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct FileRecord { - pub id: String, - pub filename: String, - pub path: String, - pub description: Option, - pub created_at: Option>, - pub pending_analysis: bool, // true if file is not yet ready for search - pub analysis_status: String, // 'Queued', 'InProgress', 'Completed', 'Failed' - pub raw_url: Option, -} - -impl FileRecord { - #[allow(dead_code)] - pub fn new( - filename: impl Into, - path: impl Into, - description: Option, - ) -> Self { - Self { - id: Uuid::new_v4().to_string(), - filename: filename.into(), - path: path.into(), - description, - created_at: None, - pending_analysis: true, - analysis_status: "Queued".to_string(), - raw_url: None, - } - } -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub enum QueryStatus { - Queued, - InProgress, - Completed, - Cancelled, - Failed, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct QueryRecord { - pub id: String, - pub status: QueryStatus, - pub payload: serde_json::Value, - pub result: Option, - pub created_at: Option>, - pub updated_at: Option>, -} - -impl QueryRecord { - pub fn new(payload: serde_json::Value) -> Self { - Self { - id: Uuid::new_v4().to_string(), - status: QueryStatus::Queued, - payload, - result: None, - created_at: None, - updated_at: None, - } - } -} diff --git a/rust-engine/src/storage.rs b/rust-engine/src/storage.rs deleted file mode 100644 index c9e9ea4..0000000 --- a/rust-engine/src/storage.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anyhow::Result; -use std::fs; -use std::io::Write; -use std::path::{Path, PathBuf}; - -pub fn storage_dir() -> PathBuf { - std::env::var("ASTRA_STORAGE") - .map(PathBuf::from) - .unwrap_or_else(|_| PathBuf::from("/app/storage")) -} - -pub fn ensure_storage_dir() -> Result<()> { - let dir = storage_dir(); - if !dir.exists() { - fs::create_dir_all(&dir)?; - } - Ok(()) -} - -pub fn save_file(filename: &str, contents: &[u8]) -> Result { - ensure_storage_dir()?; - let mut path = storage_dir(); - path.push(filename); - let mut f = fs::File::create(&path)?; - f.write_all(contents)?; - Ok(path) -} - -pub fn delete_file(path: &Path) -> Result<()> { - if path.exists() { - fs::remove_file(path)?; - } - Ok(()) -} - -pub fn public_url_for(filename: &str) -> String { - format!("/storage/{}", filename) -} diff --git a/rust-engine/src/vector.rs b/rust-engine/src/vector.rs deleted file mode 100644 index c34b0a3..0000000 --- a/rust-engine/src/vector.rs +++ /dev/null @@ -1,24 +0,0 @@ -use anyhow::Result; -use lazy_static::lazy_static; -use std::collections::HashMap; -use std::sync::Mutex; - -lazy_static! { - static ref VECTOR_STORE: Mutex>> = Mutex::new(HashMap::new()); -} - -pub fn store_embedding(id: &str, emb: Vec) -> Result<()> { - let mut s = VECTOR_STORE.lock().unwrap(); - s.insert(id.to_string(), emb); - Ok(()) -} - -pub fn query_top_k(_query_emb: &[f32], k: usize) -> Result> { - // Very naive: return up to k ids from the store. - let s = VECTOR_STORE.lock().unwrap(); - let mut out = Vec::new(); - for key in s.keys().take(k) { - out.push(key.clone()); - } - Ok(out) -} diff --git a/rust-engine/src/vector_db.rs b/rust-engine/src/vector_db.rs deleted file mode 100644 index b8142a5..0000000 --- a/rust-engine/src/vector_db.rs +++ /dev/null @@ -1,111 +0,0 @@ -use anyhow::Result; -use reqwest::Client; -use serde::Deserialize; -use serde_json::json; - -#[derive(Clone)] -pub struct QdrantClient { - base: String, - client: Client, -} - -impl QdrantClient { - /// Delete a point from collection 'files' by id - pub async fn delete_point(&self, id: &str) -> Result<()> { - let url = format!("{}/collections/files/points/delete", self.base); - let body = json!({ - "points": [id] - }); - let resp = self.client.post(&url).json(&body).send().await?; - let status = resp.status(); - if status.is_success() { - Ok(()) - } else { - let t = resp.text().await.unwrap_or_default(); - Err(anyhow::anyhow!("qdrant delete failed: {} - {}", status, t)) - } - } - pub fn new(base: &str) -> Self { - Self { - base: base.trim_end_matches('/').to_string(), - client: Client::new(), - } - } - - /// Upsert a point into collection `files` with id and vector - pub async fn upsert_point(&self, id: &str, vector: Vec) -> Result<()> { - let url = format!("{}/collections/files/points", self.base); - let body = json!({ - "points": [{ - "id": id, - "vector": vector, - "payload": {"type": "file"} - }] - }); - - let resp = self.client.post(&url).json(&body).send().await?; - let status = resp.status(); - if status.is_success() { - Ok(()) - } else { - let t = resp.text().await.unwrap_or_default(); - Err(anyhow::anyhow!("qdrant upsert failed: {} - {}", status, t)) - } - } - - /// Ensure the 'files' collection exists with the given dimension and distance metric - pub async fn ensure_files_collection(&self, dim: usize) -> Result<()> { - let url = format!("{}/collections/files", self.base); - let body = json!({ - "vectors": {"size": dim, "distance": "Cosine"} - }); - let resp = self.client.put(&url).json(&body).send().await?; - // 200 OK or 201 Created means ready; 409 Conflict means already exists - if resp.status().is_success() || resp.status().as_u16() == 409 { - Ok(()) - } else { - let status = resp.status(); - let t = resp.text().await.unwrap_or_default(); - Err(anyhow::anyhow!( - "qdrant ensure collection failed: {} - {}", - status, - t - )) - } - } - - /// Search top-k nearest points from 'files', return (id, score) - pub async fn search_top_k(&self, vector: Vec, k: usize) -> Result> { - let url = format!("{}/collections/files/points/search", self.base); - let body = json!({ - "vector": vector, - "limit": k - }); - let resp = self.client.post(&url).json(&body).send().await?; - let status = resp.status(); - if !status.is_success() { - let t = resp.text().await.unwrap_or_default(); - return Err(anyhow::anyhow!("qdrant search failed: {} - {}", status, t)); - } - #[derive(Deserialize)] - struct Hit { - id: serde_json::Value, - score: f32, - } - #[derive(Deserialize)] - struct Data { - result: Vec, - } - let data: Data = resp.json().await?; - let mut out = Vec::new(); - for h in data.result { - // id can be string or number; handle string - if let Some(s) = h.id.as_str() { - out.push((s.to_string(), h.score)); - } else { - out.push((h.id.to_string(), h.score)); - } - } - Ok(out) - } -} diff --git a/rust-engine/src/worker.rs b/rust-engine/src/worker.rs deleted file mode 100644 index 79b02d8..0000000 --- a/rust-engine/src/worker.rs +++ /dev/null @@ -1,343 +0,0 @@ -use crate::gemini_client::{demo_text_embedding, generate_text_with_model, DEMO_EMBED_DIM}; -use crate::models::{QueryRecord, QueryStatus}; -use crate::storage; -use crate::vector; -use crate::vector_db::QdrantClient; -use anyhow::Result; -use sqlx::MySqlPool; -use std::time::Duration; -use tracing::{error, info}; - -pub struct Worker { - pool: MySqlPool, - qdrant: QdrantClient, -} - -impl Worker { - pub fn new(pool: MySqlPool) -> Self { - let qdrant_url = - std::env::var("QDRANT_URL").unwrap_or_else(|_| "http://qdrant:6333".to_string()); - let qdrant = QdrantClient::new(&qdrant_url); - Self { pool, qdrant } - } - - pub async fn run(&self) { - info!("Worker starting"); - - // Ensure qdrant collection exists - if let Err(e) = self.qdrant.ensure_files_collection(DEMO_EMBED_DIM).await { - error!("Failed to ensure Qdrant collection: {}", e); - } - - // Requeue stale InProgress jobs older than cutoff (e.g., 10 minutes) - if let Err(e) = self.requeue_stale_inprogress(10 * 60).await { - error!("Failed to requeue stale jobs: {}", e); - } - - loop { - // Claim next queued query - match self.fetch_and_claim().await { - Ok(Some(mut q)) => { - info!("Processing query {}", q.id); - if let Err(e) = self.process_query(&mut q).await { - error!("Error processing {}: {}", q.id, e); - let _ = self.mark_failed(&q.id, &format!("{}", e)).await; - } - } - Ok(None) => { - tokio::time::sleep(Duration::from_secs(2)).await; - } - Err(e) => { - error!("Worker fetch error: {}", e); - tokio::time::sleep(Duration::from_secs(5)).await; - } - } - } - } - - async fn fetch_and_claim(&self) -> Result> { - // Note: MySQL transactional SELECT FOR UPDATE handling is more complex; for this hackathon scaffold - // we do a simple two-step: select one queued id, then update it to InProgress if it is still queued. - if let Some(row) = sqlx::query( - "SELECT id, payload FROM queries WHERE status = 'Queued' ORDER BY created_at LIMIT 1", - ) - .fetch_optional(&self.pool) - .await? - { - use sqlx::Row; - let id: String = row.get("id"); - let payload: serde_json::Value = row.get("payload"); - - let updated = sqlx::query( - "UPDATE queries SET status = 'InProgress' WHERE id = ? AND status = 'Queued'", - ) - .bind(&id) - .execute(&self.pool) - .await?; - - if updated.rows_affected() == 1 { - let mut q = QueryRecord::new(payload); - q.id = id; - q.status = QueryStatus::InProgress; - return Ok(Some(q)); - } - } - Ok(None) - } - - async fn process_query(&self, q: &mut QueryRecord) -> Result<()> { - // Stage 1: set InProgress (idempotent) - self.update_status(&q.id, QueryStatus::InProgress).await?; - - // Stage 2: embed query text - let text = q.payload.get("q").and_then(|v| v.as_str()).unwrap_or(""); - let emb = demo_text_embedding(text).await?; - let top_k = q.payload.get("top_k").and_then(|v| v.as_u64()).unwrap_or(5) as usize; - let top_k = top_k.max(1).min(20); - - // Check cancellation - if self.is_cancelled(&q.id).await? { - return Ok(()); - } - - // Stage 3: search top-K in Qdrant - let hits = match self.qdrant.search_top_k(emb.clone(), top_k).await { - Ok(list) if !list.is_empty() => list, - Ok(_) => Vec::new(), - Err(err) => { - error!("Qdrant search failed for query {}: {}", q.id, err); - Vec::new() - } - }; - - let hits = if hits.is_empty() { - match vector::query_top_k(&emb, top_k) { - Ok(fallback_ids) if !fallback_ids.is_empty() => { - info!("Using in-memory fallback for query {}", q.id); - fallback_ids.into_iter().map(|id| (id, 0.0)).collect() - } - _ => Vec::new(), - } - } else { - hits - }; - - // Check cancellation - if self.is_cancelled(&q.id).await? { - return Ok(()); - } - - // Stage 4: fetch file metadata for IDs and fall back to recent ready files when empty - let mut files_json = self.load_files_by_hits(&hits).await?; - - if files_json.is_empty() { - files_json = self.load_recent_ready_files(top_k).await?; - } - - // Stage 5: call Gemini to analyze relationships and propose follow-up details strictly from provided files - let relationships_prompt = build_relationships_prompt(text, &files_json); - let (relationships, final_answer) = if files_json.is_empty() { - ( - "No analyzed files are ready yet. Try seeding demo data or wait for processing to finish.".to_string(), - "I could not find any relevant documents yet. Once files finish analysis I will be able to answer.".to_string(), - ) - } else { - let relationships = generate_text_with_model("gemini-2.5-pro", &relationships_prompt) - .await - .unwrap_or_else(|e| format!("[demo] relationships error: {}", e)); - - // Stage 6: final answer synthesis with strict constraints (no speculation; say unknown when insufficient) - let final_prompt = build_final_answer_prompt(text, &files_json, &relationships); - let final_answer = generate_text_with_model("gemini-2.5-pro", &final_prompt) - .await - .unwrap_or_else(|e| format!("[demo] final answer error: {}", e)); - (relationships, final_answer) - }; - - // Stage 7: persist results - let result = serde_json::json!({ - "summary": format!("Found {} related files", files_json.len()), - "related_files": files_json, - "relationships": relationships, - "final_answer": final_answer, - }); - sqlx::query("UPDATE queries SET status = 'Completed', result = ? WHERE id = ?") - .bind(result) - .bind(&q.id) - .execute(&self.pool) - .await?; - Ok(()) - } - - async fn load_files_by_hits(&self, hits: &[(String, f32)]) -> Result> { - let mut files_json = Vec::new(); - for (fid, score) in hits { - if let Some(row) = sqlx::query( - "SELECT id, filename, path, description, analysis_status FROM files WHERE id = ? AND pending_analysis = FALSE", - ) - .bind(fid) - .fetch_optional(&self.pool) - .await? - { - use sqlx::Row; - let id: String = row.get("id"); - let filename: String = row.get("filename"); - let path: String = row.get("path"); - let description: Option = row.get("description"); - let analysis_status: Option = row.try_get("analysis_status").ok(); - let storage_url = storage::public_url_for(&filename); - files_json.push(serde_json::json!({ - "id": id, - "filename": filename, - "path": path, - "storage_url": storage_url, - "raw_url": storage_url, - "description": description, - "analysis_status": analysis_status, - "score": score - })); - } - } - Ok(files_json) - } - - async fn load_recent_ready_files(&self, top_k: usize) -> Result> { - let limit = top_k.max(1) as i64; - let rows = sqlx::query( - "SELECT id, filename, path, description, analysis_status FROM files WHERE pending_analysis = FALSE ORDER BY created_at DESC LIMIT ?", - ) - .bind(limit) - .fetch_all(&self.pool) - .await?; - - let mut files_json = Vec::new(); - for row in rows { - use sqlx::Row; - let id: String = row.get("id"); - let filename: String = row.get("filename"); - let path: String = row.get("path"); - let description: Option = row.get("description"); - let analysis_status: Option = row.try_get("analysis_status").ok(); - let storage_url = storage::public_url_for(&filename); - files_json.push(serde_json::json!({ - "id": id, - "filename": filename, - "path": path, - "storage_url": storage_url, - "raw_url": storage_url, - "description": description, - "analysis_status": analysis_status, - "score": serde_json::Value::Null - })); - } - Ok(files_json) - } - - async fn update_status(&self, id: &str, status: QueryStatus) -> Result<()> { - let s = match status { - QueryStatus::Queued => "Queued", - QueryStatus::InProgress => "InProgress", - QueryStatus::Completed => "Completed", - QueryStatus::Cancelled => "Cancelled", - QueryStatus::Failed => "Failed", - }; - sqlx::query("UPDATE queries SET status = ? WHERE id = ?") - .bind(s) - .bind(id) - .execute(&self.pool) - .await?; - Ok(()) - } - - async fn mark_failed(&self, id: &str, message: &str) -> Result<()> { - let result = serde_json::json!({"error": message}); - sqlx::query("UPDATE queries SET status = 'Failed', result = ? WHERE id = ?") - .bind(result) - .bind(id) - .execute(&self.pool) - .await?; - Ok(()) - } - - async fn requeue_stale_inprogress(&self, age_secs: i64) -> Result<()> { - // MySQL: requeue items updated_at < now()-age and status = InProgress - sqlx::query( - "UPDATE queries SET status = 'Queued' WHERE status = 'InProgress' AND updated_at < (NOW() - INTERVAL ? SECOND)" - ) - .bind(age_secs) - .execute(&self.pool) - .await?; - Ok(()) - } - - async fn is_cancelled(&self, id: &str) -> Result { - if let Some(row) = sqlx::query("SELECT status FROM queries WHERE id = ?") - .bind(id) - .fetch_optional(&self.pool) - .await? - { - use sqlx::Row; - let s: String = row.get("status"); - return Ok(s == "Cancelled"); - } - Ok(false) - } -} - -fn build_relationships_prompt(query: &str, files: &Vec) -> String { - let files_snippets: Vec = files - .iter() - .map(|f| { - format!( - "- id: {id}, filename: {name}, path: {path}, desc: {desc}", - id = f.get("id").and_then(|v| v.as_str()).unwrap_or(""), - name = f.get("filename").and_then(|v| v.as_str()).unwrap_or(""), - path = f.get("path").and_then(|v| v.as_str()).unwrap_or(""), - desc = f.get("description").and_then(|v| v.as_str()).unwrap_or("") - ) - }) - .collect(); - format!( - "You are an assistant analyzing relationships STRICTLY within the provided files.\n\ - Query: {query}\n\ - Files:\n{files}\n\ - Tasks:\n\ - 1) Summarize key details from the files relevant to the query.\n\ - 2) Describe relationships and linkages strictly supported by these files.\n\ - 3) List important follow-up questions that could be answered only using the provided files.\n\ - Rules: Do NOT guess or invent. If information is insufficient in the files, explicitly state that.", - query=query, - files=files_snippets.join("\n") - ) -} - -fn build_final_answer_prompt( - query: &str, - files: &Vec, - relationships: &str, -) -> String { - let files_short: Vec = files - .iter() - .map(|f| { - format!( - "- {name} ({id})", - id = f.get("id").and_then(|v| v.as_str()).unwrap_or(""), - name = f.get("filename").and_then(|v| v.as_str()).unwrap_or("") - ) - }) - .collect(); - format!( - "You are to compose a final answer to the user query using only the information from the files.\n\ - Query: {query}\n\ - Files considered:\n{files}\n\ - Relationship analysis:\n{rels}\n\ - Requirements:\n\ - - Use only information present in the files and analysis above.\n\ - - If the answer is uncertain or cannot be determined from the files, clearly state that limitation.\n\ - - Avoid speculation or assumptions.\n\ - Provide a concise, structured answer.", - query=query, - files=files_short.join("\n"), - rels=relationships - ) -} diff --git a/src/features/gemini/gemini.js b/src/features/gemini/gemini.js deleted file mode 100644 index 60ab8c1..0000000 --- a/src/features/gemini/gemini.js +++ /dev/null @@ -1,4 +0,0 @@ -import { createPartFromUri, GoogleGenAI } from "@google/genai" -import 'dotenv/config' - -const ai = new GoogleGenAI({ apiKey: ${} }) \ No newline at end of file diff --git a/web-app/README.md b/web-app/README.md deleted file mode 100644 index 18bc70e..0000000 --- a/web-app/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# React + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## React Compiler - -The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/web-app/components.json b/web-app/components.json deleted file mode 100644 index 7a2e4d8..0000000 --- a/web-app/components.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "style": "default", - "tailwind": { - "config": "tailwind.config.js", - "css": "src/app/globals.css", - "baseColor": "zinc", - "cssVariables": true - }, - "rsc": false, - "tsx": false, - "aliases": { - "utils": "~/lib/utils", - "components": "~/components" - } -} diff --git a/web-app/jsconfig.json b/web-app/jsconfig.json deleted file mode 100644 index 604c23f..0000000 --- a/web-app/jsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "compilerOptions": { - "baseUrl": "./", - "lib": ["es2015", "dom"] - }, - "include": ["src"] -} diff --git a/web-app/public/pdfs/SRHB.pdf b/web-app/public/pdfs/SRHB.pdf deleted file mode 100644 index 5bb4f78..0000000 Binary files a/web-app/public/pdfs/SRHB.pdf and /dev/null differ diff --git a/web-app/public/pdfs/falcon-users-guide-2025-05-09.pdf b/web-app/public/pdfs/falcon-users-guide-2025-05-09.pdf deleted file mode 100644 index 4d835a6..0000000 Binary files a/web-app/public/pdfs/falcon-users-guide-2025-05-09.pdf and /dev/null differ diff --git a/web-app/public/pdfs/spacex-falcon-9-data-sheet.pdf b/web-app/public/pdfs/spacex-falcon-9-data-sheet.pdf deleted file mode 100644 index d6ce2ae..0000000 Binary files a/web-app/public/pdfs/spacex-falcon-9-data-sheet.pdf and /dev/null differ diff --git a/web-app/public/pdfs/system-safety-handbook.pdf b/web-app/public/pdfs/system-safety-handbook.pdf deleted file mode 100644 index 7d076f2..0000000 Binary files a/web-app/public/pdfs/system-safety-handbook.pdf and /dev/null differ diff --git a/web-app/server.mjs b/web-app/server.mjs deleted file mode 100644 index 913667b..0000000 --- a/web-app/server.mjs +++ /dev/null @@ -1,76 +0,0 @@ -import express from 'express'; -import path from 'node:path'; -import helmet from 'helmet'; -import cors from 'cors'; -import http from 'node:http'; -import https from 'node:https'; -import { URL, fileURLToPath } from 'node:url'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const app = express(); -const PORT = Number(process.env.PORT) || 3000; -const HOST = process.env.HOST || '0.0.0.0'; -const RUST_ENGINE_BASE = - process.env.RUST_ENGINE_BASE || - process.env.RUST_ENGINE_URL || - 'http://rust-engine:8000'; - -app.set('trust proxy', true); -app.use(helmet({ contentSecurityPolicy: false })); -app.use(cors()); - -app.get('/api/healthz', (_req, res) => { - res.json({ status: 'ok', upstream: RUST_ENGINE_BASE }); -}); - -// Stream all /api requests directly to the rust engine (supports JSON, multipart, SSE, etc.) -app.use('/api', (req, res) => { - const targetUrl = new URL(req.originalUrl, RUST_ENGINE_BASE); - const client = targetUrl.protocol === 'https:' ? https : http; - - const headers = { ...req.headers, host: targetUrl.host }; - - const proxyReq = client.request( - targetUrl, - { - method: req.method, - headers, - }, - (upstream) => { - res.status(upstream.statusCode || 502); - for (const [key, value] of Object.entries(upstream.headers)) { - if (typeof value !== 'undefined') { - res.setHeader(key, value); - } - } - upstream.pipe(res); - } - ); - - proxyReq.on('error', (err) => { - console.error('API proxy error:', err); - if (!res.headersSent) { - res.status(502).json({ error: 'proxy_failed' }); - } else { - res.end(); - } - }); - - req.pipe(proxyReq); -}); - -// Serve static frontend -const distDir = path.resolve(__dirname, 'dist'); -app.use(express.static(distDir)); - -// SPA fallback (Express 5 requires middleware instead of bare '*') -app.use((req, res) => { - res.sendFile(path.join(distDir, 'index.html')); -}); - -app.listen(PORT, HOST, () => { - console.log(`Web app server listening on http://${HOST}:${PORT}`); - console.log(`Proxying to rust engine at ${RUST_ENGINE_BASE}`); -}); diff --git a/web-app/src/index.js b/web-app/src/index.js deleted file mode 100644 index 259ced7..0000000 --- a/web-app/src/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import express from "express"; -import bodyParser from "body-parser"; -import axios from "axios"; -import multer from "multer"; -import path from "path"; -import { fileURLToPath } from 'url'; -import fs from "fs"; - -const app = new express(); -const port = 3000; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -app.use(express.static('public')); -app.use('/uploads', express.static('uploads')); -app.use(bodyParser.urlencoded({extended: true})); -app.use(bodyParser.json()); - -const storage = multer.diskStorage({ - destination: function (req, file, cb) { - cb(null, path.join(__dirname,'/uploads')); - }, - filename: function (req, file, cb) { - cb(null, file.originalname); - } -}) -const upload = multer({ storage: storage }) - - - -//Render the main page -app.get("/", async (req, res) => { - try{ - const response = await axios.get(`${API_URL}/all`); - res.render("file", { data: response.data }); - }catch(error){ - console.error(error); - res.status(500).json("Error fetching items"); - } -}) - -app.post("/upload", upload.single('image'), async (req, res) => { - const data = { - ...req.body, - fileName: req.file.originalname, - path: req.file.path - } - try{ - await axios.post(`${API_URL}/add`, data); - res.redirect("/"); - }catch(error){ - console.error(error); - res.status(500).json("Error uploading item"); - } -}) - -app.listen(port, () => { - console.log("API is listening on port " + port); -}) \ No newline at end of file diff --git a/web-app/src/lib/api.js b/web-app/src/lib/api.js deleted file mode 100644 index 76dce1e..0000000 --- a/web-app/src/lib/api.js +++ /dev/null @@ -1,75 +0,0 @@ -const JSON_HEADERS = { - Accept: "application/json", -}; - -async function parseJsonResponse(response) { - const text = await response.text(); - const hasBody = text !== ""; - let data; - if (hasBody) { - try { - data = JSON.parse(text); - } catch (error) { - data = { raw: text }; - } - } - - if (!response.ok) { - const message = data?.error || response.statusText || "Request failed"; - const err = new Error(message); - err.status = response.status; - err.body = data; - throw err; - } - - return data ?? {}; -} - -export async function listFiles() { - const response = await fetch("/api/files/list", { - method: "GET", - headers: JSON_HEADERS, - }); - const data = await parseJsonResponse(response); - return Array.isArray(data.files) ? data.files : []; -} - -export async function createQuery(payload) { - const response = await fetch("/api/query/create", { - method: "POST", - headers: { - ...JSON_HEADERS, - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); - const data = await parseJsonResponse(response); - if (!data.id) { - throw new Error("Query creation did not return an id"); - } - return data; -} - -export async function getQueryStatus(id) { - const response = await fetch(`/api/query/status?id=${encodeURIComponent(id)}`, { - method: "GET", - headers: JSON_HEADERS, - }); - return parseJsonResponse(response); -} - -export async function getQueryResult(id) { - const response = await fetch(`/api/query/result?id=${encodeURIComponent(id)}`, { - method: "GET", - headers: JSON_HEADERS, - }); - return parseJsonResponse(response); -} - -export async function cancelQuery(id) { - const response = await fetch(`/api/query/cancel?id=${encodeURIComponent(id)}`, { - method: "GET", - headers: JSON_HEADERS, - }); - return parseJsonResponse(response); -}