Skip to main content
The server.py module provides the create_app() factory function that creates a FastAPI application with all routes, WebSocket support, and file system watching for the markdown editor.

create_app()

Create the FastAPI application for the markdown editor.
FileHandler | DirectoryHandler
required
File or folder access service. Use FileHandler for single-file mode or DirectoryHandler for folder mode.
str
default:"file"
Editor mode, either "file" or "folder". Must match the handler type.
FastAPI
Configured FastAPI application with routes, static assets, WebSocket support, and file system watching.
The mode parameter must be either "file" or "folder". Any other value raises ValueError.
Raises:
  • ValueError - If mode is not "file" or "folder"

Example

Application Lifecycle

The application uses FastAPI’s lifespan context manager to start and stop file system observers:

HTTP Routes

GET /

Serve the editor web page.
FileResponse
The main editor application HTML page (index.html).

GET /favicon.ico

Redirect browser default favicon.ico requests to the SVG favicon.
RedirectResponse
302 redirect to /static/favicon.svg.

GET /api/mode

Return the current server mode.
dict[str, str]
Mode payload with value "file" or "folder".Example: {"mode": "folder"}

GET /api/file-tree

Return the markdown file tree for folder mode.
dict[str, Any]
Nested folder/file structure (see DirectoryHandler.get_file_tree() for schema).
This endpoint is only available in folder mode. Returns 400 error in file mode.
Example Response:

GET /api/content

Return markdown content and metadata.
str | None
Relative file path in folder mode (e.g., ?file=guides/intro.md). Not used in file mode.
dict[str, object]
Response containing:
  • content (str): Markdown content as UTF-8 string
  • metadata (dict): File metadata from FileHandler.get_metadata()
    • In folder mode, includes additional relative_path field
Raises:
  • HTTPException(400) - If file parameter missing in folder mode or path invalid
  • HTTPException(404) - If file does not exist
  • HTTPException(500) - If file read fails
Example Response:

POST /api/save

Persist markdown content to disk with atomic file replacement.
Request Body (SaveRequest):
str
required
Full markdown document content to save.
str | None
Relative file path in folder mode. Not used in file mode.
dict[str, object]
Response containing:
  • status (str): Always "saved" on success
  • metadata (dict): Updated file metadata after save
Raises:
  • HTTPException(400) - If file parameter missing in folder mode or path invalid
  • HTTPException(404) - If file does not exist
  • HTTPException(500) - If write fails
The server tracks internal writes with a timestamp to distinguish them from external file changes when file watching.
Example Request:
Example Response:

POST /api/images

Save an uploaded image in the workspace images directory.
UploadFile
required
Uploaded image data from multipart form payload.
dict[str, str]
Response containing:
  • path (str): Relative image path for markdown (e.g., "images/photo-20240301-123456.png")
  • filename (str): Saved filename with timestamp
Constraints:
  • Allowed extensions: .png, .jpg, .jpeg, .gif, .webp, .svg, .bmp, .ico
  • Maximum size: 10 MB (10,485,760 bytes)
  • Filename sanitization: Non-alphanumeric characters replaced with hyphens, timestamp appended
Raises:
  • HTTPException(400) - If file format unsupported, empty, or too large
Example Response:

GET /images/{filename:path}

Serve uploaded images from the workspace images directory.
str
required
Relative filename under the images directory (e.g., "photo.png" or "subdir/photo.png").
FileResponse
Streamed image file response when present.
This endpoint validates that the path stays within the images directory. Directory traversal attempts (e.g., "../etc/passwd") return 400 error.
Raises:
  • HTTPException(400) - If path contains .. or escapes images directory
  • HTTPException(404) - If image not found

WebSocket Routes

WS /ws

Maintain WebSocket connections for external file-change notifications.
WebSocket
required
Incoming WebSocket connection from the browser.
Message Format (Server → Client): File Mode:
Folder Mode:
The WebSocket connection stays open until the client disconnects. Clients should reconnect automatically if the connection drops.
Behavior:
  • Accepts connection and registers client in WebSocketHub
  • Keeps connection alive by receiving (and ignoring) client messages
  • Removes client on disconnect or error
  • Receives external file change notifications from watchdog observer

Helper Classes

WebSocketHub

Manage active WebSocket clients and fan-out messages.
Methods:
async method
Accept and register a new WebSocket client.
async method
Remove a WebSocket client from the active set.
async method
Send a JSON payload to all currently connected clients. Automatically removes stale clients that fail to receive.

MarkdownPathEventHandler

Watchdog handler for markdown file changes in file or folder mode.
Constructor Parameters:
Callable[[Path], None]
required
Callback invoked on external file changes with the changed file path.
Callable[[], bool]
required
Callback returning True to ignore events (used to skip internal writes).
Path | None
Single-file mode target markdown path. Mutually exclusive with root_directory.
Path | None
Folder-mode workspace root path. Mutually exclusive with target_file.
Event Handling:
  • Listens for modified, moved, and created events
  • Filters out directory events (only files)
  • Validates events match target file or are markdown files in root directory
  • Throttles to max one notification per 0.2 seconds
  • Ignores events within 0.5 seconds of internal writes

App State

The FastAPI app stores state in app.state:

Complete Server Example

File Watching Details

Watch Configuration

File Mode:
  • Watches: Parent directory of target file
  • Recursive: No
  • Events: Only for the specific target file
Folder Mode:
  • Watches: Entire workspace directory
  • Recursive: Yes
  • Events: All .md and .markdown files

Event Throttling

The file watcher implements two throttling mechanisms:
  1. Time-based throttling: Maximum one notification per 0.2 seconds
  2. Internal write filtering: Ignores events within 0.5 seconds of POST /api/save requests
This prevents the editor from receiving notifications for its own save operations, which would cause unnecessary UI updates and potential conflicts.

Static Files

Static assets are mounted from markdown_os/static/:
Available at:
  • /static/index.html - Main editor HTML
  • /static/js/*.js - JavaScript modules
  • /static/css/*.css - Stylesheets
  • /static/favicon.svg - Application icon

Error Handling

The server maps internal exceptions to HTTP status codes:

Source Reference

See the complete implementation in markdown_os/server.py.