commit 6caad6ee97f9de8f43b0d725abc4395905b619d8 Author: Filip Langer Date: Thu Apr 16 12:58:54 2026 +0200 feat: Initial commit. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..9d79be5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.git +php/_logs/*.log +php/Dockerfile \ No newline at end of file diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml new file mode 100644 index 0000000..f26b49b --- /dev/null +++ b/.gitea/workflows/build.yaml @@ -0,0 +1,43 @@ +name: Build and Push Docker Image + +on: + push: + brancher: + - main + +jobs: + changes: + runs-on: ubuntu-latest + outputs: + apache: ${{ steps.filter.outputs.apache }} + php: ${{ steps.filter.outputs.php }} + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 2 + + - name: Detect changed folders + id: filter + run: | + echo "apache=$(git diff --name-only HEAD~1 HEAD | grep '^apache/' | wc -l | grep -q '^0$' && echo false || echo true)" >> $GITHUB_OUTPUT + echo "php=$(git diff --name-only HEAD~1 HEAD | grep '^php/' | wc -l | grep -q '^0$' && echo false || echo true)" >> $GITHUB_OUTPUT + + build-apache: + needs: changes + if: ${{ needs.changes.outputs.apache == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ secrets.REGISTRY_URL }} -u ${{ secrets.REGISTRY_USER }} --password-stdin + - run: docker build -t ${{ secrets.REGISTRY_URL }}/${{ gitea.repository }}/apache:latest ./apache + - run: docker push ${{ secrets.REGISTRY_URL }}/${{ gitea.repository }}/apache:latest + + build-php: + needs: changes + if: ${{ needs.changes.outputs.php == 'true' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login ${{ secrets.REGISTRY_URL }} -u ${{ secrets.REGISTRY_USER }} --password-stdin + - run: docker build -t ${{ secrets.REGISTRY_URL }}/${{ gitea.repository }}/php:latest ./php + - run: docker push ${{ secrets.REGISTRY_URL }}/${{ gitea.repository }}/php:latest \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2fd0e4c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +app/_logs/*.log \ No newline at end of file diff --git a/apache/Dockerfile b/apache/Dockerfile new file mode 100644 index 0000000..b93a096 --- /dev/null +++ b/apache/Dockerfile @@ -0,0 +1,18 @@ +FROM httpd:2.4 + +RUN apt update && \ + apt install -y curl + +COPY apache/apache_vhost.conf /usr/local/apache2/conf/sites-enabled/sslguard.skyger.cz.conf +COPY apache/my-modules.conf /usr/local/apache2/conf/extra/my-modules.conf + +RUN echo "Include conf/extra/my-modules.conf" \ + >> /usr/local/apache2/conf/httpd.conf +RUN echo "Include conf/sites-enabled/sslguard.skyger.cz.conf" \ + >> /usr/local/apache2/conf/httpd.conf +RUN echo "RemoteIPHeader X-Forwarded-For" \ + >> /usr/local/apache2/conf/httpd.conf +RUN echo "RemoteIPTrustedProxy 172.16.0.0/12" \ + >> /usr/local/apache2/conf/httpd.conf + +#RUN apachectl configtest && apachectl graceful \ No newline at end of file diff --git a/apache/apache_vhost.conf b/apache/apache_vhost.conf new file mode 100644 index 0000000..b465fc8 --- /dev/null +++ b/apache/apache_vhost.conf @@ -0,0 +1,34 @@ + + ServerName sslguard.skyger.cz + ServerAlias www.sslguard.skyger.cz + ServerAdmin admin@sslguard.skyger.cz + DocumentRoot /domains/sslguard.skyger.cz/www/ + + ErrorLog /domains/sslguard.skyger.cz/_logs/www/http-error.log + CustomLog /domains/sslguard.skyger.cz/_logs/www/http-access.log combined + + + Options -Indexes + AllowOverride All + Require all granted + + + ProxyPassMatch "^/(.*\.php(/.*)?)$" "fcgi://php-fpm:9000/domains/sslguard.skyger.cz/www/$1" + + + + ServerName api.sslguard.skyger.cz + ServerAdmin admin@sslguard.skyger.cz + DocumentRoot /domains/sslguard.skyger.cz/api/ + + ErrorLog /domains/sslguard.skyger.cz/_logs/api/http-error.log + CustomLog /domains/sslguard.skyger.cz/_logs/api/http-access.log combined + + + Options -Indexes + AllowOverride All + Require all granted + + + ProxyPassMatch "^/(.*\.php(/.*)?)$" "fcgi://php-fpm:9000/domains/sslguard.skyger.cz/api/$1" + \ No newline at end of file diff --git a/apache/my-modules.conf b/apache/my-modules.conf new file mode 100644 index 0000000..09136e5 --- /dev/null +++ b/apache/my-modules.conf @@ -0,0 +1,4 @@ +LoadModule proxy_module modules/mod_proxy.so +LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so +LoadModule rewrite_module modules/mod_rewrite.so +LoadModule remoteip_module modules/mod_remoteip.so \ No newline at end of file diff --git a/app/_logs/api/.gitkeep b/app/_logs/api/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/app/_logs/php/.gitkeep b/app/_logs/php/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/app/_logs/www/.gitkeep b/app/_logs/www/.gitkeep new file mode 100755 index 0000000..e69de29 diff --git a/app/api/.gitkeep b/app/api/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/app/Bootstrap.php b/app/app/Bootstrap.php new file mode 100644 index 0000000..0299745 --- /dev/null +++ b/app/app/Bootstrap.php @@ -0,0 +1,38 @@ +setDebugMode(true); + $configurator->enableTracy($appDir . '/_logs'); + + $configurator->setTimeZone('Europe/Prague'); + $configurator->setTempDirectory($appDir . '/tmp'); + + $configurator->createRobotLoader() + ->addDirectory(__DIR__) + ->register(); + + $configurator->addConfig($appDir . '/app/config/common.neon'); + $configurator->addConfig($appDir . '/app/config/services.neon'); + $configurator->addConfig($appDir . '/app/config/local.neon'); + + // use ENV variables from PHP + /*$configurator->addDynamicParameters([ + 'env' => getenv(), + ]);*/ + + return $configurator; + } +} \ No newline at end of file diff --git a/app/app/Presenters/BasePresenter.php b/app/app/Presenters/BasePresenter.php new file mode 100644 index 0000000..6ddea2b --- /dev/null +++ b/app/app/Presenters/BasePresenter.php @@ -0,0 +1,34 @@ +constants = $constants; + + $this->cache = new Cache($cacheStorage); + } + + protected function getConstant(string $name) { + return $this->constants[$name] ?? null; + } +} \ No newline at end of file diff --git a/app/app/Presenters/ErrorPresenter.php b/app/app/Presenters/ErrorPresenter.php new file mode 100644 index 0000000..32ee213 --- /dev/null +++ b/app/app/Presenters/ErrorPresenter.php @@ -0,0 +1,13 @@ +template->message = "An error occurred."; + } +} diff --git a/app/app/Presenters/HomepagePresenter.php b/app/app/Presenters/HomepagePresenter.php new file mode 100644 index 0000000..7ef7caa --- /dev/null +++ b/app/app/Presenters/HomepagePresenter.php @@ -0,0 +1,24 @@ +constants = $constants; + }*/ + + // default content + public function actionDefault(): void + { + } +} \ No newline at end of file diff --git a/app/app/Presenters/SharedTrait.php b/app/app/Presenters/SharedTrait.php new file mode 100644 index 0000000..d2efded --- /dev/null +++ b/app/app/Presenters/SharedTrait.php @@ -0,0 +1,46 @@ +getRenderer(); + $renderer->wrappers['controls']['container'] = null; + $renderer->wrappers['pair']['container'] = 'div class="row form-group align-items-center justify-content-center py-2 login"'; + $renderer->wrappers['pair']['.error'] = 'has-danger'; + $renderer->wrappers['control']['container'] = 'div class=col-sm-9'; + $renderer->wrappers['label']['container'] = 'div class="col-sm-3 col-form-label"'; + $renderer->wrappers['control']['description'] = 'span class=form-text'; + $renderer->wrappers['control']['errorcontainer'] = 'span class=form-control-feedback'; + $renderer->wrappers['control']['.error'] = 'is-invalid'; + + foreach ($form->getControls() as $control) { + $type = $control->getOption('type'); + if ($type === 'button') { + $control->getControlPrototype()->addClass(empty($usedPrimary) ? 'btn btn-primary' : 'btn btn-secondary'); + $usedPrimary = true; + + } elseif (in_array($type, ['text', 'textarea', 'select'], true)) { + $control->getControlPrototype()->addClass('form-control'); + + } elseif ($type === 'file') { + $control->getControlPrototype()->addClass('form-control-file'); + + } elseif (in_array($type, ['checkbox', 'radio'], true)) { + if ($control instanceof Nette\Forms\Controls\Checkbox) { + $control->getLabelPrototype()->addClass('form-check-label'); + } else { + $control->getItemLabelPrototype()->addClass('form-check-label'); + } + $control->getControlPrototype()->addClass('form-check-input'); + $control->getSeparatorPrototype()->setName('div')->addClass('form-check'); + } + } + } +} \ No newline at end of file diff --git a/app/app/Presenters/templates/@layout.latte b/app/app/Presenters/templates/@layout.latte new file mode 100644 index 0000000..e69de29 diff --git a/app/app/Presenters/templates/Homepage/default.latte b/app/app/Presenters/templates/Homepage/default.latte new file mode 100644 index 0000000..d9605cb --- /dev/null +++ b/app/app/Presenters/templates/Homepage/default.latte @@ -0,0 +1 @@ +HELLO \ No newline at end of file diff --git a/app/app/Router/RouterFactory.php b/app/app/Router/RouterFactory.php new file mode 100644 index 0000000..4a2412e --- /dev/null +++ b/app/app/Router/RouterFactory.php @@ -0,0 +1,21 @@ +addRoute('/[/]', 'Homepage:default'); + return $router; + } +} \ No newline at end of file diff --git a/app/app/config/common.neon b/app/app/config/common.neon new file mode 100644 index 0000000..e4b5065 --- /dev/null +++ b/app/app/config/common.neon @@ -0,0 +1,13 @@ +parameters: +application: + errorPresenter: Error + mapping: + *: App\*Module\Presenters\*Presenter +session: + expiration: 60 minutes + debugger: true +di: + export: + parameters: no + tags: no + \ No newline at end of file diff --git a/app/app/config/local.neon b/app/app/config/local.neon new file mode 100644 index 0000000..115e405 --- /dev/null +++ b/app/app/config/local.neon @@ -0,0 +1,11 @@ +parameters: + constants: + DEBUG_MODE: true + DEBUG_FILE: 'debug.log' + ERROR_FILE: 'error.log' + LOGS_DIR: %appDir%/../_logs/ + +database: + dsn: 'mysql:host=db;dbname=${DB_DB};port=3306' + user: 'sslguard-skyger-cz' + password: 'gsjlqfdaldw08qpihrsax' diff --git a/app/app/config/services.neon b/app/app/config/services.neon new file mode 100644 index 0000000..a99e439 --- /dev/null +++ b/app/app/config/services.neon @@ -0,0 +1,20 @@ +services: + redis.client: + factory: Kdyby\Redis\RedisClient('keydb', 6379) + + cache.storage: + factory: Kdyby\Redis\RedisStorage(@redis.client) + + basePresenter: + class: App\Presenters\BasePresenter + arguments: + - %constants% + - @cache.storage + + homepagePresenter: + class: App\Presenters\HomepagePresenter + arguments: + - %constants% + - @cache.storage + + - App\Router\RouterFactory::createRouter \ No newline at end of file diff --git a/app/www/.gitkeep b/app/www/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/www/.htaccess b/app/www/.htaccess new file mode 100644 index 0000000..9d9decd --- /dev/null +++ b/app/www/.htaccess @@ -0,0 +1,37 @@ +# Apache configuration file (see https://httpd.apache.org/docs/current/mod/quickreference.html) +Require all granted + +# disable directory listing + + Options -Indexes + + +# enable cool URL + + RewriteEngine On + #RewriteCond %{HTTPS} off + #RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + + # RewriteBase / + + # prevents files starting with dot to be viewed by browser + RewriteCond %{REQUEST_FILENAME} -f + RewriteRule /\.|^\.(?!well-known/) - [F] + + # front controller + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule !\.(pdf|js|mjs|ico|gif|jpg|jpeg|png|webp|svg|css|rar|zip|7z|tar\.gz|map|eot|ttf|otf|woff|woff2)$ index.php [L] + + +# enable gzip compression + + + AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/json application/xml image/svg+xml + + + +# enable index files + + DirectoryIndex index.php index.html + \ No newline at end of file diff --git a/app/www/index.php b/app/www/index.php new file mode 100644 index 0000000..0f28ca1 --- /dev/null +++ b/app/www/index.php @@ -0,0 +1,10 @@ +createContainer(); +$application = $container->getByType(Nette\Application\Application::class); +$application->run(); \ No newline at end of file diff --git a/app/www/robots.txt b/app/www/robots.txt new file mode 100644 index 0000000..77470cb --- /dev/null +++ b/app/www/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3b15dd8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,90 @@ +version: '3' +services: +# certbot: +# image: certbot/certbot:latest +# container_name: certbot +# volumes: +# - ./certbot/certs:/etc/letsencrypt +# command: +# - certonly +# - --standalone +# - --email=admin@sslguard.skyger.cz +# - --agree-tos +# - --no-eff-email +# - -d +# - sslguard.skyger.cz +# - -d +# - api.sslguard.skyger.cz +# ports: +# - "80:80" + db: + image: mariadb:lts + container_name: mariadb + restart: always + environment: + MYSQL_ROOT_PASSWORD: akmf5owtb4yf6r9pbmigr + MYSQL_DATABASE: gsjlqfdaldw08qpihrsax + MYSQL_USER: sslguard-skyger-cz + MYSQL_PASSWORD: gsjlqfdaldw08qpihrsax + volumes: + - ./data/mysql:/var/lib/mysql + ports: + - "127.0.0.1:3306:3306" + - "127.0.0.1:33061:33061" + networks: + - nette-project + + haproxy: + image: haproxy:lts + container_name: haproxy + volumes: + - ./haproxy/certs:/usr/local/etc/haproxy/certs/:ro + - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + ports: + - "80:80" + - "443:443" + - "9000:9000" + depends_on: + - db + - apache + networks: + - nette-project + + php-fpm: + build: + context: . + dockerfile: php/Dockerfile + container_name: php + volumes: + - ./app:/domains/sslguard.skyger.cz/ + ports: + - "127.0.0.1:9001:9000" + networks: + - nette-project + + apache: + build: + context: . + dockerfile: apache/Dockerfile + container_name: apache + ports: + - "127.0.0.1:81:80" + depends_on: + - php-fpm + volumes: + - ./app:/domains/sslguard.skyger.cz/ + networks: + - nette-project + + keydb: + image: eqalpha/keydb:latest + container_name: keydb + ports: + - "127.0.0.1:6379:6379" + command: keydb-server --appendonly yes + networks: + - nette-project + +networks: + nette-project: + driver: bridge diff --git a/php/Dockerfile b/php/Dockerfile new file mode 100644 index 0000000..1414356 --- /dev/null +++ b/php/Dockerfile @@ -0,0 +1,16 @@ +FROM php:8.4-fpm + +RUN apt-get update && \ + apt-get install -y git zip unzip libzip-dev && \ + docker-php-ext-install zip pdo pdo_mysql + +RUN pecl install redis && \ + docker-php-ext-enable redis + +RUN rm /usr/local/etc/php-fpm.d/*.conf +COPY php/fpm_pool.conf /usr/local/etc/php-fpm.d/sslguard.skyger.cz.conf + +# startup.sh +COPY php/startup.sh /usr/local/bin/startup.sh +RUN chmod +x /usr/local/bin/startup.sh +ENTRYPOINT ["/usr/local/bin/startup.sh"] \ No newline at end of file diff --git a/php/fpm_pool.conf b/php/fpm_pool.conf new file mode 100644 index 0000000..ea48766 --- /dev/null +++ b/php/fpm_pool.conf @@ -0,0 +1,20 @@ +[sslguard.skyger.cz] +user = www-data +group = www-data +listen = 9000 + +listen.owner = www-data +listen.group = www-data +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 + +php_admin_value[open_basedir] = /domains/sslguard.skyger.cz:/data/domains/sslguard.skyger.cz:/tmp +php_admin_value[upload_tmp_dir] = /tmp +php_admin_value[session.save_path] = /tmp + +php_admin_flag[display_errors] = off +php_admin_value[error_log] = /domains/sslguard.skyger.cz/_logs/php/php-error.log +php_admin_flag[log_errors] = on diff --git a/php/startup.sh b/php/startup.sh new file mode 100644 index 0000000..33a9b3e --- /dev/null +++ b/php/startup.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [[ -d "/domains/sslguard.skyger.cz/vendor/" ]]; +then + exec php-fpm -F; +else + # composer install + php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \ + php composer-setup.php && \ + php -r "unlink('composer-setup.php');" && \ + mv composer.phar /usr/local/bin/composer; + + cd /domains/sslguard.skyger.cz/ && \ + composer update; + + exec php-fpm -F; +fi; + +exit;