{"id":30029977,"url":"https://github.com/wilfreud/stasis","last_synced_at":"2026-04-05T08:34:46.584Z","repository":{"id":308547094,"uuid":"991097578","full_name":"wilfreud/stasis","owner":"wilfreud","description":"A TypeScript-based Express.js service for HTML-to-PDF conversion using Playwright and Handlebars, designed for performance and maintainability.","archived":false,"fork":false,"pushed_at":"2025-08-06T13:48:06.000Z","size":733,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-06T15:26:16.133Z","etag":null,"topics":["docker","express","handlebars","html-to-pdf","pdf-generation","playwright","rest-api","typescript","web-service"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wilfreud.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-27T05:58:27.000Z","updated_at":"2025-08-06T13:48:11.000Z","dependencies_parsed_at":"2025-08-06T15:27:38.582Z","dependency_job_id":"f0533aa6-bff9-4dd9-b1ae-d026f3e2ac81","html_url":"https://github.com/wilfreud/stasis","commit_stats":null,"previous_names":["wilfreud/stasis"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/wilfreud/stasis","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wilfreud%2Fstasis","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wilfreud%2Fstasis/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wilfreud%2Fstasis/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wilfreud%2Fstasis/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wilfreud","download_url":"https://codeload.github.com/wilfreud/stasis/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wilfreud%2Fstasis/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269135956,"owners_count":24366537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-06T02:00:09.910Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["docker","express","handlebars","html-to-pdf","pdf-generation","playwright","rest-api","typescript","web-service"],"created_at":"2025-08-06T18:43:06.358Z","updated_at":"2025-12-30T21:49:23.451Z","avatar_url":"https://github.com/wilfreud.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stasis\n\n![Stasis Logo](public/stasis.png)\n\nA high-performance (well, still gotta benchmark first), RESTful Express.js \"microservice\" (arguably) for generating PDF documents from Handlebars templates using Playwright headless browser.\n\n## Features\n\n- **RESTful API architecture** following standard conventions\n- **Template-based PDF generation** from Handlebars templates with dynamic data injection\n- **Raw HTML to PDF conversion** with optional template compilation\n- **Built-in Tailwind CSS support** for modern styling (v4 by default)\n- **File upload management** with drag-and-drop template interface and download capability\n- **Bulk template upload** supporting up to 20 files simultaneously with detailed error reporting\n- **Type-safe TypeScript implementation** with comprehensive error handling\n- **Playwright-powered rendering** for consistent cross-platform PDF output\n- **Docker-ready containerization** for scalable deployment\n- **Performance monitoring middleware** with detailed benchmarking\n- **Template persistence** with volume mapping for Docker deployments\n- **Security features** including CSRF protection and file validation\n\n## Technology Stack\n\n| Category             | Technology   | Purpose                                          |\n| -------------------- | ------------ | ------------------------------------------------ |\n| **Backend**          | Express.js   | HTTP server and API framework                    |\n| **Rendering**        | Playwright   | Headless browser for PDF generation              |\n| **Templating**       | Handlebars   | Template engine for dynamic HTML                 |\n| **Language**         | TypeScript   | Type-safe JavaScript superset                    |\n| **Package Mgmt**     | PNPM         | Fast, disk-efficient package manager             |\n| **Testing**          | Vitest       | Fast unit testing framework                      |\n| **Containerization** | Docker       | Application containerization                     |\n| **Styling**          | Tailwind CSS | Utility-first CSS framework for template styling |\n| **Security**         | Helmet       | HTTP security headers middleware                 |\n\n## Core Dependencies\n\nThe project uses the following main dependencies:\n\n- **Framework**: Express.js v5.1.0 with TypeScript\n- **PDF Generation**: Playwright v1.52.0 for headless browser automation\n- **Templating**: Handlebars v4.7.8 for dynamic HTML templating\n- **Date Handling**: date-fns v4.1.0 with French locale support\n- **File Upload**: Multer v2.0.0 for multipart/form-data handling\n- **Security**: Helmet v8.1.0 for HTTP security headers, CORS v2.8.5\n- **Testing**: Vitest v3.1.4 for fast unit testing\n- **Code Quality**: Prettier v3.5.3 for code formatting\n\n## Project Structure\n\n```\npdf-generator/\n├── src/\n│   ├── index.ts                     # Server entry point\n│   ├── mockdata.ts                  # Sample data for testing\n│   ├── controllers/\n│   │   ├── index.ts                 # HTTP request handlers with response logic\n│   │   └── template.controller.ts   # Template management endpoints\n│   ├── middlewares/\n│   │   ├── benchmark.middleware.ts  # Performance monitoring middleware\n│   │   └── multer.middleware.ts     # File upload handling middleware\n│   ├── services/\n│   │   ├── handlebars.service.ts    # Template compilation service\n│   │   ├── handlebars.service.test.ts # Unit tests for Handlebars service\n│   │   └── playwright.service.ts    # PDF generation and browser management\n│   └── types/\n│       └── index.ts                 # TypeScript interfaces and type definitions\n├── templates/\n│   ├── thermal-receipt.hbs          # Predefined Handlebars templates\n│   ├── invoice.hbs                  # Invoice template\n│   ├── resume.hbs                   # Resume template\n│   └── tw.hbs                       # Tailwind CSS demo template\n├── public/\n│   ├── index.html                   # Template management UI\n│   ├── stasis.png                   # Application logo\n│   └── ui.png                       # UI screenshot\n├── scripts/\n│   ├── build-fix.js                 # Post-build script for path aliasing\n│   ├── benchmark.py                 # Python benchmark script\n│   ├── templates-setup.js           # Template initialization script\n│   └── requirements.txt             # Python dependencies for benchmarking\n├── Dockerfile                       # Container definition\n├── compose.yaml                     # Multi-container orchestration\n├── vitest.config.ts                 # Testing configuration\n└── tsconfig.json                    # TypeScript configuration\n```\n\n## Getting Started\n\n### Prerequisites\n\n- Node.js v18+ (LTS recommended)\n- PNPM package manager (v10+ recommended)\n- Docker (optional, for containerized deployment)\n\n**Note**: This project uses ES modules (`\"type\": \"module\"` in package.json) and requires Node.js v18+ for full compatibility.\n\n### Development Setup\n\n```bash\n# Install dependencies\npnpm install\n\n# Run development server with hot reloading\npnpm dev\n\n# Format code with Prettier\npnpm format\n\n# Build TypeScript for development\npnpm build:dev\n\n# Build production artifacts\npnpm build\n\n# Start production server\npnpm start\n\n# Run unit tests\npnpm test\n```\n\n## Configuration\n\nThe service can be configured using environment variables:\n\n| Variable        | Description                         | Default Value |\n| --------------- | ----------------------------------- | ------------- |\n| `PORT`          | Server port number                  | `7070`        |\n| `NODE_ENV`      | Environment mode                    | `development` |\n| `TEMPLATES_DIR` | Directory path for template storage | `./templates` |\n\nExample `.env` file:\n\n```env\nPORT=8080\nNODE_ENV=production\nTEMPLATES_DIR=/app/custom-templates\n```\n\n## REST API Reference\n\n### PDF Generation Endpoints\n\n| Endpoint              | Method | Description                   | Request Body                 | Response           |\n| --------------------- | ------ | ----------------------------- | ---------------------------- | ------------------ |\n| `/api/health`         | GET    | Service health status         | -                            | `application/json` |\n| `/api/documents`      | POST   | Generate PDF from template    | Template options and data    | `application/pdf`  |\n| `/api/documents/raw`  | POST   | Generate PDF from raw HTML    | HTML content (optional data) | `application/pdf`  |\n| `/api/documents/test` | GET    | Test PDF generation (receipt) | -                            | `application/pdf`  |\n\n### Template Management Endpoints\n\n| Endpoint                                | Method | Description                        | Request Body                           | Response           |\n| --------------------------------------- | ------ | ---------------------------------- | -------------------------------------- | ------------------ |\n| `/api/templates/list`                   | GET    | List all available templates       | -                                      | `application/json` |\n| `/api/templates/checksums`              | GET    | Get SHA-256 checksums of templates | -                                      | `application/json` |\n| `/api/templates/download/:templateName` | GET    | Download a template file           | -                                      | `text/plain`       |\n| `/api/templates/upload`                 | POST   | Upload a new template              | multipart/form-data (file + metadata)  | `application/json` |\n| `/api/templates/upload/bulk`            | POST   | Upload multiple templates          | multipart/form-data (files + metadata) | `application/json` |\n| `/api/templates/delete`                 | DELETE | Delete an existing template        | JSON with template name                | `application/json` |\n\n### Request Body Examples\n\n#### `POST /api/templates/upload` - Upload a Template\n\nThis endpoint accepts `multipart/form-data` with the following fields:\n\n```\ntemplateName: \"invoice\"      // Name to identify the template (without extension)\ntemplateFile: [Binary File]  // .hbs or .handlebars file content\noverwrite: \"true\"            // Optional boolean to allow overwriting existing templates\npageToken: \"auth-token\"      // Security token for authentication\n```\n\n#### `DELETE /api/templates/delete` - Delete a Template\n\n```json\n{\n  \"templateName\": \"invoice\", // Name of the template to delete (without extension)\n  \"pageToken\": \"auth-token\" // Security token for authentication\n}\n```\n\n#### `GET /api/templates/download/:templateName` - Download a Template\n\nThis endpoint allows you to download the source code of an existing template.\n\n**URL Parameters:**\n\n- `templateName`: The name of the template to download (without .hbs extension)\n\n**Response:**\n\n- Content-Type: `text/plain`\n- Content-Disposition: `attachment; filename=\"templateName.hbs\"`\n- Body: The raw Handlebars template content\n\n**Example:**\n\n```bash\ncurl -o invoice.hbs http://localhost:7070/api/templates/download/invoice\n```\n\n**Use Cases:**\n\n- **Backup templates** before making modifications\n- **Version control** - download templates for git tracking\n- **Template sharing** between environments\n- **Local editing** - download, edit locally, then re-upload\n\n#### `GET /api/templates/checksums` - Get Template Checksums\n\nThis endpoint returns SHA-256 checksums for all templates, useful for integrity verification and change detection.\n\n**Response Format:**\n\n```json\n{\n  \"invoice\": \"ee422544d142278bfe3e1a307923d455f55b9fe3a7e35ad75ca74799e28f5dcc\",\n  \"receipt\": \"842cbc74d2bb5baa68adaea4781b82fa3d4af67cd0febe87bb59f2f35e1ffe7a\",\n  \"resume\": \"77905cade7e32d2dc495c3d9c4ebb403c3231ae96eaeb4b8362297d65cc085f7\"\n}\n```\n\n**Use Cases:**\n\n- **Integrity Verification**: Detect if templates have been modified\n- **Synchronization**: Compare templates between environments\n- **Audit Trail**: Track template changes over time\n- **Automation**: Verify template consistency in CI/CD pipelines\n\n#### `POST /api/templates/upload/bulk` - Upload Multiple Templates\n\nThis endpoint accepts `multipart/form-data` for uploading up to 20 templates simultaneously:\n\n```\ntemplateFiles: [Multiple Binary Files]  // .hbs or .handlebars files (max 20 files, 2MB each)\noverwrite: \"true\"                       // Optional boolean to allow overwriting existing templates\npageToken: \"auth-token\"                 // Security token for authentication\n```\n\n**Response Format:**\n\n```json\n{\n  \"status\": \"success\",\n  \"message\": \"All 3 templates uploaded successfully\",\n  \"totalFiles\": 3,\n  \"successCount\": 3,\n  \"errorCount\": 0,\n  \"skippedCount\": 0,\n  \"results\": [\n    {\n      \"originalName\": \"invoice.hbs\",\n      \"templateName\": \"invoice\",\n      \"status\": \"success\",\n      \"message\": \"Template created successfully\"\n    },\n    {\n      \"originalName\": \"receipt.hbs\",\n      \"templateName\": \"receipt\",\n      \"status\": \"success\",\n      \"message\": \"Template created successfully\"\n    },\n    {\n      \"originalName\": \"resume.hbs\",\n      \"templateName\": \"resume\",\n      \"status\": \"success\",\n      \"message\": \"Template created successfully\"\n    }\n  ]\n}\n```\n\n**Status Codes:**\n\n- `201`: All templates uploaded successfully\n- `207`: Partial success (some succeeded, some failed/skipped)\n- `400`: No files provided or invalid request\n- `500`: Server error or all uploads failed\n\n**Template Name Generation:**\n\n- Template names are auto-generated from filenames\n- Extensions (`.hbs`, `.handlebars`) are removed\n- Special characters are replaced with hyphens\n- Names are converted to lowercase\n- Example: `My-Invoice Template.hbs` → `my-invoice-template`\n\n#### `POST /api/documents` - Generate PDF from Template\n\n```json\n{\n  \"templateId\": \"thermal-receipt\",\n  \"data\": {\n    \"invoiceNumber\": \"twA63I31dsrG0V\",\n    \"date\": \"2025-05-26\",\n    \"dueDate\": \"2025-06-25\",\n    \"company\": {\n      \"name\": \"HIBOUTIK\",\n      \"address\": \"30 place du Centre, 01234 MAVILLE\",\n      \"phone\": \"01 23 45 67 89\",\n      \"website\": \"hiboutik\",\n      \"email\": \"contact@hiboutik.com\"\n    },\n    \"client\": {\n      \"name\": \"Pierre\",\n      \"address\": \"456 Client Ave, Business City, BC 67890\",\n      \"email\": \"accounts@acme.com\"\n    },\n    \"items\": [\n      {\n        \"description\": \"Pizza\",\n        \"quantity\": 1,\n        \"unitPrice\": \"12.00\",\n        \"total\": \"12.00\"\n      }\n    ],\n    \"tax\": \"0.63\",\n    \"taxRate\": \"5.5\",\n    \"totalAmount\": \"12.00\",\n    \"subtotal\": \"12.00\",\n    \"paymentMethod\": \"ESP\",\n    \"amountGiven\": \"15.00\",\n    \"amountReturned\": \"3.00\",\n    \"ticketNumber\": \"5232\",\n    \"paymentTerms\": \"Net 30 days\"\n  },\n  \"pdfOptions\": {\n    \"format\": \"A4\",\n    \"margin\": {\n      \"top\": \"1cm\",\n      \"bottom\": \"1cm\"\n    }\n  },\n  \"outputFileName\": \"invoice.pdf\"\n}\n```\n\n#### `POST /api/documents/raw` - Generate PDF from Raw HTML\n\nThis endpoint accepts raw HTML input and optionally supports dynamic data injection using Handlebars templates. If no `data` is provided, the raw HTML is used directly to generate the PDF. Set `loadExternalResources` to true if the page needs external resources (e.g. Tailwind via CDN). The `useTailwindCss` option enables built-in Tailwind CSS support.\n\n```json\n{\n  \"rawHtml\": \"\u003chtml\u003e\u003cbody\u003e\u003ch1\u003e{{title}}\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\",\n  \"data\": {\n    \"title\": \"My Document\"\n  },\n  \"pdfOptions\": {\n    \"format\": \"A4\"\n  },\n  \"outputFileName\": \"document.pdf\",\n  \"loadExternalResources\": true,\n  \"useTailwindCss\": true\n}\n```\n\n**Updated Behavior**:\n\n- If `data` is provided, it will be used to compile the Handlebars template.\n- If `data` is omitted, the `rawHtml` will be used as-is to generate the PDF.\n\n### Response Formats\n\n#### Success Responses\n\n- **PDF Generation**: Returns the PDF binary with `Content-Type: application/pdf` and appropriate filename headers.\n\n```json\n{\n  \"status\": \"success\",\n  \"message\": \"PDF generated successfully\",\n  \"fileName\": \"document.pdf\"\n}\n```\n\n#### Error Responses\n\nAll error responses follow a consistent format with HTTP status codes:\n\n```json\n{\n  \"status\": \"error\",\n  \"message\": \"Failed to generate PDF document\",\n  \"error\": \"Raw HTML is required to generate PDF\"\n}\n```\n\n## Deployment Options\n\n### Docker Deployment\n\nThe service includes Docker configuration for containerized deployment in any environment.\n\n```bash\n# Build the Docker image\ndocker build -t pdf-generator .\n\n# Run the container with port mapping\ndocker run -p 7070:7070 pdf-generator\n```\n\n### Docker Compose\n\nFor multi-container deployments or complex configurations:\n\n```bash\n# Start with Docker Compose\ndocker compose up\n\n# Run in detached mode for production\ndocker compose up -d\n\n# View container logs\ndocker compose logs -f\n```\n\nThe Docker Compose configuration includes a named volume to persist templates, ensuring that uploaded templates are not lost when containers are recreated:\n\n```yaml\nvolumes:\n  - pdf_templates:/app/templates # Use named volume for template persistence\n\nvolumes:\n  pdf_templates:\n    # Named volume for templates that persists across container recreations\n```\n\nUsing a named volume provides better portability and proper Docker-managed lifecycle for your template files.\n\nFor more details on testing template persistence, see [DOCKER-PERSISTENCE.md](DOCKER-PERSISTENCE.md).\n\n## Template Development\n\n### Handlebars Integration\n\nTemplates can be developed using Handlebars syntax and registered with the service. The framework supports:\n\n- **Standard templates**: Stored in `templates/` directory (or path specified by TEMPLATES_DIR environment variable)\n- **Custom templates**: Sent directly in API requests\n- **Dynamic data binding**: Any JSON object structure can be rendered\n- **Nested objects**: Access deeply nested properties with dot notation\n\n### Available Helpers\n\nThe service extends Handlebars with additional helper functions:\n\n| Helper        | Purpose                    | Example Usage                        |\n| ------------- | -------------------------- | ------------------------------------ |\n| `capitalize`  | Capitalize first letter    | `{{capitalize name}}`                |\n| `uppercase`   | Convert to uppercase       | `{{uppercase text}}`                 |\n| `lowercase`   | Convert to lowercase       | `{{lowercase text}}`                 |\n| `formatDate`  | Format dates with date-fns | `{{formatDate date \"yyyy-MM-dd\"}}`   |\n| `gt`          | Greater than comparison    | `{{#if (gt value 10)}}...{{/if}}`    |\n| `or`          | Logical OR operation       | `{{#if (or cond1 cond2)}}...{{/if}}` |\n| `currentDate` | Get current date           | `{{currentDate \"yyyy-MM-dd\"}}`       |\n\n**Note**: Date formatting uses French locale (fr) by default and supports all date-fns format patterns.\n\n### Template Creation Process\n\n1. Create a new `.hbs` or `.handlebars` file in the templates directory (e.g., `invoice.hbs`)\n2. Use Handlebars syntax for dynamic content injection\n3. Include any required CSS for styling (inline or via Tailwind CSS support)\n4. Upload via the web interface or place directly in the templates directory\n5. Reference the template by its ID (filename without extension) in API calls: `\"templateId\": \"invoice\"`\n\n### Supported Template Features\n\n- **File formats**: `.hbs` and `.handlebars` extensions\n- **Built-in CSS frameworks**: Tailwind CSS v4 support with `useTailwindCss` option\n- **External resources**: Load external stylesheets and scripts with `loadExternalResources`\n- **Custom styling**: Inline CSS and external stylesheet references\n- **Template inheritance**: Handlebars partials and layouts (if configured)\n- **File size limits**: Maximum 2MB per template file\n\n### Included Templates\n\nThe project comes with several pre-built templates:\n\n1. **thermal-receipt.hbs**: Point-of-sale thermal receipt template\n2. **invoice.hbs**: Professional invoice template\n3. **resume.hbs**: Resume/CV template\n4. **tw.hbs**: Tailwind CSS demonstration template\n\nThese templates serve as examples and can be customized or replaced as needed.\n\n## Architecture \u0026 Design\n\n### Component Architecture\n\nThe service follows a modern, layered architecture with clean separation of concerns:\n\n```\n┌─────────────┐      ┌─────────────┐      ┌─────────────┐\n│  Controller │ ──── │   Service   │ ──── │  Playwright │\n│   (Express) │      │   (Logic)   │      │  (Browser)  │\n└─────────────┘      └─────────────┘      └─────────────┘\n       │                    │                    │\n       │                    │                    │\n       ▼                    ▼                    ▼\n┌─────────────┐      ┌─────────────┐      ┌─────────────┐\n│    Types    │      │ Handlebars  │      │    Utils    │\n│ (Interfaces)│      │ (Templates) │      │  (Helpers)  │\n└─────────────┘      └─────────────┘      └─────────────┘\n```\n\n### REST API Design Principles\n\n- **Resource-Oriented**: API endpoints focus on resources (documents) not actions\n- **Standard HTTP Methods**: POST for creation, GET for retrieval\n- **Consistent Response Formats**: Standard error and success response structures\n- **Proper Status Codes**: 201 for creation, 400 for validation errors, etc.\n- **Content Negotiation**: Supports both JSON and PDF responses\n\n### Performance Considerations\n\n- **Browser Instance Management**: Reuse of Playwright browser instances\n- **Template Compilation Caching**: Optimization for repeated template use\n- **Asynchronous Processing**: Full async/await implementation\n- **Binary Response Streaming**: Efficient delivery of PDF documents\n- **Comprehensive Error Handling**: Graceful degradation and detailed error information\n\n## Monitoring \u0026 Maintenance\n\nThe service includes built-in performance monitoring that tracks:\n\n- Request processing time\n- Template compilation time\n- PDF rendering duration\n- Memory usage statistics\n\n## Testing\n\nThe project includes unit tests using Vitest for fast and reliable testing:\n\n```bash\n# Run all tests\npnpm test\n\n# Run tests in watch mode (development)\npnpm test --watch\n```\n\nTests are located alongside their corresponding source files (e.g., `handlebars.service.test.ts`).\n\n## Scripts and Utilities\n\nThe project includes several utility scripts in the `scripts/` directory:\n\n- **build-fix.js**: Post-build script that adds path aliasing configuration\n- **benchmark.py**: Python-based benchmarking tool for performance testing\n- **templates-setup.js**: Script for initializing template directory structure\n- **test-bulk-upload.js**: Node.js script for testing bulk template upload functionality\n- **test-bulk-upload.ps1**: PowerShell script for testing bulk upload with detailed reporting\n- **test-checksums.js**: Node.js script for testing template checksum functionality\n- **run-benchmark.bat**: Batch file for running benchmarks on Windows\n\n## License\n\nISC License\n\n## Contributing\n\nContributions are welcome! Please follow these steps:\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'feat: add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\nFor major changes, please open an issue first to discuss proposed changes.\n\n## Template Management UI\n\n![Template Management UI](public/ui.png)\n\nThe service includes a web-based template management interface (`public/index.html`) that allows users to:\n\n1. **Upload new Handlebars templates** with drag-and-drop support\n2. **Bulk upload multiple templates** simultaneously (up to 20 files)\n3. **Calculate template checksums** for integrity verification and change detection\n4. **View existing templates** in a clean, organized list\n5. **Delete templates** when no longer needed\n6. **Overwrite protection** with optional toggle for existing templates\n\n### Security Features\n\nThe template management interface includes security features:\n\n- **Token-based authentication** between the frontend and API\n- **File extension validation** (only `.hbs` and `.handlebars` files)\n- **File size limits** (max 2MB per template)\n- **Input validation and sanitization** for template names\n- **CSRF protection** through pageToken mechanism\n\n### Using the Template Manager\n\n1. **Access the UI**: Navigate to `http://localhost:7070` in your browser\n2. **Upload Template**:\n   - **Single Upload**: Enter a template name (without extension), select a `.hbs` file or drag and drop, toggle overwrite protection if needed\n   - **Bulk Upload**: Select multiple `.hbs` files simultaneously (up to 20 files, 2MB each)\n3. **View Templates**: All existing templates are displayed automatically\n4. **Download Template**: Click the blue \"Download\" button next to any template to save it locally\n5. **Delete Template**: Click the red \"Delete\" button next to any template to remove it\n\nWhen running in Docker, templates are persisted through volume mapping, ensuring they remain available after container restarts.\n\n## Bulk Template Upload\n\nFor detailed information about the bulk template upload functionality, including advanced usage examples, error handling, and testing scripts, see the [Bulk Upload Documentation](docs/BULK-UPLOAD.md).\n\n**Quick Example:**\n\n````bash\n**Quick Example:**\n```bash\n# Test bulk upload with PowerShell\npowershell -ExecutionPolicy Bypass -File scripts/test-bulk-upload.ps1\n\n# Test template checksums\nnode scripts/test-checksums.js\n\n# Or create test templates and upload manually\nnode scripts/test-bulk-upload.js\n````\n\n```\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilfreud%2Fstasis","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwilfreud%2Fstasis","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwilfreud%2Fstasis/lists"}