Atri Website

Back

Frontend Admin Panel Development Summary#

Overall Architecture Design#

This development implements a complete admin panel for an Astro+Svelte blog project, including features like CRUD operations for posts, Monaco Editor integration, and JWT authentication.

Tech Stack#

  • Frontend: Astro + Svelte + TypeScript
  • Editor: Monaco Editor (via CDN)
  • Backend: FastAPI + Python
  • Database: SQLite
  • Authentication: JWT
  • File Management: Markdown files + Frontmatter

API Design#

MethodEndpointDescription
POST/tokenLogin to get JWT
GET/api/admin/postsGet post list
GET/api/admin/posts/{slug}Get a single post
POST/api/admin/postsCreate a post
PUT/api/admin/posts/{slug}Update a post
DELETE/api/admin/posts/{slug}Delete a post

Core Design Principles#

  1. Frontend-Backend Separation: Data interaction is handled via APIs, allowing the frontend to focus on UI and user experience.
  2. Component-Driven Development: Complex features are broken down into independent Svelte components.
  3. Type Safety: TypeScript is used to ensure data type consistency across interfaces.
  4. Route Guarding: Admin pages are protected using JWT authentication.
  5. Responsive Design: The panel supports access from both desktop and mobile devices.

This architecture ensures the system’s maintainability, scalability, and security.

Authentication System Implementation#

JWT Authentication Service (authService.ts)#

Route Guard Implementation#

    // Inline script in each admin page
<script is:inline>
  function checkAuthentication() {
    const token = localStorage.getItem('admin_token');
    if (!token) {
      window.location.href = '/admin/login';
    }
  }
  checkAuthentication();
</script>
  
plaintext

API Client Implementation#

Unified API Client (apiClient.ts)#

Monaco Editor Integration#

Core Editor Component Implementation (Editor.svelte)#

Post Management Table#

Key Features of PostTable Component#

Major Issues and Challenges#

Issue 1: Monaco Editor Loading and Integration Problems#

Problem Description#

Several frontend obstacles were encountered while integrating Monaco Editor into the Astro + Svelte project:

  • SvelteKit Import Conflict: The code used a SvelteKit-specific environment module, import { browser } from ‘$app/environment’, which is not recognized in Astro’s build environment, causing build failures.
  • Container Binding Failure: The bind:this={editorContainer} directive, used in the Svelte component to bind the editor’s DOM container, resulted in an undefined variable at initialization when used inside an #if block due to the delayed creation of the DOM element.
  • CDN Loading Failure: The initial loading logic attempted to load Monaco Editor and Marked.js (a Markdown parsing library) simultaneously or sequentially. Complex dependencies and network issues led to unstable loading or failures.

Solution#

Several key steps were taken to resolve these issues:

  1. Replace SvelteKit-Specific Imports: SvelteKit’s proprietary API was abandoned in favor of a universal, framework-agnostic method for browser environment detection.

        // Incorrect approach
    import { browser } from '$app/environment';
    
    // Correct approach: Use typeof window for detection, suitable for any frontend environment
    const isBrowser = typeof window !== 'undefined';
      
    plaintext
  2. Fix Container Binding Issue:

    ​ The editor container’s rendering logic was decoupled from the loading state’s display logic. The container is now always rendered to ensure bind:this can execute successfully, while a loading animation is implemented as an absolutely positioned overlay.

        <!-- The container always exists to ensure successful binding -->
    <div bind:this={editorContainer} class="editor-container"></div>
    
    <!-- The loading animation acts as an overlay and does not affect the container's rendering -->
    {#if isLoading}
      <div class="loading-overlay">...</div>
    {/if}
      
    plaintext
  3. Simplify Dependencies, Load Only Monaco Editor: The CDN dependency on Marked.js was removed. All Markdown-to-HTML conversion logic was moved entirely to the backend. The frontend loading script now focuses solely on loading the Monaco Editor core, simplifying the logic and improving stability.

        function loadMonacoFromCDN() {
      return new Promise((resolve, reject) => {
        // If already loaded, return immediately
        if (window.monaco) {
          resolve(window.monaco);
          return;
        }
        // Remove Marked.js dependency, load only Monaco
        const script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/min/vs/loader.js';
        // ... (omitting success and failure callbacks)
      });
    }
      
    plaintext

Issue 2: Docker Environment Path Configuration Problem#

Problem Description#

After containerizing the FastAPI backend application, file read/write operations failed:

  • Path Mismatch: The backend configuration file had hardcoded absolute Windows paths from the developer’s PC, while the FastAPI application was running in a Linux filesystem within a Docker container.
  • API Returned Empty Array: The API for fetching the post list consistently returned an empty array because it couldn’t find any markdown files at the incorrect Windows path inside the container.

Solution#

The core solution was to unify path references for development and production environments to point to paths inside the Docker container, and then use Volume Mounts to map local files to those paths.

  1. Correct Paths in Configuration Files: All hardcoded local paths were replaced with absolute paths defined within the Docker container.

        # Incorrect configuration - uses local Windows paths
    ASTRO_CONTENT_PATH: str = "D:/Coding/Wrote_Codes/webTest/yukina/src/contents/posts"
    ASTRO_PROJECT_PATH: str = "D:/Coding/Wrote_Codes/webTest/yukina"
    
    # Correct configuration - uses paths inside the Docker container
    ASTRO_CONTENT_PATH: str = "/code/yukina/src/contents/posts"
    ASTRO_PROJECT_PATH: str = "/code/yukina"
      
    plaintext
  2. Configure Volume Mounts in Docker Compose: A mapping was established between the local project folder and the container’s internal path in the docker-compose.yml file.

        # Volume mounts in Docker-compose.yml
    volumes:
      # Map the local ../yukina folder to the /code/yukina directory inside the container
      - ../yukina:/code/yukina
      
    plaintext

Issue 3: Node.js Script Execution Failure#

Problem Description#

The initial backend design relied on calling external Node.js scripts via Python’s subprocess module to create and modify Markdown files, which led to several problems:

  • HTTP 500/400 Errors: The server frequently returned internal server errors or bad request errors when the frontend called the create and delete post APIs.
  • Environmental Complexity: It required maintaining a correctly configured Node.js environment, including package.json and node_modules, inside the Python container.
  • Module System Conflicts: The Node.js scripts used ES Modules (import/export) syntax, which had compatibility issues with the subprocess calling method and was prone to dependency version conflicts.

Solution#

Architectural Simplification: The dependency on Node.js scripts was completely removed, and all file manipulation logic was reimplemented in pure Python. This significantly reduced environmental complexity and the likelihood of errors.

Issue 4: Astro Content Collection Schema Validation Failure#

Problem Description#

After the backend generated Markdown files using pure Python, Astro’s Content Collections validation process failed during the pnpm run build command.

  • Data Type Mismatch: The backend treated all Frontmatter values as generic strings, but Astro’s schema defined strict types. For example, title was expected to be a string, but the backend might output a number.
  • Incorrect Date Format: The date field was incorrectly wrapped in quotes, causing the YAML parser to recognize it as a string instead of a date object.
  • Incorrect Boolean Format: Python’s boolean values True/False were written directly, whereas the YAML standard for booleans is true/false.

Solution#

​ A type-aware formatting function was added to the backend when creating Markdown files to ensure the generated Frontmatter strictly adheres to YAML specifications and the Astro schema requirements.

Issue 5: 404 Error for Dynamic Routes in Build Environment#

The admin panel’s post editor page (/admin/editor/[slug].astro) worked correctly in the development environment (pnpm run dev) but returned a 404 Not Found error when deployed to a production environment after running pnpm run build.

  • Incorrect getStaticPaths Configuration: Astro relies on the getStaticPaths function at build time to determine which static pages to pre-generate. The function was returning an empty array, which told Astro that no pages needed to be generated for this dynamic route.

Solution#

The /admin/editor/[slug].astro file was modified. Its getStaticPaths function was updated to read all posts from the src/content/posts directory and pre-generate a corresponding editor page for each one.

Solution Summary and Best Practices#

Final Solution Architecture#

​ After in-depth exploration and practice, the project settled on a robust and efficient architecture for development and deployment. This architecture balances the agility of the development experience with the stability of the production environment by clearly defining the responsibilities of each environment.

Dev vs. Build Environment Comparison#

FeatureDev EnvironmentBuild EnvironmentUsage
Post CRUD❌ File watcher conflicts✅ Stable & ReliableBuild Environ
Live Preview✅ Hot Reload❌ Requires RebuildDev Environ
PerformanceSlowFastBuild Environ
Error DebuggingEasyHarderDev Environ
Production StabilityUnstableStableBuild Environ

Core Technical Decisions#

  1. Use the Build Environment for Admin Functions

    To ensure the stability and atomicity of data operations, all management functions involving content creation, updates, and deletions (CRUD) are performed in Astro’s Build environment.

        # Build the production version of the application
    npm run build
    
    # Start a local server that simulates the production environment
    npm run preview
    
    # Access the admin panel for content management
    # Visit http://localhost:4321/admin
      
    plaintext
  2. Use the Dev Environment for Content Development and UI Debugging

    To leverage Astro’s powerful Hot Module Replacement (HMR) for instant feedback, all frontend UI component development, style adjustments, and content writing previews are performed in the Dev environment.

        # Start the development server
    npm run dev
    
    # Visit http://localhost:4321 for content writing and UI debugging
      
    plaintext

Key Design Patterns#

Service Layer Pattern#

The project uses a classic layered design to completely decouple UI interaction, business logic, and network requests, which improves code maintainability and testability.

  • Data Flow: UI Component (Svelte) → Service Layer (TypeScript) → API Client (TypeScript) → Backend API
  • Example: PostTable.svelte → postService.ts → apiClient.ts → FastAPI Backend

Component Communication Pattern#

    // Editor.svelte: After the editor initializes, it dispatches a global event and exposes its API
const editorReadyEvent = new CustomEvent('editor-ready', {
  detail: {
    getValue: () => editor.getValue(),
    setValue: (newValue) => editor.setValue(newValue),
    focus: () => editor.focus()
  }
});
document.dispatchEvent(editorReadyEvent);

// new.astro: The page listens for this global event to gain control of the editor instance
document.addEventListener('editor-ready', (event) => {
  editorAPI = event.detail;
});
  
plaintext

Best Practices Summary#

Environment Configuration Management#

Sensitive configurations and environment-specific parameters are managed via environment variables, achieving a separation of code and configuration.

    # FastAPI Backend: Use environment variables to override default settings
ASTRO_CONTENT_PATH: str = os.getenv(
    "ASTRO_CONTENT_PATH",
    "/code/yukina/src/contents/posts"  # Provide a default path suitable for Docker
)

# Differentiate between development and production to execute different logic
ENVIRONMENT: str = os.getenv("ENVIRONMENT", "production")

if settings.ENVIRONMENT == "production":
    trigger_astro_rebuild()  # Only trigger auto-rebuild in production
  
plaintext

Type Safety and Data Validation#

Build-Time Route Generation#

Leverage Astro’s SSG (Static Site Generation) feature to pre-generate static pages for all posts at build time based on the content collection, achieving ultimate loading performance.

// src/pages/blog/[slug].astro
export async function getStaticPaths() {
  const posts = await getCollection('posts');

  return posts.map((post) => ({
    params: { slug: post.id }, // Corresponds to URL /blog/post-id
    props: { post },          // Data passed to the page
  }));
}
javascript

Responsive Design Principles#

Follow a mobile-first design principle and use media queries to ensure the admin panel provides a good user experience on devices of different sizes.

Deployment Recommendations#

Production Workflow#

  1. Development Phase: Develop UI and write content.

    npm run dev
    plaintext
  2. Management Phase: Perform CRUD operations on posts.

    npm run build
    npm run preview
    # Access http://localhost:4321/admin for content management
      
    plaintext
  3. Publishing Phase: After content updates, rebuild the website and deploy.

    npm run build
    # Deploy the generated dist/ directory to a static file server (e.g., Nginx)
      
    bash

Security Considerations#

  • JWT Authentication: All admin endpoints are protected by JWT tokens.
  • Route Guarding: The frontend implements a client-side route guard via script to prevent unauthorized access to admin pages.
  • Automatic Auth Header: The ApiClient automatically attaches the authentication header to all protected API requests.
  • 401 Auto-Logout: The ApiClient automatically clears local authentication info and redirects to the login page upon detecting a 401 Unauthorized response.
  • HTTPS: HTTPS must be enabled in the production environment via Cloudflare or other means.

Project Outcomes#

Completed Functional Modules#

  • Complete Admin Panel: A modern UI providing a seamless content management experience.
  • Monaco Editor Integration: Features a vs-dark theme, supports Markdown syntax highlighting, and offers rich editing functionalities.
  • JWT Authentication System: A secure and reliable login authentication mechanism.
  • Post CRUD Operations: Supports the creation, reading, updating, and deletion of posts.
  • Responsive Design: Perfectly adapts to both desktop and mobile devices.
  • Automatic Build Integration: Content changes can automatically trigger a rebuild of the static site.
  • Type Safety: Full-stack use of TypeScript ensures code robustness.

Technical Highlights#

  • Frontend-Backend Separation Architecture: Clear responsibilities, easy to maintain and extend.
  • Component-Driven Development: High code reusability, improving development efficiency.
  • Comprehensive Error Handling: Provides friendly user feedback and a robust system.
  • Optimized Build Environment: Ensures the stability and reliability of the production environment.
  • Standardized API Design: Complies with RESTful standards, easy to understand and integrate with.

This admin panel provides a complete content management solution for the blog system. It performs reliably in the Build environment and is a production-ready solution.

Summary of front-end administrator panel development
Author Juyao Huang
Published at October 1, 2025
Comment seems to stuck. Try to refresh?✨