Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ MATRIX_OIDC_CLIENT_ID=replace-with-your-registered-client-id
# MATRIX_OIDC_CLIENT_SECRET=only-if-the-registered-client-has-one
MATRIX_OIDC_REDIRECT_URI=https://your-public-host/api/auth/matrix/callback
SESSION_COOKIE_SECURE=false # set to true when using HTTPS in production
VITE_DOCS_URL=docs_url
VITE_DOCS_URL=http://127.0.0.1:8001
7 changes: 5 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
python -m pip install -e ".[dev,docs]"

- name: Check formatting
run: black --check app db tests
Expand All @@ -48,6 +48,9 @@ jobs:
- name: Run tests
run: pytest -q

- name: Build documentation
run: cd ../docs && mkdocs build --strict

frontend:
name: Frontend quality
runs-on: ubuntu-latest
Expand All @@ -74,4 +77,4 @@ jobs:
run: npm run lint

- name: Type-check and build
run: npm run build
run: npm run build
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ __pycache__/
coverage.xml
htmlcov/
backend/matrix_directory.db
backend/static/
frontend/node_modules/
frontend/dist/
frontend/.env.*
Expand All @@ -18,4 +19,4 @@ frontend/vite.config.d.ts
.vscode
matrix_directory_backend.egg-info
.mypy_cache
docs/site
docs/site/
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Docker with Docker Compose is required.
The services will be available at:

- Frontend: http://localhost:5173
- API documentation: http://localhost:8000/docs
- Swagger UI: http://localhost:8000/api/docs

To stop the environment:

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ docker compose up --build
The services will be available at:

- [Frontend](http://localhost:5173)
- [API documentation](http://localhost:8000/docs)
- [Swagger UI](http://localhost:8000/api/docs)

The backend automatically applies database migrations on startup.

Expand Down Expand Up @@ -89,7 +89,7 @@ See [Authentication Architecture](docs/docs/architecture/authentication.md) for

Interactive API documentation is available at:

- [Swagger UI](http://localhost:8000/docs)
- [Swagger UI](http://localhost:8000/api/docs)

Core endpoints include:

Expand Down
44 changes: 31 additions & 13 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
from app.config import get_settings

settings = get_settings()
app = FastAPI(title=settings.app_name, version="0.1.0")
app = FastAPI(
title=settings.app_name,
version="0.1.0",
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json",
)

app.add_middleware(
SessionMiddleware,
Expand Down Expand Up @@ -44,28 +50,40 @@ def health() -> dict[str, str]:
return {"status": "ok"}


frontend_directory = Path(__file__).resolve().parent.parent / "static"
frontend_index = frontend_directory / "index.html"
frontend_assets = frontend_directory / "assets"
def mount_static_sites(application: FastAPI, static_directory: Path) -> None:
"""Mount the built documentation and frontend when their files exist."""
documentation_directory = static_directory / "docs"
frontend_index = static_directory / "index.html"
frontend_assets = static_directory / "assets"

if documentation_directory.is_dir():
application.mount(
"/docs",
StaticFiles(directory=documentation_directory, html=True),
name="documentation",
)

if not frontend_index.is_file():
return

if frontend_index.is_file():
if frontend_assets.is_dir():
app.mount(
application.mount(
"/assets",
StaticFiles(directory=frontend_assets),
name="frontend-assets",
)

@app.get("/{path:path}", include_in_schema=False)
@application.get("/{path:path}", include_in_schema=False)
def frontend(path: str) -> FileResponse:
if path == "api" or path.startswith("api/"):
reserved_path = path in {"api", "docs"} or path.startswith(("api/", "docs/"))
if reserved_path:
raise HTTPException(status_code=404, detail="Not Found")

requested_file = (frontend_directory / path).resolve()
if (
requested_file.is_relative_to(frontend_directory)
and requested_file.is_file()
):
requested_file = (static_directory / path).resolve()
if requested_file.is_relative_to(static_directory) and requested_file.is_file():
return FileResponse(requested_file)

return FileResponse(frontend_index)


mount_static_sites(app, Path(__file__).resolve().parent.parent / "static")
73 changes: 73 additions & 0 deletions backend/tests/test_static_sites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from pathlib import Path

from fastapi import FastAPI
from fastapi.testclient import TestClient

from app.main import app, mount_static_sites


def test_api_documentation_routes__expect_swagger_and_schema() -> None:
client = TestClient(app)

swagger_response = client.get("/api/docs")
schema_response = client.get("/api/openapi.json")

assert swagger_response.status_code == 200
assert "swagger-ui" in swagger_response.text
assert schema_response.status_code == 200
assert schema_response.json()["info"]["title"] == "Matrix Directory API"


def test_documentation_static_files__expect_served_under_docs(
tmp_path: Path,
) -> None:
static_directory = tmp_path / "static"
documentation_directory = static_directory / "docs"
guide_directory = documentation_directory / "guide"
guide_directory.mkdir(parents=True)
(documentation_directory / "index.html").write_text(
"<h1>Documentation home</h1>",
encoding="utf-8",
)
(guide_directory / "index.html").write_text(
"<h1>Documentation guide</h1>",
encoding="utf-8",
)

application = FastAPI()
mount_static_sites(application, static_directory)
client = TestClient(application)

home_response = client.get("/docs/")
guide_response = client.get("/docs/guide/")

assert home_response.status_code == 200
assert "Documentation home" in home_response.text
assert guide_response.status_code == 200
assert "Documentation guide" in guide_response.text


def test_reserved_routes__expect_not_fall_through_to_frontend(
tmp_path: Path,
) -> None:
static_directory = tmp_path / "static"
static_directory.mkdir()
(static_directory / "index.html").write_text(
"<h1>Frontend application</h1>",
encoding="utf-8",
)

application = FastAPI()
mount_static_sites(application, static_directory)
client = TestClient(application)

api_response = client.get("/api/not-a-real-route")
docs_response = client.get("/docs/not-a-real-page")
frontend_response = client.get("/bots/example")

assert api_response.status_code == 404
assert "Frontend application" not in api_response.text
assert docs_response.status_code == 404
assert "Frontend application" not in docs_response.text
assert frontend_response.status_code == 200
assert "Frontend application" in frontend_response.text
3 changes: 0 additions & 3 deletions docs/.dockerignore

This file was deleted.

19 changes: 0 additions & 19 deletions docs/Dockerfile

This file was deleted.

2 changes: 1 addition & 1 deletion docs/docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ services are ready, open:
| Service | Address |
| --- | --- |
| Frontend | <http://localhost:5173> |
| API documentation | <http://localhost:8000/docs> |
| Swagger UI | <http://localhost:8000/api/docs> |

You now have the frontend, backend, and database running together. The frontend
reloads as its source changes; restart the API service after backend changes.
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ profiles, project ownership, and directory data.
Once the services are ready, open:

- [Matrix Directory](http://localhost:5173)
- [Interactive API documentation](http://localhost:8000/docs)
- [Interactive API documentation](http://localhost:8000/api/docs)

=== "Stop or reset"

Expand Down
4 changes: 2 additions & 2 deletions docs/docs/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
The FastAPI backend exposes JSON application endpoints under `/api`. This
page describes the stable behaviors a client needs; the running application
provides the complete generated schemas in its
[Swagger UI](https://matrix-directory.codesociety.xyz/docs).
[Swagger UI](https://matrix-directory.codesociety.xyz/api/docs).

## At a glance

Expand Down Expand Up @@ -217,4 +217,4 @@ Changing `matrix_id` clears any existing verification. Clients cannot set

- [Development guide](../development.md)
- [Authentication architecture](../architecture/authentication.md)
- [Interactive API documentation](https://matrix-directory.codesociety.xyz/docs)
- [Interactive API documentation](https://matrix-directory.codesociety.xyz/api/docs)
3 changes: 2 additions & 1 deletion docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

site_name: Matrix Directory
site_description: A community directory for projects in the Matrix ecosystem.
site_url: https://docs.matrix-directory.codesociety.xyz/
site_url: https://matrix-directory.codesociety.xyz/docs/
dev_addr: 127.0.0.1:8001
repo_url: https://github.com/Code-Society-Lab/matrix-directory
repo_name: Code-Society-Lab/matrix-directory
Expand Down Expand Up @@ -79,6 +79,7 @@ markdown_extensions:
emoji_generator: !!python/name:material.extensions.emoji.to_svg
- pymdownx.snippets:
base_path: [".."]

nav:
- Home: index.md
- Guides:
Expand Down
19 changes: 0 additions & 19 deletions docs/nginx.conf.template

This file was deleted.

2 changes: 0 additions & 2 deletions docs/requirements.txt

This file was deleted.

3 changes: 2 additions & 1 deletion frontend/src/components/SiteHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import logoUrl from '../assets/matrix-directory-mark.svg'


const docsUrl =
import.meta.env.VITE_DOCS_URL ?? 'http://127.0.0.1:8001'
import.meta.env.VITE_DOCS_URL ??
(import.meta.env.DEV ? 'http://127.0.0.1:8001' : '/docs/')

const router = useRouter()
const route = useRoute()
Expand Down
10 changes: 7 additions & 3 deletions railpack.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
{ "src": ".", "dest": "." },
"python3 -m venv .venv",
".venv/bin/pip install --upgrade pip",
".venv/bin/pip install ./backend",
".venv/bin/pip install ./backend[docs]",
"cd frontend && npm ci"
]
},
Expand All @@ -26,12 +26,16 @@
{ "step": "install" }
],
"variables": {
"VITE_API_URL": "/api"
"VITE_API_URL": "/api",
"VITE_DOCS_URL": "/docs/"
},
"commands": [
"cd frontend && npm run build",
"cd docs && ../.venv/bin/mkdocs build --strict",
"mkdir -p backend/static",
"cp -R frontend/dist/. backend/static/"
"cp -R frontend/dist/. backend/static/",
"mkdir -p backend/static/docs",
"cp -R docs/site/. backend/static/docs/"
]
}
},
Expand Down