From c1b4875f4ebad1c37d42855897d93bf33fae1538 Mon Sep 17 00:00:00 2001 From: Felix Albrigtsen Date: Sat, 21 Jan 2023 18:39:28 +0100 Subject: [PATCH] Initial upload, docker environment --- .gitignore | 2 + README.md | 70 + dev.sh | 5 + docker-compose.yml | 32 + docs/database_model.png | Bin 0 -> 31465 bytes docs/database_model.puml | 50 + phpdocker/README.html | 208 +++ phpdocker/README.md | 144 ++ phpdocker/nginx/nginx.conf | 29 + phpdocker/php-fpm/Dockerfile | 8 + phpdocker/php-fpm/php-ini-overrides.ini | 2 + src/adminer-4.8.1.php | 1795 +++++++++++++++++++++++ src/db_config.php | 8 + src/db_connect.php | 7 + src/index.php | 16 + utils/initdb.sql | 45 + 16 files changed, 2421 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 dev.sh create mode 100644 docker-compose.yml create mode 100644 docs/database_model.png create mode 100644 docs/database_model.puml create mode 100644 phpdocker/README.html create mode 100644 phpdocker/README.md create mode 100644 phpdocker/nginx/nginx.conf create mode 100644 phpdocker/php-fpm/Dockerfile create mode 100644 phpdocker/php-fpm/php-ini-overrides.ini create mode 100644 src/adminer-4.8.1.php create mode 100644 src/db_config.php create mode 100644 src/db_connect.php create mode 100644 src/index.php create mode 100644 utils/initdb.sql diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd732d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/phpdocker/postgres-data + diff --git a/README.md b/README.md new file mode 100644 index 0000000..cd7a342 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# PVVMDB +## A new attempt to keep records of PVV members + +The projects should keep track of some basic member information: +- Usernames of all existing users +- Basic contact information +- Membership payments +- Disk Quota payments + +This will help us keep track of: +- Active and inactive members (based on payment of the membership fee) +- Total disk quota +- Date of registration + +This information gives us the ability to lock inactive user accounts, and keep statistics of our userbase. +Locking inactive accounts is useful for security reasons and to incentivize actually paying the fee. + +## Requirements + +There are two recommended ways of running the application: + +### Docker-compose / dev +The development environment is available through docker, as described below. +This includes a database and all required runtimes. +Requires: + +- Docker +- Docker-compose + +## Production / native +As the application is a normal PHP application, you will need: + +- PHP (Tested with 8.2) +- A web server (e.g. nginx) +- php-pgsql +- A PostgreSQL server + + +## Development + +The project is written in basic PHP, without external frameworks. +The dev environment is built with docker-compose, and contains a PostgreSQL database, php-fpm and nginx. + +Start it by running `docker-compose up -d`. + +You can then view the page at [localhost:3010](http://localhost:3010). + +The database can be administered with [adminer](http://localhost:3010/adminer-4.8.1.php). + +Some docs should probably be written in /docs + +Some tools should probably be made in /utils + +## TODO: + +- Start Web interface +- Input methods: + - [ ] Web form + - [ ] Import from bank statements (PDF/CSV) + - [ ] Import from GNUCash +- Make admin system + - Save list of admins or integrate with www.pvv.ntnu.no/admin/ + - Auth / login, one of: + - SSO with idp.pvv.ntnu.no + - PAM / Unix auth +- Integrate with PVV "New user" scripts +- Allow normal users to update their own contact info? +- ... + + diff --git a/dev.sh b/dev.sh new file mode 100755 index 0000000..7c3e05c --- /dev/null +++ b/dev.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# test -e composer.phar || curl -O https://getcomposer.org/composer.phar + +php -S ${DOCKER_HOST:-[::1]}:${DOCKER_PORT:-1080} -d error_reporting=E_ALL -d display_errors=1 -t src/ diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..611af42 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,32 @@ +############################################################################### +# Generated on phpdocker.io # +############################################################################### +version: '3.1' +services: + postgres: + image: 'postgres:11.1-alpine' + working_dir: /docker + volumes: + - ./phpdocker/postgres-data:/var/lib/postgresql/data + environment: + - POSTGRES_USER=pvvmdb + - POSTGRES_PASSWORD=pvvmdb + - POSTGRES_DB=pvvmdb + ports: + - '3014:5432' + + webserver: + image: 'nginx:alpine' + working_dir: /docker + volumes: + - ./src:/docker + - ./phpdocker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf + ports: + - '3010:80' + + php-fpm: + build: phpdocker/php-fpm + working_dir: /docker + volumes: + - ./src:/docker + - ./phpdocker/php-fpm/php-ini-overrides.ini:/etc/php/8.2/fpm/conf.d/99-overrides.ini diff --git a/docs/database_model.png b/docs/database_model.png new file mode 100644 index 0000000000000000000000000000000000000000..c69695736c8875a9e7abcf38cc299530b10ac515 GIT binary patch literal 31465 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 P__4Z*mhyAO(^virAZ?Bq literal 0 HcmV?d00001 diff --git a/docs/database_model.puml b/docs/database_model.puml new file mode 100644 index 0000000..6c55761 --- /dev/null +++ b/docs/database_model.puml @@ -0,0 +1,50 @@ +@startuml + +entity "User" as user { + id: int + -- + * username: string + name: string + phone: string + external_email: string + first_created: date + locked: boolean + comment: string +} + +entity "Purchase" as purchase { + id: int + -- + * _user_id_: int + * amount_paid: number + * date: date + comment: string +} + +entity "Disk Purchase" as disk_purchase { + -- + * size_mb: int +} + +entity "Membership Type" as membership_type { + id: int + -- + * name: string + * price: number + comment: string +} + +entity "Membership Purchase" as membership_purchase { + -- + * _membership_type_id_: int +} + + +user "1..1" -- "0..*" purchase : makes +disk_purchase --|> purchase +membership_purchase --|> purchase + +membership_type "1..1" -- "0..*" membership_purchase : is + + +@enduml diff --git a/phpdocker/README.html b/phpdocker/README.html new file mode 100644 index 0000000..c796f74 --- /dev/null +++ b/phpdocker/README.html @@ -0,0 +1,208 @@ + + + PHPDocker.io Readme + + + + + + +
+
+
+

PHPDocker.io generated environment

+ +

Add to your project

+ +

Simply, unzip the file into your project, this will create docker-compose.yml on the root of your project and a folder +named phpdocker containing nginx and php-fpm config for it.

+ +

Ensure the webserver config on phpdocker/nginx/nginx.conf is correct for your project. PHPDocker.io will have +customised this file according to the front controller location relative to the docker-compose file you chose on the +generator (by default public/index.php).

+ +

Note: you may place the files elsewhere in your project. Make sure you modify the locations for the php-fpm dockerfile, +the php.ini overrides and nginx config on docker-compose.yml if you do so.

+ +

How to run

+ +

Dependencies:

+ + + +

Once you're done, simply cd to your project and run docker-compose up -d. This will initialise and start all the +containers, then leave them running in the background.

+ +

Services exposed outside your environment

+ +

You can access your application via localhost. Mailhog and nginx both respond to any hostname, in case you want to +add your own hostname on your /etc/hosts

+ + + + + + + + + + + + + + + + + + +
ServiceAddress outside containers
Webserverlocalhost:3010
PostgreSQLhost: localhost; port: 3014
+ +

Hosts within your environment

+ +

You'll need to configure your application to use any services you enabled:

+ + + + + + + + + + + + + + + + + + + + + +
ServiceHostnamePort number
php-fpmphp-fpm9000
Postgrespostgres5432 (default)
+ +

Docker compose cheatsheet

+ +

Note: you need to cd first to where your docker-compose.yml file lives.

+ +
    +
  • Start containers in the background: docker-compose up -d
  • +
  • Start containers on the foreground: docker-compose up. You will see a stream of logs for every container running. +ctrl+c stops containers.
  • +
  • Stop containers: docker-compose stop
  • +
  • Kill containers: docker-compose kill
  • +
  • View container logs: docker-compose logs for all containers or docker-compose logs SERVICE_NAME for the logs of +all containers in SERVICE_NAME.
  • +
  • Execute command inside of container: docker-compose exec SERVICE_NAME COMMAND where COMMAND is whatever you want +to run. Examples: + +
      +
    • Shell into the PHP container, docker-compose exec php-fpm bash
    • +
    • Run symfony console, docker-compose exec php-fpm bin/console
    • +
    • Open a mysql shell, docker-compose exec mysql mysql -uroot -pCHOSEN_ROOT_PASSWORD
    • +
  • +
+ +

Application file permissions

+ +

As in all server environments, your application needs the correct file permissions to work properly. You can change the +files throughout the container, so you won't care if the user exists or has the same ID on your host.

+ +

docker-compose exec php-fpm chown -R www-data:www-data /docker/public

+ +

Recommendations

+ +

It's hard to avoid file permission issues when fiddling about with containers due to the fact that, from your OS point +of view, any files created within the container are owned by the process that runs the docker engine (this is usually +root). Different OS will also have different problems, for instance you can run stuff in containers +using docker exec -it -u $(id -u):$(id -g) CONTAINER_NAME COMMAND to force your current user ID into the process, but +this will only work if your host OS is Linux, not mac. Follow a couple of simple rules and save yourself a world of +hurt.

+ +
    +
  • Run composer outside of the php container, as doing so would install all your dependencies owned by root within your +vendor folder.
  • +
  • Run commands (ie Symfony's console, or Laravel's artisan) straight inside of your container. You can easily open a +shell as described above and do your thing from there.
  • +
+ +

Simple basic Xdebug configuration with integration to PHPStorm

+ +

Xdebug 2

+ +

To configure Xdebug 2 you need add these lines in php-fpm/php-ini-overrides.ini:

+ +

For linux:

+ +
xdebug.remote_enable = 1
+xdebug.remote_connect_back = 1
+xdebug.remote_autostart = 1
+
+ +

For macOS and Windows:

+ +
xdebug.remote_enable = 1
+xdebug.remote_host = host.docker.internal
+xdebug.remote_autostart = 1
+
+ +

Xdebug 3

+ +

To configure Xdebug 3 you need add these lines in php-fpm/php-ini-overrides.ini:

+ +

For linux:

+ +
xdebug.mode = debug
+xdebug.remote_connect_back = true
+xdebug.start_with_request = yes
+
+ +

For macOS and Windows:

+ +
xdebug.mode = debug
+xdebug.remote_host = host.docker.internal
+xdebug.start_with_request = yes
+
+ +

Add the section “environment” to the php-fpm service in docker-compose.yml:

+ +
environment:
+  PHP_IDE_CONFIG: "serverName=Docker"
+
+ +

Create a server configuration in PHPStorm:

+ +
    +
  • In PHPStorm open Preferences | Languages & Frameworks | PHP | Servers
  • +
  • Add new server
  • +
  • The “Name” field should be the same as the parameter “serverName” value in “environment” in docker-compose.yml (i.e. * +Docker* in the example above)
  • +
  • A value of the "port" field should be the same as first port(before a colon) in "webserver" service in +docker-compose.yml
  • +
  • Select "Use path mappings" and set mappings between a path to your project on a host system and the Docker container.
  • +
  • Finally, add “Xdebug helper” extension in your browser, set breakpoints and start debugging
  • +
+
+
+
+ + + + diff --git a/phpdocker/README.md b/phpdocker/README.md new file mode 100644 index 0000000..349fac0 --- /dev/null +++ b/phpdocker/README.md @@ -0,0 +1,144 @@ +PHPDocker.io generated environment +================================== + +# Add to your project # + +Simply, unzip the file into your project, this will create `docker-compose.yml` on the root of your project and a folder +named `phpdocker` containing nginx and php-fpm config for it. + +Ensure the webserver config on `phpdocker/nginx/nginx.conf` is correct for your project. PHPDocker.io will have +customised this file according to the front controller location relative to the docker-compose file you chose on the +generator (by default `public/index.php`). + +Note: you may place the files elsewhere in your project. Make sure you modify the locations for the php-fpm dockerfile, +the php.ini overrides and nginx config on `docker-compose.yml` if you do so. + +# How to run # + +Dependencies: + +* docker. See [https://docs.docker.com/engine/installation](https://docs.docker.com/engine/installation) +* docker-compose. See [docs.docker.com/compose/install](https://docs.docker.com/compose/install/) + +Once you're done, simply `cd` to your project and run `docker-compose up -d`. This will initialise and start all the +containers, then leave them running in the background. + +## Services exposed outside your environment ## + +You can access your application via **`localhost`**. Mailhog and nginx both respond to any hostname, in case you want to +add your own hostname on your `/etc/hosts` + +Service|Address outside containers +-------|-------------------------- +Webserver|[localhost:3010](http://localhost:3010) +PostgreSQL|**host:** `localhost`; **port:** `3014` + +## Hosts within your environment ## + +You'll need to configure your application to use any services you enabled: + +Service|Hostname|Port number +------|---------|----------- +php-fpm|php-fpm|9000 +Postgres|postgres|5432 (default) + +# Docker compose cheatsheet # + +**Note:** you need to cd first to where your docker-compose.yml file lives. + +* Start containers in the background: `docker-compose up -d` +* Start containers on the foreground: `docker-compose up`. You will see a stream of logs for every container running. + ctrl+c stops containers. +* Stop containers: `docker-compose stop` +* Kill containers: `docker-compose kill` +* View container logs: `docker-compose logs` for all containers or `docker-compose logs SERVICE_NAME` for the logs of + all containers in `SERVICE_NAME`. +* Execute command inside of container: `docker-compose exec SERVICE_NAME COMMAND` where `COMMAND` is whatever you want + to run. Examples: + * Shell into the PHP container, `docker-compose exec php-fpm bash` + * Run symfony console, `docker-compose exec php-fpm bin/console` + * Open a mysql shell, `docker-compose exec mysql mysql -uroot -pCHOSEN_ROOT_PASSWORD` + +# Application file permissions # + +As in all server environments, your application needs the correct file permissions to work properly. You can change the +files throughout the container, so you won't care if the user exists or has the same ID on your host. + +`docker-compose exec php-fpm chown -R www-data:www-data /docker/public` + +# Recommendations # + +It's hard to avoid file permission issues when fiddling about with containers due to the fact that, from your OS point +of view, any files created within the container are owned by the process that runs the docker engine (this is usually +root). Different OS will also have different problems, for instance you can run stuff in containers +using `docker exec -it -u $(id -u):$(id -g) CONTAINER_NAME COMMAND` to force your current user ID into the process, but +this will only work if your host OS is Linux, not mac. Follow a couple of simple rules and save yourself a world of +hurt. + +* Run composer outside of the php container, as doing so would install all your dependencies owned by `root` within your + vendor folder. +* Run commands (ie Symfony's console, or Laravel's artisan) straight inside of your container. You can easily open a + shell as described above and do your thing from there. + +# Simple basic Xdebug configuration with integration to PHPStorm + +## Xdebug 2 + +To configure **Xdebug 2** you need add these lines in php-fpm/php-ini-overrides.ini: + +### For linux: + +``` +xdebug.remote_enable = 1 +xdebug.remote_connect_back = 1 +xdebug.remote_autostart = 1 +``` + +### For macOS and Windows: + +``` +xdebug.remote_enable = 1 +xdebug.remote_host = host.docker.internal +xdebug.remote_autostart = 1 +``` + +## Xdebug 3 + +To configure **Xdebug 3** you need add these lines in php-fpm/php-ini-overrides.ini: + +### For linux: + +``` +xdebug.mode = debug +xdebug.remote_connect_back = true +xdebug.start_with_request = yes +``` + +### For macOS and Windows: + +``` +xdebug.mode = debug +xdebug.remote_host = host.docker.internal +xdebug.start_with_request = yes +``` + +## Add the section “environment” to the php-fpm service in docker-compose.yml: + +``` +environment: + PHP_IDE_CONFIG: "serverName=Docker" +``` + +### Create a server configuration in PHPStorm: + +* In PHPStorm open Preferences | Languages & Frameworks | PHP | Servers +* Add new server +* The “Name” field should be the same as the parameter “serverName” value in “environment” in docker-compose.yml (i.e. * + Docker* in the example above) +* A value of the "port" field should be the same as first port(before a colon) in "webserver" service in + docker-compose.yml +* Select "Use path mappings" and set mappings between a path to your project on a host system and the Docker container. +* Finally, add “Xdebug helper” extension in your browser, set breakpoints and start debugging + + + diff --git a/phpdocker/nginx/nginx.conf b/phpdocker/nginx/nginx.conf new file mode 100644 index 0000000..a852d4c --- /dev/null +++ b/phpdocker/nginx/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 80 default; + + client_max_body_size 108M; + + access_log /var/log/nginx/application.access.log; + + root /docker/.; + index index.php; + + # try to serve file directly, fallback to index.php + location / { + try_files $uri /index.php$is_args$args; + } + + if (!-e $request_filename) { + rewrite ^.*$ /index.php last; + } + + location ~ \.php$ { + fastcgi_pass php-fpm:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PHP_VALUE "error_log=/var/log/nginx/application_php_errors.log"; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + include fastcgi_params; + } +} diff --git a/phpdocker/php-fpm/Dockerfile b/phpdocker/php-fpm/Dockerfile new file mode 100644 index 0000000..7c4649d --- /dev/null +++ b/phpdocker/php-fpm/Dockerfile @@ -0,0 +1,8 @@ +FROM phpdockerio/php:8.2-fpm +WORKDIR "/docker" + +RUN apt-get update; \ + apt-get -y --no-install-recommends install \ + php8.2-pgsql; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/* diff --git a/phpdocker/php-fpm/php-ini-overrides.ini b/phpdocker/php-fpm/php-ini-overrides.ini new file mode 100644 index 0000000..850ae11 --- /dev/null +++ b/phpdocker/php-fpm/php-ini-overrides.ini @@ -0,0 +1,2 @@ +upload_max_filesize = 100M +post_max_size = 108M diff --git a/src/adminer-4.8.1.php b/src/adminer-4.8.1.php new file mode 100644 index 0000000..762aaaf --- /dev/null +++ b/src/adminer-4.8.1.php @@ -0,0 +1,1795 @@ +$W){unset($tg[$z][$he]);if(is_array($W)){$tg[$z][stripslashes($he)]=$W;$tg[]=&$tg[$z][stripslashes($he)];}else$tg[$z][stripslashes($he)]=($ad?$W:stripslashes($W));}}}}function +bracket_escape($v,$Na=false){static$ui=array(':'=>':1',']'=>':2','['=>':3','"'=>':4');return +strtr($v,($Na?array_flip($ui):$ui));}function +min_version($Zi,$De="",$h=null){global$g;if(!$h)$h=$g;$nh=$h->server_info;if($De&&preg_match('~([\d.]+)-MariaDB~',$nh,$C)){$nh=$C[1];$Zi=$De;}return(version_compare($nh,$Zi)>=0);}function +charset($g){return(min_version("5.5.3",0,$g)?"utf8mb4":"utf8");}function +script($yh,$ti="\n"){return"$yh$ti";}function +script_src($Ni){return"\n";}function +nonce(){return' nonce="'.get_nonce().'"';}function +target_blank(){return' target="_blank" rel="noreferrer noopener"';}function +h($P){return +str_replace("\0","�",htmlspecialchars($P,ENT_QUOTES,'utf-8'));}function +nl_br($P){return +str_replace("\n","
",$P);}function +checkbox($D,$Y,$db,$me="",$uf="",$hb="",$ne=""){$I="".($uf?script("qsl('input').onclick = function () { $uf };",""):"");return($me!=""||$hb?"$I".h($me)."":$I);}function +optionlist($_f,$gh=null,$Ri=false){$I="";foreach($_f +as$he=>$W){$Af=array($he=>$W);if(is_array($W)){$I.='';$Af=$W;}foreach($Af +as$z=>$X)$I.=''.h($X);if(is_array($W))$I.='';}return$I;}function +html_select($D,$_f,$Y="",$tf=true,$ne=""){if($tf)return"".(is_string($tf)?script("qsl('select').onchange = function () { $tf };",""):"");$I="";foreach($_f +as$z=>$X)$I.="";return$I;}function +select_input($Ia,$_f,$Y="",$tf="",$fg=""){$Yh=($_f?"select":"input");return"<$Yh$Ia".($_f?">