├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── config ├── .env_polr ├── fpm-pool.conf ├── nginx.conf ├── php.ini └── supervisord.conf ├── docker-compose.yml ├── seeders └── AdminSeeder.php ├── start.sh └── wait-for-it.sh /.env.example: -------------------------------------------------------------------------------- 1 | # Database 2 | DB_HOST=mariadb 3 | DB_PORT=3306 4 | DB_DATABASE=polr 5 | DB_USERNAME=polr 6 | DB_PASSWORD=password 7 | DB_ROOT_PASSWORD=password 8 | 9 | # Admin 10 | ADMIN_USERNAME=admin 11 | ADMIN_PASSWORD=password 12 | ADMIN_EMAIL= 13 | 14 | # Configs 15 | APP_NAME=Shorturl 16 | APP_PROTOCOL=http:// 17 | APP_ADDRESS=localhost:8080 18 | 19 | POLR_ALLOW_ACCT_CREATION=false 20 | POLR_ACCT_ACTIVATION=false 21 | POLR_ACCT_CREATION_RECAPTCHA=false 22 | POLR_RECAPTCHA_SITE_KEY= 23 | POLR_RECAPTCHA_SECRET= 24 | POLR_BASE=62 25 | 26 | SETTING_PUBLIC_INTERFACE=true 27 | SETTING_SHORTEN_PERMISSION=false 28 | SETTING_INDEX_REDIRECT= 29 | SETTING_REDIRECT_404= 30 | SETTING_PASSWORD_RECOV= 31 | SETTING_AUTO_API=false 32 | SETTING_ANON_API= 33 | SETTING_ANON_API_QUOTA= 34 | SETTING_PSEUDORANDOM_ENDING=false 35 | SETTING_ADV_ANALYTICS=false 36 | SETTING_RESTRICT_EMAIL_DOMAIN= 37 | SETTING_ALLOWED_EMAIL_DOMAINS= 38 | 39 | MAIL_HOST= 40 | MAIL_PORT= 41 | MAIL_USERNAME= 42 | MAIL_PASSWORD= 43 | MAIL_FROM_ADDRESS= 44 | MAIL_FROM_NAME= 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Forked from TrafeX/docker-php-nginx (https://github.com/TrafeX/docker-php-nginx/) 2 | 3 | FROM alpine:3 4 | LABEL Maintainer="Aurélien JANVIER " \ 5 | Description="Unofficial Docker image for Polr." 6 | 7 | # Environment variables 8 | ENV APP_NAME My Polr 9 | ENV APP_PROTOCOL https:// 10 | ENV DB_CONNECTION mysql 11 | ENV DB_PORT 3306 12 | ENV DB_DATABASE polr 13 | ENV DB_USERNAME polr 14 | ENV POLR_BASE 62 15 | 16 | # Install packages and remove default server definition 17 | RUN apk --no-cache add bash git nginx supervisor curl && \ 18 | apk --no-cache --repository=http://dl-cdn.alpinelinux.org/alpine/edge/testing/ add php7 php7-fpm \ 19 | php7-opcache php7-mysqli php7-json php7-openssl php7-curl php7-zlib php7-xml php7-xmlwriter \ 20 | php7-phar php7-intl php7-dom php7-xmlreader php7-ctype php7-session php7-mbstring php7-gd \ 21 | php7-pdo php7-pdo_mysql php7-pdo_sqlite php7-tokenizer && \ 22 | apk add --update libintl && \ 23 | apk add --virtual build_deps gettext && \ 24 | cp /usr/bin/envsubst /usr/local/bin/envsubst && \ 25 | rm -f /etc/nginx/conf.d/default.conf 26 | 27 | # Configure nginx 28 | COPY config/nginx.conf /etc/nginx/nginx.conf 29 | 30 | # Configure PHP-FPM 31 | COPY config/fpm-pool.conf /etc/php7/php-fpm.d/www.conf 32 | COPY config/php.ini /etc/php7/conf.d/custom.ini 33 | 34 | # Configure supervisord 35 | COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf 36 | 37 | # Add symbolic link for php 38 | RUN ln -s php7 /usr/bin/php 39 | 40 | # Install composer 41 | RUN curl -sS https://getcomposer.org/installer \ 42 | | php -- --install-dir=/usr/local/bin --filename=composer 43 | 44 | # Setup document root 45 | RUN mkdir -p /var/www/html 46 | 47 | # Pull Polr 48 | RUN git clone https://github.com/cydrobolt/polr.git /var/www/html 49 | 50 | # Make sure files/folders needed by the processes are accessable when they run under the nobody user 51 | RUN chown -R nobody.nobody /var/www/html && \ 52 | chown -R nobody.nobody /run && \ 53 | chown -R nobody.nobody /var/lib/nginx && \ 54 | chown -R nobody.nobody /var/log/nginx 55 | 56 | # Switch to use a non-root user from here on 57 | USER nobody 58 | 59 | # Add application 60 | WORKDIR /var/www/html 61 | # COPY --chown=nobody src/ /var/www/html/ 62 | 63 | # Copy env file and setup values 64 | COPY --chown=nobody config/.env_polr .env_polr 65 | 66 | # Copy admin seeder 67 | COPY --chown=nobody seeders/AdminSeeder.php AdminSeeder_withoutEnv.php 68 | 69 | # Install dependencies 70 | RUN composer install --no-dev -o 71 | 72 | # Setting logs permissions 73 | RUN mkdir -p storage/logs && \ 74 | touch storage/logs/lumen.log && \ 75 | chmod -R go+w storage 76 | 77 | # Copy start.sh script 78 | COPY --chown=nobody start.sh /start.sh 79 | RUN chmod u+x /start.sh 80 | 81 | # Copy wait-for-it.sh 82 | COPY --chown=nobody wait-for-it.sh /wait-for-it.sh 83 | RUN chmod u+x /wait-for-it.sh 84 | 85 | # Expose the port nginx is reachable on 86 | EXPOSE 8080 87 | 88 | # Configure a healthcheck to validate that everything is up&running 89 | HEALTHCHECK --timeout=10s CMD curl --silent --fail http://127.0.0.1:8080/fpm-ping 90 | 91 | # Bootup 92 | ENTRYPOINT /wait-for-it.sh $DB_HOST:$DB_PORT --strict --timeout=120 -- /start.sh 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⚠️ POLR IS UNMAINTAINED SINCE FEBRUARY 2021⚠️ 2 |
I suggest you to use more up-to-date alternatives such as [Shlink](https://github.com/shlinkio/shlink) or [Kutt.it](https://github.com/thedevs-network/kutt). 3 | ---- 4 | 5 | Polr on Alpine Linux with Docker 6 | ============================================== 7 | ![Docker Image Size](https://img.shields.io/docker/image-size/ajanvier/polr) 8 | ![Docker Pulls](https://img.shields.io/docker/pulls/ajanvier/polr) 9 | ![Docker Latest Version](https://img.shields.io/docker/v/ajanvier/polr?sort=semver) 10 | 11 | A Docker image for [Polr](https://github.com/cydrobolt/polr). 12 | 13 | Usage 14 | ----- 15 | Start the Docker container: 16 | 17 | docker run -p 8080:8080 \ 18 | -e "DB_HOST=localhost" \ 19 | -e "DB_DATABASE=polr" \ 20 | -e "DB_USERNAME=polr" \ 21 | -e "DB_PASSWORD=password" \ 22 | -e "APP_ADDRESS=example.com" \ 23 | -e "ADMIN_USERNAME=admin" \ 24 | -e "ADMIN_PASSWORD=admin" \ 25 | ajanvier/polr 26 | 27 | Or use the example docker-compose.yml. 28 | 29 | SSL not included, I made it originally to work with nginx-proxy. 30 | 31 | Environment variables 32 | ----- 33 | | Name | Description | 34 | |--|--| 35 | | DB_CONNECTION | Type of database *(default: mysql)* | 36 | | DB_HOST | **(required)** Host of the MySQL server | 37 | | DB_PORT | Port of the MySQL server *(default: 3306)* | 38 | | DB_DATABASE | Name of the MySQL database *(default: polr)*, or path of sqlite file | 39 | | DB_USERNAME | Name of the MySQL user *(default: polr)* | 40 | | DB_PASSWORD | **(required)** Password of the MySQL user | 41 | | | | 42 | | APP_NAME | Your app's name (shown on interface) *(default: My Polr)* | 43 | | APP_PROTOCOL | Protocol to access your app. *(default: https://)* | 44 | | APP_ADDRESS | **(required)** Your app's external address (ex: example.com) | 45 | | | | 46 | | ADMIN_USERNAME | **(required)** Username of the admin user | 47 | | ADMIN_PASSWORD | **(required)** Password of the admin user | 48 | | ADMIN_EMAIL | Email of the admin user | 49 | | | | 50 | | SETTING_PUBLIC_INTERFACE | Set to true to show an interface to logged off users
If false, set the SETTING_INDEX_REDIRECT
You may login by heading to /login if the public interface is off
*(default: true)* | 51 | | SETTING_SHORTEN_PERMISSION | Set to true to require users to be logged in before shortening URLs
*(default: false)* | 52 | | SETTING_INDEX_REDIRECT | You must set SETTING_INDEX_REDIRECT if SETTING_PUBLIC_INTERFACE is false
Polr will redirect logged off users to this URL | 53 | | SETTING_REDIRECT_404 | Set to true if you wish to redirect 404s to SETTING_INDEX_REDIRECT
Polr will redirect logged off users to this URL | 54 | | SETTING_PASSWORD_RECOV | Set to true to enable password recovery *(default: false)* | 55 | | SETTING_AUTO_API | Set to true to generate API keys for each user on registration *(default: false)* | 56 | | SETTING_ANON_API | Set to true to allow anonymous API access *(default: false)* | 57 | | SETTING_ANON_API_QUOTA | Set the anonymous API quota per IP | 58 | | SETTING_PSEUDORANDOM_ENDING | Set to true to use pseudorandom strings rather than using a counter by default *(default: false)* | 59 | | SETTING_ADV_ANALYTICS | Set to true to record advanced analytics *(default: false)* | 60 | | SETTING_RESTRICT_EMAIL_DOMAIN | Set to true to restrict registration to a specific email domain *(default: false)* | 61 | | SETTING_ALLOWED_EMAIL_DOMAINS | A comma-separated list of permitted email domains | 62 | | | | 63 | | POLR_ALLOW_ACCT_CREATION | Set to true to allow signups, false to disable *(default: false)* | 64 | | POLR_ACCT_ACTIVATION | Set to true to require activation by email *(default: false)* | 65 | | POLR_ACCT_CREATION_RECAPTCHA | Set to true to require reCAPTCHAs on sign up pages
If this setting is enabled, you must also provide your reCAPTCHA keys in POLR_RECAPTCHA_SITE_KEY and POLR_RECAPTCHA_SECRET_KEY
*(default: false)* | 66 | | POLR_BASE | 32 or 62 *(default: 62)* | 67 | | POLR_RECAPTCHA_SITE_KEY | reCAPTCHA site key | 68 | | POLR_RECAPTCHA_SECRET_KEY | reCAPTCHA secret key | 69 | | | | 70 | | **Set each to blank to disable mail** | | 71 | | MAIL_HOST | Hostname of the mail server | 72 | | MAIL_PORT | Port of the mail server | 73 | | MAIL_USERNAME | User name on the mail server | 74 | | MAIL_PASSWORD | User password | 75 | | MAIL_FROM_ADDRESS | "From" address | 76 | | MAIL_FROM_NAME | "From" name | 77 | -------------------------------------------------------------------------------- /config/.env_polr: -------------------------------------------------------------------------------- 1 | APP_ENV=production 2 | 3 | # Set to true if debugging 4 | APP_DEBUG=false 5 | 6 | # 32-character key (e.g 3EWBLwxTfh%*f&xRBqdGEIUVvn4%$Hfi) 7 | APP_KEY=${APP_KEY} 8 | 9 | # Your app's name (shown on interface) 10 | APP_NAME="${APP_NAME}" 11 | 12 | # Protocol to access your app. e.g https:// 13 | APP_PROTOCOL="${APP_PROTOCOL}" 14 | 15 | # Your app's external address (e.g example.com) 16 | APP_ADDRESS="${APP_ADDRESS}" 17 | 18 | # Your app's bootstrap stylesheet 19 | # e.g https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/flatly/bootstrap.min.css 20 | APP_STYLESHEET= 21 | 22 | # Set to today's date (e.g November 3, 2015) 23 | POLR_GENERATED_AT="${POLR_GENERATED_AT}" 24 | 25 | # Set to true after running setup script 26 | # e.g true 27 | POLR_SETUP_RAN=true 28 | 29 | DB_CONNECTION="${DB_CONNECTION}" 30 | # Set to your DB host (e.g localhost) 31 | DB_HOST="${DB_HOST}" 32 | # DB port (e.g 3306) 33 | DB_PORT=${DB_PORT} 34 | # Set to your DB name (e.g polr) 35 | DB_DATABASE="${DB_DATABASE}" 36 | # DB credentials 37 | # e.g root 38 | DB_USERNAME="${DB_USERNAME}" 39 | DB_PASSWORD="${DB_PASSWORD}" 40 | 41 | # Polr Settings 42 | 43 | # Set to true to show an interface to logged off users 44 | # If false, set the SETTING_INDEX_REDIRECT 45 | # You may login by heading to /login if the public interface is off 46 | SETTING_PUBLIC_INTERFACE=${SETTING_PUBLIC_INTERFACE} 47 | 48 | # Set to true to allow signups, false to disable (e.g true/false) 49 | POLR_ALLOW_ACCT_CREATION=${POLR_ALLOW_ACCT_CREATION} 50 | 51 | # Set to true to require activation by email (e.g true/false) 52 | POLR_ACCT_ACTIVATION=${POLR_ACCT_ACTIVATION} 53 | 54 | # Set to true to require reCAPTCHAs on sign up pages 55 | # If this setting is enabled, you must also provide your reCAPTCHA keys 56 | # in POLR_RECAPTCHA_SITE_KEY and POLR_RECAPTCHA_SECRET_KEY 57 | POLR_ACCT_CREATION_RECAPTCHA=${POLR_ACCT_CREATION_RECAPTCHA} 58 | 59 | # Set to true to require users to be logged in before shortening URLs 60 | SETTING_SHORTEN_PERMISSION=${SETTING_SHORTEN_PERMISSION} 61 | 62 | # You must set SETTING_INDEX_REDIRECT if SETTING_PUBLIC_INTERFACE is false 63 | # Polr will redirect logged off users to this URL 64 | SETTING_INDEX_REDIRECT=${SETTING_INDEX_REDIRECT} 65 | 66 | # Set to true if you wish to redirect 404s to SETTING_INDEX_REDIRECT 67 | # Otherwise, an error message will be shown 68 | SETTING_REDIRECT_404=${SETTING_REDIRECT_404} 69 | 70 | # Set to true to enable password recovery 71 | SETTING_PASSWORD_RECOV=${SETTING_PASSWORD_RECOV} 72 | 73 | # Set to true to generate API keys for each user on registration 74 | SETTING_AUTO_API=${SETTING_AUTO_API} 75 | 76 | # Set to true to allow anonymous API access 77 | SETTING_ANON_API=${SETTING_ANON_API} 78 | 79 | # Set the anonymous API quota per IP 80 | SETTING_ANON_API_QUOTA=${SETTING_ANON_API_QUOTA} 81 | 82 | # Set to true to use pseudorandom strings rather than using a counter by default 83 | SETTING_PSEUDORANDOM_ENDING=${SETTING_PSEUDORANDOM_ENDING} 84 | 85 | # Set to true to record advanced analytics 86 | SETTING_ADV_ANALYTICS=${SETTING_ADV_ANALYTICS} 87 | 88 | # Set to true to restrict registration to a specific email domain 89 | SETTING_RESTRICT_EMAIL_DOMAIN=${SETTING_RESTRICT_EMAIL_DOMAIN} 90 | 91 | # A comma-separated list of permitted email domains 92 | SETTING_ALLOWED_EMAIL_DOMAINS="${SETTING_ALLOWED_EMAIL_DOMAINS}" 93 | 94 | # reCAPTCHA site key 95 | POLR_RECAPTCHA_SITE_KEY="${POLR_RECAPTCHA_SITE_KEY}" 96 | 97 | # reCAPTCHA secret key 98 | POLR_RECAPTCHA_SECRET_KEY="${POLR_RECAPTCHA_SECRET}" 99 | 100 | # Set each to blank to disable mail 101 | MAIL_DRIVER=smtp 102 | # e.g mailtrap.io 103 | MAIL_HOST="${MAIL_HOST}" 104 | # e.g 2525 105 | MAIL_PORT="${MAIL_PORT}" 106 | MAIL_USERNAME="${MAIL_USERNAME}" 107 | MAIL_PASSWORD="${MAIL_PASSWORD}" 108 | # e.g noreply@example.com 109 | MAIL_FROM_ADDRESS="${MAIL_FROM_ADDRESS}" 110 | MAIL_FROM_NAME="${MAIL_FROM_NAME}" 111 | 112 | # Set to 32 or 62 -- do not touch after initial configuration 113 | POLR_BASE=${POLR_BASE} 114 | 115 | APP_LOCALE=en 116 | APP_FALLBACK_LOCALE=en 117 | 118 | CACHE_DRIVER=file 119 | SESSION_DRIVER=file 120 | QUEUE_DRIVER=database 121 | 122 | _API_KEY_LENGTH=15 123 | _ANALYTICS_MAX_DAYS_DIFF=365 124 | _PSEUDO_RANDOM_KEY_LENGTH=5 125 | 126 | # FILESYSTEM_DRIVER=local 127 | # FILESYSTEM_CLOUD=s3 128 | 129 | # S3_KEY=null 130 | # S3_SECRET=null 131 | # S3_REGION=null 132 | # S3_BUCKET=null 133 | 134 | # RACKSPACE_USERNAME=null 135 | # RACKSPACE_KEY=null 136 | # RACKSPACE_CONTAINER=null 137 | # RACKSPACE_REGION=null 138 | 139 | POLR_RELDATE="Jan 28, 2021" 140 | POLR_VERSION="2.3.0" 141 | POLR_SECRET_BYTES=2 142 | -------------------------------------------------------------------------------- /config/fpm-pool.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | ; Log to stderr 3 | error_log = /dev/stderr 4 | 5 | [www] 6 | ; The address on which to accept FastCGI requests. 7 | ; Valid syntaxes are: 8 | ; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on 9 | ; a specific port; 10 | ; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on 11 | ; a specific port; 12 | ; 'port' - to listen on a TCP socket to all addresses 13 | ; (IPv6 and IPv4-mapped) on a specific port; 14 | ; '/path/to/unix/socket' - to listen on a unix socket. 15 | ; Note: This value is mandatory. 16 | listen = 127.0.0.1:9000 17 | 18 | ; Enable status page 19 | pm.status_path = /fpm-status 20 | 21 | ; Ondemand process manager 22 | pm = ondemand 23 | 24 | ; The number of child processes to be created when pm is set to 'static' and the 25 | ; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. 26 | ; This value sets the limit on the number of simultaneous requests that will be 27 | ; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. 28 | ; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP 29 | ; CGI. The below defaults are based on a server without much resources. Don't 30 | ; forget to tweak pm.* to fit your needs. 31 | ; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' 32 | ; Note: This value is mandatory. 33 | pm.max_children = 100 34 | 35 | ; The number of seconds after which an idle process will be killed. 36 | ; Note: Used only when pm is set to 'ondemand' 37 | ; Default Value: 10s 38 | pm.process_idle_timeout = 10s; 39 | 40 | ; The number of requests each child process should execute before respawning. 41 | ; This can be useful to work around memory leaks in 3rd party libraries. For 42 | ; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. 43 | ; Default Value: 0 44 | pm.max_requests = 1000 45 | 46 | ; Make sure the FPM workers can reach the environment variables for configuration 47 | clear_env = no 48 | 49 | ; Catch output from PHP 50 | catch_workers_output = yes 51 | 52 | ; Remove the 'child 10 said into stderr' prefix in the log and only show the actual message 53 | decorate_workers_output = no 54 | 55 | ; Enable ping page to use in healthcheck 56 | ping.path = /fpm-ping 57 | -------------------------------------------------------------------------------- /config/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | error_log stderr warn; 3 | pid /run/nginx.pid; 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | http { 10 | include mime.types; 11 | default_type application/octet-stream; 12 | 13 | # Define custom log format to include reponse times 14 | log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" ' 15 | '$status $body_bytes_sent "$http_referer" ' 16 | '"$http_user_agent" "$http_x_forwarded_for" ' 17 | '$request_time $upstream_response_time $pipe $upstream_cache_status'; 18 | 19 | access_log /dev/stdout main_timed; 20 | error_log /dev/stderr notice; 21 | 22 | keepalive_timeout 65; 23 | 24 | # Write temporary files to /tmp so they can be created as a non-privileged user 25 | client_body_temp_path /tmp/client_temp; 26 | proxy_temp_path /tmp/proxy_temp_path; 27 | fastcgi_temp_path /tmp/fastcgi_temp; 28 | uwsgi_temp_path /tmp/uwsgi_temp; 29 | scgi_temp_path /tmp/scgi_temp; 30 | 31 | # Default server definition 32 | server { 33 | listen [::]:8080 default_server; 34 | listen 8080 default_server; 35 | server_name _; 36 | 37 | sendfile off; 38 | 39 | root /var/www/html/public; 40 | index index.php index.html; 41 | 42 | location / { 43 | # First attempt to serve request as file, then 44 | # as directory, then fall back to index.php 45 | try_files $uri $uri/ /index.php$is_args$args; 46 | } 47 | 48 | # Redirect server error pages to the static page /50x.html 49 | error_page 500 502 503 504 /50x.html; 50 | location = /50x.html { 51 | root /var/lib/nginx/html; 52 | } 53 | 54 | # Pass the PHP scripts to PHP-FPM listening on 127.0.0.1:9000 55 | location ~ \.php$ { 56 | try_files $uri =404; 57 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 58 | fastcgi_pass 127.0.0.1:9000; 59 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 60 | fastcgi_param SCRIPT_NAME $fastcgi_script_name; 61 | fastcgi_index index.php; 62 | include fastcgi_params; 63 | } 64 | 65 | location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ { 66 | expires 5d; 67 | } 68 | 69 | # Deny access to . files, for security 70 | location ~ /\. { 71 | log_not_found off; 72 | deny all; 73 | } 74 | 75 | # Allow fpm ping and status from localhost 76 | location ~ ^/(fpm-status|fpm-ping)$ { 77 | access_log off; 78 | allow 127.0.0.1; 79 | deny all; 80 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 81 | include fastcgi_params; 82 | fastcgi_pass 127.0.0.1:9000; 83 | } 84 | } 85 | 86 | gzip on; 87 | gzip_proxied any; 88 | gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss; 89 | gzip_vary on; 90 | gzip_disable "msie6"; 91 | 92 | # Include other server configs 93 | include /etc/nginx/conf.d/*.conf; 94 | } 95 | -------------------------------------------------------------------------------- /config/php.ini: -------------------------------------------------------------------------------- 1 | [Date] 2 | date.timezone="UTC" 3 | -------------------------------------------------------------------------------- /config/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/dev/null 4 | logfile_maxbytes=0 5 | pidfile=/run/supervisord.pid 6 | 7 | [program:php-fpm] 8 | command=php-fpm7 -F 9 | stdout_logfile=/dev/stdout 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/dev/stderr 12 | stderr_logfile_maxbytes=0 13 | autorestart=false 14 | startretries=0 15 | 16 | [program:nginx] 17 | command=nginx -g 'daemon off;' 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | autorestart=false 23 | startretries=0 24 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | 3 | services: 4 | polr: 5 | image: ajanvier/polr 6 | container_name: polr 7 | ports: 8 | - "8080:8080" 9 | env_file: 10 | - .env 11 | depends_on: 12 | - mariadb 13 | 14 | mariadb: 15 | image: mariadb 16 | container_name: mariadb 17 | restart: always 18 | expose: 19 | - "3306" 20 | environment: 21 | - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD} 22 | - MYSQL_DATABASE=${DB_DATABASE} 23 | - MYSQL_USER=${DB_USERNAME} 24 | - MYSQL_PASSWORD=${DB_PASSWORD} 25 | volumes: 26 | - /var/lib/mysql 27 | -------------------------------------------------------------------------------- /seeders/AdminSeeder.php: -------------------------------------------------------------------------------- 1 | ".env" 12 | 13 | rm -f .env_polr 14 | php artisan migrate:install 15 | php artisan migrate --force 16 | composer dump-autoload 17 | php artisan geoip:update 18 | fi 19 | 20 | if [ ! -f "database/seeds/AdminSeeder.php" ]; then 21 | envsubst < "AdminSeeder_withoutEnv.php" > "database/seeds/AdminSeeder.php" 22 | 23 | rm -f AdminSeeder_withoutEnv.php 24 | composer dump-autoload 25 | php artisan db:seed --class=AdminSeeder --force 26 | fi 27 | 28 | # Let supervisord start nginx & php-fpm 29 | /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf 30 | -------------------------------------------------------------------------------- /wait-for-it.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Use this script to test if a given TCP host/port are available 3 | 4 | WAITFORIT_cmdname=${0##*/} 5 | 6 | echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } 7 | 8 | usage() 9 | { 10 | cat << USAGE >&2 11 | Usage: 12 | $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] 13 | -h HOST | --host=HOST Host or IP under test 14 | -p PORT | --port=PORT TCP port under test 15 | Alternatively, you specify the host and port as host:port 16 | -s | --strict Only execute subcommand if the test succeeds 17 | -q | --quiet Don't output any status messages 18 | -t TIMEOUT | --timeout=TIMEOUT 19 | Timeout in seconds, zero for no timeout 20 | -- COMMAND ARGS Execute command with args after the test finishes 21 | USAGE 22 | exit 1 23 | } 24 | 25 | wait_for() 26 | { 27 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 28 | echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 29 | else 30 | echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" 31 | fi 32 | WAITFORIT_start_ts=$(date +%s) 33 | while : 34 | do 35 | if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then 36 | nc -z $WAITFORIT_HOST $WAITFORIT_PORT 37 | WAITFORIT_result=$? 38 | else 39 | (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 40 | WAITFORIT_result=$? 41 | fi 42 | if [[ $WAITFORIT_result -eq 0 ]]; then 43 | WAITFORIT_end_ts=$(date +%s) 44 | echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" 45 | break 46 | fi 47 | sleep 1 48 | done 49 | return $WAITFORIT_result 50 | } 51 | 52 | wait_for_wrapper() 53 | { 54 | # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 55 | if [[ $WAITFORIT_QUIET -eq 1 ]]; then 56 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 57 | else 58 | timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & 59 | fi 60 | WAITFORIT_PID=$! 61 | trap "kill -INT -$WAITFORIT_PID" INT 62 | wait $WAITFORIT_PID 63 | WAITFORIT_RESULT=$? 64 | if [[ $WAITFORIT_RESULT -ne 0 ]]; then 65 | echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" 66 | fi 67 | return $WAITFORIT_RESULT 68 | } 69 | 70 | # process arguments 71 | while [[ $# -gt 0 ]] 72 | do 73 | case "$1" in 74 | *:* ) 75 | WAITFORIT_hostport=(${1//:/ }) 76 | WAITFORIT_HOST=${WAITFORIT_hostport[0]} 77 | WAITFORIT_PORT=${WAITFORIT_hostport[1]} 78 | shift 1 79 | ;; 80 | --child) 81 | WAITFORIT_CHILD=1 82 | shift 1 83 | ;; 84 | -q | --quiet) 85 | WAITFORIT_QUIET=1 86 | shift 1 87 | ;; 88 | -s | --strict) 89 | WAITFORIT_STRICT=1 90 | shift 1 91 | ;; 92 | -h) 93 | WAITFORIT_HOST="$2" 94 | if [[ $WAITFORIT_HOST == "" ]]; then break; fi 95 | shift 2 96 | ;; 97 | --host=*) 98 | WAITFORIT_HOST="${1#*=}" 99 | shift 1 100 | ;; 101 | -p) 102 | WAITFORIT_PORT="$2" 103 | if [[ $WAITFORIT_PORT == "" ]]; then break; fi 104 | shift 2 105 | ;; 106 | --port=*) 107 | WAITFORIT_PORT="${1#*=}" 108 | shift 1 109 | ;; 110 | -t) 111 | WAITFORIT_TIMEOUT="$2" 112 | if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi 113 | shift 2 114 | ;; 115 | --timeout=*) 116 | WAITFORIT_TIMEOUT="${1#*=}" 117 | shift 1 118 | ;; 119 | --) 120 | shift 121 | WAITFORIT_CLI=("$@") 122 | break 123 | ;; 124 | --help) 125 | usage 126 | ;; 127 | *) 128 | echoerr "Unknown argument: $1" 129 | usage 130 | ;; 131 | esac 132 | done 133 | 134 | if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then 135 | echoerr "Error: you need to provide a host and port to test." 136 | usage 137 | fi 138 | 139 | WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} 140 | WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} 141 | WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} 142 | WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} 143 | 144 | # Check to see if timeout is from busybox? 145 | WAITFORIT_TIMEOUT_PATH=$(type -p timeout) 146 | WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) 147 | 148 | WAITFORIT_BUSYTIMEFLAG="" 149 | if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then 150 | WAITFORIT_ISBUSY=1 151 | # Check if busybox timeout uses -t flag 152 | # (recent Alpine versions don't support -t anymore) 153 | if timeout &>/dev/stdout | grep -q -e '-t '; then 154 | WAITFORIT_BUSYTIMEFLAG="-t" 155 | fi 156 | else 157 | WAITFORIT_ISBUSY=0 158 | fi 159 | 160 | if [[ $WAITFORIT_CHILD -gt 0 ]]; then 161 | wait_for 162 | WAITFORIT_RESULT=$? 163 | exit $WAITFORIT_RESULT 164 | else 165 | if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then 166 | wait_for_wrapper 167 | WAITFORIT_RESULT=$? 168 | else 169 | wait_for 170 | WAITFORIT_RESULT=$? 171 | fi 172 | fi 173 | 174 | if [[ $WAITFORIT_CLI != "" ]]; then 175 | if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then 176 | echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" 177 | exit $WAITFORIT_RESULT 178 | fi 179 | exec "${WAITFORIT_CLI[@]}" 180 | else 181 | exit $WAITFORIT_RESULT 182 | fi --------------------------------------------------------------------------------