diff --git a/.github/workflows/documentation_workflow.yml b/.github/workflows/documentation_workflow.yml
index 880ca35..f8dfcc0 100644
--- a/.github/workflows/documentation_workflow.yml
+++ b/.github/workflows/documentation_workflow.yml
@@ -1,31 +1,71 @@
-name: Documentation
+name: Deploy Documentation to GitHub Pages
on:
push:
- branches:
- - main
+ branches: ["main"]
+ workflow_dispatch:
-jobs:
- documentation:
- runs-on: macos-latest
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+jobs:
+ build:
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - name: Checkout
+ uses: actions/checkout@v4
- name: Set up JDK 17
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v4
with:
java-version: 17
- distribution: 'adopt'
+ distribution: temurin
- name: Make gradlew executable
run: chmod +x ./gradlew
- - name: Generate documentation
+ - name: Generate KDoc
run: ./gradlew dokkaHtmlMultiModule
- - name: Upload documentation
- uses: actions/upload-artifact@v4
+ - name: Setup Ruby
+ uses: ruby/setup-ruby@4a9ddd6f338a97768b8006bf671dfbad383215f4
+ with:
+ ruby-version: '3.3'
+ bundler-cache: true
+ cache-version: 0
+ working-directory: documentation
+
+ - name: Setup Pages
+ id: pages
+ uses: actions/configure-pages@v5
+
+ - name: Build Jekyll site
+ working-directory: documentation
+ run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
+ env:
+ JEKYLL_ENV: production
+
+ - name: Copy KDoc into site
+ run: cp -r build/dokka/htmlMultiModule documentation/_site/kdoc
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
with:
- name: ProjectMiddleware-KDoc-Documentation
- path: 'build/dokka/htmlMultiModule'
+ path: documentation/_site
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v5
diff --git a/.gitignore b/.gitignore
index 0bfb28a..423c775 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,13 @@ bin/
### Mac OS ###
.DS_Store
+### Jekyll ###
+documentation/_site/
+documentation/.jekyll-cache/
+documentation/.jekyll-metadata
+documentation/vendor/
+documentation/.bundle/
+
local.properties
**/ClientHiddenConstants.kt
CLAUDE.md
diff --git a/README.md b/README.md
index 704ea35..f589ad8 100644
--- a/README.md
+++ b/README.md
@@ -1,54 +1,38 @@
-# Project Middleware Overview
+# ProjectMiddleware
-Welcome to the **Project Middleware**! This API allows you to transform data from any public API to a format that best fits your needs. The process involves defining mapping rules to convert fields, handle nested structures, and concatenate values where necessary.
+**ProjectMiddleware** is a Kotlin/Ktor API transformation service. It lets you define mapping rules that call an external API and reshape its JSON response — so every client gets exactly the format it needs, without touching the upstream service.
-## Official Documentation
+## Documentation
-You can access out official documentation for the Project Middleware API here:
-- [Project Middleware Official Documentation](https://leonardos-organization-15.gitbook.io/projectmiddleware-public-docs)
+Full documentation: **[leonardobai12.github.io/ProjectMiddleware](https://leonardobai12.github.io/ProjectMiddleware/)**
## How It Works
-1. **Define Mapping Rules**: Use the provided rules in [Documentation for Mapping Rules](./documentation/MappingRules.md) to create custom mapping logic for your API responses. You can transform field names, exclude empty values, and more.
+1. **Define mapping rules** — specify which external endpoint to call and how to transform its response: rename fields, extract nested values, concatenate multiple fields, and more. See [Mapping Rules](https://leonardobai12.github.io/ProjectMiddleware/getting-started/mapping-rules/).
-2. **Test with Preview Route**: Before creating a permanent mapping route, it's recommended to test your configuration using the **Preview Route**. This ensures the mapped response matches your expectations without committing to a permanent route.
+2. **Preview before committing** — call `/v1/preview` with your rules to verify the transformed output before creating a permanent route. See [Preview Route](https://leonardobai12.github.io/ProjectMiddleware/getting-started/preview-route/).
- - For more information, see [Documentation for Preview Route](./documentation/PreviewRoute.md).
+3. **Create the mapped route** — once the preview looks right, register the route. It gets a UUID-based path and stays live from that point on. See [Mapping Request](https://leonardobai12.github.io/ProjectMiddleware/getting-started/mapping-request/).
-3. **Create a Mapped Route**: Once you’re satisfied with the preview response, you can proceed to create the actual mapped route using the **Mapping Request** guidelines.
-
- - Learn more about this in [Documentation for Mapping Request](./documentation/MappingRequest.md).
+4. **Call the route** — send requests to `/v1/{uuid}/{path}` and receive the transformed response in real time.
## Key Features
-- **Flexible Mapping**: You can rename fields, extract values from deeply nested structures, and concatenate multiple fields into one.
-- **Preview Before Creation**: Use the preview functionality to verify your mappings before committing to a route.
-- **Easy Field Transformation**: Transform field names and types seamlessly with custom rules.
-
-## Steps to Create a Mapped Route
-
-1. **Preview the Response**:
- - Call the `/v1/preview` endpoint with your rules to ensure that the response matches your expected output. This step is crucial before proceeding to create a permanent mapped route.
- - [Documentation for Mapping Rules](./documentation/MappingRules.md): Detailed explanation of how to define your mapping rules.
- - [Documentation for Preview Route](./documentation/PreviewRoute.md): Guidelines for testing your mappings.
-
-2. **Create the Mapped Route**:
- - After confirming the previewed response, create the route using the mapping request as described in these docs:
- - [Documentation for Mapping Request](./documentation/MappingRequest.md): Full instructions on how to set up your mapped route.
-
-3. **Call the Mapped Route**:
- - Once the route is created, you can start using the mapped route to get transformed responses by calling `/v1/{uuid}/{path}`.
+- **Flexible field mapping** — rename fields, extract values from deeply nested structures, and concatenate multiple fields into one.
+- **Preview before creation** — validate mappings against live data before committing to a permanent route.
+- **Dynamic routing** — every registered route is served instantly via its UUID path with no redeployment needed.
-## API Endpoints Overview
+## API Endpoints
-### Base URL: https://projectmiddleware.fly.dev/
+**Base URL:** `https://projectmiddleware.fly.dev/`
-- **`GET https://projectmiddleware.fly.dev/v1/preview`**: Preview a mapped response before creating the route.
-- **`GET https://projectmiddleware.fly.dev/v1/routes`**: Retrieve all mapped routes.
-- **`POST https://projectmiddleware.fly.dev/v1/mapping`**: Define a new mapping rule for an API.
-- **`GET https://projectmiddleware.fly.dev/v1/{uuid}/{path}`**: Retrieve the mapped response using the specific UUID and path.
+| Method | Path | Description |
+|--------|------|-------------|
+| `POST` | `/v1/mapping` | Create a mapped route |
+| `GET` | `/v1/routes` | List all mapped routes |
+| `POST` | `/v1/preview` | Preview a mapped response |
+| `*` | `/v1/{uuid}/{path}` | Call a mapped route |
-## Postman Documentation
+## Postman Collection
-### [Postman Collection URL](https://documenter.getpostman.com/view/28162587/2sAXjRX9p1#intro)
-For a complete list of routes, examples, and how to use the API, visit our Postman collection.
+For a complete list of routes and ready-to-run examples, see the [Postman collection](https://documenter.getpostman.com/view/28162587/2sAXjRX9p1#intro).
diff --git a/documentation/Gemfile b/documentation/Gemfile
new file mode 100644
index 0000000..f63a769
--- /dev/null
+++ b/documentation/Gemfile
@@ -0,0 +1,9 @@
+source "https://rubygems.org"
+
+gem "jekyll", "~> 4.3"
+gem "jekyll-theme-chirpy", "~> 7.2"
+
+group :jekyll_plugins do
+ gem "jekyll-sitemap"
+ gem "jekyll-seo-tag", "~> 2.8"
+end
diff --git a/documentation/Gemfile.lock b/documentation/Gemfile.lock
new file mode 100644
index 0000000..51f3282
--- /dev/null
+++ b/documentation/Gemfile.lock
@@ -0,0 +1,108 @@
+GEM
+ remote: https://rubygems.org/
+ specs:
+ addressable (2.9.0)
+ public_suffix (>= 2.0.2, < 8.0)
+ base64 (0.3.0)
+ bigdecimal (4.1.2)
+ colorator (1.1.0)
+ concurrent-ruby (1.3.6)
+ csv (3.3.5)
+ em-websocket (0.5.3)
+ eventmachine (>= 0.12.9)
+ http_parser.rb (~> 0)
+ eventmachine (1.2.7)
+ ffi (1.17.4-arm64-darwin)
+ ffi (1.17.4-x86_64-linux-gnu)
+ forwardable-extended (2.6.0)
+ google-protobuf (4.35.0-arm64-darwin)
+ bigdecimal
+ rake (~> 13.3)
+ google-protobuf (4.35.0-x86_64-linux-gnu)
+ bigdecimal
+ rake (~> 13.3)
+ http_parser.rb (0.8.1)
+ i18n (1.14.8)
+ concurrent-ruby (~> 1.0)
+ jekyll (4.4.1)
+ addressable (~> 2.4)
+ base64 (~> 0.2)
+ colorator (~> 1.0)
+ csv (~> 3.0)
+ em-websocket (~> 0.5)
+ i18n (~> 1.0)
+ jekyll-sass-converter (>= 2.0, < 4.0)
+ jekyll-watch (~> 2.0)
+ json (~> 2.6)
+ kramdown (~> 2.3, >= 2.3.1)
+ kramdown-parser-gfm (~> 1.0)
+ liquid (~> 4.0)
+ mercenary (~> 0.3, >= 0.3.6)
+ pathutil (~> 0.9)
+ rouge (>= 3.0, < 5.0)
+ safe_yaml (~> 1.0)
+ terminal-table (>= 1.8, < 4.0)
+ webrick (~> 1.7)
+ jekyll-archives (2.3.0)
+ jekyll (>= 3.6, < 5.0)
+ jekyll-include-cache (0.2.1)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-paginate (1.1.0)
+ jekyll-sass-converter (3.1.0)
+ sass-embedded (~> 1.75)
+ jekyll-seo-tag (2.9.0)
+ jekyll (>= 3.8, < 5.0)
+ jekyll-sitemap (1.4.0)
+ jekyll (>= 3.7, < 5.0)
+ jekyll-theme-chirpy (7.5.0)
+ jekyll (~> 4.3)
+ jekyll-archives (~> 2.2)
+ jekyll-include-cache (~> 0.2)
+ jekyll-paginate (~> 1.1)
+ jekyll-seo-tag (~> 2.8)
+ jekyll-sitemap (~> 1.4)
+ jekyll-watch (2.2.1)
+ listen (~> 3.0)
+ json (2.19.8)
+ kramdown (2.5.2)
+ rexml (>= 3.4.4)
+ kramdown-parser-gfm (1.1.0)
+ kramdown (~> 2.0)
+ liquid (4.0.4)
+ listen (3.10.0)
+ logger
+ rb-fsevent (~> 0.10, >= 0.10.3)
+ rb-inotify (~> 0.9, >= 0.9.10)
+ logger (1.7.0)
+ mercenary (0.4.0)
+ pathutil (0.16.2)
+ forwardable-extended (~> 2.6)
+ public_suffix (7.0.5)
+ rake (13.4.2)
+ rb-fsevent (0.11.2)
+ rb-inotify (0.11.1)
+ ffi (~> 1.0)
+ rexml (3.4.4)
+ rouge (4.7.0)
+ safe_yaml (1.0.5)
+ sass-embedded (1.100.0-arm64-darwin)
+ google-protobuf (~> 4.31)
+ sass-embedded (1.100.0-x86_64-linux-gnu)
+ google-protobuf (~> 4.31)
+ terminal-table (3.0.2)
+ unicode-display_width (>= 1.1.1, < 3)
+ unicode-display_width (2.6.0)
+ webrick (1.9.2)
+
+PLATFORMS
+ arm64-darwin-22
+ x86_64-linux
+
+DEPENDENCIES
+ jekyll (~> 4.3)
+ jekyll-seo-tag (~> 2.8)
+ jekyll-sitemap
+ jekyll-theme-chirpy (~> 7.2)
+
+BUNDLED WITH
+ 2.4.10
diff --git a/documentation/_config.yml b/documentation/_config.yml
new file mode 100644
index 0000000..1e91b41
--- /dev/null
+++ b/documentation/_config.yml
@@ -0,0 +1,89 @@
+theme: jekyll-theme-chirpy
+
+# -------------------------
+# Site metadata
+# -------------------------
+title: ProjectMiddleware
+tagline: A Kotlin/Ktor middleware for transforming and mapping external API responses
+description: >-
+ ProjectMiddleware is an API transformation layer that lets clients register
+ mapping rules, call external APIs, and receive clean transformed responses —
+ all without changing upstream services.
+
+url: "https://leonardobai12.github.io"
+baseurl: "/ProjectMiddleware"
+
+lang: en
+timezone: America/Sao_Paulo
+
+# -------------------------
+# Author
+# -------------------------
+github:
+ username: "LeonardoBai12"
+
+social:
+ name: "Leonardo Bai"
+ email: "lb.io.dev@gmail.com"
+ links:
+ - https://github.com/LeonardoBai12
+
+# -------------------------
+# Theme preferences
+# -------------------------
+theme_mode: # leave blank to follow system default; or "light" / "dark"
+
+avatar: "/assets/images/middleware-icon.jpg"
+
+toc: true
+
+# -------------------------
+# Analytics (optional)
+# -------------------------
+google_analytics:
+ id: ""
+
+# -------------------------
+# SEO
+# -------------------------
+google_site_verification: ""
+
+# -------------------------
+# Jekyll build settings
+# -------------------------
+permalink: pretty
+
+exclude:
+ - "*.gem"
+ - "*.gemspec"
+ - Gemfile
+ - Gemfile.lock
+ - README.md
+ - node_modules
+ - vendor
+ - .bundle
+ - .sass-cache
+ - .jekyll-cache
+ - MappingRules.md
+ - MappingRequest.md
+ - PreviewRoute.md
+
+markdown: kramdown
+highlighter: rouge
+
+kramdown:
+ syntax_highlighter: rouge
+ syntax_highlighter_opts:
+ css_class: highlight
+ span:
+ line_numbers: false
+ block:
+ line_numbers: true
+ start_line: 1
+
+# -------------------------
+# Plugins
+# -------------------------
+plugins:
+ - jekyll-sitemap
+ - jekyll-seo-tag
diff --git a/documentation/_data/navigation.yml b/documentation/_data/navigation.yml
new file mode 100644
index 0000000..da4bcf6
--- /dev/null
+++ b/documentation/_data/navigation.yml
@@ -0,0 +1,49 @@
+- title: "Overview"
+ url_prefix: "overview"
+ url: "/overview/"
+ chapters:
+ - title: "How It Works"
+ url: "/overview/"
+ - title: "Key Features"
+ url: "/overview/key-features/"
+
+- title: "Getting Started"
+ url_prefix: "getting-started"
+ url: "/getting-started/mapping-rules/"
+ chapters:
+ - title: "Mapping Rules"
+ url: "/getting-started/mapping-rules/"
+ - title: "Mapping Request"
+ url: "/getting-started/mapping-request/"
+ - title: "Preview Route"
+ url: "/getting-started/preview-route/"
+
+- title: "Endpoints"
+ url_prefix: "endpoints"
+ url: "/endpoints/"
+ chapters:
+ - title: "Endpoints Overview"
+ url: "/endpoints/"
+ - title: "Postman Collection"
+ url: "/endpoints/postman/"
+
+- title: "API Reference"
+ url_prefix: "api-reference"
+ url: "/api-reference/"
+ chapters:
+ - title: "KDoc Reference"
+ url: "/api-reference/"
+
+- title: "Ecosystem"
+ url_prefix: "ecosystem"
+ url: "/ecosystem/"
+ chapters:
+ - title: "Related Projects"
+ url: "/ecosystem/"
+
+- title: "About"
+ url_prefix: "about"
+ url: "/about/"
+ chapters:
+ - title: "About the Creator"
+ url: "/about/"
diff --git a/documentation/_includes/metadata-hook.html b/documentation/_includes/metadata-hook.html
new file mode 100644
index 0000000..34359db
--- /dev/null
+++ b/documentation/_includes/metadata-hook.html
@@ -0,0 +1 @@
+
diff --git a/documentation/_includes/sidebar.html b/documentation/_includes/sidebar.html
new file mode 100644
index 0000000..8fe0a45
--- /dev/null
+++ b/documentation/_includes/sidebar.html
@@ -0,0 +1,167 @@
+
+
+
+
diff --git a/documentation/about/index.md b/documentation/about/index.md
new file mode 100644
index 0000000..cc8f66a
--- /dev/null
+++ b/documentation/about/index.md
@@ -0,0 +1,49 @@
+---
+layout: page
+title: About the Creator
+---
+
+
+

+
+
Leonardo Bai
+
Android Engineer · Kotlin · Ktor · KMP
+
Brazil
+
+
+
+Hi, I'm Leonardo — an Android engineer with 5+ years of experience designing and shipping production mobile applications. I specialize in Kotlin, Jetpack Compose, Clean Architecture, and MVVM, with a focus on testability and code quality.
+
+ProjectMiddleware started as a personal backend experiment to solve a real frustration: external APIs rarely return exactly what you need. Instead of writing bespoke transformation code in every app, I built a middleware layer that lets you define the transformation once and reuse it forever.
+
+---
+
+## Portfolio
+
+A personal portfolio site summarising experience, skills, and notable projects.
+
+[**leonardobai12.github.io/LeonardoBai-Portfolio**](https://leonardobai12.github.io/LeonardoBai-Portfolio/){:target="_blank"}
+
+---
+
+### Advanced Kotlin & Android Engineering
+
+A deep-dive reference guide for senior-level Kotlin and Android development — covering language fundamentals, data structures, concurrency, the Android application layer, architecture, and testing. Built as a personal **Knowledge Portfolio** (inspired by *The Pragmatic Programmer*), treating knowledge as an investment that must be accumulated, diversified, and shared.
+
+> "Technologies change, frameworks come and go, and expertise naturally depreciates over time. The ability to learn, adapt, and continuously invest in your own growth is one of the most valuable skills an engineer can develop."
+
+[**leonardobai12.github.io/AdvancedKotlinAndroidEngineering**](https://leonardobai12.github.io/AdvancedKotlinAndroidEngineering/){:target="_blank"}
+
+---
+
+## Get in touch
+
+| | |
+|-|-|
+| **GitHub** | [github.com/LeonardoBai12](https://github.com/LeonardoBai12) |
+| **LinkedIn** | [linkedin.com/in/leonardo-bai-a4822917b](https://www.linkedin.com/in/leonardo-bai-a4822917b/) |
+| **Medium** | [leonardobbai.medium.com](https://leonardobbai.medium.com) |
+| **Email** | lb.io.dev@gmail.com |
diff --git a/documentation/api-reference/index.md b/documentation/api-reference/index.md
new file mode 100644
index 0000000..fcd1164
--- /dev/null
+++ b/documentation/api-reference/index.md
@@ -0,0 +1,33 @@
+---
+layout: page
+title: KDoc Reference
+---
+
+The KDoc reference is auto-generated from the source code on every push to `main` using [Dokka](https://github.com/Kotlin/dokka). It documents all public and internal declarations across every module.
+
+---
+
+## View the KDoc
+
+[**Open KDoc Reference →**](../kdoc/index.html){:target="_blank"}
+
+---
+
+## Modules covered
+
+| Module | Description |
+|--------|-------------|
+| `common:shared` | `MiddlewareException`, `Resource` |
+| `common:data` | Core models (`MappedRoute`, `OriginalRoute`, `MappedResponse`) and service interfaces |
+| `middleware:domain` | Use cases, state machine, repository interface |
+| `middleware:data` | `MiddlewareRepositoryImpl`, `MiddlewareDataSource` |
+| `middleware:mapper` | JSON transformation engine |
+| `impl:ktor-server` | HTTP server routing and `ServerServiceImpl` |
+| `impl:ktor-client` | HTTP client, JWT token generation, `ClientServiceImpl` |
+| `impl:mongo-database` | MongoDB persistence, `DatabaseServiceImpl` |
+
+---
+
+## How it stays up to date
+
+The GitHub Actions workflow regenerates KDoc from the source on every push to `main`, then publishes the updated HTML alongside this site. No manual steps required.
diff --git a/documentation/assets/css/custom.css b/documentation/assets/css/custom.css
new file mode 100644
index 0000000..46c565b
--- /dev/null
+++ b/documentation/assets/css/custom.css
@@ -0,0 +1,208 @@
+/* Avatar: centered, image zoomed 3x inside the circle */
+#avatar {
+ margin-left: auto !important;
+ margin-right: auto !important;
+}
+
+#avatar img {
+ object-fit: cover;
+ object-position: center;
+ transform: scale(3);
+}
+
+#avatar img:hover {
+ transform: scale(3);
+}
+
+
+/* ── Documentation navigation tree ── */
+
+.docs-divider {
+ margin: 0.5rem 1rem;
+ opacity: 0.2;
+}
+
+.docs-nav {
+ list-style: none;
+ margin: 0;
+ padding: 0 0 0.5rem 0;
+ overflow-y: auto;
+ overflow-x: hidden;
+ max-height: calc(100vh - 300px);
+ scrollbar-width: thin;
+ scrollbar-color: rgba(128,128,128,0.25) transparent;
+}
+
+/* #sidebar specificity (1,0,1) beats Chirpy's #sidebar ul { margin-bottom: 2rem } */
+#sidebar .docs-nav ul {
+ margin: 0;
+}
+
+/* ── Part headers ── */
+.docs-part-item {
+ margin-bottom: 0.25rem;
+}
+
+.docs-part-summary {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+ width: 100%;
+ padding: 0.6rem 1rem;
+ cursor: pointer;
+ border: none;
+ background: none;
+ border-radius: 0.75rem;
+ color: var(--sidebar-btn-color, inherit);
+ font-size: 0.88rem;
+ font-weight: 600;
+ user-select: none;
+ transition: background-color 0.3s ease-in-out;
+ text-align: left;
+}
+
+.docs-part-summary:hover {
+ background: var(--sidebar-hover-bg, rgba(128,128,128,0.12));
+}
+
+.docs-part-summary i {
+ margin-right: 0.5rem;
+ font-size: 0.85rem;
+ opacity: 0.75;
+ flex-shrink: 0;
+}
+
+.docs-part-summary span {
+ flex: 1;
+ min-width: 0;
+}
+
+/* Part collapse arrow — rotates when open, via Bootstrap .collapsed class */
+.docs-part-summary::after {
+ content: "\f105";
+ font-family: "Font Awesome 5 Free";
+ font-weight: 900;
+ flex-shrink: 0;
+ margin-left: 0.4rem;
+ font-size: 0.75rem;
+ opacity: 0.5;
+ transition: transform 0.2s;
+ transform: rotate(90deg);
+}
+
+.docs-part-summary.collapsed::after {
+ transform: rotate(0deg);
+}
+
+/* ── Chapter links ── */
+.docs-chapters-list {
+ list-style: none;
+ padding: 0.1rem 0 0.4rem 1rem;
+ margin: 0;
+}
+
+.docs-chapter-item {
+ margin-bottom: 0.1rem;
+}
+
+/* Shared base for both and