diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..46fb3ddf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +.composer/ +.editorconfig +.env* +.git/ +.gitattributes +.github/ +.gitignore +.idea/ +.sops.yaml + +codestyle.php +docker-compose* +eslint.config.js +Taskfile.yml +node_modules/ +phpstan* +public/build/ +public/hot +readme.md +renovate.json5 +vendor/ + +/environment +!/environment/.deployment/scripts/post-deploy-actions.sh +!/environment/.docker +!/environment/dev diff --git a/.env.example b/.env.example index 121994b8..8a0ebf77 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,11 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_TIMEZONE=Europe/Warsaw -APP_URL=http://lmt.blumilk.localhost +APP_URL=https://lmt.blumilk.local.env + +APP_DOCKER_HOST_NAME=lmt.blumilk.local.env +MAILPIT_DOCKER_HOST_NAME=lmt-mailpit.blumilk.local.env +VITE_DEV_SERVER_DOCKER_HOST_NAME=lmt-vite-dev-server.blumilk.local.env APP_LOCALE=pl APP_FALLBACK_LOCALE=en @@ -21,7 +25,7 @@ LOG_DEPRECATIONS_CHANNEL=null LOG_LEVEL=debug DB_CONNECTION=pgsql -DB_HOST=lmt-db-dev +DB_HOST=lmt-db-local DB_PORT=5432 DB_DATABASE=lmt DB_USERNAME=lmt @@ -41,7 +45,7 @@ CACHE_STORE=database CACHE_PREFIX= MAIL_MAILER=smtp -MAIL_HOST=lmt-dev-mailpit-container +MAIL_HOST=lmt-mailpit-local MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null @@ -51,6 +55,7 @@ REGISTRATION_NOTIFICATION_EMAIL="hello@example.com" VITE_APP_NAME="${APP_NAME}" +SOPS_AGE_DEV_SECRET_KEY= SOPS_AGE_PROD_SECRET_KEY= DOCKER_APP_HOST_PORT=8051 @@ -58,3 +63,5 @@ DOCKER_DATABASE_HOST_PORT=8055 DOCKER_MAILPIT_DASHBOARD_HOST_PORT=8052 DOCKER_HOST_USER_ID=1000 +DOCKER_INSTALL_XDEBUG=false +ALLOWED_EMAIL_DOMAIN=@example.com diff --git a/.github/workflows/deploy-to-dev.yml b/.github/workflows/deploy-to-dev.yml index beb372a2..c8fafa75 100644 --- a/.github/workflows/deploy-to-dev.yml +++ b/.github/workflows/deploy-to-dev.yml @@ -18,6 +18,7 @@ jobs: DOCKER_REGISTRY_PROJECT_NAME: internal-public DOCKER_REGISTRY_REPO_NAME: lmt TARGET_DIR_ON_SERVER: /blumilk/deployments/dev/projects + ENVIRONMENT: dev steps: - name: set branch name run: echo "BRANCH_NAME=$GITHUB_REF_NAME" >> $GITHUB_ENV @@ -27,3 +28,141 @@ jobs: with: fetch-depth: 0 ref: ${{ env.BRANCH_NAME }} + + - name: sync with main branch + run: | + git config user.name "GitHub Actions Bot" + git config user.email "<>" + git merge --no-commit --no-ff origin/main + + - name: set deployment project version + run: echo "DEPLOYMENT_PROJECT_VERSION=$(bash ./environment/.deployment/scripts/version.sh --long)" >> $GITHUB_ENV + + - name: set docker image name + run: echo "DOCKER_IMAGE_NAME=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REGISTRY_PROJECT_NAME }}/${{ env.DOCKER_REGISTRY_REPO_NAME }}" >> $GITHUB_ENV + + - name: fetch server secrets + uses: Infisical/secrets-action@v1.0.8 + with: + domain: https://infisical.blumilk.pl + client-id: ${{ secrets.INFISICAL_MACHINE_IDENTITY_GHA_BOT_CLIENT_ID }} + client-secret: ${{ secrets.INFISICAL_MACHINE_IDENTITY_GHA_BOT_CLIENT_SECRET }} + project-slug: blumilk-infra-pv-ih + env-slug: infra + secret-path: /servers/ovh/ns31445530 + export-type: env + recursive: true + include-imports: true + + - name: set up Docker Buildx + uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + + - name: login to Docker Registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + with: + registry: ${{ env.DOCKER_REGISTRY }} + username: ${{ env.DOCKER_REGISTRY_USER_NAME }} + password: ${{ env.HARBOR_ROBOT_BLUMILKBOT_TOKEN }} # masked secret fetched from Infisical + + - name: set docker app database image name + run: echo "DOCKER_APP_DATABASE_IMAGE_NAME=${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_REGISTRY_PROJECT_NAME }}/${{ env.DOCKER_REGISTRY_REPO_NAME }}-postgres" >> $GITHUB_ENV + + + - name: Docker meta for app database + id: meta-app-database + uses: docker/metadata-action@v5.5.1 + with: + images: | + ${{ env.DOCKER_APP_DATABASE_IMAGE_NAME }} + tags: | + type=raw,value=dev + context: workflow + + - name: build and push app database image + uses: docker/build-push-action@v5.1.0 + with: + context: . + file: ./environment/.docker/postgres/Dockerfile + labels: ${{ steps.meta-app-database.outputs.labels }} + tags: ${{ steps.meta-app-database.outputs.tags }} + push: true + cache-from: type=gha, ref=${{ env.DOCKER_APP_DATABASE_IMAGE_NAME }}-dev-build-cache + cache-to: type=gha, ref=${{ env.DOCKER_APP_DATABASE_IMAGE_NAME }}-dev-build-cache, mode=max + + - name: Docker meta for app + id: meta + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + images: ${{ env.DOCKER_IMAGE_NAME }} + tags: | + type=raw,value=dev + context: workflow + + - name: build and push app image + uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5.4.0 + with: + context: . + file: ./environment/.docker/app/Dockerfile + target: production + build-args: | + DEPLOYMENT_PROJECT_VERSION_ARG=${{ env.DEPLOYMENT_PROJECT_VERSION }} + ENVIRONMENT=${{ env.ENVIRONMENT }} + labels: ${{ steps.meta.outputs.labels }} + tags: ${{ steps.meta.outputs.tags }} + push: true + cache-from: type=gha, ref=${{ env.DOCKER_IMAGE_NAME }}-dev-build-cache + cache-to: type=gha, ref=${{ env.DOCKER_IMAGE_NAME }}-dev-build-cache, mode=max + + - name: set deployment path on server + run: echo "DEPLOYMENT_PATH_ON_SERVER=${{ env.TARGET_DIR_ON_SERVER }}/${{ env.DOCKER_REGISTRY_PROJECT_NAME }}/${{ env.DOCKER_REGISTRY_REPO_NAME }}" >> $GITHUB_ENV + + - name: copy files via ssh + uses: appleboy/scp-action@917f8b81dfc1ccd331fef9e2d61bdc6c8be94634 # v0.1.7 + with: + timeout: 10s + command_timeout: 10m + host: ${{ env.SERVER_OVH_NS31445530_IP }} # masked secret fetched from Infisical + port: ${{ env.SERVER_OVH_NS31445530_SSH_PORT }} # masked secret fetched from Infisical + username: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_NAME }} # masked secret fetched from Infisical + key: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_SSH_PRIVATE_KEY }} # masked secret fetched from Infisical + passphrase: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_SSH_PRIVATE_KEY_PASSPHRASE }} # masked secret fetched from Infisical + source: "./environment/.deployment/dev/*,./environment/.deployment/Taskfile.yml" + target: ${{ env.DEPLOYMENT_PATH_ON_SERVER }} + rm: true + + + - name: fetch project deployment secrets + uses: Infisical/secrets-action@v1.0.8 + with: + domain: https://infisical.blumilk.pl + client-id: ${{ secrets.INFISICAL_MACHINE_IDENTITY_GHA_BOT_CLIENT_ID }} + client-secret: ${{ secrets.INFISICAL_MACHINE_IDENTITY_GHA_BOT_CLIENT_SECRET }} + project-slug: lmt-d-hr8 + env-slug: dev + secret-path: /deployment + export-type: env + recursive: false + include-imports: false + + - name: run deployment script over ssh + uses: appleboy/ssh-action@v1.2.2 + with: + timeout: 10s + command_timeout: 10m + host: ${{ env.SERVER_OVH_NS31445530_IP }} # masked secret fetched from Infisical + port: ${{ env.SERVER_OVH_NS31445530_SSH_PORT }} # masked secret fetched from Infisical + username: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_NAME }} # masked secret fetched from Infisical + key: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_SSH_PRIVATE_KEY }} # masked secret fetched from Infisical + passphrase: ${{ env.SERVER_OVH_NS31445530_BLUMILKBOT_USER_SSH_PRIVATE_KEY_PASSPHRASE }} # masked secret fetched from Infisical + # masked secrets from Infisical: HARBOR_ROBOT_BLUMILKBOT_TOKEN, SOPS_AGE_DEV_SECRET_KEY + script: | + cd ${{ env.DEPLOYMENT_PATH_ON_SERVER }}/environment/.deployment/ + mv Taskfile.yml ${{ env.ENVIRONMENT }}/ + cd ${{ env.ENVIRONMENT }}/ + echo ${{ env.HARBOR_ROBOT_BLUMILKBOT_TOKEN }} | docker login ${{ env.DOCKER_REGISTRY }} --username ${{ env.DOCKER_REGISTRY_USER_NAME }} --password-stdin + export SOPS_AGE_KEY=${{ env.SOPS_AGE_DEV_SECRET_KEY }} + export ENVIRONMENT=${{ env.ENVIRONMENT }} + task deploy + docker images --filter dangling=true | grep "${{ env.DOCKER_IMAGE_NAME }}" | awk '{print $3}'| xargs --no-run-if-empty docker rmi + docker images --filter dangling=true | grep ${{ env.DOCKER_APP_DATABASE_IMAGE_NAME }} | awk '{print $3}'| xargs --no-run-if-empty docker rmi + docker logout ${{ env.DOCKER_REGISTRY }} diff --git a/.github/workflows/deploy-to-prod-init.yml b/.github/workflows/deploy-to-prod-init.yml deleted file mode 100644 index eb7111dc..00000000 --- a/.github/workflows/deploy-to-prod-init.yml +++ /dev/null @@ -1,87 +0,0 @@ -name: Deploy to production - -concurrency: - group: deploy-prod - cancel-in-progress: false - -on: - release: - types: [ published ] - workflow_dispatch: - -jobs: - deploy: - environment: production - runs-on: ubuntu-22.04 - name: Deploy to production - env: - DOCKER_REGISTRY: ghcr.io - REPO_NAME: ${{ github.event.repository.name }} - REPO_OWNER: ${{ github.repository_owner }} - TARGET_DIR_ON_SERVER: /blumilk/production - steps: - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - - name: set deployment project version - run: echo "DEPLOYMENT_PROJECT_VERSION=$(bash ./environment/prod/deployment/scripts/version.sh --long)" >> $GITHUB_ENV - - - name: set up Docker Buildx - uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0 - - - name: login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3 - with: - registry: ${{ env.DOCKER_REGISTRY }} - username: ${{ github.actor }} - password: ${{ github.token }} - - - name: Docker meta - id: meta - uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1 - with: - images: ${{ env.DOCKER_REGISTRY }}/${{ github.repository_owner }}/${{ env.REPO_NAME }} - tags: | - type=raw,value=latest - context: workflow - - - name: build and push image - uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0 - with: - context: . - file: ./environment/prod/app/Dockerfile - build-args: DEPLOYMENT_PROJECT_VERSION_ARG=${{ env.DEPLOYMENT_PROJECT_VERSION }} - push: true - labels: ${{ steps.meta.outputs.labels }} - tags: ${{ steps.meta.outputs.tags }} - cache-from: type=gha, ref=${{ env.DOCKER_REGISTRY }}/${{ github.repository_owner }}/${{ env.REPO_NAME }}-prod-build-cache - cache-to: type=gha, ref=${{ env.DOCKER_REGISTRY }}/${{ github.repository_owner }}/${{ env.REPO_NAME }}-prod-build-cache, mode=max - - - name: copy files via ssh - uses: appleboy/scp-action@917f8b81dfc1ccd331fef9e2d61bdc6c8be94634 # v0.1.7 - with: - timeout: 10s - command_timeout: 10m - host: ${{ secrets.VPS_OVH_BF7EC892_HOST }} - port: ${{ secrets.VPS_OVH_BF7EC892_PORT }} - username: ${{ secrets.VPS_OVH_BF7EC892_USERNAME }} - key: ${{ secrets.VPS_OVH_BF7EC892_SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.VPS_OVH_BF7EC892_SSH_PRIVATE_KEY_PASSPHRASE }} - source: "./environment/prod/deployment/prod/*,./environment/prod/deployment/scripts/*" - target: ${{ env.TARGET_DIR_ON_SERVER }}/${{ env.REPO_NAME }} - rm: true - - - uses: appleboy/ssh-action@7eaf76671a0d7eec5d98ee897acda4f968735a17 # v1.2.0 - with: - timeout: 10s - command_timeout: 10m - host: ${{ secrets.VPS_OVH_BF7EC892_HOST }} - port: ${{ secrets.VPS_OVH_BF7EC892_PORT }} - username: ${{ secrets.VPS_OVH_BF7EC892_USERNAME }} - key: ${{ secrets.VPS_OVH_BF7EC892_SSH_PRIVATE_KEY }} - passphrase: ${{ secrets.VPS_OVH_BF7EC892_SSH_PRIVATE_KEY_PASSPHRASE }} - script_stop: true - script: | - cd ${{ env.TARGET_DIR_ON_SERVER }}/${{ env.REPO_NAME }}/environment/prod/deployment/prod - make prod-deploy SOPS_AGE_KEY=${{ secrets.SOPS_AGE_PROD_SECRET_KEY }} - docker images --filter dangling=true | grep "${{ env.DOCKER_REGISTRY }}/${{ env.REPO_OWNER }}/${{ env.REPO_NAME }}" | awk '{print $3}'| xargs --no-run-if-empty docker rmi diff --git a/.gitignore b/.gitignore index d5c76a03..7322f145 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,8 @@ .idea .vscode /vendor/ +*.decrypted .env -.env.prod.secrets.decrypted /cache/* !/cache/.gitkeep node_modules/ diff --git a/.sops.yaml b/.sops.yaml index 3d63c84f..7ccac98d 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,5 +1,10 @@ creation_rules: + - name: dev + path_regex: \.env\.dev\.secrets.*$ + age: >- + age1m3ruqh8ldq9wy9w5rpyj2wed0nc0n4ejda2lau2009w2rlvu7qjqacfqp2 + - name: prod path_regex: \.env\.prod\.secrets.*$ age: >- diff --git a/Makefile b/Makefile deleted file mode 100644 index cd3f5c80..00000000 --- a/Makefile +++ /dev/null @@ -1,59 +0,0 @@ -include .env - -SHELL := /bin/bash - -DOCKER_COMPOSE_FILE = docker-compose.yml -DOCKER_COMPOSE_APP_CONTAINER = app - -CURRENT_USER_ID = $(shell id --user) -CURRENT_USER_GROUP_ID = $(shell id --group) -CURRENT_DIR = $(shell pwd) -PHP_SERVICE_NAME=app - -DATABASE_USERNAME=lmt -TEST_DATABASE_NAME=lmt-test - -build: - docker compose build --pull - -run: - docker compose up -d - -dev: - docker compose exec --user ${CURRENT_USER_ID} ${PHP_SERVICE_NAME} npm run dev - -stop: - docker compose stop - -restart: stop run - -lint: - docker compose exec --user ${CURRENT_USER_ID} ${PHP_SERVICE_NAME} npm run lint - -lintf: - docker compose exec --user ${CURRENT_USER_ID} ${PHP_SERVICE_NAME} npm run lintf - -shell: - @docker compose exec --user ${CURRENT_USER_ID} ${PHP_SERVICE_NAME} bash - -encrypt-prod-secrets: - @$(MAKE) encrypt-secrets SECRETS_ENV=prod - -decrypt-prod-secrets: - @$(MAKE) decrypt-secrets SECRETS_ENV=prod AGE_SECRET_KEY=${SOPS_AGE_PROD_SECRET_KEY} - -decrypt-secrets: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" --env SOPS_AGE_KEY=${AGE_SECRET_KEY} ${DOCKER_COMPOSE_APP_CONTAINER} \ - bash -c "echo 'Decrypting ${SECRETS_ENV} secrets' \ - && cd ./environment/prod/deployment/${SECRETS_ENV} \ - && sops --decrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets.decrypted .env.${SECRETS_ENV}.secrets \ - && echo 'Done'" - -encrypt-secrets: - @docker compose --file ${DOCKER_COMPOSE_FILE} exec --user "${CURRENT_USER_ID}:${CURRENT_USER_GROUP_ID}" ${DOCKER_COMPOSE_APP_CONTAINER} \ - bash -c "echo 'Encrypting ${SECRETS_ENV} secrets' \ - && cd ./environment/prod/deployment/${SECRETS_ENV} \ - && sops --encrypt --input-type=dotenv --output-type=dotenv --output .env.${SECRETS_ENV}.secrets .env.${SECRETS_ENV}.secrets.decrypted \ - && echo 'Done'" - -.PHONY: build run tailwind stop restart shell encrypt-prod-secrets decrypt-prod-secrets decrypt-secrets encrypt-secrets dev diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 00000000..ed3099b8 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,138 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +env: + COMPOSE_DOCKER_CLI_BUILD: 1 + DOCKER_BUILDKIT: 1 + CURRENT_USER_ID: + sh: id --user + DOCKER_COMPOSE_APP_CONTAINER: app + DOCKER_COMPOSE_DATABASE_CONTAINER: database + +dotenv: + - .env + +includes: + secops: ./environment/secops-Taskfile.yml + devops: ./environment/devops-Taskfile.yml + +tasks: + default: + desc: "List all available tasks" + cmds: + - task --list-all + + init-project: + desc: "Initialize the project" + aliases: [init] + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer install + - task: _set-app-key + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan migrate --seed + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan storage:link + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm install + - task: create-test-database + + dev: + desc: "Run Vite local dev server" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm run dev + + run-containers: + desc: "Run containers" + aliases: [run] + deps: [build] + preconditions: + - sh: test -f .env + msg: "Please create .env for app." + cmds: + - cmd: docker compose up --detach + + stop-containers: + desc: "Stop containers" + aliases: [stop] + cmds: + - cmd: docker compose stop + + restart-containers: + desc: "Restart containers" + aliases: [restart] + cmds: + - task: stop-containers + - task: run-containers + + build-containers: + desc: "Build containers" + aliases: [build] + cmds: + - cmd: docker compose build --pull + + shell-app: + desc: "Enter app shell" + aliases: [shell] + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER bash + + shell-app-root: + desc: "Enter app shell as root" + aliases: [shell-root] + deps: [run] + cmds: + - cmd: docker compose exec --user root $DOCKER_COMPOSE_APP_CONTAINER bash + + test: + desc: "Run tests" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer test + + fix: + desc: "Run fixers" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer csf + + analyse: + desc: "Run composer analyse" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER composer analyse + + type-check: + desc: "Run npm type-check" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER npm run type-check + + queue: + desc: "Run queue:work" + deps: [run] + cmds: + - cmd: docker compose exec --user $CURRENT_USER_ID $DOCKER_COMPOSE_APP_CONTAINER php artisan queue:work + + create-test-database: + desc: "Create test database" + deps: [run] + aliases: [create-test-db] + vars: + TEST_DB_NAME: lmt-test + cmds: + - cmd: docker compose exec $DOCKER_COMPOSE_DATABASE_CONTAINER bash -c "createdb --username=$DB_USERNAME {{ .TEST_DB_NAME }} && echo 'Created database for tests ({{ .TEST_DB_NAME }}).'" + ignore_error: true + + _set-app-key: + desc: "Create APP_KEY if not set" + internal: true + cmds: + - | + APP_KEY_VALUE=$(grep APP_KEY .env | cut --delimiter '=' --fields 2-) + + if [ -z "${APP_KEY_VALUE}" ]; then + echo "APP_KEY is not set. Creating:" + docker compose exec $DOCKER_COMPOSE_APP_CONTAINER php artisan key:generate + fi diff --git a/app/Models/User.php b/app/Models/User.php index 904a5220..2e1319eb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,6 +5,8 @@ namespace Blumilksoftware\Lmt\Models; use database\factories\UserFactory; +use Filament\Models\Contracts\FilamentUser; +use Filament\Panel; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Support\Carbon; @@ -18,7 +20,7 @@ * @property Carbon $created_at * @property Carbon $updated_at */ -class User extends Authenticatable +class User extends Authenticatable implements FilamentUser { /** @use HasFactory */ use HasFactory; @@ -29,6 +31,11 @@ class User extends Authenticatable "remember_token", ]; + public function canAccessPanel(Panel $panel): bool + { + return str_ends_with($this->email, config("app.allowed_email_domain")); + } + protected function casts(): array { return [ diff --git a/bootstrap/app.php b/bootstrap/app.php index d5b09ce6..83e485ba 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -12,6 +12,7 @@ health: "/up", ) ->withMiddleware(function (Middleware $middleware): void { + $middleware->trustProxies(at: "*"); }) ->withExceptions(function (Exceptions $exceptions): void { })->create(); diff --git a/config/app.php b/config/app.php index 1c531b92..9e4ac8a9 100644 --- a/config/app.php +++ b/config/app.php @@ -22,4 +22,5 @@ "driver" => env("APP_MAINTENANCE_DRIVER", "file"), "store" => env("APP_MAINTENANCE_STORE", "database"), ], + "allowed_email_domain" => env("ALLOWED_EMAIL_DOMAIN", "@example.com"), ]; diff --git a/docker-compose.yml b/docker-compose.yml index f145cfe6..0c2a54b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ networks: - lmt-dev: + lmt-local: driver: bridge - traefik-proxy-blumilk-local: + traefik-proxy-blumilk-local-environment: external: true volumes: @@ -10,42 +10,52 @@ volumes: services: app: + container_name: lmt-app-local build: - context: ./environment/dev/app - dockerfile: Dockerfile + context: . + dockerfile: environment/.docker/app/Dockerfile + target: local args: + - INSTALL_XDEBUG=${DOCKER_INSTALL_XDEBUG:-false} - USER_ID=${DOCKER_HOST_USER_ID:-1000} labels: - "traefik.enable=true" - - "traefik.blumilk.environment=true" + - "traefik.blumilk.local.environment=true" # HTTP - - "traefik.http.routers.lmt-http-router.rule=Host(`lmt.blumilk.localhost`)" + - "traefik.http.routers.lmt-http-router.rule=Host(`${APP_DOCKER_HOST_NAME}`)" - "traefik.http.routers.lmt-http-router.entrypoints=web" - # HTTP to HTTPS redirect - # - "traefik.http.routers.lmt-http-router.middlewares=https-redirect@file" + - "traefik.http.routers.lmt-http-router.service=lmt-app" # HTTPS - - "traefik.http.routers.lmt-https-router.rule=Host(`lmt.blumilk.localhost`)" + - "traefik.http.routers.lmt-https-router.rule=Host(`${APP_DOCKER_HOST_NAME}`)" - "traefik.http.routers.lmt-https-router.entrypoints=websecure" - "traefik.http.routers.lmt-https-router.tls=true" - container_name: lmt-dev-app-container + - "traefik.http.routers.lmt-https-router.service=lmt-app" + # APP LOADBALANCER + - "traefik.http.services.lmt-app.loadbalancer.server.port=80" + # VITE DEV SERVER + - "traefik.http.routers.lmt-vite-dev-server-https-router.rule=Host(`${VITE_DEV_SERVER_DOCKER_HOST_NAME}`)" + - "traefik.http.routers.lmt-vite-dev-server-https-router.entrypoints=websecure" + - "traefik.http.routers.lmt-vite-dev-server-https-router.tls=true" + - "traefik.http.routers.lmt-vite-dev-server-https-router.service=lmt-vite-dev-server" + - "traefik.http.services.lmt-vite-dev-server.loadbalancer.server.port=5173" working_dir: /application volumes: - - ./environment/dev/app/nginx.conf:/etc/nginx/nginx.conf:ro - - ./environment/dev/app/php.ini:/usr/local/etc/php/conf.d/zzz-overrides.ini:ro - - ./environment/dev/app/php-fpm.conf:/usr/local/etc/php-fpm.d/zzz-overrides.conf:ro - - ./environment/dev/app/supervisord.conf:/etc/supervisor/custom-supervisord.conf:ro + - ./environment/local/app/php.ini:/usr/local/etc/php/conf.d/zzz-overrides.ini:ro + - ./environment/local/app/php-fpm.conf:/usr/local/etc/php-fpm.d/zzz-overrides.conf:ro + - ./environment/local/app/supervisord.conf:/etc/supervisor/custom-supervisord.conf:ro - .:/application ports: - ${DOCKER_APP_HOST_PORT:-8051}:80 networks: - - lmt-dev - - traefik-proxy-blumilk-local + - lmt-local + - traefik-proxy-blumilk-local-environment restart: unless-stopped database: build: - context: environment/dev/postgres - container_name: lmt-db-dev + context: . + dockerfile: environment/.docker/postgres/Dockerfile + container_name: lmt-db-local environment: - POSTGRES_USER=${DB_USERNAME} - POSTGRES_PASSWORD=${DB_PASSWORD} @@ -61,30 +71,28 @@ services: volumes: - lmt-postgres-data:/var/lib/postgresql/data networks: - - lmt-dev + - lmt-local restart: unless-stopped mailpit: image: axllent/mailpit:v1.22.0@sha256:1ebd4123a99eb6b74799d13d4811f9fc5c3b70df26e1ae536af82987022fcb48 - container_name: lmt-dev-mailpit-container + container_name: lmt-mailpit-local ports: - ${EXTERNAL_MAILPIT_DASHBOARD_PORT:-8052}:8025 - ${EXTERNAL_MAILPIT_SMTP_PORT:-8053}:1025 networks: - - lmt-dev - - traefik-proxy-blumilk-local + - lmt-local + - traefik-proxy-blumilk-local-environment labels: - "traefik.enable=true" - - "traefik.blumilk.environment=true" + - "traefik.blumilk.local.environment=true" # HTTP - - "traefik.http.routers.lmt-mailpit-http-router.rule=Host(`lmt-mailpit.blumilk.localhost`)" + - "traefik.http.routers.lmt-mailpit-http-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" - "traefik.http.routers.lmt-mailpit-http-router.entrypoints=web" - # HTTP to HTTPS redirect - # - "traefik.http.routers.lmt-mailpit-http-router.middlewares=https-redirect@file" # HTTPS - - "traefik.http.routers.lmt-mailpit-https-router.rule=Host(`lmt-mailpit.blumilk.localhost`)" + - "traefik.http.routers.lmt-mailpit-https-router.rule=Host(`${MAILPIT_DOCKER_HOST_NAME}`)" - "traefik.http.routers.lmt-mailpit-https-router.entrypoints=websecure" - "traefik.http.routers.lmt-mailpit-https-router.tls=true" - # LOADBALANCER MAILHOG PORT + # LOADBALANCER MAILPIT PORT - "traefik.http.services.lmt-mailpit.loadbalancer.server.port=8025" restart: unless-stopped diff --git a/environment/.deployment/Taskfile.yml b/environment/.deployment/Taskfile.yml new file mode 100644 index 00000000..5a06a322 --- /dev/null +++ b/environment/.deployment/Taskfile.yml @@ -0,0 +1,38 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +tasks: + deploy: + deps: [create-deployment-file, decrypt-secrets] + vars: + DOCKER_COMPOSE_FILE: docker-compose.{{ .ENVIRONMENT }}.yml + DOCKER_COMPOSE_APP_SERVICE: lmt-{{ .ENVIRONMENT }}-app + POST_DEPLOY_SCRIPT_FILE: post-deploy-actions.sh + cmds: + - cmd: docker compose --file {{ .DOCKER_COMPOSE_FILE }} pull + - cmd: docker compose --file {{ .DOCKER_COMPOSE_FILE }} up --detach + - cmd: sleep 5 + - cmd: echo "Run app post deploy actions" + - cmd: docker compose --file {{ .DOCKER_COMPOSE_FILE }} exec --user www-data --workdir /application/environment/.deployment/scripts {{ .DOCKER_COMPOSE_APP_SERVICE }} bash {{ .POST_DEPLOY_SCRIPT_FILE }} + + decrypt-secrets: + requires: + vars: [SOPS_AGE_KEY] + vars: + DOTENV_FILE: .env.{{ .ENVIRONMENT }} + SECRETS_FILE: .env.{{ .ENVIRONMENT }}.secrets + cmds: + - cmd: echo "Moving {{ .DOTENV_FILE }} to .env" + - cmd: mv {{ .DOTENV_FILE }} .env + - cmd: echo "Decrypting secrets from {{ .SECRETS_FILE }}" + - cmd: sops --decrypt --input-type=dotenv --output-type=dotenv {{ .SECRETS_FILE }} >> .env + - cmd: echo "Done" + + create-deployment-file: + vars: + DATETIME: + sh: TZ=Europe/Warsaw date --rfc-3339=seconds + cmds: + - cmd: echo "DEPLOY_DATE='{{ .DATETIME }}'" >> .deployment diff --git a/environment/.deployment/dev/.env.dev b/environment/.deployment/dev/.env.dev new file mode 100644 index 00000000..aa49e578 --- /dev/null +++ b/environment/.deployment/dev/.env.dev @@ -0,0 +1,45 @@ +APP_NAME="LMT DEV" +APP_ENV=dev +APP_DEBUG=false +APP_TIMEZONE=Europe/Warsaw +LMT_HOST_NAME=dev.lmt.blumilk.pl +APP_URL=https://${LMT_HOST_NAME} +TELESCOPE_ENABLED=false +ALLOWED_EMAIL_DOMAIN=@example.com + +COMPOSE_PROJECT_NAME=lmt-dev + +APP_LOCALE=pl +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=pl_PL + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=pgsql +DB_HOST=lmt-dev-database +DB_PORT=5432 + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +CACHE_PREFIX= + +MAIL_MAILER=smtp +MAIL_HOST=smtp.gmail.com +MAIL_PORT=587 +MAIL_ENCRYPTION=tls +MAIL_FROM_ADDRESS="no-reply@blumilk.pl" +MAIL_FROM_NAME="${APP_NAME}" diff --git a/environment/.deployment/dev/.env.dev.secrets b/environment/.deployment/dev/.env.dev.secrets new file mode 100644 index 00000000..5818a98f --- /dev/null +++ b/environment/.deployment/dev/.env.dev.secrets @@ -0,0 +1,13 @@ +APP_KEY=ENC[AES256_GCM,data:UtEkAzHrRNWleKdfyLcZH/Fg3GtDRHu2upqNK7wNp/AUUhG/xqyaJi/s6mCi/KWCWhwl,iv:X0mT1/mmHyWXP4mctpOtaTq3XW4DD7WAMXB1ck1er5w=,tag:cF3MVhltaGQETnOXqxjKlg==,type:str] +MAIL_USERNAME=ENC[AES256_GCM,data:94ATmtsFdOwUTwH8rJhLDFsb3w==,iv:quEHA+EYS7OEsJ8Ksy0R1xFN/J1GpLZcNvgn5Cr5eKA=,tag:dBBX/WKqT5gN1NWtK+VqLQ==,type:str] +MAIL_PASSWORD=ENC[AES256_GCM,data:u+eiFebZdU9+yGNRTxAA8uWS3qBK,iv:SBbZOVqaQl6vWag4OmOo8ayofA73M0tzAfJgD1Nbf5A=,tag:ozyqTqx/7psn9tC7MVCZgQ==,type:str] +REGISTRATION_NOTIFICATION_EMAIL=ENC[AES256_GCM,data:xStQHqmFhGbwKgxtdC9b+qPcBnCOtjtK5759xuwj,iv:apIGrp6eMyWFlmSKPR+6w/eLa9+3TQOUCavFkhnpO10=,tag:Nt2l5y2RGlAdG2UxQ4vCHA==,type:str] +DB_DATABASE=ENC[AES256_GCM,data:RfxjaQkVsg==,iv:DGFZqkw6HcxQmZYpdz+Y7zBnkmbpGKCzdKQLp1CVY70=,tag:oUkQxKD2Z79r5mNM0o3srA==,type:str] +DB_USERNAME=ENC[AES256_GCM,data:XsOvcS1E8g==,iv:t1BpO6HE+uRFJiimRirt02owxmLU2TZoCHLcVlruIec=,tag:ynSL/pnWF2C0inqhSf1uwg==,type:str] +DB_PASSWORD=ENC[AES256_GCM,data:ZQBCxZ81bVHEVgK3eD16jDjqrge5CanaXg==,iv:IoLgy3nh9vkxL78bzSU0NbT6TLFlVGTuwoOu6DU4Y8A=,tag:hVGmv1tVr5FXrqEWkVQBvQ==,type:str] +sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aS9wcnlhczU5OGZrZkpW\nVWhWbFlvazlseWRCUitOUGRBZWhzZmdWMlRzCm0zQTRqa2NDYUZNUFpleUdubkVi\nNGFuQnQ1K2U4SlFmTkVvUlBvT3l3VTQKLS0tIHRXZTlwQ2VnbDVGWGhpQlR5WW1V\nVFMrOFZqa0xPMmxjbnpLQk5ZV2t5V1UK9DlzfHVO4HW6Ni5mB+DlTedxT4A6ZhiD\nG4F+ZtXmojGkxFJTN3oIHxy+yspHRMJb0ZiUFszDSo8TU82w/CzpXA==\n-----END AGE ENCRYPTED FILE-----\n +sops_age__list_0__map_recipient=age1m3ruqh8ldq9wy9w5rpyj2wed0nc0n4ejda2lau2009w2rlvu7qjqacfqp2 +sops_lastmodified=2025-04-24T10:46:53Z +sops_mac=ENC[AES256_GCM,data:VzwqATi2EENXeEBm16txHAzagtrjyTihEuHC63iZTyVwvQG5hYIJ81YXpjTI+hKZSxFcXKTe/QVElGuVKJhBDuoCGKmsDLB1OyKHPwAVk/VMlBVDEcVJxQVCuON5S8wf52dF2WN3+4CHaefQQcE3UymRmeVNyVrg/myDjjNHnZU=,iv:S0/BMCXZCWtrQ/FmuRZojOHpgkSRexkV22C5d4ipA9k=,tag:5H8k/kbAZJUzdUZDL57x+w==,type:str] +sops_unencrypted_suffix=_unencrypted +sops_version=3.9.4 diff --git a/environment/.deployment/dev/docker-compose.dev.yml b/environment/.deployment/dev/docker-compose.dev.yml new file mode 100644 index 00000000..f5b85848 --- /dev/null +++ b/environment/.deployment/dev/docker-compose.dev.yml @@ -0,0 +1,76 @@ +networks: + traefik-proxy: + external: true + lmt-dev: + driver: bridge + +volumes: + lmt-dev-postgres-data: + name: lmt-dev-postgres-data + +services: + lmt-dev-app: + image: registry.blumilk.pl/internal-public/lmt:dev + container_name: lmt-dev-app-container + pull_policy: always + logging: + driver: "json-file" + options: + max-size: "50m" + max-file: "5" + deploy: + mode: replicated + replicas: 1 + resources: + limits: + memory: 1GB + labels: + - "traefik.enable=true" + - "traefik.http.routers.lmt-dev-app.rule=Host(`${LMT_HOST_NAME}`)" + - "traefik.http.routers.lmt-dev-app.entrypoints=websecure" + - "traefik.http.routers.lmt-dev-app.tls=true" + - "traefik.http.routers.lmt-dev-app.tls.certresolver=lets-encrypt-resolver" + - "traefik.http.routers.lmt-dev-app.middlewares=no-index-robots-response-header@file,security-headers@file" + - "io.portainer.accesscontrol.teams=LMT" + working_dir: /application + volumes: + - ./.env:/application/.env:ro + networks: + - lmt-dev + - traefik-proxy + restart: unless-stopped + env_file: + - .deployment + + lmt-dev-database: + image: registry.blumilk.pl/internal-public/lmt-postgres:dev + pull_policy: always + container_name: lmt-dev-database-container + deploy: + mode: replicated + replicas: 1 + resources: + limits: + memory: 512M + logging: + driver: "json-file" + options: + max-size: "50m" + max-file: "5" + labels: + - "io.portainer.accesscontrol.teams=LMT" + environment: + - POSTGRES_USER=${DB_USERNAME:? variable DB_USERNAME not set} + - POSTGRES_PASSWORD=${DB_PASSWORD:? variable DB_PASSWORD not set} + - POSTGRES_DB=${DB_DATABASE:? variable DB_DATABASE not set} + - PGDATA=/var/lib/postgresql/data + healthcheck: + test: [ "CMD-SHELL", "pg_isready --dbname ${DB_DATABASE} --username ${DB_USERNAME}" ] + interval: 3s + timeout: 3s + retries: 5 + volumes: + - lmt-dev-postgres-data:/var/lib/postgresql/data + networks: + - lmt-dev + restart: unless-stopped diff --git a/environment/.deployment/scripts/post-deploy-actions.sh b/environment/.deployment/scripts/post-deploy-actions.sh new file mode 100644 index 00000000..7721d5cc --- /dev/null +++ b/environment/.deployment/scripts/post-deploy-actions.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# -e is for "automatic error detection", tell shell to abort any time an error occurred +set -e + +ARTISAN_PATH="/application/artisan" + +php ${ARTISAN_PATH} storage:link && \ +php ${ARTISAN_PATH} migrate --force && \ +php ${ARTISAN_PATH} optimize && \ +php ${ARTISAN_PATH} filament:optimize diff --git a/environment/prod/deployment/scripts/version.sh b/environment/.deployment/scripts/version.sh similarity index 100% rename from environment/prod/deployment/scripts/version.sh rename to environment/.deployment/scripts/version.sh diff --git a/environment/.docker/app/Dockerfile b/environment/.docker/app/Dockerfile new file mode 100644 index 00000000..9c3a79de --- /dev/null +++ b/environment/.docker/app/Dockerfile @@ -0,0 +1,153 @@ +FROM registry.blumilk.pl/internal-public/secops-tools-bin:v1.0.0 AS secops-tools-bin +FROM composer:2.8.6 AS composer +FROM node:23.7.0-bookworm-slim AS node + +FROM php:8.3.10-fpm-bookworm AS base + +COPY --from=composer /usr/bin/composer /usr/bin/composer + +# For other versions check: http://nginx.org/packages/mainline/debian/pool/nginx/n/nginx/ +ARG NGINX_VERSION="1.25.4-1~bookworm" + +RUN apt-get update \ + && apt-get install --assume-yes gpg \ + && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmour --output /etc/apt/trusted.gpg.d/apt.nginx.org.gpg > /dev/null \ + && echo "deb https://nginx.org/packages/mainline/debian bookworm nginx" | tee /etc/apt/sources.list.d/nginx.list \ + && apt-get update && apt-get install --assume-yes \ + nginx=${NGINX_VERSION} \ + libzip-dev \ + libjpeg-dev \ + libpng-dev \ + libwebp-dev \ + libpq-dev \ + libicu-dev \ + supervisor \ + cron \ + exif \ + && docker-php-ext-configure \ + gd --with-jpeg --with-webp \ + && docker-php-ext-install \ + zip \ + gd \ + intl \ + exif \ + pdo_pgsql + +COPY ./environment/.docker/app/entrypoint.sh /entrypoint.sh +COPY ./environment/.docker/app/nginx.conf /etc/nginx/nginx.conf + +WORKDIR /application + +ENTRYPOINT ["/entrypoint.sh"] + +FROM base AS local + +COPY --from=secops-tools-bin /usr/local/bin/age /usr/local/bin/age-keygen /usr/local/bin/sops /usr/local/bin/ + +RUN mv "${PHP_INI_DIR}/php.ini-development" "${PHP_INI_DIR}/php.ini" + +ARG USER_NAME=host-user +ARG USER_ID=1000 +ARG PHP_FPM_GROUP=www-data + +RUN adduser \ + --disabled-password \ + --uid ${USER_ID} \ + ${USER_NAME} \ + && usermod \ + --append \ + --groups \ + ${PHP_FPM_GROUP} \ + ${USER_NAME} + +# Add node, npm, npx binaries +COPY --from=node --chown=${USER_NAME}:root /usr/local/lib/node_modules /usr/local/lib/node_modules +COPY --from=node --chown=${USER_NAME}:root /usr/local/bin/node /usr/local/bin/node +RUN ln --symbolic /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npm \ + && ln --symbolic /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ + && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npx + +# renovate: datasource=github-tags depName=xdebug/xdebug +ARG XDEBUG_VERSION=3.4.2 +ARG INSTALL_XDEBUG=false + +RUN if [ ${INSTALL_XDEBUG} = true ]; then \ + pecl install xdebug-${XDEBUG_VERSION} \ + && docker-php-ext-enable xdebug \ +;fi + +FROM composer AS vendor-build + +WORKDIR /app_composer_dependencies + +COPY composer.json composer.lock ./ + +RUN composer install \ + --no-interaction \ + --no-plugins \ + --no-scripts \ + --no-dev \ + --prefer-dist \ + --ignore-platform-reqs + +FROM node AS frontend-build + +ARG ENVIRONMENT + +WORKDIR /app_frontend_dependencies + +# last position is target directory +COPY package.json \ + package-lock.json \ + vite.config.js \ + ./ + +COPY ./environment/${ENVIRONMENT}/app/vite.${ENVIRONMENT}.env .env + +RUN npm clean-install + +COPY resources/js ./resources/js/ +COPY resources/css ./resources/css/ +COPY resources/views ./resources/views/ + +COPY --from=vendor-build /app_composer_dependencies/vendor/livewire ./vendor/livewire + + +RUN npm run build + +FROM base AS production + +ARG ENVIRONMENT + +RUN docker-php-ext-install \ + opcache + +RUN mv "${PHP_INI_DIR}/php.ini-production" "${PHP_INI_DIR}/php.ini" + +COPY ./environment/${ENVIRONMENT}/app/php.ini ${PHP_INI_DIR}/conf.d/zzz-overrides-php.ini +COPY ./environment/${ENVIRONMENT}/app/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-overrides.conf +COPY ./environment/${ENVIRONMENT}/app/supervisord.conf /etc/supervisor/custom-supervisord.conf + +# Copy Frontend build +COPY --chown=www-data:www-data --from=frontend-build /app_frontend_dependencies/public/build ./public/build/ + +# Copy Composer dependencies +COPY --chown=www-data:www-data --from=vendor-build /app_composer_dependencies/vendor ./vendor/ + +# Copy App files +COPY --chown=www-data:www-data . . + +# Remove unnecessary files +RUN rm --recursive --force \ + vite.config.js \ + .dockerignore \ + environment/.docker \ + environment/dev + +RUN composer dump-autoload --optimize + +EXPOSE 80 + +ARG PROJECT_BUILD_VERSION_ARG +ENV PROJECT_BUILD_VERSION=${PROJECT_BUILD_VERSION_ARG} diff --git a/environment/dev/app/entrypoint.sh b/environment/.docker/app/entrypoint.sh similarity index 100% rename from environment/dev/app/entrypoint.sh rename to environment/.docker/app/entrypoint.sh diff --git a/environment/dev/app/nginx.conf b/environment/.docker/app/nginx.conf similarity index 100% rename from environment/dev/app/nginx.conf rename to environment/.docker/app/nginx.conf diff --git a/environment/dev/postgres/Dockerfile b/environment/.docker/postgres/Dockerfile similarity index 86% rename from environment/dev/postgres/Dockerfile rename to environment/.docker/postgres/Dockerfile index 7c859e81..512ec714 100644 --- a/environment/dev/postgres/Dockerfile +++ b/environment/.docker/postgres/Dockerfile @@ -4,4 +4,4 @@ RUN localedef -i pl_PL -c -f UTF-8 -A /usr/share/locale/locale.alias pl_PL.UTF-8 ENV LANG=pl_PL.UTF-8 ENV LC_COLLATE=pl_PL.UTF-8 ENV LC_CTYPE=pl_PL.UTF-8 -ENV LC_MESSAGES=en_US.UTF-8 \ No newline at end of file +ENV LC_MESSAGES=en_US.UTF-8 diff --git a/environment/dev/app/Dockerfile b/environment/dev/app/Dockerfile deleted file mode 100644 index f0612d1e..00000000 --- a/environment/dev/app/Dockerfile +++ /dev/null @@ -1,86 +0,0 @@ -# https://hub.docker.com/_/alpine -FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099 AS secops-tools - -# https://github.com/FiloSottile/age/releases -ARG AGE_VERSION="1.1.1" - -# https://github.com/getsops/sops/releases -ARG SOPS_VERSION="3.8.1" - -RUN wget --output-document age.tar.gz "https://github.com/FiloSottile/age/releases/download/v${AGE_VERSION}/age-v${AGE_VERSION}-linux-amd64.tar.gz" \ - && tar --extract --file age.tar.gz \ - && mv age/age /usr/local/bin \ - && mv age/age-keygen /usr/local/bin \ - && wget --output-document /usr/local/bin/sops "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64" \ - && chmod +x /usr/local/bin/sops - -FROM composer/composer:2.8.5-bin@sha256:f39305913df47b4de56c64f4719d35eedbd65d7d96ff554110b9e9bd0c7a18c0 AS composer-bin - -FROM node:23.7.0-bookworm AS node - -FROM php:8.3.10-fpm-bookworm - -COPY --from=composer-bin ./composer /usr/bin/composer -COPY --from=secops-tools /usr/local/bin/age /usr/local/bin/age -COPY --from=secops-tools /usr/local/bin/age-keygen /usr/local/bin/age-keygen -COPY --from=secops-tools /usr/local/bin/sops /usr/local/bin/sops - - -ARG USER_NAME=host-user -ARG USER_ID=1000 -ARG PHP_FPM_GROUP=www-data - -RUN adduser \ - --disabled-password \ - --uid ${USER_ID} \ - ${USER_NAME} \ - && usermod \ - --append \ - --groups \ - ${PHP_FPM_GROUP} \ - ${USER_NAME} - -#Add node and npm -COPY --from=node --chown=${USER_NAME}:root /usr/local/lib/node_modules /usr/local/lib/node_modules -COPY --from=node --chown=${USER_NAME}:root /usr/local/bin/node /usr/local/bin/node -RUN ln --symbolic /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ - && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npm \ - && ln --symbolic /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ - && chown --no-dereference ${USER_NAME}:root /usr/local/bin/npx - -# Use the default development configuration -RUN mv "${PHP_INI_DIR}/php.ini-development" "${PHP_INI_DIR}/php.ini" - -# For other versions check: http://nginx.org/packages/mainline/debian/pool/nginx/n/nginx/ -ARG NGINX_VERSION="1.25.4-1~bookworm" - -RUN apt-get update \ - && apt-get install --assume-yes gpg \ - && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmour --output /etc/apt/trusted.gpg.d/apt.nginx.org.gpg > /dev/null \ - && echo "deb https://nginx.org/packages/mainline/debian bookworm nginx" | tee /etc/apt/sources.list.d/nginx.list \ - && apt-get update && apt-get install --assume-yes \ - nginx=${NGINX_VERSION} \ - libzip-dev \ - libjpeg-dev \ - libpng-dev \ - libwebp-dev \ - libpq-dev \ - libicu-dev \ - supervisor \ - cron \ - git \ - exif \ - && docker-php-ext-configure \ - gd --with-jpeg --with-webp \ - && docker-php-ext-install \ - zip \ - gd \ - intl \ - exif \ - pdo_pgsql - -COPY ./entrypoint.sh /entrypoint.sh - -WORKDIR /application - -ENTRYPOINT ["/entrypoint.sh"] diff --git a/environment/dev/app/php-fpm.conf b/environment/dev/app/php-fpm.conf index 883caeca..bc61b749 100644 --- a/environment/dev/app/php-fpm.conf +++ b/environment/dev/app/php-fpm.conf @@ -4,5 +4,3 @@ daemonize = no [www] listen = /run/php-fpm.sock listen.owner = nginx - -user = host-user diff --git a/environment/dev/app/php.ini b/environment/dev/app/php.ini index 22352707..cb9dd788 100644 --- a/environment/dev/app/php.ini +++ b/environment/dev/app/php.ini @@ -1,4 +1,15 @@ [PHP] memory_limit = 256M +allow_url_fopen = 0 +expose_php = 0 +log_errors = 1 +display_startup_errors = 0 post_max_size = 30M upload_max_filesize = 30M + +[opcache] +opcache.enable = 1 +opcache.enable_cli = 1 +opcache.memory_consumption = 512 +opcache.max_accelerated_files = 20000 +opcache.interned_strings_buffer = 16 diff --git a/environment/dev/app/supervisord.conf b/environment/dev/app/supervisord.conf index 2acd0725..828127a4 100644 --- a/environment/dev/app/supervisord.conf +++ b/environment/dev/app/supervisord.conf @@ -20,3 +20,12 @@ stdout_logfile = /dev/stdout stdout_logfile_maxbytes = 0 stderr_logfile = /dev/stderr stderr_logfile_maxbytes = 0 + +[program:laravel-queue-worker] +command = php /application/artisan queue:work --timeout=3600 --tries=3 +user = www-data +stdout_logfile = /dev/stdout +stdout_logfile_maxbytes = 0 +stderr_logfile = /dev/stderr +stderr_logfile_maxbytes = 0 + diff --git a/environment/dev/app/vite.dev.env b/environment/dev/app/vite.dev.env new file mode 100644 index 00000000..3de240a7 --- /dev/null +++ b/environment/dev/app/vite.dev.env @@ -0,0 +1 @@ +VITE_APP_NAME="LMT DEV" diff --git a/environment/devops-Taskfile.yml b/environment/devops-Taskfile.yml new file mode 100644 index 00000000..c98201ed --- /dev/null +++ b/environment/devops-Taskfile.yml @@ -0,0 +1,10 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +tasks: + generate-age-keys: + desc: "Generate age keys" + cmds: + - cmd: docker run --rm registry.blumilk.pl/internal-public/secops-tools-bin:v1.0.0 age-keygen diff --git a/environment/prod/app/php-fpm.conf b/environment/local/app/php-fpm.conf similarity index 81% rename from environment/prod/app/php-fpm.conf rename to environment/local/app/php-fpm.conf index bc61b749..883caeca 100644 --- a/environment/prod/app/php-fpm.conf +++ b/environment/local/app/php-fpm.conf @@ -4,3 +4,5 @@ daemonize = no [www] listen = /run/php-fpm.sock listen.owner = nginx + +user = host-user diff --git a/environment/local/app/php.ini b/environment/local/app/php.ini new file mode 100644 index 00000000..22352707 --- /dev/null +++ b/environment/local/app/php.ini @@ -0,0 +1,4 @@ +[PHP] +memory_limit = 256M +post_max_size = 30M +upload_max_filesize = 30M diff --git a/environment/prod/app/supervisord.conf b/environment/local/app/supervisord.conf similarity index 100% rename from environment/prod/app/supervisord.conf rename to environment/local/app/supervisord.conf diff --git a/environment/prod/app/Dockerfile b/environment/prod/app/Dockerfile deleted file mode 100644 index be1f3f02..00000000 --- a/environment/prod/app/Dockerfile +++ /dev/null @@ -1,74 +0,0 @@ -### PHP DEPENDENCIES ### -FROM composer:2.8.5@sha256:e0c9ac329256c25b0dee572df37d986570fb26bb6baaa7d0abe69b84181701e1 AS vendor - -WORKDIR /app_composer_dependencies - -COPY composer.json composer.lock ./ - -RUN composer install \ - --no-interaction \ - --no-plugins \ - --no-scripts \ - --prefer-dist \ - --ignore-platform-reqs - -### FRONTEND ### -FROM node:21.7.3-bookworm-slim@sha256:dfc05dee209a1d7adf2ef189bd97396daad4e97c6eaa85778d6f75205ba1b0fb AS frontend - -WORKDIR /app_frontend_dependencies - -COPY package.json package-lock.json styles.css tailwind.config.js ./ - -RUN npm clean-install - -COPY ./public/meetup.html ./public/meetup.html -COPY ./public/index.html ./public/index.html - -RUN npx tailwindcss -i ./styles.css -o ./public/output.css - -### APPLICATION ### -FROM php:8.4.3-fpm-bookworm@sha256:5148d67c6a67e00736c7a1be2e3b5287e370fd64d24f7777f5e39bf4c45b9175 AS app - -COPY --from=vendor /usr/bin/composer /usr/local/bin/composer - -# For other versions check: http://nginx.org/packages/mainline/debian/pool/nginx/n/nginx/ -ARG NGINX_VERSION="1.25.4-1~bookworm" - -RUN apt-get update \ - && apt-get install --assume-yes gpg \ - && curl https://nginx.org/keys/nginx_signing.key | gpg --dearmour --output /etc/apt/trusted.gpg.d/apt.nginx.org.gpg > /dev/null \ - && echo "deb https://nginx.org/packages/mainline/debian bookworm nginx" | tee /etc/apt/sources.list.d/nginx.list \ - && apt-get update && apt-get install --assume-yes \ - nginx=${NGINX_VERSION} \ - 7zip \ - libzip-dev \ - supervisor \ - cron - -RUN mv "${PHP_INI_DIR}/php.ini-production" "${PHP_INI_DIR}/php.ini" - -COPY ./environment/prod/app/php.ini "${PHP_INI_DIR}/conf.d/zzz-overrides-php.ini" -COPY ./environment/prod/app/php-fpm.conf "/usr/local/etc/php-fpm.d/zzz-overrides.conf" -COPY ./environment/prod/app/nginx.conf "/etc/nginx/nginx.conf" -COPY ./environment/prod/app/supervisord.conf "/etc/supervisor/custom-supervisord.conf" -COPY ./environment/prod/app/entrypoint.sh /entrypoint.sh - -WORKDIR /application - -# Copy Frontend build -COPY --chown=www-data:www-data --from=frontend /app_frontend_dependencies/public/output.css ./public/output.css - -# Copy Composer dependencies -COPY --chown=www-data:www-data --from=vendor /app_composer_dependencies/vendor ./vendor/ - -# Copy App files -COPY --chown=www-data:www-data . . - -RUN composer dump-autoload --optimize - -EXPOSE 80 - -ENTRYPOINT ["/entrypoint.sh"] - -ARG DEPLOYMENT_PROJECT_VERSION_ARG -ENV DEPLOYMENT_VERSION=${DEPLOYMENT_PROJECT_VERSION_ARG} diff --git a/environment/prod/app/entrypoint.sh b/environment/prod/app/entrypoint.sh deleted file mode 100755 index b951e259..00000000 --- a/environment/prod/app/entrypoint.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# -e is for "automatic error detection", tell shell to abort any time an error occurred -set -e - -# bash is not responding to the sigterm and container always have 10 second timeout (when stop/restart) -# exec is related with -# https://docs.docker.com/compose/faq/#why-do-my-services-take-10-seconds-to-recreate-or-stop -# https://github.com/moby/moby/issues/3766 -# https://unix.stackexchange.com/a/196053 - -exec supervisord --configuration /etc/supervisor/custom-supervisord.conf diff --git a/environment/prod/app/nginx.conf b/environment/prod/app/nginx.conf deleted file mode 100644 index 0bd53da7..00000000 --- a/environment/prod/app/nginx.conf +++ /dev/null @@ -1,56 +0,0 @@ -daemon off; - -user nginx; -worker_processes auto; - -error_log /var/log/nginx/error.log notice; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - sendfile on; - keepalive_timeout 65; - - server { - listen 80 default; - server_name lmt-nginx; - - access_log /dev/stdout; - error_log /dev/stderr; - - root /application/public; - index index.html; - - location = /meetup.html { - if ($arg_meetupId) { - return 301 /meetup?$query_string; - } - return 404; # Return 404 if no meetupId is present - } - - location = /meetup { - try_files /meetup.html =404; - } - - location / { - try_files $uri $uri/ =404; - } - - location ~ \.php$ { - fastcgi_pass unix:/run/php-fpm.sock; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - include fastcgi_params; - } - } -} diff --git a/environment/prod/app/php.ini b/environment/prod/app/php.ini deleted file mode 100644 index 39f373e4..00000000 --- a/environment/prod/app/php.ini +++ /dev/null @@ -1,2 +0,0 @@ -[PHP] -memory_limit = 256M diff --git a/environment/prod/deployment/prod/.env.prod b/environment/prod/deployment/prod/.env.prod deleted file mode 100644 index cc054069..00000000 --- a/environment/prod/deployment/prod/.env.prod +++ /dev/null @@ -1,4 +0,0 @@ -APP_NAME="Legnicki Meetup Technologiczny" -ENVIRONMENT=prod - -LMT_HOST_NAME=meetup.legnica.pl diff --git a/environment/prod/deployment/prod/.env.prod.secrets b/environment/prod/deployment/prod/.env.prod.secrets deleted file mode 100644 index cfa41420..00000000 --- a/environment/prod/deployment/prod/.env.prod.secrets +++ /dev/null @@ -1,12 +0,0 @@ -SMTP_HOST=ENC[AES256_GCM,data:wAn4XtrjkT1l1s9XD9Y=,iv:+I2yPi4swha2ywJYwZomVaHwuLZ/HfUvcEauen+ulIo=,tag:08RDU/6kznLb81l4r/PGXw==,type:str] -SMTP_PORT=ENC[AES256_GCM,data:EYA9,iv:HeIQr+603f3StbGsLLeQpDINYtup1d5kQwJsauyo0oc=,tag:QqhqAn6Z8BjeYuVhz2d8cA==,type:str] -SMTP_USERNAME=ENC[AES256_GCM,data:brnySEovpIJk3mXLQF2Gaq6zfA==,iv:g8DDoXRhRiBP72sYxGhkCM8jqvyWRJy5OMCcsJPnVeE=,tag:gZ4y7QX2q1+1PxWB2lA4oA==,type:str] -SMTP_PASSWORD=ENC[AES256_GCM,data:1nuW3QwTlqmh2KMSRQhKVsnt0YdC,iv:+wKvBPbRlvbiPNJ8Wh8RdVqf/LsDF5lWOt2ReBTMum0=,tag:wDxrCXESHMD1l00VM662RA==,type:str] -EMAIL_FROM=ENC[AES256_GCM,data:ucpZ2+jJwQvsWcOWzuMNow==,iv:uyjzGenqn+LGeGFQvxRtDIg1ouKPpjZrVA9zeV7m9+4=,tag:4BimPA43rilYdsCL7F5gzA==,type:str] -REGISTRATION_NOTIFICATION_EMAIL=ENC[AES256_GCM,data:ArpgRjpKH8iWpGXeavOywcqRjcfc+nvXCc0=,iv:mqKGUB8PhCcRBCFR8j9FiahytFUoEzriFrSgMCMUowI=,tag:7+c3/Xj7E+BewgBPDdlhUw==,type:str] -sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAzQ1lRZ0ZLNzRFbHE3RVQ5\nSlZKcjBUaC9nVmNGb3lTWTR3QnRjbW5WdlhNCllaV1JDYUVUSFBtTExyVGwrL0dW\na0kxLzltS3BDOW5jd0FhTDBqK2pxMGsKLS0tIDBUZlBGc3orYlJJTTNod0dKdVRy\neC9aS3JwWXl0RzBMUk5oanRWS05rKzgKJWcyHWZY/mneQHr4Hq5LjRsiCAO04vD4\ngq0XATNraPCH/zWpX4vyLha4JXIu6zTsOaK9uScwimwRaMtIoa/NuA==\n-----END AGE ENCRYPTED FILE-----\n -sops_age__list_0__map_recipient=age1ys9k3fkd2cxzkpllfe75gagjrfks45r6x9rhllr4lqvfxx520qaqxtj697 -sops_lastmodified=2024-03-15T07:35:20Z -sops_mac=ENC[AES256_GCM,data:s5Gfy839w9ZB43q9o0nOngHTXUeurA5gUQG5w7orhopDu765B3OnE1KCHem9RdJkEq/mjG3y0yZvYU/x6e7ZcDQT06t7JSs0nwWc//gYuZgGpjh8Efw2f6MCoWbGJA/0qqLYKkA6sqFcM18biokfTykXi4MiWglWqAMmC1hxz8U=,iv:85HoW/cmxFxJYMbbYsX17iahBr5JivyGSrDO9FNsEfU=,tag:ggGPzsjl3IpeTvZO4wBpkg==,type:str] -sops_unencrypted_suffix=_unencrypted -sops_version=3.8.1 diff --git a/environment/prod/deployment/prod/Makefile b/environment/prod/deployment/prod/Makefile deleted file mode 100644 index 29387363..00000000 --- a/environment/prod/deployment/prod/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -export COMPOSE_DOCKER_CLI_BUILD = 1 -export DOCKER_BUILDKIT = 1 - -MAKEFLAGS += --no-print-directory - -DOCKER_COMPOSE_FILENAME = docker-compose.prod.yml -DOCKER_COMPOSE_APP_SERVICE = lmt-prod-app-container - -CURRENT_DIR = $(shell pwd) - -prod-deploy: decrypt-secrets create-deployment-file - @docker compose --file ${DOCKER_COMPOSE_FILENAME} pull && \ - docker compose --file ${DOCKER_COMPOSE_FILENAME} up --detach - -SOPS_VERSION=3.8.1 - -decrypt-secrets: - @wget --output-document ./sops "https://github.com/getsops/sops/releases/download/v${SOPS_VERSION}/sops-v${SOPS_VERSION}.linux.amd64" \ - && chmod +x ./sops \ - && mv .env.prod .env \ - && echo "Decrypting secrets" \ - && ./sops --decrypt --input-type=dotenv --output-type=dotenv .env.prod.secrets >> .env \ - && echo "Done" - -DEPLOYMENT_DATETIME = $(shell TZ=Europe/Warsaw date --rfc-3339=seconds) - -create-deployment-file: - @echo "\ - DEPLOY_DATE='${DEPLOYMENT_DATETIME}'\ - " > .deployment - -.PHONY: prod-deploy decrypt-secrets create-deployment-file diff --git a/environment/prod/deployment/prod/docker-compose.prod.yml b/environment/prod/deployment/prod/docker-compose.prod.yml deleted file mode 100644 index 141cb548..00000000 --- a/environment/prod/deployment/prod/docker-compose.prod.yml +++ /dev/null @@ -1,38 +0,0 @@ -networks: - traefik-proxy: - external: true - lmt-prod: - driver: bridge - -services: - lmt-prod-app: - image: ghcr.io/blumilksoftware/lmt:latest - container_name: lmt-prod-app-container - pull_policy: always - logging: - driver: "json-file" - options: - max-size: "50m" - max-file: "5" - deploy: - mode: replicated - replicas: 1 - resources: - limits: - memory: 1GB - labels: - - "traefik.enable=true" - - "traefik.http.routers.lmt-prod-app.rule=Host(`${LMT_HOST_NAME}`)" - - "traefik.http.routers.lmt-prod-app.entrypoints=websecure" - - "traefik.http.routers.lmt-prod-app.tls=true" - - "traefik.http.routers.lmt-prod-app.tls.certresolver=lets-encrypt-resolver" - - "traefik.http.routers.lmt-prod-app.middlewares=response-gzip-compress@file" - working_dir: /application - volumes: - - ./.env:/application/.env:ro - networks: - - lmt-prod - - traefik-proxy - restart: unless-stopped - env_file: - - .deployment diff --git a/environment/secops-Taskfile.yml b/environment/secops-Taskfile.yml new file mode 100644 index 00000000..2dadf17e --- /dev/null +++ b/environment/secops-Taskfile.yml @@ -0,0 +1,38 @@ +# https://taskfile.dev +version: "3.42.1" + +silent: true + +tasks: + encrypt-dev-secrets: + desc: "Encrypt app dev secrets" + cmds: + - task: _encrypt-secrets + vars: + ENVIRONMENT: dev + + decrypt-dev-secrets: + desc: "Decrypt app dev secrets" + cmds: + - task: _decrypt-secrets + vars: + ENVIRONMENT: dev + AGE_SECRET_KEY: ${SOPS_AGE_DEV_SECRET_KEY} + + _encrypt-secrets: + internal: true + cmds: + - cmd: echo "Encrypting {{ .ENVIRONMENT }} secrets" + - cmd: | + docker compose exec --user $CURRENT_USER_ID --workdir /application/environment/.deployment/{{ .ENVIRONMENT }} $DOCKER_COMPOSE_APP_CONTAINER \ + sops --encrypt --input-type=dotenv --output-type=dotenv --output .env.{{ .ENVIRONMENT }}.secrets .env.{{ .ENVIRONMENT }}.secrets.decrypted + - cmd: echo "Done" + + _decrypt-secrets: + internal: true + cmds: + - cmd: echo "Decrypting {{ .ENVIRONMENT }} secrets" + - cmd: | + docker compose exec --user $CURRENT_USER_ID --env SOPS_AGE_KEY={{ .AGE_SECRET_KEY }} --workdir /application/environment/.deployment/{{ .ENVIRONMENT }} $DOCKER_COMPOSE_APP_CONTAINER \ + sops --decrypt --input-type=dotenv --output-type=dotenv --output .env.{{ .ENVIRONMENT }}.secrets.decrypted .env.{{ .ENVIRONMENT }}.secrets + - cmd: echo 'Done' diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC3958.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC3958.webp deleted file mode 100644 index fa51dc23..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC3958.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4010-Enhanced-NR.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4010-Enhanced-NR.webp deleted file mode 100644 index d4506aa7..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4010-Enhanced-NR.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4054.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4054.webp deleted file mode 100644 index 9640ad2f..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4054.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4066.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4066.webp deleted file mode 100644 index 89cec60f..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4066.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4074.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4074.webp deleted file mode 100644 index 12f51fcb..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4074.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4076-Enhanced-NR.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4076-Enhanced-NR.webp deleted file mode 100644 index 17f59243..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4076-Enhanced-NR.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4187.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4187.webp deleted file mode 100644 index 26c2e711..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4187.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4448-Enhanced-NR-2.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4448-Enhanced-NR-2.webp deleted file mode 100644 index b721cf86..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4448-Enhanced-NR-2.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4490.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4490.webp deleted file mode 100644 index fbdbe78f..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4490.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4494-Enhanced-NR.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4494-Enhanced-NR.webp deleted file mode 100644 index 7093dc07..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4494-Enhanced-NR.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4508.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4508.webp deleted file mode 100644 index cf60aebd..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4508.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4643.webp b/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4643.webp deleted file mode 100644 index dc60c196..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/images/gallery/_DSC4643.webp and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/presentations/kamil-piech.pdf b/public/assets/meetups/2025-02-20-lmt-03/presentations/kamil-piech.pdf deleted file mode 100644 index 49ec9428..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/presentations/kamil-piech.pdf and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/presentations/lukasz-tkacz.pdf b/public/assets/meetups/2025-02-20-lmt-03/presentations/lukasz-tkacz.pdf deleted file mode 100644 index 6038f5e8..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/presentations/lukasz-tkacz.pdf and /dev/null differ diff --git a/public/assets/meetups/2025-02-20-lmt-03/presentations/szymon-sandura.pdf b/public/assets/meetups/2025-02-20-lmt-03/presentations/szymon-sandura.pdf deleted file mode 100644 index 3901861d..00000000 Binary files a/public/assets/meetups/2025-02-20-lmt-03/presentations/szymon-sandura.pdf and /dev/null differ diff --git a/readme.md b/readme.md index b1a5749e..0f833a3d 100644 --- a/readme.md +++ b/readme.md @@ -4,61 +4,109 @@ ![./screenshot.png](./screenshot.png) -### Docker environment: +--- +## Taskfile setup +### Linux -Copy `.env.example` to `.env` and set your environment variables: +If you don't have Task binary installed, you can install it by running command below. \ +If you don't want to install to `/usr/local/bin` (dir for all users in the system) change `-b` flag value. \ +Be sure that provided path is in system $PATH, that binary will be available in the terminal. +To check system paths type `$PATH` in the terminal. +```shell +sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin v3.42.1 ``` -cp .env.example .env -``` - -Build app containers: +_-b sets bindir or installation directory, Defaults to ./bin_ \ +_-d turns on debug logging_ -``` -make build -``` +Other installation methods: https://taskfile.dev/installation \ +GitHub: https://github.com/go-task/task \ +Taskfile releases: https://github.com/go-task/task/releases -Run containers: +# Task commands +--- +To list all task commands just run: +```shell +task ``` -make run -``` - -Enter the app container with `make shell`, then install php and node dependencies: +### Task commands completions: +--- +Add this line to `.bashrc` if you are using bash: ``` -composer install -npm install +eval "$(task --completion bash)" ``` +For other shells see: \ +https://taskfile.dev/installation/#option-1-load-the-completions-in-your-shells-startup-config-recommended -Run `php artisan storage:link`: +# Project initialization +Before first use, project has to be initialized. + +First, prepare `.env` file +```shell +cp .env.example .env ``` -php artisan storage:link + +To initialize project run: +```shell +task init ``` +This command will check if `.env` file exists. +Build and run containers. \ +Then it will: +- install composer dependencies +- generate `APP_KEY` if not set +- run migrations with seed +- link Laravel storage +- install npm dependencies +- create test database if not exist -Run `make dev` to build stylesheets: +### Develop project +To develop project run: +```shell +task dev ``` -make dev -``` +This command will run Vite development server. \ +App will be available at: +- https://lmt.blumilk.local.env- if you ran Traefik in Blumilk environment. Don't forget to update hosts file. +- http://\:5173 - link will be displayed in console + +### Running tests -Run `make lint` to check for lint issues: +You can run PHPUnit test cases ``` -make lint +task test ``` -Run `make lintf` to fix lint issues: +### Code style check + +You can run PHP-CS-Fixer: ``` -make lintf +task fix ``` -The website should be available at [localhost:8051](localhost:8051) and [lmt.blumilk.localhost](lmt.blumilk.localhost) +The website should be available at [localhost:8051](localhost:8051) and [lmt.blumilk.local.env](lmt.blumilk.local.env) if we use a Blumilk local traefik proxy. -| service | container name | default external port | -|---------|---------------------------|-----------------------| -| app | lmt-dev-app-container | 8051 | -| mailpit | lmt-dev-mailpit-container | 8052 | +| service | container name | default external port | +|---------------------|-------------------------------------------------------------|--------------------------------| +| app | [lmt-app-local](https://lmt.blumilk.local.env) | [8051](http://localhost:8051/) | +| mailpit (dashboard) | [lmt-mailpit-local ](https://lmt-mailpit.blumilk.local.env) | [8052](http://localhost:8052/) | +| database | lmt-db-local | 8055 | + + +### Working with encrypted data + +To encrypt/decrypt environment secrets or json files, you can use task commands: \ +E.g.: `task secops:decrypt-dev-secrets` + +* secops:decrypt-dev-secrets: Decrypt app dev secrets +* secops:encrypt-dev-secrets: Encrypt app dev secrets + +Remember that decryption requires private key (e.g. `SOPS_AGE_DEV_SECRET_KEY` for dev environment) which should be set in `.env` file. +Encryption uses public key which is added in `.sops.yaml` file. \ No newline at end of file diff --git a/renovate.json5 b/renovate.json5 index 6a282245..1cca49a3 100644 --- a/renovate.json5 +++ b/renovate.json5 @@ -6,5 +6,7 @@ additionalReviewers: [ 'Blusia', // Agnieszka Rudek ], - ignoreDeps: ['ghcr.io/blumilksoftware/lmt'], + ignoreDeps: [ + 'registry.blumilk.pl/internal-public/lmt', + ], } diff --git a/resources/views/components/partials/speakers.blade.php b/resources/views/components/partials/speakers.blade.php index 5d200d71..aa81237c 100644 --- a/resources/views/components/partials/speakers.blade.php +++ b/resources/views/components/partials/speakers.blade.php @@ -48,7 +48,7 @@ class="h-full overflow-hidden"

@if ($speaker->slides) + href="{{ $speaker->slides->getUrl() }}">

{{ $speaker->presentation }}

@@ -90,20 +90,43 @@ class="flex h-[68px] w-[68px] cursor-pointer items-center justify-center rounded alt="{{ $speaker->full_name }}" >
-
-
{{ $speaker->first_name }}
-
{{ $speaker->last_name }}
-
-
-
-

- {{ $speaker->description }} -

+
+
+
{{ $speaker->first_name }}
+
{{ $speaker->last_name }}
+
+
+ @foreach($speaker->companies as $company) + {{ $company['name'] }} + @endforeach +
+
+

+ {{ $speaker->description }} +

+
+
+ @if ($speaker->slides) + +
+ +

{{ $speaker->presentation }}

+
+
+ @endif +
+
+ @if ($speaker->video_url) + +
+ +

Obejrzyj na YouTube

+
+
+ @endif +
@endforeach diff --git a/vite.config.js b/vite.config.js index 4c34114d..f756b02c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,7 +1,6 @@ import { defineConfig, loadEnv } from 'vite' import laravel from 'laravel-vite-plugin' import tailwindcss from '@tailwindcss/vite' -import { networkInterfaces } from 'os' export default ({ mode }) => { process.env = { ...process.env, ...loadEnv(mode, process.cwd()) } @@ -11,8 +10,11 @@ export default ({ mode }) => { outDir: './public/build/', }, server: { - host: Object.values(networkInterfaces()).flat().find(i => i.family === 'IPv4' && !i.internal).address, - port: process.env.VITE_PORT, + host: true, + port: 5173, + strictPort: true, + origin: 'https://' + process.env.VITE_DEV_SERVER_DOCKER_HOST_NAME, + cors: true, // Allow any origin }, resolve: { alias: {