leptos_portfolio_admin Tailwind Templates

Leptos_portfolio_admin

Leptos Portfolio Admin is a fullstack SSR portfolio & LLM chat site built in Rust with a dynamic admin panel. It uses Leptos, Actix Web, SurrealDB, and Tailwind CSS to update portfolio content in real time and serves as a learning resource for modern Rust SSR development.

Leptos Portfolio Admin

Fullstack SSR Pure Rust Portfolio Site with Administration Panel

Overview

Leptos Portfolio Admin is a comprehensive, full-stack portfolio website solution built entirely in Rust. It leverages the Leptos framework for server-side rendering (SSR) and interactive frontend components, coupled with an Actix Web backend API. You can see a demo of the project by visiting my website. Check it out!

Key Highlights:

  • Dynamic Content Management: Features a secure admin panel allowing the site owner to easily add, update, and delete portfolio content (profile, skills, experience, projects, education, contact info) without modifying static code. The admin panel includes a WYSIWYG editor (TinyMCE) for rich text formatting.
  • LLM Chat Integration: Includes an interactive chat page powered by a separate AI agent backend (dynamic-agent), allowing visitors to ask questions about the portfolio content.
  • Pure Rust Stack: Demonstrates a full-stack web application using popular Rust crates like Leptos (SSR Frontend), Actix Web (Backend), SurrealDB (Database), and Redis (Caching).
  • Rich Portfolio Display: Presents a clean, responsive portfolio page showcasing detailed information, including rich text descriptions ("About Me"), project galleries with tech stacks, skill levels, professional experience timelines, and more.
  • PDF Generation: Includes functionality to automatically generate a downloadable PDF version of the portfolio content using Chromium in the backend.
  • Configurable & Themeable: Supports site title customization and includes a dark mode toggle.
  • Learning Resource: Serves as a practical example for developers interested in exploring Rust for web development, particularly SSR with Leptos and backend integration with Actix Web and SurrealDB.

This project aims to provide a ready-to-use, customizable portfolio site that is easy to maintain through its dedicated admin interface and offers an engaging chat experience.

Stacks

  • Leptos 0.6: A Rust frontend framework with SSR capabilities.
  • Actix Web: A Rust backend framework for API handling.
  • SurrealDB: The database used for data storage.
  • Tailwind CSS: A utility-first CSS framework for UI design.
  • Redis: Used for caching database queries.
  • Chromium: Used by the backend to generate PDF versions of the portfolio from HTML.
  • TinyMCE: A WYSIWYG HTML editor integrated into the admin panel.
  • dynamic-agent (Optional Backend): A Rust-based AI agent framework for powering the LLM chat feature. Dynamic Agent is a flexible and configurable AI agent framework that provides a foundation for creating Retrieval-Augmented Generation (RAG) agents. It supports multiple LLM providers (Ollama, OpenAI, Anthropic, etc.) and vector stores (Redis, Qdrant, SurrealDB, etc.), offering a configurable RAG pipeline, conversation history, and a secure WebSocket interface for client communication.

Features

Portfolio Page (Public View)

  • Profile: Displays name, age, nationality, gender, and job role.
  • About Me: Supports rich HTML text for detailed descriptions.
  • Skills: Lists skills with proficiency levels.
  • Contact: Dynamically configurable contact methods with associated icons show as link or popup message.
  • Experiences: Shows professional history including company logo, name, position, work period, and description.
  • Portfolio: Features projects with names, photos, open-source status, descriptions, and technology stacks.
  • Education: Lists educational background including institute name, address, major, degree, and GPA.
  • Language: Displays languages known and their proficiency levels.
  • PDF: Provides an option to view/download a PDF version of the portfolio.

LLM Chat Page (Requires dynamic-agent backend)

  • Interactive Chat: Allows visitors to ask questions about the portfolio content (profile, experience, projects, etc.).
  • Retrieval-Augmented Generation (RAG): When asked about specific details (e.g., "What is your profile fullname?", "Tell me about your experience with Rust"), the dynamic-agent backend can query its configured vector database. This database should be populated with your portfolio information (profile, skills, experiences, etc.). The agent retrieves relevant text chunks and uses them, along with the user's query, to generate an informed answer via the LLM. This ensures responses are grounded in your actual portfolio data.
  • Real-time Responses: Provides responses generated by a configured Large Language Model via a WebSocket connection.
  • Processing Indicator: Shows when the AI agent is processing a request.
  • Timestamps: Displays timestamps for both user messages and agent responses.
  • Secure Connection: Supports API key authentication using HMAC-SHA256 (signing a timestamp with a shared secret) and optional TLS (WSS) for the WebSocket connection. This prevents the raw API key from being transmitted and adds a layer of protection against replay attacks.
  • Pre-defined Suggestions: Displays a set of categorized question suggestions when the chat interface is first opened, helping guide users. These are configurable via a JSON file.

Configuring Chat Suggestions and RAG

The pre-defined chat suggestions are loaded from the /assets/pre-suggest.json file. You can customize these suggestions to better fit your portfolio content and to trigger specific RAG queries.

1. Edit assets/pre-suggest.json:

The file should be an array of suggestion groups. Each group object has the following structure:

[
  {
    "group_title": "General Topics",
    "icon": "BsChatRightText", // Icon identifier
    "suggestions": [
      "Hello, how are you?",
      "What can you do?"
    ]
  },
  {
    "group_title": "Vector Database (Profile)", // Example for RAG
    "icon": "ImProfile", 
    "suggestions": [
      "What is your profile fullname?", // This query will leverage RAG
      "List profile position name from experiences." // This also uses RAG
    ]
  }
  // Add more groups as needed
]
  • group_title: The title displayed for the category of suggestions.
  • icon: A string identifier for the icon to be displayed next to the group title.
  • suggestions: An array of strings, where each string is a pre-defined question or prompt. Queries aimed at retrieving specific portfolio details (like those under "Vector Database (Profile)") will typically be handled by the dynamic-agent's RAG pipeline.

2. Configure Icons:

The icon field in pre-suggest.json is a key that maps to an actual icon from the icondata crate. This mapping is defined in the ICON_MAP static variable within the src/app/utils/utils.rs file.

  • Finding Icons: You can browse available icons at the icondata icon explorer.

  • Adding New Icons to ICON_MAP: If you want to use an icon that's not already in ICON_MAP, you'll need to add it. Open src/app/utils/utils.rs and add a new entry to the ICON_MAP. For example, if you find an icon AiStarOutline in icondata (which would be i::AiStarOutline in the code) and you want to use the key "MyStarIcon" in your pre-suggest.json:

    // filepath: src/app/utils/utils.rs
    // ...
    pub static ICON_MAP: phf::Map<
        &'static str,
        &'static icondata_core::IconData
    > = phf_map! {
        // ... existing icons ...
        "BsChatRightText" => i::BsChatRightText,
        "ImProfile" => i::ImProfile,
        "MyStarIcon" => i::AiStarOutline, // Add your new icon here
        // ...
    };
    // ...
    

    Ensure you have the correct use icondata as i; statement at the top of utils.rs. After updating ICON_MAP, you can then use "MyStarIcon" as the value for the icon field in assets/pre-suggest.json.

3. Ensure dynamic-agent is Configured for RAG:

For the chat to effectively answer questions about your "profile" or other specific data using RAG:

  • The dynamic-agent backend must be configured with a data source containing your portfolio information (e.g., markdown files, database connection).
  • It must be configured with a vector store (e.g., Qdrant, Redis with vector search, SurrealDB) where this information is indexed as embeddings.
  • Refer to the dynamic-agent README for detailed instructions on setting up its data sources, vector store, and RAG pipeline. The effectiveness of queries like "What profile fullname?" depends entirely on this backend configuration.

Admin Edit Page

  • Permission Modes:
    • Viewer Mode: Allows viewing the admin sections without requiring a password (read-only).
    • Admin Mode: Requires a password (set in .env) to enable adding, editing, and deleting content.
  • Editable Sections:
    • Profile: Edit profile details and the "About Me" section using TinyMCE.
    • Skills: Add/Edit/Remove skills order by high level.
    • Experiences: Add/Edit/Remove work experiences order by job start date.
    • Portfolio: Add/Edit/Remove portfolio projects order by index (can reindex).
    • Contact: Add/Edit/Remove contact and dynamic icon order by button type.
    • Education: Add/Edit/Remove education entries order by graduated year .
    • Language: Add/Edit/Remove language proficiencies order by high level.
    • PDF: Configure PDF generation (e.g., use generated HTML or a custom PDF link) and other related settings.

General Features

  • WYSIWYG Content: Display your content as HTML, allowing complete freedom to design beautiful information.
  • PDF Template: Generate an HTML PDF from portfolio data using a minimal, cool resume template.
  • Form Validation: Ensure that all required fields are validated and prevent updates if any required fields are missing.
  • Toast Notifications: Provides feedback for actions performed on the admin page.
  • Backend Server: A simple Actix Web API server that interacts with the SurrealDB database.
  • Responsive UI: The user interface adapts smoothly to all devices screen sizes using Tailwind CSS.
  • Caching: Utilizes Redis to cache profile data and generated PDF files, improving website performance by reducing database queries and server processing.
  • Site Configuration:
    • Set the website title.
    • Meta Tags: Under development.
    • Other SEO Tags: Planned.
  • Dark Mode: Toggle between light and dark themes.
  • Security: Password hashing using Argon2 with protection against timing attacks, along with Redis-based IP rate limiting. WebSocket security via API Key and optional TLS.
  • Intro Animation: Welcome intro animation using Tailwind CSS..
  • Other: Dialog Popup , Text Field Array,

Prerequisites

  1. Clone Project:

    git clone https://github.com/DevsHero/leptos_portfolio_admin.git
    cd leptos_portfolio_admin
    
  2. Prepare .env file: Copy the example environment file. You will need to fill this file with your specific configuration details.

    cp .env-example .env
    

    Below are the variables defined in .env-example and their purpose:

    # --- SurrealDB Connection ---
    # Defines the protocol (http/https) and host/port for the SurrealDB instance.
    # Example: http:127.0.0.1:8000 or https:[your-cloud-instance.com](https://www.google.com/search?q=your-cloud-instance.com)
    SURREAL_PROTOCOL_HOST=http:127.0.0.1:8000
    # Username for SurrealDB authentication.
    SURREAL_USER=root
    # Password for SurrealDB authentication.
    SURREAL_PASS=root
    # The specific database name to use within SurrealDB.
    SURREAL_DB=portfolio
    # The namespace within SurrealDB.
    SURREAL_NAMESPACE=portfolio
    
    # --- Admin Panel ---
    # Password required to access the Admin Mode for editing site content.
    # Generate this using `cargo run --bin hash-password`
    ADMIN_PASSWORD_HASH_ENCODED=
    
    # --- Site Configuration ---
    # The title displayed in the browser tab.
    SITE_TITLE="Portfolio site based on pure rust"
    
    # --- Redis Connection ---
    # Redis connection URL used during local development (cargo leptos watch).
    REDIS_URL_DEV="redis://localhost:6379"
    # Redis connection URL used in the production Docker environment (connects to the 'redis' service).
    REDIS_URL_PROD="redis://redis:6379"
    
    # --- LLM Chat WebSocket Configuration (Optional) ---
    # Full WebSocket URL (including ws:// or wss:// and port) for the dynamic-agent backend.
    # Example: ws://127.0.0.1:4000 or wss://your-agent.example.com
    WS_HOST="ws://127.0.0.1:4000"
    # Optional secret API key used by this Leptos application to sign WebSocket connection requests to the dynamic-agent backend.
    # If the dynamic-agent's SERVER_API_KEY is set, this key must match it.
    # The Leptos server uses this key to generate an HMAC-SHA256 signature (X-Api-Sign) based on a timestamp (X-Api-Ts),
    # which are then sent as query parameters to the WebSocket server for authentication.
    CLIENT_WS_API_KEY=
    

    Important: Replace the default values (like SURREAL_USER, SURREAL_PASS) with your own secure settings before deployment. Ensure ADMIN_PASSWORD_HASH_ENCODED is generated and WS_HOST points to your running dynamic-agent instance if using the chat feature. The CLIENT_WS_API_KEY should be a strong, unique secret if used.

  3. Setup SurrealDB: You need a running SurrealDB instance. You can set one up locally or use a cloud provider.

  4. Initialize Database Schema: Connect to your SurrealDB instance (using the surreal sql command-line tool or a GUI like Surrealist). Copy and execute all the commands from the surreal/script.surql file to set up the necessary tables and schemas. Ensure you are connected to the correct namespace and database defined in your .env file (NAMESPACE portfolio; USE DB portfolio;).

How to Run

First Step: Setup Admin Password

This project uses an environment variable (ADMIN_PASSWORD_HASH_ENCODED) stored in a .env file for local admin access. Use this script to generate the hash and automatically update the file:

1. Run the Script:

  • Open your terminal in the root directory of the project (where the main Cargo.toml is).
  • Run the script:
    cargo run --bin hash-password
    
  • Follow the prompts to enter and confirm your desired admin password (input will be hidden).

2. Check .env File:

  • The script will automatically create or update the .env file in your project root, adding or replacing the ADMIN_PASSWORD_HASH_ENCODED line with the newly generated hash.
  • You can check the .env file to confirm the change and ensure other variables are still present. (Note: If you're running the project via Docker, make sure to copy the .env file and include your ADMIN_PASSWORD_HASH_ENCODED value inside same docker-compose.yml)

Second Step: Setup LLM Chat Backend (Optional)

To enable the LLM Chat feature, you need to run the dynamic-agent project as the backend. This is a flexible AI agent framework built in Rust, designed for Retrieval-Augmented Generation (RAG). It can connect to various LLM providers and vector stores, allowing you to create a chat experience tailored to your portfolio data.

  1. Clone dynamic-agent:
    git clone https://github.com/DevsHero/dynamic-agent.git
    cd dynamic-agent
    
  2. Configure dynamic-agent:
    • Copy the example environment file: cp .env-example .env
    • Edit the .env file in the dynamic-agent directory. You must configure:
      • Your chosen LLM provider API key (e.g., OPENAI_API_KEY, ANTHROPIC_API_KEY).
      • Vector store settings (e.g., Qdrant URL/API Key or local path).
      • The data source for the agent (e.g., path to markdown files, database connection details). This data will be used by the agent to answer questions about your portfolio.
    • Crucially, configure the WebSocket server settings:
      • LISTEN_ADDR: The address and port the agent server will listen on (e.g., 0.0.0.0:4000).
      • SERVER_API_KEY (Optional): If you set this, clients (like leptos_portfolio_admin) must provide this key to connect.
      • TLS_CERT_PATH / TLS_KEY_PATH (Optional): Provide paths to your TLS certificate and key files to enable secure wss:// connections.
    • Refer to the dynamic-agent README for detailed configuration options.
  3. Run dynamic-agent:
    • Locally:
      cargo run --release
      
    • Via Docker: (Refer to dynamic-agent's Docker instructions if available).
  4. Configure leptos_portfolio_admin:
    • Go back to the leptos_portfolio_admin directory.
    • Edit your .env file:
      • Set WS_HOST to the full URL of your running dynamic-agent server (e.g., ws://127.0.0.1:4000 or wss://your-agent.domain.com if using TLS).
      • If you set SERVER_API_KEY in dynamic-agent (this is the secret key for HMAC verification on the server), set the same secret value for CLIENT_WS_API_KEY in leptos_portfolio_admin's .env file. This CLIENT_WS_API_KEY will be used by the Leptos frontend server to sign its WebSocket connection requests.

Third Step: Run the Portfolio Application

Select an option to run the main leptos_portfolio_admin application:

Option 1: Via Local Development

  1. Install Rust: If you don't have Rust installed, get it from rustup.rs:
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    source "$HOME/.cargo/env" # Or restart your terminal
    
  2. Install Leptos Toolchain:
    rustup toolchain install nightly
    rustup default nightly
    rustup target add wasm32-unknown-unknown
    cargo install cargo-leptos
    
  3. Install Redis: The easiest way is often using Docker:
    # Ensure Docker is running
    docker compose -f docker-compose.yml up -d redis
    
    (Alternatively, install Redis directly via your system's package manager.)
  4. Install Chromium: This is required for PDF generation. It must be chromium, not google-chrome. Installation methods vary by OS. See this guide for help: Chromium Installation Guide
  5. Run the Application: This command compiles both frontend (WASM) and backend, watches for changes, and serves the site using the settings in your .env file (specifically REDIS_URL_DEV and potentially WS_HOST/CLIENT_WS_API_KEY).
    cargo leptos watch
    
    (If you have updated information on the web production, I suggest clearing the local Redis cache to sync the latest data from the database.)
    docker exec -i dragonfly_redis redis-cli FLUSHDB
    
  6. Access the site at http://localhost:3000. The chat page should now connect to your dynamic-agent backend if configured.

Option 2: Via Local Docker Build

  1. Build the Docker Image: (Note: Build times can be significant, e.g., ~5-10 minutes depend on machine specs. Adjust --platform if needed.)
    docker build --platform linux/amd64 -t leptos-portfolio-admin:latest .
    
  2. Run using Docker Compose: Make sure your .env file is configured correctly in the project root. This command will start the application container and the Redis container. The application inside Docker uses REDIS_URL_PROD. Ensure WS_HOST in your .env is accessible from within the Docker network (e.g., use Docker service names if dynamic-agent is also containerized).
    docker compose -f docker-compose.yml up -d --force-recreate leptos-portfolio-admin redis
    
    (This assumes your docker-compose.yml defines services named leptos-portfolio-admin and redis, configured to use the built image and read the .env file.)
  3. Access the site at http://localhost:8080 (or the port mapped in your docker-compose.yml).

Option 3: Via Docker Hub Image (If available)

(Assuming you have published an image to Docker Hub and have a suitable docker-compose.yml)

  1. Pull and Start Containers: Make sure your .env file is configured, paying attention to WS_HOST accessibility.
    # Ensure your docker-compose.yml points to the Docker Hub image for the leptos-portfolio-admin service
    docker compose pull && docker compose up -d --force-recreate
    
  2. Access the site at http://localhost:8080 (or the port mapped in your docker-compose.yml).

Planned Features

  • One-Script Setup: Script for automated prerequisite setup.
  • Leptos 0.7 Migration: Attempted, but faced issues. Will revisit.

Contributing

Contributions are welcome! This is my first Rust project. Please open issues or PRs for bugs, suggestions, or new features. Note: Primarily tested on macOS and Debian. Windows stability (Docker/WSL) not guaranteed.

Contact Me

Thanon Aphithanawat (Hero)

Top categories

Loading Svelte Themes