Merge branch 'docker'

This commit is contained in:
Yohan Boniface 2023-06-05 17:00:46 +02:00
commit 4b502e9857
13 changed files with 306 additions and 67 deletions

8
.dockerignore Normal file
View file

@ -0,0 +1,8 @@
tmp/
build/
dist/
*.egg-info/
node_modules/
umap/settings/local.py
Dockerfile
.git/

75
Dockerfile Normal file
View file

@ -0,0 +1,75 @@
FROM node:alpine AS vendors
RUN apk add git
WORKDIR /srv/umap
COPY package.json .
RUN npm install
COPY . .
RUN npm run vendors
# This part installs deps needed at runtime.
FROM python:3.11-slim as common
RUN apt-get update && \
apt-get install -y --no-install-recommends \
tini \
uwsgi \
sqlite3 \
libpq-dev \
gdal-bin \
&& \
apt-get autoremove -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
# This part adds deps needed only at buildtime.
FROM common as build
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
binutils \
libproj-dev \
curl \
git \
gettext \
python3-venv \
libffi-dev \
libtiff5-dev \
libjpeg62-turbo-dev \
zlib1g-dev \
libfreetype6-dev \
liblcms2-dev \
libwebp-dev
RUN python -m venv /venv
WORKDIR /srv/umap
COPY . /srv/umap
RUN /venv/bin/pip install .[docker]
FROM common
COPY --from=build /srv/umap/docker/ /srv/umap/docker/
COPY --from=build /venv/ /venv/
COPY --from=vendors /srv/umap/umap/static/umap/vendors /srv/umap/umap/static/umap/vendors
WORKDIR /srv/umap
RUN mkdir -p /srv/umap/uploads
ENV PYTHONUNBUFFERED=1 \
PORT=8000
EXPOSE 8000
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["/srv/umap/docker/entrypoint.sh"]

View file

@ -19,30 +19,7 @@ messages:
cd umap && umap makemessages -l en cd umap && umap makemessages -l en
node node_modules/leaflet-i18n/bin/i18n.js --dir_path=umap/static/umap/js/ --dir_path=umap/static/umap/vendors/measurable/ --locale_dir_path=umap/static/umap/locale/ --locale_codes=en --mode=json --clean --default_values node node_modules/leaflet-i18n/bin/i18n.js --dir_path=umap/static/umap/js/ --dir_path=umap/static/umap/vendors/measurable/ --locale_dir_path=umap/static/umap/locale/ --locale_codes=en --mode=json --clean --default_values
vendors: vendors:
mkdir -p umap/static/umap/vendors/leaflet/ && cp -r node_modules/leaflet/dist/** umap/static/umap/vendors/leaflet/ npm run vendors
mkdir -p umap/static/umap/vendors/editable/ && cp -r node_modules/leaflet-editable/src/*.js umap/static/umap/vendors/editable/
mkdir -p umap/static/umap/vendors/editable/ && cp -r node_modules/leaflet.path.drag/src/*.js umap/static/umap/vendors/editable/
mkdir -p umap/static/umap/vendors/hash/ && cp -r node_modules/leaflet-hash/*.js umap/static/umap/vendors/hash/
mkdir -p umap/static/umap/vendors/i18n/ && cp -r node_modules/leaflet-i18n/*.js umap/static/umap/vendors/i18n/
mkdir -p umap/static/umap/vendors/editinosm/ && cp -r node_modules/leaflet-editinosm/{Leaflet.EditInOSM.*,edit-in-osm.png} umap/static/umap/vendors/editinosm/
mkdir -p umap/static/umap/vendors/minimap/ && cp -r node_modules/leaflet-minimap/src/** umap/static/umap/vendors/minimap/
mkdir -p umap/static/umap/vendors/loading/ && cp -r node_modules/leaflet-loading/src/** umap/static/umap/vendors/loading/
mkdir -p umap/static/umap/vendors/markercluster/ && cp -r node_modules/leaflet.markercluster/dist/** umap/static/umap/vendors/markercluster/
mkdir -p umap/static/umap/vendors/contextmenu/ && cp -r node_modules/leaflet-contextmenu/dist/** umap/static/umap/vendors/contextmenu/
mkdir -p umap/static/umap/vendors/heat/ && cp -r node_modules/leaflet.heat/dist/** umap/static/umap/vendors/heat/
mkdir -p umap/static/umap/vendors/fullscreen/ && cp -r node_modules/leaflet-fullscreen/dist/** umap/static/umap/vendors/fullscreen/
mkdir -p umap/static/umap/vendors/toolbar/ && cp -r node_modules/leaflet-toolbar/dist/** umap/static/umap/vendors/toolbar/
mkdir -p umap/static/umap/vendors/formbuilder/ && cp -r node_modules/leaflet-formbuilder/*.js umap/static/umap/vendors/formbuilder/
mkdir -p umap/static/umap/vendors/measurable/ && cp -r node_modules/leaflet-measurable/Leaflet.Measurable.* umap/static/umap/vendors/measurable/
mkdir -p umap/static/umap/vendors/photon/ && cp -r node_modules/leaflet.photon/*.js umap/static/umap/vendors/photon/
mkdir -p umap/static/umap/vendors/csv2geojson/ && cp -r node_modules/csv2geojson/*.js umap/static/umap/vendors/csv2geojson/
mkdir -p umap/static/umap/vendors/togeojson/ && cp -r node_modules/togeojson/*.js umap/static/umap/vendors/togeojson/
mkdir -p umap/static/umap/vendors/osmtogeojson/ && cp -r node_modules/osmtogeojson/osmtogeojson.js umap/static/umap/vendors/osmtogeojson/
mkdir -p umap/static/umap/vendors/georsstogeojson/ && cp -r node_modules/georsstogeojson/GeoRSSToGeoJSON.js umap/static/umap/vendors/georsstogeojson/
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/{dist/L.Control.Locate.css,src/L.Control.Locate.js} umap/static/umap/vendors/locatecontrol/
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.js umap/static/umap/vendors/dompurify/
installjs: installjs:
npm install npm install
testjsfx: testjsfx:

25
docker-compose.yml Normal file
View file

@ -0,0 +1,25 @@
version: '3'
services:
db:
image: postgis/postgis:14-3.3-alpine
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
- db:/var/lib/postgresql/data
app:
image: umap:1.3.0
ports:
- "8001:8000"
environment:
- DATABASE_URL=postgis://postgres@db/postgres
- SECRET_KEY=some-long-and-weirdly-unrandom-secret-key
- SITE_URL=https://umap.local/
- UMAP_ALLOW_ANONYMOUS=True
- DEBUG=1
volumes:
- data:/srv/umap/uploads
volumes:
data:
db:

31
docker/entrypoint.sh Executable file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -eo pipefail
source /venv/bin/activate
# default variables
: "${SLEEP:=1}"
: "${TRIES:=60}"
function wait_for_database {(
echo "Waiting for database to respond..."
tries=0
while true; do
[[ $tries -lt $TRIES ]] || return
(echo "from django.db import connection; connection.connect()" | umap shell) >/dev/null
[[ $? -eq 0 ]] && return
sleep $SLEEP
tries=$((tries + 1))
done
)}
# first wait for the database
wait_for_database
# then migrate the database
umap migrate
# then collect static files
umap collectstatic --noinput
# compress static files
umap compress
# run uWSGI
exec uwsgi --ini docker/uwsgi.ini

11
docker/uwsgi.ini Normal file
View file

@ -0,0 +1,11 @@
[uwsgi]
http = :$(PORT)
home = /venv
module = umap.wsgi:application
master = True
vacuum = True
max-requests = 5000
processes = 4
enable-threads = true
static-map = /static=/srv/umap/static
static-map = /uploads=/srv/umap/uploads

33
docs/docker.md Normal file
View file

@ -0,0 +1,33 @@
# Docker
There is now an official [uMap](https://hub.docker.com/r/umap/umap) image.
To run it with docker compose, use a `docker-compose.yml` like this:
```yaml
version: '3'
services:
db:
image: postgis/postgis:14-3.3-alpine
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
- db:/var/lib/postgresql/data
app:
image: umap/umap:x.x.x
ports:
- "8001:8000"
environment:
- DATABASE_URL=postgis://postgres@db/postgres
- SECRET_KEY=some-long-and-weirdly-unrandom-secret-key
- SITE_URL=https://umap.local/
- UMAP_ALLOW_ANONYMOUS=True
- DEBUG=1
volumes:
- data:/srv/umap/uploads
volumes:
data:
db:
```

View file

@ -7,5 +7,6 @@ nav:
- how-tos: - how-tos:
- Ubuntu from scratch: ubuntu.md - Ubuntu from scratch: ubuntu.md
- Customize your uMap style: custom.md - Customize your uMap style: custom.md
- Install with Docker: docker.md
- Changelog: changelog.md - Changelog: changelog.md
theme: readthedocs theme: readthedocs

View file

@ -10,16 +10,14 @@
"happen": "~0.1.3", "happen": "~0.1.3",
"lebab": "^3.2.1", "lebab": "^3.2.1",
"mocha": "^2.3.3", "mocha": "^2.3.3",
"mocha-phantomjs": "^4.0.1",
"optimist": "~0.4.0", "optimist": "~0.4.0",
"phantomjs": "^1.9.18",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"sinon": "^15.1.0", "sinon": "^15.1.0",
"uglify-js": "~3.17.4" "uglify-js": "~3.17.4"
}, },
"scripts": { "scripts": {
"test": "firefox test/index.html", "test": "firefox test/index.html",
"build": "grunt" "vendors": "scripts/vendorsjs.sh"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

30
scripts/vendorsjs.sh Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env sh
mkdir -p umap/static/umap/vendors/leaflet/ && cp -r node_modules/leaflet/dist/** umap/static/umap/vendors/leaflet/
mkdir -p umap/static/umap/vendors/editable/ && cp -r node_modules/leaflet-editable/src/*.js umap/static/umap/vendors/editable/
mkdir -p umap/static/umap/vendors/editable/ && cp -r node_modules/leaflet.path.drag/src/*.js umap/static/umap/vendors/editable/
mkdir -p umap/static/umap/vendors/hash/ && cp -r node_modules/leaflet-hash/*.js umap/static/umap/vendors/hash/
mkdir -p umap/static/umap/vendors/i18n/ && cp -r node_modules/leaflet-i18n/*.js umap/static/umap/vendors/i18n/
mkdir -p umap/static/umap/vendors/editinosm/ && cp -r node_modules/leaflet-editinosm/Leaflet.EditInOSM.* umap/static/umap/vendors/editinosm/
mkdir -p umap/static/umap/vendors/editinosm/ && cp -r node_modules/leaflet-editinosm/edit-in-osm.png umap/static/umap/vendors/editinosm/
mkdir -p umap/static/umap/vendors/minimap/ && cp -r node_modules/leaflet-minimap/src/** umap/static/umap/vendors/minimap/
mkdir -p umap/static/umap/vendors/loading/ && cp -r node_modules/leaflet-loading/src/** umap/static/umap/vendors/loading/
mkdir -p umap/static/umap/vendors/markercluster/ && cp -r node_modules/leaflet.markercluster/dist/** umap/static/umap/vendors/markercluster/
mkdir -p umap/static/umap/vendors/contextmenu/ && cp -r node_modules/leaflet-contextmenu/dist/** umap/static/umap/vendors/contextmenu/
mkdir -p umap/static/umap/vendors/heat/ && cp -r node_modules/leaflet.heat/dist/** umap/static/umap/vendors/heat/
mkdir -p umap/static/umap/vendors/fullscreen/ && cp -r node_modules/leaflet-fullscreen/dist/** umap/static/umap/vendors/fullscreen/
mkdir -p umap/static/umap/vendors/toolbar/ && cp -r node_modules/leaflet-toolbar/dist/** umap/static/umap/vendors/toolbar/
mkdir -p umap/static/umap/vendors/formbuilder/ && cp -r node_modules/leaflet-formbuilder/*.js umap/static/umap/vendors/formbuilder/
mkdir -p umap/static/umap/vendors/measurable/ && cp -r node_modules/leaflet-measurable/Leaflet.Measurable.* umap/static/umap/vendors/measurable/
mkdir -p umap/static/umap/vendors/photon/ && cp -r node_modules/leaflet.photon/*.js umap/static/umap/vendors/photon/
mkdir -p umap/static/umap/vendors/csv2geojson/ && cp -r node_modules/csv2geojson/*.js umap/static/umap/vendors/csv2geojson/
mkdir -p umap/static/umap/vendors/togeojson/ && cp -r node_modules/togeojson/*.js umap/static/umap/vendors/togeojson/
mkdir -p umap/static/umap/vendors/osmtogeojson/ && cp -r node_modules/osmtogeojson/osmtogeojson.js umap/static/umap/vendors/osmtogeojson/
mkdir -p umap/static/umap/vendors/georsstogeojson/ && cp -r node_modules/georsstogeojson/GeoRSSToGeoJSON.js umap/static/umap/vendors/georsstogeojson/
mkdir -p umap/static/umap/vendors/togpx/ && cp -r node_modules/togpx/togpx.js umap/static/umap/vendors/togpx/
mkdir -p umap/static/umap/vendors/tokml && cp -r node_modules/tokml/tokml.js umap/static/umap/vendors/tokml
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css umap/static/umap/vendors/locatecontrol/
mkdir -p umap/static/umap/vendors/locatecontrol/ && cp -r node_modules/leaflet.locatecontrol/src/L.Control.Locate.js umap/static/umap/vendors/locatecontrol/
mkdir -p umap/static/umap/vendors/dompurify/ && cp -r node_modules/dompurify/dist/purify.js umap/static/umap/vendors/dompurify/
echo 'Done!'

View file

@ -29,6 +29,7 @@ install_requires =
Django>=4.1 Django>=4.1
django-agnocomplete==2.2.0 django-agnocomplete==2.2.0
django-compressor==4.3.1 django-compressor==4.3.1
django-environ==0.10.0
Pillow==9.5.0 Pillow==9.5.0
psycopg2==2.9.6 psycopg2==2.9.6
requests==2.30.0 requests==2.30.0
@ -43,7 +44,8 @@ test =
factory-boy==3.2.1 factory-boy==3.2.1
pytest==6.2.5 pytest==6.2.5
pytest-django==4.5.2 pytest-django==4.5.2
docker =
uwsgi==2.0.21
[options.entry_points] [options.entry_points]
console_scripts = console_scripts =

View file

@ -17,31 +17,30 @@ if not path:
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), path = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'local.py') 'local.py')
if not os.path.exists(path): if not os.path.exists(path):
msg = ('You must configure UMAP_SETTINGS or define ' print(colorize('No valid UMAP_SETTINGS found', fg='yellow'))
'/etc/umap/umap.conf') path = None
print(colorize(msg, fg='red'))
sys.exit(1)
d = types.ModuleType('config') if path:
d.__file__ = path d = types.ModuleType('config')
try: d.__file__ = path
with open(path) as config_file: try:
exec(compile(config_file.read(), path, 'exec'), d.__dict__) with open(path) as config_file:
except IOError as e: exec(compile(config_file.read(), path, 'exec'), d.__dict__)
msg = 'Unable to import {} from UMAP_SETTINGS'.format(path) except IOError as e:
print(colorize(msg, fg='red')) msg = 'Unable to import {} from UMAP_SETTINGS'.format(path)
sys.exit(e) print(colorize(msg, fg='red'))
else: sys.exit(e)
print('Loaded local config from', path) else:
for key in dir(d): print('Loaded local config from', path)
if key.isupper(): for key in dir(d):
value = getattr(d, key) if key.isupper():
if key.startswith('LEAFLET_STORAGE'): value = getattr(d, key)
# Retrocompat pre 1.0, remove me in 1.1. if key.startswith('LEAFLET_STORAGE'):
globals()['UMAP' + key[15:]] = value # Retrocompat pre 1.0, remove me in 1.1.
elif key == 'UMAP_CUSTOM_TEMPLATES': globals()['UMAP' + key[15:]] = value
globals()['TEMPLATES'][0]['DIRS'].insert(0, value) elif key == 'UMAP_CUSTOM_TEMPLATES':
elif key == 'UMAP_CUSTOM_STATICS': globals()['TEMPLATES'][0]['DIRS'].insert(0, value)
globals()['STATICFILES_DIRS'].insert(0, value) elif key == 'UMAP_CUSTOM_STATICS':
else: globals()['STATICFILES_DIRS'].insert(0, value)
globals()[key] = value else:
globals()[key] = value

View file

@ -1,13 +1,24 @@
"""Base settings shared by all environments""" """Base settings shared by all environments"""
# Import global settings to make it easier to extend settings. # Import global settings to make it easier to extend settings.
from email.utils import parseaddr
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.conf.locale import LANG_INFO from django.conf.locale import LANG_INFO
import environ
env = environ.Env()
# ============================================================================= # =============================================================================
# Generic Django project settings # Generic Django project settings
# ============================================================================= # =============================================================================
DEBUG = True
INTERNAL_IPS = env.list('INTERNAL_IPS', default=['127.0.0.1'])
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
ADMINS = tuple(parseaddr(email) for email in env.list('ADMINS', default=[]))
DEBUG = env.bool('DEBUG', default=False)
SITE_ID = 1 SITE_ID = 1
# Add languages we're missing from Django # Add languages we're missing from Django
@ -92,7 +103,7 @@ LANGUAGES = (
) )
# Make this unique, and don't share it with anybody. # Make this unique, and don't share it with anybody.
SECRET_KEY = '' SECRET_KEY = env('SECRET_KEY', default=None)
INSTALLED_APPS = ( INSTALLED_APPS = (
'django.contrib.auth', 'django.contrib.auth',
@ -200,21 +211,25 @@ MIDDLEWARE = (
# Auth / security # Auth / security
# ============================================================================= # =============================================================================
ENABLE_ACCOUNT_LOGIN = False # Set to True if login into django account should be possible. Default is to
# only use OAuth flow.
ENABLE_ACCOUNT_LOGIN = env.bool("ENABLE_ACCOUNT_LOGIN", default=False)
# ============================================================================= # =============================================================================
# Miscellaneous project settings # Miscellaneous project settings
# ============================================================================= # =============================================================================
UMAP_ALLOW_ANONYMOUS = False UMAP_ALLOW_ANONYMOUS = env.bool("UMAP_ALLOW_ANONYMOUS", default=False)
UMAP_EXTRA_URLS = { UMAP_EXTRA_URLS = {
'routing': 'http://www.openstreetmap.org/directions?engine=osrm_car&route={lat},{lng}&locale={locale}#map={zoom}/{lat}/{lng}', # noqa 'routing': 'http://www.openstreetmap.org/directions?engine=osrm_car&route={lat},{lng}&locale={locale}#map={zoom}/{lat}/{lng}', # noqa
'ajax_proxy': '/ajax-proxy/?url={url}&ttl={ttl}', 'ajax_proxy': '/ajax-proxy/?url={url}&ttl={ttl}',
'search': 'https://photon.komoot.io/api/?', 'search': 'https://photon.komoot.io/api/?',
} }
UMAP_KEEP_VERSIONS = 10 UMAP_KEEP_VERSIONS = env.int('UMAP_KEEP_VERSIONS', default=10)
SITE_URL = "http://umap.org" SITE_URL = env("SITE_URL", default="http://umap.org")
SHORT_SITE_URL = env('SHORT_SITE_URL', default=None)
SITE_NAME = 'uMap' SITE_NAME = 'uMap'
UMAP_DEMO_SITE = False UMAP_DEMO_SITE = env('UMAP_DEMO_SITE', default=False)
UMAP_EXCLUDE_DEFAULT_MAPS = False UMAP_EXCLUDE_DEFAULT_MAPS = False
UMAP_MAPS_PER_PAGE = 5 UMAP_MAPS_PER_PAGE = 5
UMAP_MAPS_PER_PAGE_OWNER = 10 UMAP_MAPS_PER_PAGE_OWNER = 10
@ -222,15 +237,18 @@ UMAP_SEARCH_CONFIGURATION = "simple"
UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa
USER_MAPS_URL = 'user_maps' USER_MAPS_URL = 'user_maps'
DATABASES = { DATABASES = {
'default': { 'default': env.db(default='postgis://localhost:5432/umap')
'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME': 'umap',
}
} }
UMAP_READONLY = False
UMAP_READONLY = env('UMAP_READONLY', default=False)
UMAP_GZIP = True UMAP_GZIP = True
LOCALE_PATHS = [os.path.join(PROJECT_DIR, 'locale')] LOCALE_PATHS = [os.path.join(PROJECT_DIR, 'locale')]
LEAFLET_LONGITUDE = env.int('LEAFLET_LONGITUDE', default=2)
LEAFLET_LATITUDE = env.int('LEAFLET_LATITUDE', default=51)
LEAFLET_ZOOM = env.int('LEAFLET_ZOOM', default=6)
# ============================================================================= # =============================================================================
# Third party app settings # Third party app settings
# ============================================================================= # =============================================================================
@ -243,3 +261,34 @@ SOCIAL_AUTH_NO_DEFAULT_PROTECTED_USER_FIELDS = True
SOCIAL_AUTH_PROTECTED_USER_FIELDS = ("id", ) SOCIAL_AUTH_PROTECTED_USER_FIELDS = ("id", )
LOGIN_URL = "login" LOGIN_URL = "login"
SOCIAL_AUTH_LOGIN_REDIRECT_URL = "/login/popup/end/" SOCIAL_AUTH_LOGIN_REDIRECT_URL = "/login/popup/end/"
AUTHENTICATION_BACKENDS = ()
SOCIAL_AUTH_OPENSTREETMAP_KEY = env('SOCIAL_AUTH_OPENSTREETMAP_KEY', default="")
SOCIAL_AUTH_OPENSTREETMAP_SECRET = env('SOCIAL_AUTH_OPENSTREETMAP_SECRET', default="")
if SOCIAL_AUTH_OPENSTREETMAP_KEY and SOCIAL_AUTH_OPENSTREETMAP_SECRET:
AUTHENTICATION_BACKENDS += (
'social_core.backends.openstreetmap.OpenStreetMapOAuth',
)
AUTHENTICATION_BACKENDS += (
'django.contrib.auth.backends.ModelBackend',
)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'ERROR',
'filters': None,
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'ERROR',
},
},
}