Session 2026-05-17: GeoServer, PostGIS dédié, MapStore, ChirpStack

- GeoServer: workspace Digitribe + Data Store PostGIS dédié
- PostGIS dédié: conteneur postgis-smartcity (PostGIS 3.4)
- Couche sensors: 55 capteurs IoT importés depuis OpenRemote
- MapStore: GeoServer WMS ajouté au CORS
- ChirpStack: credentials réinitialisés (admin/admin1234)
- BunkerM: DNS corrigé (underscores → hyphens)
- Ditto: config MongoDB et auth devops
- Documentation: session_resume + TODO.md
This commit is contained in:
Eric FELIXINE
2026-05-17 19:18:24 -04:00
parent 1006df137d
commit 7477410813
11 changed files with 598 additions and 92 deletions

View File

@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""GeoJSON proxy service for OpenRemote assets map display."""
import json
import os
import urllib.request
import urllib.error
from http.server import HTTPServer, BaseHTTPRequestHandler
OR_URL = os.environ.get("OR_URL", "http://openremote_manager_1:8080")
OR_ADMIN_USER = os.environ.get("OR_ADMIN_USER", "admin")
OR_ADMIN_PASS = os.environ.get("OR_ADMIN_PASS", "")
OR_TOKEN = os.environ.get("OR_TOKEN", "")
def get_token():
"""Fetch an OpenRemote access token using admin credentials."""
if OR_TOKEN:
return OR_TOKEN
data = json.dumps({
"username": OR_ADMIN_USER,
"password": OR_ADMIN_PASS,
"grant_type": "password",
"client_id": "openremote"
}).encode()
req = urllib.request.Request(
f"{OR_URL}/auth/realms/master/protocol/openid-connect/token",
data=data,
headers={"Content-Type": "application/json"},
method="POST"
)
resp = urllib.request.urlopen(req, timeout=10)
body = json.loads(resp.read())
return body["access_token"]
def fetch_assets(token):
"""Fetch IoT sensor assets from OpenRemote."""
url = f"{OR_URL}/api/master/assets?type=IOTSensor"
req = urllib.request.Request(
url,
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/json"
},
method="GET"
)
resp = urllib.request.urlopen(req, timeout=15)
return json.loads(resp.read())
def to_geojson(assets):
"""Convert OpenRemote assets to a GeoJSON FeatureCollection."""
features = []
for asset in assets:
attrs = asset.get("attributes", {})
location = attrs.get("location", {})
if not location:
continue
value = location.get("value")
if not value:
continue
coords = value.get("coordinates")
if not coords or len(coords) < 2:
continue
# Build properties from asset attributes
properties = {}
for attr_name, attr_val in attrs.items():
v = attr_val.get("value")
if v is not None:
properties[attr_name] = v
# Ensure key fields are at top level for Mapbox filters
properties.setdefault("id", asset.get("id"))
properties.setdefault("name", asset.get("name", ""))
properties.setdefault("type", asset.get("type", ""))
properties.setdefault("sensorType", attrs.get("sensorType", {}).get("value", ""))
features.append({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [coords[0], coords[1]]
},
"properties": properties
})
return {
"type": "FeatureCollection",
"features": features
}
class GeoJSONHandler(BaseHTTPRequestHandler):
def do_GET(self):
path = self.path.split("?")[0]
if path == "/geojson":
try:
token = get_token()
assets = fetch_assets(token)
geojson = to_geojson(assets)
body = json.dumps(geojson).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
except Exception as e:
error_body = json.dumps({"error": str(e)}).encode()
self.send_response(500)
self.send_header("Content-Type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(error_body)))
self.end_headers()
self.wfile.write(error_body)
elif path == "/health":
body = json.dumps({"status": "ok"}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
else:
self.send_response(404)
self.end_headers()
def log_message(self, format, *args):
print(f"[geojson-proxy] {args[0]}")
if __name__ == "__main__":
server = HTTPServer(("0.0.0.0", 8080), GeoJSONHandler)
print("[geojson-proxy] Listening on 0.0.0.0:8080")
server.serve_forever()