blat 2 years ago
commit
b2eca58f38
13 changed files with 477 additions and 0 deletions
  1. 71 0
      Dockerfile
  2. 51 0
      README.md
  3. 29 0
      docker-compose.yml
  4. 31 0
      docker-entrypoint.sh
  5. 4 0
      requirements-docker.txt
  6. 20 0
      scripts/auth-su.sh
  7. 13 0
      scripts/backup.sh
  8. 32 0
      scripts/install.sh
  9. 16 0
      scripts/restore.sh
  10. BIN
      scripts/umap.png
  11. 46 0
      scripts/upgrade.sh
  12. 154 0
      umap/settings/docker.py
  13. 10 0
      uwsgi.ini

+ 71 - 0
Dockerfile

@@ -0,0 +1,71 @@
+FROM node:12 AS vendors
+
+COPY . /srv/umap
+
+WORKDIR /srv/umap
+
+RUN make installjs
+
+RUN make vendors
+
+FROM python:3.8-slim
+
+ENV PYTHONUNBUFFERED=1 \
+    UMAP_SETTINGS=/srv/umap/umap/settings/docker.py \
+    PORT=8000
+
+RUN mkdir -p /srv/umap/data && \
+    mkdir -p /srv/umap/uploads
+
+COPY . /srv/umap
+
+COPY --from=vendors /srv/umap/umap/static/umap/vendors /srv/umap/umap/static/umap/vendors
+
+WORKDIR /srv/umap
+
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        uwsgi \
+        libpq-dev \
+        build-essential \
+        binutils \
+        gdal-bin \
+        libproj-dev \
+        curl \
+        git \
+        gettext \
+        sqlite3 \
+        libffi-dev \
+        libtiff5-dev \
+        libjpeg62-turbo-dev \
+        zlib1g-dev \
+        libfreetype6-dev \
+        liblcms2-dev \
+        libwebp-dev \
+        && \
+    pip install --no-cache -r requirements-docker.txt && pip install . && \
+    apt-get remove -y \
+        binutils \
+        libproj-dev \
+        libffi-dev \
+        libtiff5-dev \
+        libjpeg62-turbo-dev \
+        zlib1g-dev \
+        libfreetype6-dev \
+        liblcms2-dev \
+        libwebp-dev \
+        && \
+    apt-get autoremove -y && \
+    apt-get clean && \
+    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# Add Tini
+ENV TINI_VERSION v0.14.0
+ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
+RUN chmod +x /tini
+
+EXPOSE 8000
+
+ENTRYPOINT ["/tini", "--", "/srv/umap/docker-entrypoint.sh"]
+
+CMD ["/srv/umap/docker-entrypoint.sh"]

+ 51 - 0
README.md

@@ -0,0 +1,51 @@
+uMap
+===
+
+### About
+
+uMap lets you create maps with OpenStreetMap layers in a minute and embed them in your site.
+*Because we think that the more OSM will be used, the more OSM will be improved.*
+It uses [django-leaflet-storage](https://github.com/umap-project/django-leaflet-storage) and [Leaflet.Storage](https://github.com/umap-project/Leaflet.Storage),  built on top of Django and Leaflet.
+
+![Umap](scripts/umap.png)
+
+docker support from https://github.com/Duvel/umap    
+umap documentation at https://umap-project.readthedocs.io/en/latest/install/
+
+
+#### Install
+```
+cd /opt/
+git clone https://git.lattuga.net/blat/umap
+chmod -x ./scripts/install.sh
+./scripts/install.sh
+```
+##### Create admin
+```
+docker exec -ti umap_app_1 umap createsuperuser
+```
+
+#### Backup
+There are two important places where your data are located, in your database and in your media folder.
+```
+chmod -x ./scripts/backup.sh
+./scripts/backup.sh
+```
+
+read more about backups: https://github.com/umap-project/umap/blob/update-docs-backup-commands/docs/backup.md
+
+#### Restore
+edit db_$(date +"%Y-%B-%d").zip with your backup date      
+* require root privilegies to fix permission on db folder
+```
+chmod -x ./scripts/restore.sh
+./scripts/restore.sh
+```
+
+#### Upgrade
+select a version and build a new image
+* require root privilegies to fix permission on db folder
+```
+chmod -x ./scripts/upgrade.sh
+./scripts/upgrade.sh
+```

+ 29 - 0
docker-compose.yml

@@ -0,0 +1,29 @@
+version: '3'
+services:
+  db:
+    image: mdillon/postgis:9.6-alpine
+    volumes:
+      - ./db:/var/lib/postgresql/data
+
+  redis:
+    image: redis:latest
+
+  app:
+    image: umap:latest
+    environment:
+      - DATABASE_URL=postgis://postgres@db/postgres
+      - REDIS_URL=redis://redis:6379/0
+      - ADMIN_EMAIL=umap
+      - ENABLE_ACCOUNT_LOGIN=True
+      - SECRET_KEY=some-long-and-weirdly-unrandom-secret-key
+      - ALLOWED_HOSTS=*
+      - SITE_URL=http://localhost/
+      - LEAFLET_STORAGE_ALLOW_ANONYMOUS=True
+    volumes:
+      - ./uploads:/srv/umap/uploads
+      - ./static:/srv/umap/static
+    ports:
+      - 8000:8000
+    depends_on:
+      - db
+    restart: always

+ 31 - 0
docker-entrypoint.sh

@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -eo pipefail
+
+# 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 2>&1
+    [[ $? -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
+# create languagae files
+#umap storagei18n
+# compress static files
+umap compress
+# run uWSGI
+exec uwsgi --ini uwsgi.ini

+ 4 - 0
requirements-docker.txt

@@ -0,0 +1,4 @@
+-r requirements.txt
+django-environ==0.4.1
+django-redis==4.7.0
+uwsgi==2.0.14

+ 20 - 0
scripts/auth-su.sh

@@ -0,0 +1,20 @@
+#!/bin/su root
+ARG=$1
+
+function auth_su {
+  if [[ -z $ARG ]]; then
+    # ok postgres
+    chown -R 70:$USER db ; \
+    chmod g=rwx,o-rwx -R db ;
+  elif [[ $ARG == "user" ]]; then
+    # ok build
+    chown -R $USER:$USER db ; \
+    chmod g=rwx,o-rwx -R db ;
+  elif [[ $ARG == "delete" ]]; then
+    # delete old volumes
+    rm -rf db ; \
+    rm -rf uploads ;
+  fi
+}
+
+auth_su $ARG

+ 13 - 0
scripts/backup.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+docker-compose down
+
+[[ -f $HOME/backup ]] && mkdir $HOME/backup ; mkdir $HOME/backup/umap
+
+# db
+zip -r $HOME/backup/umap/db_$(date +"%Y-%B-%d").zip db
+
+# media and geojson
+zip -r $HOME/backup/umap/uploads_$(date +"%Y-%B-%d").zip uploads
+
+docker-compose up -d

+ 32 - 0
scripts/install.sh

@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# backup README
+cp README.md DOC.md
+
+# delete this git repo and sync the offical one
+rm -rf .git
+git clone https://github.com/umap-project/umap.git tmp
+rsync -r -L tmp/ .
+rm -rf tmp/
+
+# checkout to latest tag
+TAG=$(git tag -l | tail -1)
+git checkout $TAG
+
+# create two default branch
+git switch -c current
+git switch -c upstream
+
+# build docker image
+chmod +x docker-entrypoint.sh
+docker build --tag umap:$TAG .
+
+# initialize directories and
+# fix db volume permission to enable backup without root needed
+mkdir static uploads db
+chown -R 70:$USER db
+chmod g=rwx,o-rwx -R db
+
+# setup umap container tagname
+sed -i 's/umap:latest/umap:'${TAG}'/g' docker-compose.yml;
+docker-compose up -d;

+ 16 - 0
scripts/restore.sh

@@ -0,0 +1,16 @@
+#!/bin/bash
+
+docker-compose down
+
+# delete db and media volumes
+chmod +x ./scripts/auth-su.sh
+./scripts/auth-su.sh delete
+
+# db
+unzip $HOME/backup/umap/db_$(date +"%Y-%B-%d").zip
+./scripts/auth-su.sh
+
+# media and geojson
+unzip $HOME/backup/umap/uploads_$(date +"%Y-%B-%d").zip
+
+docker-compose up -d

BIN
scripts/umap.png


+ 46 - 0
scripts/upgrade.sh

@@ -0,0 +1,46 @@
+#!/bin/bash
+
+docker-compose down
+
+git checkout current
+TAG=$(git describe --tags)
+git fetch --tags
+
+# select from last 5 tags
+if [ latestTag="" ] ; then
+PS3="Enter number of version: "
+select version in $(git tag -l | tail -5)
+do
+    latestTag=$version;
+    echo "Upgrading to $version ..."
+    break;
+done
+fi
+
+# or use latest
+# latestTag=$(git tag -l | tail -1`)
+
+# update upstream
+git branch -D upstream
+git pull
+git checkout $latestTag
+git switch -c upstream
+
+# fix permission and rebuild
+chmod +x docker-entrypoint.sh
+chmod +x ./scripts/auth-su.sh
+./scripts/auth-su.sh user
+
+docker build --tag umap:$latestTag .
+
+# fix permission
+chmod +x ./scripts/auth-su.sh
+./scripts/auth-su.sh
+
+# restart
+sed -i 's/umap:'${TAG}'/umap:'${latestTag}'/g' docker-compose.yml;
+docker-compose up -d;
+
+# resync current
+git branch -D current
+git branch current

+ 154 - 0
umap/settings/docker.py

@@ -0,0 +1,154 @@
+# -*- coding:utf-8 -*-
+"""
+Settings for Docker development
+Use this file as a base for your local development settings and copy
+it to umap/settings/local.py. It should not be checked into
+your code repository.
+"""
+import environ
+from umap.settings.base import *   # pylint: disable=W0614,W0401
+
+env = environ.Env()
+
+SECRET_KEY = env('SECRET_KEY')
+INTERNAL_IPS = env.list('INTERNAL_IPS', default='127.0.0.1')
+ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default='*')
+
+DEBUG = env.bool('DEBUG', default=False)
+
+ADMIN_EMAILS = env.list('ADMIN_EMAIL', default='')
+ADMINS = [(email, email) for email in ADMIN_EMAILS]
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': env.db(default='postgis://localhost:5432/umap')
+}
+
+COMPRESS_ENABLED = True
+COMPRESS_OFFLINE = True
+
+LANGUAGE_CODE = 'en'
+
+# Set to False if login into django account should not be possible. You can
+# administer accounts in the admin interface.
+ENABLE_ACCOUNT_LOGIN = env.bool('ENABLE_ACCOUNT_LOGIN', default=True)
+
+AUTHENTICATION_BACKENDS = ()
+
+# We need email to associate with other Oauth providers
+SOCIAL_AUTH_GITHUB_SCOPE = ['user:email']
+SOCIAL_AUTH_GITHUB_KEY = env('GITHUB_KEY', default='')
+SOCIAL_AUTH_GITHUB_SECRET = env('GITHUB_SECRET', default='')
+if SOCIAL_AUTH_GITHUB_KEY and SOCIAL_AUTH_GITHUB_SECRET:
+    AUTHENTICATION_BACKENDS += (
+        'social_core.backends.github.GithubOAuth2',
+    )
+SOCIAL_AUTH_BITBUCKET_KEY = env('BITBUCKET_KEY', default='')
+SOCIAL_AUTH_BITBUCKET_SECRET = env('BITBUCKET_SECRET', default='')
+if SOCIAL_AUTH_BITBUCKET_KEY and SOCIAL_AUTH_BITBUCKET_SECRET:
+    AUTHENTICATION_BACKENDS += (
+        'social_core.backends.bitbucket.BitbucketOAuth',
+    )
+
+SOCIAL_AUTH_TWITTER_KEY = env('TWITTER_KEY', default='')
+SOCIAL_AUTH_TWITTER_SECRET = env('TWITTER_SECRET', default='')
+if SOCIAL_AUTH_TWITTER_KEY and SOCIAL_AUTH_TWITTER_SECRET:
+    AUTHENTICATION_BACKENDS += (
+        'social_core.backends.twitter.TwitterOAuth',
+    )
+SOCIAL_AUTH_OPENSTREETMAP_KEY = env('OPENSTREETMAP_KEY', default='')
+SOCIAL_AUTH_OPENSTREETMAP_SECRET = env('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',
+)
+
+# MIDDLEWARE_CLASSES += (
+#     'social_django.middleware.SocialAuthExceptionMiddleware',
+# )
+
+SOCIAL_AUTH_RAISE_EXCEPTIONS = False
+SOCIAL_AUTH_BACKEND_ERROR_URL = "/"
+
+# If you want to add a playgroud map, add its primary key
+# UMAP_DEMO_PK = 204
+# If you want to add a showcase map on the home page, add its primary key
+# UMAP_SHOWCASE_PK = 1156
+# Add a baner to warn people this instance is not production ready.
+UMAP_DEMO_SITE = False
+
+# Whether to allow non authenticated people to create maps.
+LEAFLET_STORAGE_ALLOW_ANONYMOUS = env.bool(
+    'LEAFLET_STORAGE_ALLOW_ANONYMOUS',
+    default=False,
+)
+
+# This setting will exclude empty maps (in fact, it will exclude all maps where
+# the default center has not been updated)
+UMAP_EXCLUDE_DEFAULT_MAPS = False
+
+# How many maps should be showcased on the main page resp. on the user page
+UMAP_MAPS_PER_PAGE = 0
+# How many maps should be showcased on the user page, if owner
+UMAP_MAPS_PER_PAGE_OWNER = 10
+
+SITE_URL = env('SITE_URL')
+SHORT_SITE_URL = env('SHORT_SITE_URL', default=None)
+
+CACHES = {'default': env.cache('REDIS_URL', default='locmem://')}
+
+# POSTGIS_VERSION = (2, 1, 0)
+EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+
+# You need to unable accent extension before using UMAP_USE_UNACCENT
+# python manage.py dbshell
+# CREATE EXTENSION unaccent;
+UMAP_USE_UNACCENT = False
+
+# For static deployment
+STATIC_ROOT = '/srv/umap/static'
+
+# For users' statics (geojson mainly)
+MEDIA_ROOT = '/srv/umap/uploads'
+
+# Default map location for new maps
+LEAFLET_LONGITUDE = env.int('LEAFLET_LONGITUDE', default=2)
+LEAFLET_LATITUDE = env.int('LEAFLET_LATITUDE', default=51)
+LEAFLET_ZOOM = env.int('LEAFLET_ZOOM', default=6)
+
+# Number of old version to keep per datalayer.
+LEAFLET_STORAGE_KEEP_VERSIONS = env.int(
+    'LEAFLET_STORAGE_KEEP_VERSIONS',
+    default=10,
+)
+
+import sys
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'formatters': {
+        'verbose': {
+            'format': '[django] %(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
+        }
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'stream': sys.stdout,
+            'formatter': 'verbose'
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console'],
+            'level': 'DEBUG',
+            'propagate': True,
+        },
+    },
+}

+ 10 - 0
uwsgi.ini

@@ -0,0 +1,10 @@
+[uwsgi]
+http = :$(PORT)
+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