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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Local dev — database for running osmsg CLI or the API standalone
DATABASE_URL=postgresql://osmsg:osmsg@localhost:5432/osmsg

# Comma-separated browser origins allowed to call the API.
OSMSG_CORS_ORIGINS=http://localhost:5500,http://127.0.0.1:5500,https://osgeonepal.github.io,https://osmsg.osgeonepal.org

# For self-hosting with docker compose, copy infra/.env.example instead
26 changes: 24 additions & 2 deletions api/app.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import os
from contextlib import asynccontextmanager
from pathlib import Path

Expand All @@ -15,12 +16,29 @@
from .schemas import HealthResponse

TEMPLATES = Path(__file__).parent / "templates"
DEFAULT_CORS_ORIGINS = (
"http://localhost:5173",
"http://127.0.0.1:5173",
"http://localhost:5500",
"http://127.0.0.1:5500",
"http://localhost:5520",
"http://127.0.0.1:5520",
"https://osgeonepal.github.io",
"https://osmsg.osgeonepal.org",
)


def get_cors_origins() -> list[str]:
raw_origins = os.getenv("OSMSG_CORS_ORIGINS", "")
origins = [origin.strip() for origin in raw_origins.split(",") if origin.strip()]
return origins or list(DEFAULT_CORS_ORIGINS)


@asynccontextmanager
async def lifespan(app: Litestar):
await open_pool()
await ensure_schema()
if os.getenv("OSMSG_SKIP_SCHEMA_ENSURE", "").lower() not in {"1", "true", "yes"}:
await ensure_schema()
try:
yield
finally:
Expand Down Expand Up @@ -49,7 +67,11 @@ async def health() -> HealthResponse:
app = Litestar(
route_handlers=[home, health, v1_router],
lifespan=[lifespan],
cors_config=CORSConfig(allow_origins=["*"]),
cors_config=CORSConfig(
allow_origins=get_cors_origins(),
allow_methods=["GET", "OPTIONS"],
allow_headers=["*"],
),
openapi_config=OpenAPIConfig(
title="OSMSG API",
version="1.0.0",
Expand Down
22 changes: 22 additions & 0 deletions api/pagination.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import TypeVar

from litestar.pagination import OffsetPagination
from pydantic import BaseModel, Field

RowT = TypeVar("RowT")


class PaginationParams(BaseModel):
limit: int = Field(default=100, ge=1, le=1000)
offset: int = Field(default=0, ge=0)

@property
def query_limit(self) -> int:
return self.limit + 1


def paginate_items(rows: list[RowT], params: PaginationParams) -> OffsetPagination[RowT]:
has_next = len(rows) > params.limit
items = rows[: params.limit]
total = params.offset + len(items) + int(has_next)
return OffsetPagination(items=items, limit=params.limit, offset=params.offset, total=total)
18 changes: 12 additions & 6 deletions api/pg_schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
PG_SCHEMA = """
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE TABLE IF NOT EXISTS users (
uid BIGINT PRIMARY KEY,
username TEXT NOT NULL
Expand All @@ -10,10 +9,17 @@
created_at TIMESTAMPTZ,
hashtags TEXT[],
editor TEXT,
geom GEOMETRY(POLYGON)
min_lon DOUBLE PRECISION,
min_lat DOUBLE PRECISION,
max_lon DOUBLE PRECISION,
max_lat DOUBLE PRECISION
);
CREATE INDEX IF NOT EXISTS idx_changesets_created_at ON changesets USING BTREE (created_at);
CREATE INDEX IF NOT EXISTS idx_changesets_hashtags ON changesets USING GIN (hashtags);
CREATE INDEX IF NOT EXISTS idx_changesets_editor ON changesets USING BTREE (editor);
CREATE INDEX IF NOT EXISTS idx_changesets_bbox ON changesets USING GIST (
box(point(min_lon, min_lat), point(max_lon, max_lat))
);
CREATE INDEX IF NOT EXISTS idx_changesets_created_at ON changesets(created_at);
CREATE INDEX IF NOT EXISTS idx_changesets_geom ON changesets USING GIST (geom);
CREATE TABLE IF NOT EXISTS changeset_stats (
changeset_id BIGINT NOT NULL REFERENCES changesets(changeset_id),
seq_id BIGINT NOT NULL,
Expand All @@ -32,8 +38,8 @@
tag_stats JSONB,
PRIMARY KEY (seq_id, changeset_id)
);
CREATE INDEX IF NOT EXISTS idx_changeset_stats_uid ON changeset_stats(uid);
CREATE INDEX IF NOT EXISTS idx_changeset_stats_changeset_id ON changeset_stats(changeset_id);
CREATE INDEX IF NOT EXISTS idx_changeset_stats_uid ON changeset_stats USING BTREE (uid);
CREATE INDEX IF NOT EXISTS idx_changeset_stats_changeset_id ON changeset_stats USING BTREE (changeset_id);
CREATE TABLE IF NOT EXISTS state (
source_url TEXT PRIMARY KEY,
last_seq BIGINT NOT NULL,
Expand Down
Loading
Loading