The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.
+diff --git a/.app_version b/.app_version index 082b4352..a67cebaf 100644 --- a/.app_version +++ b/.app_version @@ -1 +1 @@ -0.19.7 +0.21.1 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..39a87c65 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,41 @@ +# Basis-Image für Ruby und Node.js +FROM ruby:3.3.4-alpine + +ENV APP_PATH=/var/app +ENV BUNDLE_VERSION=2.5.9 +ENV BUNDLE_PATH=/usr/local/bundle/gems +ENV TMP_PATH=/tmp/ +ENV RAILS_LOG_TO_STDOUT=true +ENV RAILS_PORT=3000 + +# Install dependencies for application +RUN apk -U add --no-cache \ + build-base \ + git \ + postgresql-dev \ + postgresql-client \ + libxml2-dev \ + libxslt-dev \ + nodejs \ + yarn \ + imagemagick \ + tzdata \ + less \ + yaml-dev \ + # gcompat for nokogiri on mac m1 + gcompat \ + && rm -rf /var/cache/apk/* \ + && mkdir -p $APP_PATH + +RUN gem update --system 3.5.7 && gem install bundler --version "$BUNDLE_VERSION" \ + && rm -rf $GEM_HOME/cache/* + +# FIXME It would be a good idea to use a other user than root, but this lead to permission error on export and maybe more yet. +# RUN adduser -D -h ${APP_PATH} vscode +USER root + +# Navigate to app directory +WORKDIR $APP_PATH + +EXPOSE $RAILS_PORT + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..8acaef4f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "Ruby and Node DevContainer", + "dockerComposeFile": ["docker-compose.yml"], + "service": "dawarich_dev", + "settings": { + "terminal.integrated.defaultProfile.linux": "bash" + }, + "extensions": [ + "rebornix.ruby", // Ruby-Support + "esbenp.prettier-vscode", // Prettier for JS-Formating + "dbaeumer.vscode-eslint" // ESLint for JavaScript + ], + "postCreateCommand": "yarn install && bundle config set --local path 'vendor/bundle' && bundle install --jobs 20 --retry 5", + "forwardPorts": [3000], // Redirect to Rails-App-Server + "remoteUser": "root", + "workspaceFolder": "/var/app" +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 00000000..5e2f006a --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,79 @@ +networks: + dawarich: +services: + dawarich_dev: + build: + context: . + dockerfile: Dockerfile + container_name: dawarich_dev + volumes: + - "${PWD}:/var/app:cached" + - dawarich_gem_cache_app:/usr/local/bundle/gems_app + - dawarich_public:/var/app/public + - dawarich_watched:/var/app/tmp/imports/watched + networks: + - dawarich + ports: + - 3000:3000 + - 9394:9394 + stdin_open: true + tty: true + environment: + RAILS_ENV: development + REDIS_URL: redis://dawarich_redis:6379/0 + DATABASE_HOST: dawarich_db + DATABASE_USERNAME: postgres + DATABASE_PASSWORD: password + DATABASE_NAME: dawarich_development + MIN_MINUTES_SPENT_IN_CITY: 60 + APPLICATION_HOST: localhost + APPLICATION_HOSTS: localhost + TIME_ZONE: Europe/London + APPLICATION_PROTOCOL: http + DISTANCE_UNIT: km + PHOTON_API_HOST: photon.komoot.io + PHOTON_API_USE_HTTPS: true + PROMETHEUS_EXPORTER_ENABLED: false + PROMETHEUS_EXPORTER_HOST: 0.0.0.0 + PROMETHEUS_EXPORTER_PORT: 9394 + ENABLE_TELEMETRY: false # More on telemetry: https://dawarich.app/docs/tutorials/telemetry + dawarich_redis: + image: redis:7.0-alpine + container_name: dawarich_redis + command: redis-server + networks: + - dawarich + volumes: + - dawarich_shared:/data + restart: always + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + dawarich_db: + image: postgres:14.2-alpine + container_name: dawarich_db + volumes: + - dawarich_db_data:/var/lib/postgresql/data + - dawarich_shared:/var/shared + networks: + - dawarich + restart: always + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres -d dawarich_development" ] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password +volumes: + dawarich_db_data: + dawarich_gem_cache_app: + dawarich_gem_cache_sidekiq: + dawarich_shared: + dawarich_public: + dawarich_watched: diff --git a/.gitignore b/.gitignore index 3c01a870..9583fb0a 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,13 @@ .env .byebug_history + + +.devcontainer/.onCreateCommandMarker +.devcontainer/.postCreateCommandMarker +.devcontainer/.updateContentCommandMarker + +.vscode-server/ +.ash_history +.cache/ +.dotnet/ diff --git a/CHANGELOG.md b/CHANGELOG.md index ce3bb1f3..7d5c2fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,123 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +# 0.21.1 - 2024-12-24 + +### Added + +- Cache cleaning and preheating upon application start. +- `PHOTON_API_KEY` env var to set Photon API key. It's an optional env var, but it's required if you want to use Photon API as a Patreon supporter. +- 'X-Dawarich-Response' header to the `GET /api/v1/health` endpoint. It's set to 'Hey, I\'m alive!' to make it easier to check if the API is working. + +### Changed + +- Custom config for PostgreSQL is now optional in `docker-compose.yml`. + +# 0.21.0 - 2024-12-20 + +⚠️ This release introduces a breaking change. ⚠️ + +The `dawarich_db` service now uses a custom `postgresql.conf` file. + +As @tabacha pointed out in #549, the default `shm_size` for the `dawarich_db` service is too small and it may lead to database performance issues. This release introduces a `shm_size` parameter to the `dawarich_db` service to increase the size of the shared memory for PostgreSQL. This should help database with peforming vacuum and other operations. Also, it introduces a custom `postgresql.conf` file to the `dawarich_db` service. + +To mount a custom `postgresql.conf` file, you need to create a `postgresql.conf` file in the `dawarich_db` service directory and add the following line to it: + +```diff + dawarich_db: + image: postgres:14.2-alpine + shm_size: 1G + container_name: dawarich_db + volumes: + - dawarich_db_data:/var/lib/postgresql/data + - dawarich_shared:/var/shared ++ - ./postgresql.conf:/etc/postgresql/postgres.conf # Provide path to custom config + ... + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres -d dawarich_development" ] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s ++ command: postgres -c config_file=/etc/postgresql/postgres.conf # Use custom config +``` + +To ensure your database is using custom config, you can connect to the container (`docker exec -it dawarich_db psql -U postgres`) and run `SHOW config_file;` command. It should return the following path: `/etc/postgresql/postgresql.conf`. + +An example of a custom `postgresql.conf` file is provided in the `postgresql.conf.example` file. + +### Added + +- A button on a year stats card to update stats for the whole year. #466 +- A button on a month stats card to update stats for a specific month. #466 +- A confirmation alert on the Notifications page before deleting all notifications. +- A `shm_size` parameter to the `dawarich_db` service to increase the size of the shared memory for PostgreSQL. This should help database with peforming vacuum and other operations. + +```diff + ... + dawarich_db: + image: postgres:14.2-alpine ++ shm_size: 1G + ... +``` + +- In addition to `api_key` parameter, `Authorization` header is now being used to authenticate API requests. #543 + +Example: + +``` +Authorization: Bearer YOUR_API_KEY +``` + +### Changed + +- The map borders were expanded to make it easier to scroll around the map for New Zealanders. +- The `dawarich_db` service now uses a custom `postgresql.conf` file. +- The popup over polylines now shows dates in the user's format, based on their browser settings. + +# 0.20.2 - 2024-12-17 + +### Added + +- A point id is now being shown in the point popup. + +### Fixed + +- North Macedonia is now being shown on the scratch map. #537 + +### Changed + +- The app process is now bound to :: instead of 0.0.0.0 to provide compatibility with IPV6. +- The app was updated to use Rails 8.0.1. + +# 0.20.1 - 2024-12-16 + +### Fixed + +- Setting `reverse_geocoded_at` for points that don't have geodata is now being performed in background job, in batches of 10,000 points to prevent memory exhaustion and long-running data migration. + +# 0.20.0 - 2024-12-16 + +### Added + +- `GET /api/v1/points/tracked_months` endpoint added to get list of tracked years and months. +- `GET /api/v1/countries/visited_cities` endpoint added to get list of visited cities. +- A link to the docs leading to a help chart for k8s. #550 +- A button to delete all notifications. #548 +- A support for `RAILS_LOG_LEVEL` env var to change log level. More on that here: https://guides.rubyonrails.org/debugging_rails_applications.html#log-levels. The available log levels are: `:debug`, `:info`, `:warn`, `:error`, `:fatal`, and `:unknown`, corresponding to the log level numbers from 0 up to 5, respectively. The default log level is `:debug`. #540 +- A devcontainer to improve developers experience. #546 + +### Fixed + +- A point popup is no longer closes when hovering over a polyline. #536 +- When polylines layer is disabled and user deletes a point from its popup, polylines layer is no longer being enabled right away. #552 +- Paths to gems within the sidekiq and app containers. #499 + +### Changed + +- Months and years navigation is moved to a map panel on the right side of the map. +- List of visited cities is now being shown in a map panel on the right side of the map. + # 0.19.7 - 2024-12-11 ### Fixed diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 00000000..8b1b6a97 --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,21 @@ +If you want to develop with dawarich you can use the devcontainer, with your IDE. It is tested with visual studio code. + +Load the directory in Vs-Code and press F1. And Run the command: `Dev Containers: Rebuild Containers` after a while you should see a terminal. + +Now you can create/prepare the Database (this need to be done once): +```bash +bundle exec rails db:prepare +``` + +Afterwards you can run sidekiq: +```bash +bundle exec sidekiq + +``` + +And in a second terminal the dawarich-app: +```bash +bundle exec bin/dev +``` + +You can connect with a web browser to http://127.0.0.l:3000/ and login with the default credentials. diff --git a/Gemfile b/Gemfile index c096f50e..103b7688 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ gem 'pg' gem 'prometheus_exporter' gem 'puma' gem 'pundit' -gem 'rails' +gem 'rails', '~> 8.0' gem 'rswag-api' gem 'rswag-ui' gem 'shrine', '~> 3.6' diff --git a/Gemfile.lock b/Gemfile.lock index cdff83dc..47ea71bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,66 +10,65 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (7.2.2) - actionpack (= 7.2.2) - activesupport (= 7.2.2) + actioncable (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.2.2) - actionpack (= 7.2.2) - activejob (= 7.2.2) - activerecord (= 7.2.2) - activestorage (= 7.2.2) - activesupport (= 7.2.2) + actionmailbox (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) mail (>= 2.8.0) - actionmailer (7.2.2) - actionpack (= 7.2.2) - actionview (= 7.2.2) - activejob (= 7.2.2) - activesupport (= 7.2.2) + actionmailer (8.0.1) + actionpack (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activesupport (= 8.0.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.2.2) - actionview (= 7.2.2) - activesupport (= 7.2.2) + actionpack (8.0.1) + actionview (= 8.0.1) + activesupport (= 8.0.1) nokogiri (>= 1.8.5) - racc - rack (>= 2.2.4, < 3.2) + rack (>= 2.2.4) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (7.2.2) - actionpack (= 7.2.2) - activerecord (= 7.2.2) - activestorage (= 7.2.2) - activesupport (= 7.2.2) + actiontext (8.0.1) + actionpack (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.2.2) - activesupport (= 7.2.2) + actionview (8.0.1) + activesupport (= 8.0.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (7.2.2) - activesupport (= 7.2.2) + activejob (8.0.1) + activesupport (= 8.0.1) globalid (>= 0.3.6) - activemodel (7.2.2) - activesupport (= 7.2.2) - activerecord (7.2.2) - activemodel (= 7.2.2) - activesupport (= 7.2.2) + activemodel (8.0.1) + activesupport (= 8.0.1) + activerecord (8.0.1) + activemodel (= 8.0.1) + activesupport (= 8.0.1) timeout (>= 0.4.0) - activestorage (7.2.2) - actionpack (= 7.2.2) - activejob (= 7.2.2) - activerecord (= 7.2.2) - activesupport (= 7.2.2) + activestorage (8.0.1) + actionpack (= 8.0.1) + activejob (= 8.0.1) + activerecord (= 8.0.1) + activesupport (= 8.0.1) marcel (~> 1.0) - activesupport (7.2.2) + activesupport (8.0.1) base64 benchmark (>= 0.3) bigdecimal @@ -81,6 +80,7 @@ GEM minitest (>= 5.1) securerandom (>= 0.3) tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) @@ -105,12 +105,12 @@ GEM cronex (0.15.0) tzinfo unicode (>= 0.4.4.5) - csv (3.3.0) + csv (3.3.2) data_migrate (11.2.0) activerecord (>= 6.1) railties (>= 6.1) - date (3.4.0) - debug (1.9.2) + date (3.4.1) + debug (1.10.0) irb (~> 1.10) reline (>= 0.3.8) devise (4.9.4) @@ -121,14 +121,14 @@ GEM warden (~> 1.2.3) diff-lcs (1.5.1) docile (1.4.1) - dotenv (3.1.4) - dotenv-rails (3.1.4) - dotenv (= 3.1.4) + dotenv (3.1.7) + dotenv-rails (3.1.7) + dotenv (= 3.1.7) railties (>= 6.1) down (5.4.2) addressable (~> 2.8) drb (2.2.1) - erubi (1.13.0) + erubi (1.13.1) et-orbi (1.2.11) tzinfo factory_bot (6.5.0) @@ -156,12 +156,12 @@ GEM multi_xml (>= 0.5.2) i18n (1.14.6) concurrent-ruby (~> 1.0) - importmap-rails (2.0.3) + importmap-rails (2.1.0) actionpack (>= 6.0.0) activesupport (>= 6.0.0) railties (>= 6.0.0) - io-console (0.7.2) - irb (1.14.1) + io-console (0.8.0) + irb (1.14.3) rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.7.4) @@ -180,7 +180,7 @@ GEM kaminari-core (= 1.2.2) kaminari-core (1.2.2) language_server-protocol (3.17.0.3) - logger (1.6.1) + logger (1.6.4) lograge (0.14.0) actionpack (>= 4) activesupport (>= 4) @@ -197,11 +197,11 @@ GEM marcel (1.0.4) method_source (1.1.0) mini_mime (1.1.5) - minitest (5.25.2) + minitest (5.25.4) msgpack (1.7.3) multi_xml (0.7.1) bigdecimal (~> 3.1) - net-imap (0.5.0) + net-imap (0.5.2) date net-protocol net-pop (0.1.2) @@ -211,24 +211,24 @@ GEM net-smtp (0.5.0) net-protocol nio4r (2.7.4) - nokogiri (1.16.7-aarch64-linux) + nokogiri (1.17.2-aarch64-linux) racc (~> 1.4) - nokogiri (1.16.7-arm-linux) + nokogiri (1.17.2-arm-linux) racc (~> 1.4) - nokogiri (1.16.7-arm64-darwin) + nokogiri (1.17.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86-linux) + nokogiri (1.17.2-x86-linux) racc (~> 1.4) - nokogiri (1.16.7-x86_64-darwin) + nokogiri (1.17.2-x86_64-darwin) racc (~> 1.4) - nokogiri (1.16.7-x86_64-linux) + nokogiri (1.17.2-x86_64-linux) racc (~> 1.4) - oj (3.16.7) + oj (3.16.8) bigdecimal (>= 3.0) ostruct (>= 0.2) optimist (3.2.0) orm_adapter (0.5.0) - ostruct (0.6.0) + ostruct (0.6.1) parallel (1.26.3) parser (3.3.5.0) ast (~> 2.4.1) @@ -246,7 +246,8 @@ GEM pry (>= 0.13, < 0.15) pry-rails (0.3.11) pry (>= 0.13.0) - psych (5.2.0) + psych (5.2.2) + date stringio public_suffix (6.0.1) puma (6.5.0) @@ -262,30 +263,30 @@ GEM rack (>= 1.3) rackup (2.2.1) rack (>= 3) - rails (7.2.2) - actioncable (= 7.2.2) - actionmailbox (= 7.2.2) - actionmailer (= 7.2.2) - actionpack (= 7.2.2) - actiontext (= 7.2.2) - actionview (= 7.2.2) - activejob (= 7.2.2) - activemodel (= 7.2.2) - activerecord (= 7.2.2) - activestorage (= 7.2.2) - activesupport (= 7.2.2) + rails (8.0.1) + actioncable (= 8.0.1) + actionmailbox (= 8.0.1) + actionmailer (= 8.0.1) + actionpack (= 8.0.1) + actiontext (= 8.0.1) + actionview (= 8.0.1) + activejob (= 8.0.1) + activemodel (= 8.0.1) + activerecord (= 8.0.1) + activestorage (= 8.0.1) + activesupport (= 8.0.1) bundler (>= 1.15.0) - railties (= 7.2.2) + railties (= 8.0.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (7.2.2) - actionpack (= 7.2.2) - activesupport (= 7.2.2) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.1) + actionpack (= 8.0.1) + activesupport (= 8.0.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -293,14 +294,14 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.2.1) - rdoc (6.8.1) + rdoc (6.10.0) psych (>= 4.0.0) redis (5.3.0) redis-client (>= 0.22.0) - redis-client (0.22.2) + redis-client (0.23.0) connection_pool regexp_parser (2.9.2) - reline (0.5.11) + reline (0.6.0) io-console (~> 0.5) request_store (1.7.0) rack (>= 1.4) @@ -354,13 +355,13 @@ GEM rubocop (>= 1.52.0, < 2.0) rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (1.13.0) - securerandom (0.3.2) + securerandom (0.4.1) shoulda-matchers (6.4.0) activesupport (>= 5.2.0) shrine (3.6.0) content_disposition (~> 1.0) down (~> 5.1) - sidekiq (7.3.6) + sidekiq (7.3.7) connection_pool (>= 2.3.0) logger rack (>= 2.2.4) @@ -410,7 +411,8 @@ GEM concurrent-ruby (~> 1.0) unicode (0.4.4.5) unicode-display_width (2.6.0) - useragent (0.16.10) + uri (1.0.2) + useragent (0.16.11) warden (1.2.9) rack (>= 2.0.9) webmock (3.24.0) @@ -456,7 +458,7 @@ DEPENDENCIES pry-rails puma pundit - rails + rails (~> 8.0) redis rspec-rails rswag-api diff --git a/Procfile.dev b/Procfile.dev index e6096674..a0f88c84 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1 +1 @@ -web: bin/rails server -p 3000 -b 0.0.0.0 +web: bin/rails server -p 3000 -b :: diff --git a/Procfile.prometheus.dev b/Procfile.prometheus.dev index 965e5e25..71fe0374 100644 --- a/Procfile.prometheus.dev +++ b/Procfile.prometheus.dev @@ -1,2 +1,2 @@ -prometheus_exporter: bundle exec prometheus_exporter -b 0.0.0.0 -web: bin/rails server -p 3000 -b 0.0.0.0 +prometheus_exporter: bundle exec prometheus_exporter -b ANY +web: bin/rails server -p 3000 -b :: \ No newline at end of file diff --git a/README.md b/README.md index 16878306..a087aab4 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Donate using crypto: [0x6bAd13667692632f1bF926cA9B421bEe7EaEB8D4](https://ethers ## ⚠️ Disclaimer +- 💔 **DO NOT UPDATE AUTOMATICALLY**: Read release notes before updating. Automatic updates may break your setup. - 🛠️ **Under active development**: Expect frequent updates, bugs, and breaking changes. - ❌ **Do not delete your original data** after importing into Dawarich. - 📦 **Backup before updates**: Always [backup your data](https://dawarich.app/docs/tutorials/backup-and-restore) before upgrading. diff --git a/app/controllers/api/v1/countries/visited_cities_controller.rb b/app/controllers/api/v1/countries/visited_cities_controller.rb new file mode 100644 index 00000000..85e53f7d --- /dev/null +++ b/app/controllers/api/v1/countries/visited_cities_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class Api::V1::Countries::VisitedCitiesController < ApiController + before_action :validate_params + + def index + start_at = DateTime.parse(params[:start_at]).to_i + end_at = DateTime.parse(params[:end_at]).to_i + + points = current_api_user + .tracked_points + .where(timestamp: start_at..end_at) + + render json: { data: CountriesAndCities.new(points).call } + end + + private + + def required_params + %i[start_at end_at] + end +end diff --git a/app/controllers/api/v1/health_controller.rb b/app/controllers/api/v1/health_controller.rb index 1e5ab2f1..53563cb0 100644 --- a/app/controllers/api/v1/health_controller.rb +++ b/app/controllers/api/v1/health_controller.rb @@ -4,6 +4,8 @@ class Api::V1::HealthController < ApiController skip_before_action :authenticate_api_key def index + response.set_header('X-Dawarich-Response', 'Hey, I\'m alive!') render json: { status: 'ok' } end end + diff --git a/app/controllers/api/v1/points/tracked_months_controller.rb b/app/controllers/api/v1/points/tracked_months_controller.rb new file mode 100644 index 00000000..cd430879 --- /dev/null +++ b/app/controllers/api/v1/points/tracked_months_controller.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Api::V1::Points::TrackedMonthsController < ApiController + def index + render json: current_api_user.years_tracked + end +end diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 8e2b43e2..c193148e 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -13,6 +13,26 @@ class ApiController < ApplicationController end def current_api_user - @current_api_user ||= User.find_by(api_key: params[:api_key]) + @current_api_user ||= User.find_by(api_key:) + end + + def api_key + params[:api_key] || request.headers['Authorization']&.split(' ')&.last + end + + def validate_params + missing_params = required_params.select { |param| params[param].blank? } + + if missing_params.any? + render json: { + error: "Missing required parameters: #{missing_params.join(', ')}" + }, status: :bad_request and return + end + + params.permit(*required_params) + end + + def required_params + [] end end diff --git a/app/controllers/notifications_controller.rb b/app/controllers/notifications_controller.rb index 98d96d34..0516063c 100644 --- a/app/controllers/notifications_controller.rb +++ b/app/controllers/notifications_controller.rb @@ -15,10 +15,15 @@ class NotificationsController < ApplicationController def mark_as_read current_user.notifications.unread.update_all(read_at: Time.zone.now) - redirect_to notifications_url, notice: 'All notifications marked as read.', status: :see_other end + + def destroy_all + current_user.notifications.destroy_all + redirect_to notifications_url, notice: 'All notifications where successfully destroyed.', status: :see_other + end + def destroy @notification.destroy! redirect_to notifications_url, notice: 'Notification was successfully destroyed.', status: :see_other diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 809f1d98..b7e68f41 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -16,9 +16,27 @@ class StatsController < ApplicationController end def update - current_user.years_tracked.each do |year| + if params[:month] == 'all' (1..12).each do |month| - Stats::CalculatingJob.perform_later(current_user.id, year, month) + Stats::CalculatingJob.perform_later(current_user.id, params[:year], month) + end + + target = "the whole #{params[:year]}" + else + Stats::CalculatingJob.perform_later(current_user.id, params[:year], params[:month]) + + target = "#{Date::MONTHNAMES[params[:month].to_i]} of #{params[:year]}" + end + + redirect_to stats_path, notice: "Stats for #{target} are being updated", status: :see_other + end + + def update_all + current_user.years_tracked.each do |year| + year[:months].each do |month| + Stats::CalculatingJob.perform_later( + current_user.id, year[:year], Date::ABBR_MONTHNAMES.index(month) + ) end end diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index d0bb046d..40893763 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -32,6 +32,8 @@ export default class extends Controller { settingsButtonAdded = false; layerControl = null; + visitedCitiesCache = new Map(); + trackedMonthsCache = null; connect() { console.log("Map controller connected"); @@ -52,8 +54,8 @@ export default class extends Controller { this.map = L.map(this.containerTarget).setView([this.center[0], this.center[1]], 14); // Set the maximum bounds to prevent infinite scroll - var southWest = L.latLng(-90, -180); - var northEast = L.latLng(90, 180); + var southWest = L.latLng(-120, -210); + var northEast = L.latLng(120, 210); var bounds = L.latLngBounds(southWest, northEast); this.map.setMaxBounds(bounds); @@ -171,12 +173,37 @@ export default class extends Controller { if (this.liveMapEnabled) { this.setupSubscription(); } + + // Add the toggle panel button + this.addTogglePanelButton(); + + // Check if we should open the panel based on localStorage or URL params + const urlParams = new URLSearchParams(window.location.search); + const isPanelOpen = localStorage.getItem('mapPanelOpen') === 'true'; + const hasDateParams = urlParams.has('start_at') && urlParams.has('end_at'); + + // Always create the panel first + this.toggleRightPanel(); + + // Then hide it if it shouldn't be open + if (!isPanelOpen && !hasDateParams) { + const panel = document.querySelector('.leaflet-right-panel'); + if (panel) { + panel.style.display = 'none'; + localStorage.setItem('mapPanelOpen', 'false'); + } + } } disconnect() { if (this.handleDeleteClick) { document.removeEventListener('click', this.handleDeleteClick); } + // Store panel state before disconnecting + if (this.rightPanel) { + const finalState = document.querySelector('.leaflet-right-panel').style.display !== 'none' ? 'true' : 'false'; + localStorage.setItem('mapPanelOpen', finalState); + } this.map.remove(); } @@ -382,10 +409,14 @@ export default class extends Controller { .then(data => { // Remove the marker and update all layers this.removeMarker(id); - + let wasPolyLayerVisible = false; // Explicitly remove old polylines layer from map if (this.polylinesLayer) { + if (this.map.hasLayer(this.polylinesLayer)) { + wasPolyLayerVisible = true; + } this.map.removeLayer(this.polylinesLayer); + } // Create new polylines layer @@ -397,10 +428,12 @@ export default class extends Controller { this.userSettings, this.distanceUnit ); - - // Add new polylines layer to map and to layer control - this.polylinesLayer.addTo(this.map); - + if (wasPolyLayerVisible) { + // Add new polylines layer to map and to layer control + this.polylinesLayer.addTo(this.map); + } else { + this.map.removeLayer(this.polylinesLayer); + } // Update the layer control if (this.layerControl) { this.map.removeControl(this.layerControl); @@ -898,8 +931,385 @@ export default class extends Controller { ${photo.type === 'video' ? '🎥 Video' : '📷 Photo'} `; - marker.bindPopup(popupContent); + marker.bindPopup(popupContent, { autoClose: false }); this.photoMarkers.addLayer(marker); } + + addTogglePanelButton() { + const TogglePanelControl = L.Control.extend({ + onAdd: (map) => { + const button = L.DomUtil.create('button', 'toggle-panel-button'); + button.innerHTML = '📅'; + + button.style.backgroundColor = 'white'; + button.style.width = '48px'; + button.style.height = '48px'; + button.style.border = 'none'; + button.style.cursor = 'pointer'; + button.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)'; + + // Disable map interactions when clicking the button + L.DomEvent.disableClickPropagation(button); + + // Toggle panel on button click + L.DomEvent.on(button, 'click', () => { + this.toggleRightPanel(); + }); + + return button; + } + }); + + // Add the control to the map + this.map.addControl(new TogglePanelControl({ position: 'topright' })); + } + + toggleRightPanel() { + if (this.rightPanel) { + const panel = document.querySelector('.leaflet-right-panel'); + if (panel) { + if (panel.style.display === 'none') { + panel.style.display = 'block'; + localStorage.setItem('mapPanelOpen', 'true'); + } else { + panel.style.display = 'none'; + localStorage.setItem('mapPanelOpen', 'false'); + } + return; + } + } + + this.rightPanel = L.control({ position: 'topright' }); + + this.rightPanel.onAdd = () => { + const div = L.DomUtil.create('div', 'leaflet-right-panel'); + const allMonths = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + + // Get current date from URL query parameters + const urlParams = new URLSearchParams(window.location.search); + const startDate = urlParams.get('start_at'); + const currentYear = startDate + ? new Date(startDate).getFullYear().toString() + : new Date().getFullYear().toString(); + const currentMonth = startDate + ? allMonths[new Date(startDate).getMonth()] + : allMonths[new Date().getMonth()]; + + // Initially create select with loading state and current year if available + div.innerHTML = ` +
Loading visited places...
+Error loading visited places
'; + } + } + } + + displayVisitedCities(citiesData) { + const container = document.getElementById('visited-cities-list'); + if (!container) return; + + if (!citiesData || citiesData.length === 0) { + container.innerHTML = 'No places visited during this period
'; + return; + } + + const html = citiesData.map(country => ` +<%= stat.distance %><%= DISTANCE_UNIT %>
<% if REVERSE_GEOCODING_ENABLED %><% cache [current_user, 'year_distance_stat', year], skip_digest: true do %> diff --git a/bin/rubocop b/bin/rubocop new file mode 100755 index 00000000..40330c0f --- /dev/null +++ b/bin/rubocop @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +require "rubygems" +require "bundler/setup" + +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + +load Gem.bin_path("rubocop", "rubocop") diff --git a/bin/setup b/bin/setup index 3cd5a9d7..28818af1 100755 --- a/bin/setup +++ b/bin/setup @@ -1,8 +1,8 @@ #!/usr/bin/env ruby require "fileutils" -# path to your application root. APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "dawarich" def system!(*args) system(*args, exception: true) @@ -30,4 +30,8 @@ FileUtils.chdir APP_ROOT do puts "\n== Restarting application server ==" system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" end diff --git a/config/application.rb b/config/application.rb index 9320f3ce..3d2dd0be 100644 --- a/config/application.rb +++ b/config/application.rb @@ -11,7 +11,7 @@ Bundler.require(*Rails.groups) module Dawarich class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.0 + config.load_defaults 8.0 # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. diff --git a/config/environment.rb b/config/environment.rb index c27e2a9f..7e5c58f9 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -6,6 +6,8 @@ require_relative 'application' # Initialize the Rails application. Rails.application.initialize! -# Clear the cache of the application version +# Clear the cache +Cache::CleaningJob.perform_later -Rails.cache.delete(CheckAppVersion::VERSION_CACHE_KEY) +# Preheat the cache +Cache::PreheatingJob.perform_later diff --git a/config/environments/development.rb b/config/environments/development.rb index 43f8e399..dd27f7bd 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -19,6 +19,11 @@ Rails.application.configure do # Enable server timing config.server_timing = true + # Info include generic and useful information about system operation, but avoids logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). If you + # want to log everything, leave the level on "debug". + config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'debug') + # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join('tmp/caching-dev.txt').exist? @@ -74,7 +79,7 @@ Rails.application.configure do # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true + config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true diff --git a/config/environments/production.rb b/config/environments/production.rb index f541929a..8b1a2d2a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,6 @@ -require "active_support/core_ext/integer/time" +# frozen_string_literal: true + +require 'active_support/core_ext/integer/time' Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. @@ -39,6 +41,8 @@ Rails.application.configure do # Store uploaded files on the local file system (see config/storage.yml for options). config.active_storage.service = :local + config.silence_healthcheck_path = '/api/v1/health' + # Mount Action Cable outside main process or domain. # config.action_cable.mount_path = nil # config.action_cable.url = "wss://example.com/cable" @@ -52,17 +56,17 @@ Rails.application.configure do config.force_ssl = true # Log to STDOUT by default - config.logger = ActiveSupport::Logger.new(STDOUT) - .tap { |logger| logger.formatter = ::Logger::Formatter.new } - .then { |logger| ActiveSupport::TaggedLogging.new(logger) } + config.logger = ActiveSupport::Logger.new($stdout) + .tap { |logger| logger.formatter = ::Logger::Formatter.new } + .then { |logger| ActiveSupport::TaggedLogging.new(logger) } # Prepend all log lines with the following tags. - config.log_tags = [ :request_id ] + config.log_tags = [:request_id] # Info include generic and useful information about system operation, but avoids logging too much # information to avoid inadvertent exposure of personally identifiable information (PII). If you # want to log everything, set the level to "debug". - config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") + config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'info') # Use a different cache store in production. # config.cache_store = :mem_cache_store diff --git a/config/environments/test.rb b/config/environments/test.rb index 3b958189..048b1342 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_support/core_ext/integer/time' # The test environment is used exclusively to run your application's @@ -24,11 +26,11 @@ Rails.application.configure do } # Show full error reports and disable caching. - config.consider_all_requests_local = true + config.consider_all_requests_local = true config.action_controller.perform_caching = false config.cache_store = :null_store - # Raise exceptions instead of rendering exception templates. + # Render exception templates for rescuable exceptions and raise for other exceptions. config.action_dispatch.show_exceptions = :rescuable # Disable request forgery protection in test environment. @@ -37,6 +39,8 @@ Rails.application.configure do # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. @@ -44,6 +48,10 @@ Rails.application.configure do # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: 'www.example.com' } + # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr @@ -59,6 +67,6 @@ Rails.application.configure do # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true end diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 2eeef966..019d0bbb 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -1,12 +1,9 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Version of your assets, change this if you want to expire all your assets. -Rails.application.config.assets.version = "1.0" +Rails.application.config.assets.version = '1.0' # Add additional assets to the asset load path. # Rails.application.config.assets.paths << Emoji.images_path - -# Precompile additional assets. -# application.js, application.css, and all non-JS/CSS in the app/assets -# folder are already added. -# Rails.application.config.assets.precompile += %w( admin.js admin.css ) diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index c2d89e28..1d8ba580 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,8 +1,10 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. -Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +Rails.application.config.filter_parameters += %i[ + passw email secret token _key crypt salt certificate otp ssn cvv cvc latitude longitude lat lng ] diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb index d873c8ea..837fb394 100644 --- a/config/initializers/geocoder.rb +++ b/config/initializers/geocoder.rb @@ -17,4 +17,6 @@ if defined?(PHOTON_API_HOST) settings[:photon] = { use_https: PHOTON_API_USE_HTTPS, host: PHOTON_API_HOST } end +settings[:http_headers] = { 'X-Api-Key' => PHOTON_API_KEY } if defined?(PHOTON_API_KEY) + Geocoder.configure(settings) diff --git a/config/initializers/httparty.rb b/config/initializers/httparty.rb new file mode 100644 index 00000000..49c4675d --- /dev/null +++ b/config/initializers/httparty.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Suppress warnings about nil deprecation +# https://github.com/jnunemaker/httparty/issues/568#issuecomment-1450473603 + +HTTParty::Response.class_eval do + def warn_about_nil_deprecation; end +end diff --git a/config/initializers/new_framework_defaults_7_1.rb b/config/initializers/new_framework_defaults_7_1.rb deleted file mode 100644 index d41af162..00000000 --- a/config/initializers/new_framework_defaults_7_1.rb +++ /dev/null @@ -1,223 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file eases your Rails 7.1 framework defaults upgrade. -# -# Uncomment each configuration one by one to switch to the new default. -# Once your application is ready to run with all new defaults, you can remove -# this file and set the `config.load_defaults` to `7.1`. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. -# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html - -# No longer add autoloaded paths into `$LOAD_PATH`. This means that you won't be able -# to manually require files that are managed by the autoloader, which you shouldn't do anyway. -# This will reduce the size of the load path, making `require` faster if you don't use bootsnap, or reduce the size -# of the bootsnap cache if you use it. -# Rails.application.config.add_autoload_paths_to_load_path = false - -# Remove the default X-Download-Options headers since it is used only by Internet Explorer. -# If you need to support Internet Explorer, add back `"X-Download-Options" => "noopen"`. -# Rails.application.config.action_dispatch.default_headers = { -# "X-Frame-Options" => "SAMEORIGIN", -# "X-XSS-Protection" => "0", -# "X-Content-Type-Options" => "nosniff", -# "X-Permitted-Cross-Domain-Policies" => "none", -# "Referrer-Policy" => "strict-origin-when-cross-origin" -# } - -# Do not treat an `ActionController::Parameters` instance -# as equal to an equivalent `Hash` by default. -# Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality = false - -# Active Record Encryption now uses SHA-256 as its hash digest algorithm. Important: If you have -# data encrypted with previous Rails versions, there are two scenarios to consider: -# -# 1. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA1 (the default -# before Rails 7.0), you need to configure SHA-1 for Active Record Encryption too: -# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA1 -# 2. If you have +config.active_support.key_generator_hash_digest_class+ configured as SHA256 (the new default -# in 7.0), then you need to configure SHA-256 for Active Record Encryption: -# Rails.application.config.active_record.encryption.hash_digest_class = OpenSSL::Digest::SHA256 -# -# If you don't currently have data encrypted with Active Record encryption, you can disable this setting to -# configure the default behavior starting 7.1+: -# Rails.application.config.active_record.encryption.support_sha1_for_non_deterministic_encryption = false - -# No longer run after_commit callbacks on the first of multiple Active Record -# instances to save changes to the same database row within a transaction. -# Instead, run these callbacks on the instance most likely to have internal -# state which matches what was committed to the database, typically the last -# instance to save. -# Rails.application.config.active_record.run_commit_callbacks_on_first_saved_instances_in_transaction = false - -# Configures SQLite with a strict strings mode, which disables double-quoted string literals. -# -# SQLite has some quirks around double-quoted string literals. -# It first tries to consider double-quoted strings as identifier names, but if they don't exist -# it then considers them as string literals. Because of this, typos can silently go unnoticed. -# For example, it is possible to create an index for a non existing column. -# See https://www.sqlite.org/quirks.html#double_quoted_string_literals_are_accepted for more details. -# Rails.application.config.active_record.sqlite3_adapter_strict_strings_by_default = true - -# Disable deprecated singular associations names -# Rails.application.config.active_record.allow_deprecated_singular_associations_name = false - -# Enable the Active Job `BigDecimal` argument serializer, which guarantees -# roundtripping. Without this serializer, some queue adapters may serialize -# `BigDecimal` arguments as simple (non-roundtrippable) strings. -# -# When deploying an application with multiple replicas, old (pre-Rails 7.1) -# replicas will not be able to deserialize `BigDecimal` arguments from this -# serializer. Therefore, this setting should only be enabled after all replicas -# have been successfully upgraded to Rails 7.1. -# Rails.application.config.active_job.use_big_decimal_serializer = true - -# Specify if an `ArgumentError` should be raised if `Rails.cache` `fetch` or -# `write` are given an invalid `expires_at` or `expires_in` time. -# Options are `true`, and `false`. If `false`, the exception will be reported -# as `handled` and logged instead. -# Rails.application.config.active_support.raise_on_invalid_cache_expiration_time = true - -# Specify whether Query Logs will format tags using the SQLCommenter format -# (https://open-telemetry.github.io/opentelemetry-sqlcommenter/), or using the legacy format. -# Options are `:legacy` and `:sqlcommenter`. -# Rails.application.config.active_record.query_log_tags_format = :sqlcommenter - -# Specify the default serializer used by `MessageEncryptor` and `MessageVerifier` -# instances. -# -# The legacy default is `:marshal`, which is a potential vector for -# deserialization attacks in cases where a message signing secret has been -# leaked. -# -# In Rails 7.1, the new default is `:json_allow_marshal` which serializes and -# deserializes with `ActiveSupport::JSON`, but can fall back to deserializing -# with `Marshal` so that legacy messages can still be read. -# -# In Rails 7.2, the default will become `:json` which serializes and -# deserializes with `ActiveSupport::JSON` only. -# -# Alternatively, you can choose `:message_pack` or `:message_pack_allow_marshal`, -# which serialize with `ActiveSupport::MessagePack`. `ActiveSupport::MessagePack` -# can roundtrip some Ruby types that are not supported by JSON, and may provide -# improved performance, but it requires the `msgpack` gem. -# -# For more information, see -# https://guides.rubyonrails.org/v7.1/configuring.html#config-active-support-message-serializer -# -# If you are performing a rolling deploy of a Rails 7.1 upgrade, wherein servers -# that have not yet been upgraded must be able to read messages from upgraded -# servers, first deploy without changing the serializer, then set the serializer -# in a subsequent deploy. -# Rails.application.config.active_support.message_serializer = :json_allow_marshal - -# Enable a performance optimization that serializes message data and metadata -# together. This changes the message format, so messages serialized this way -# cannot be read by older versions of Rails. However, messages that use the old -# format can still be read, regardless of whether this optimization is enabled. -# -# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have -# not yet been upgraded must be able to read messages from upgraded servers, -# leave this optimization off on the first deploy, then enable it on a -# subsequent deploy. -# Rails.application.config.active_support.use_message_serializer_for_metadata = true - -# Set the maximum size for Rails log files. -# -# `config.load_defaults 7.1` does not set this value for environments other than -# development and test. -# -# if Rails.env.local? -# Rails.application.config.log_file_size = 100 * 1024 * 1024 -# end - -# Enable raising on assignment to attr_readonly attributes. The previous -# behavior would allow assignment but silently not persist changes to the -# database. -# Rails.application.config.active_record.raise_on_assign_to_attr_readonly = true - -# Enable validating only parent-related columns for presence when the parent is mandatory. -# The previous behavior was to validate the presence of the parent record, which performed an extra query -# to get the parent every time the child record was updated, even when parent has not changed. -# Rails.application.config.active_record.belongs_to_required_validates_foreign_key = false - -# Enable precompilation of `config.filter_parameters`. Precompilation can -# improve filtering performance, depending on the quantity and types of filters. -# Rails.application.config.precompile_filter_parameters = true - -# Enable before_committed! callbacks on all enrolled records in a transaction. -# The previous behavior was to only run the callbacks on the first copy of a record -# if there were multiple copies of the same record enrolled in the transaction. -# Rails.application.config.active_record.before_committed_on_all_records = true - -# Disable automatic column serialization into YAML. -# To keep the historic behavior, you can set it to `YAML`, however it is -# recommended to explicitly define the serialization method for each column -# rather than to rely on a global default. -# Rails.application.config.active_record.default_column_serializer = nil - -# Enable a performance optimization that serializes Active Record models -# in a faster and more compact way. -# -# To perform a rolling deploy of a Rails 7.1 upgrade, wherein servers that have -# not yet been upgraded must be able to read caches from upgraded servers, -# leave this optimization off on the first deploy, then enable it on a -# subsequent deploy. -# Rails.application.config.active_record.marshalling_format_version = 7.1 - -# Run `after_commit` and `after_*_commit` callbacks in the order they are defined in a model. -# This matches the behaviour of all other callbacks. -# In previous versions of Rails, they ran in the inverse order. -# Rails.application.config.active_record.run_after_transaction_callbacks_in_order_defined = true - -# Whether a `transaction` block is committed or rolled back when exited via `return`, `break` or `throw`. -# -# Rails.application.config.active_record.commit_transaction_on_non_local_return = true - -# Controls when to generate a value for has_secure_token declarations. -# -# Rails.application.config.active_record.generate_secure_token_on = :initialize - -# ** Please read carefully, this must be configured in config/application.rb ** -# Change the format of the cache entry. -# Changing this default means that all new cache entries added to the cache -# will have a different format that is not supported by Rails 7.0 -# applications. -# Only change this value after your application is fully deployed to Rails 7.1 -# and you have no plans to rollback. -# When you're ready to change format, add this to `config/application.rb` (NOT -# this file): -# config.active_support.cache_format_version = 7.1 - -# Configure Action View to use HTML5 standards-compliant sanitizers when they are supported on your -# platform. -# -# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action View to use HTML5-compliant -# sanitizers if they are supported, else fall back to HTML4 sanitizers. -# -# In previous versions of Rails, Action View always used `Rails::HTML4::Sanitizer` as its vendor. -# -# Rails.application.config.action_view.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor - -# Configure Action Text to use an HTML5 standards-compliant sanitizer when it is supported on your -# platform. -# -# `Rails::HTML::Sanitizer.best_supported_vendor` will cause Action Text to use HTML5-compliant -# sanitizers if they are supported, else fall back to HTML4 sanitizers. -# -# In previous versions of Rails, Action Text always used `Rails::HTML4::Sanitizer` as its vendor. -# -# Rails.application.config.action_text.sanitizer_vendor = Rails::HTML::Sanitizer.best_supported_vendor - -# Configure the log level used by the DebugExceptions middleware when logging -# uncaught exceptions during requests -# Rails.application.config.action_dispatch.debug_exception_log_level = :error - -# Configure the test helpers in Action View, Action Dispatch, and rails-dom-testing to use HTML5 -# parsers. -# -# Nokogiri::HTML5 isn't supported on JRuby, so JRuby applications must set this to :html4. -# -# In previous versions of Rails, these test helpers always used an HTML4 parser. -# -# Rails.application.config.dom_testing_default_html_version = :html5 diff --git a/config/initializers/new_framework_defaults_8_0.rb b/config/initializers/new_framework_defaults_8_0.rb new file mode 100644 index 00000000..92efa951 --- /dev/null +++ b/config/initializers/new_framework_defaults_8_0.rb @@ -0,0 +1,30 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 8.0 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `8.0`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +### +# Specifies whether `to_time` methods preserve the UTC offset of their receivers or preserves the timezone. +# If set to `:zone`, `to_time` methods will use the timezone of their receivers. +# If set to `:offset`, `to_time` methods will use the UTC offset. +# If `false`, `to_time` methods will convert to the local system UTC offset instead. +#++ +# Rails.application.config.active_support.to_time_preserves_timezone = :zone + +### +# When both `If-Modified-Since` and `If-None-Match` are provided by the client +# only consider `If-None-Match` as specified by RFC 7232 Section 6. +# If set to `false` both conditions need to be satisfied. +#++ +# Rails.application.config.action_dispatch.strict_freshness = true + +### +# Set `Regexp.timeout` to `1`s by default to improve security over Regexp Denial-of-Service attacks. +#++ +# Regexp.timeout = 1 diff --git a/config/puma.rb b/config/puma.rb index d3094caa..e0eb3db7 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -6,9 +6,8 @@ # the maximum value specified for Puma. Default is set to 5 threads for minimum # and maximum; this matches the default thread size of Active Record. # -max_threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) -min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } -threads min_threads_count, max_threads_count +threads_count = ENV.fetch('RAILS_MAX_THREADS', 5) +threads threads_count, threads_count # Specifies the `worker_timeout` threshold that Puma will use to wait before # terminating a worker in development environments. @@ -50,7 +49,7 @@ if ENV['PROMETHEUS_EXPORTER_ENABLED'].to_s == 'true' before_fork do PrometheusExporter::Client.default = PrometheusExporter::Client.new( - host: ENV.fetch('PROMETHEUS_EXPORTER_HOST', '0.0.0.0'), + host: ENV.fetch('PROMETHEUS_EXPORTER_HOST', 'ANY'), port: ENV.fetch('PROMETHEUS_EXPORTER_PORT', 9394) ) end diff --git a/config/routes.rb b/config/routes.rb index 2c40e93d..8d28efde 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,12 +38,17 @@ Rails.application.routes.draw do end resources :notifications, only: %i[index show destroy] post 'notifications/mark_as_read', to: 'notifications#mark_as_read', as: :mark_notifications_as_read + post 'notifications/destroy_all', to: 'notifications#destroy_all', as: :delete_all_notifications resources :stats, only: :index do collection do - post :update + put :update_all end end get 'stats/:year', to: 'stats#show', constraints: { year: /\d{4}/ } + put 'stats/:year/:month/update', + to: 'stats#update', + as: :update_year_month_stats, + constraints: { year: /\d{4}/, month: /\d{1,2}|all/ } root to: 'home#index' devise_for :users, skip: [:registrations] @@ -52,8 +57,6 @@ Rails.application.routes.draw do put 'users' => 'devise/registrations#update', :as => 'user_registration' end - # And then modify the app/views/devise/shared/_links.erb - get 'map', to: 'map#index' namespace :api do @@ -78,6 +81,11 @@ Rails.application.routes.draw do namespace :countries do resources :borders, only: :index + resources :visited_cities, only: :index + end + + namespace :points do + get 'tracked_months', to: 'tracked_months#index' end resources :photos, only: %i[index] do diff --git a/db/data/20241202125248_set_reverse_geocoded_at_for_points.rb b/db/data/20241202125248_set_reverse_geocoded_at_for_points.rb index 289380cf..cf0faba8 100644 --- a/db/data/20241202125248_set_reverse_geocoded_at_for_points.rb +++ b/db/data/20241202125248_set_reverse_geocoded_at_for_points.rb @@ -2,9 +2,7 @@ class SetReverseGeocodedAtForPoints < ActiveRecord::Migration[7.2] def up - # rubocop:disable Rails/SkipsModelValidations - Point.where.not(geodata: {}).update_all(reverse_geocoded_at: Time.current) - # rubocop:enable Rails/SkipsModelValidations + DataMigrations::SetReverseGeocodedAtForPointsJob.perform_later end def down diff --git a/db/migrate/[timestamp]_add_index_to_points_timestamp.rb b/db/migrate/[timestamp]_add_index_to_points_timestamp.rb deleted file mode 100644 index 8e4bc3fa..00000000 --- a/db/migrate/[timestamp]_add_index_to_points_timestamp.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -class AddIndexToPointsTimestamp < ActiveRecord::Migration[7.2] - disable_ddl_transaction! - - def change - add_index :points, %i[user_id timestamp], algorithm: :concurrently - end -end diff --git a/docker-compose.yml b/docker-compose.yml index b3dc7f96..fc46ae30 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,10 +18,12 @@ services: timeout: 10s dawarich_db: image: postgres:14.2-alpine + shm_size: 1G container_name: dawarich_db volumes: - dawarich_db_data:/var/lib/postgresql/data - dawarich_shared:/var/shared + # - ./postgresql.conf:/etc/postgresql/postgresql.conf # Optional, uncomment if you want to use a custom config networks: - dawarich environment: @@ -34,11 +36,12 @@ services: retries: 5 start_period: 30s timeout: 10s + # command: postgres -c config_file=/etc/postgresql/postgresql.conf # Use custom config, uncomment if you want to use a custom config dawarich_app: image: freikin/dawarich:latest container_name: dawarich_app volumes: - - dawarich_gem_cache_app:/usr/local/bundle/gems_app + - dawarich_gem_cache_app:/usr/local/bundle/gems - dawarich_public:/var/app/public - dawarich_watched:/var/app/tmp/imports/watched networks: @@ -97,7 +100,7 @@ services: image: freikin/dawarich:latest container_name: dawarich_sidekiq volumes: - - dawarich_gem_cache_sidekiq:/usr/local/bundle/gems_sidekiq + - dawarich_gem_cache_sidekiq:/usr/local/bundle/gems - dawarich_public:/var/app/public - dawarich_watched:/var/app/tmp/imports/watched networks: diff --git a/docs/How_to_install_Dawarich_in_k8s.md b/docs/How_to_install_Dawarich_in_k8s.md index 890a4b42..f21c8658 100644 --- a/docs/How_to_install_Dawarich_in_k8s.md +++ b/docs/How_to_install_Dawarich_in_k8s.md @@ -1,5 +1,7 @@ # How to install Dawarich on Kubernetes +> An **unofficial Helm chart** is available [here](https://github.com/Cogitri/charts/tree/master/charts/dawarich). For a manual installation using YAML manifests, see below. + ## Prerequisites - Kubernetes cluster and basic kubectl knowledge. @@ -7,6 +9,7 @@ - Working Postgres and Redis instances. In this example Postgres lives in 'db' namespace and Redis in 'redis' namespace. - Ngingx ingress controller with Letsencrypt integeation. - This example uses 'example.com' as a domain name, you want to change it to your own. +- This will work on IPv4 and IPv6 Single Stack clusters, as well as Dual Stack deployments. ## Installation @@ -140,8 +143,8 @@ spec: image: freikin/dawarich:0.16.4 imagePullPolicy: Always volumeMounts: - - mountPath: /usr/local/bundle/gems_app - name: gem-cache + - mountPath: /usr/local/bundle/gems + name: gem-app - mountPath: /var/app/public name: public - mountPath: /var/app/tmp/imports/watched @@ -149,7 +152,7 @@ spec: command: - "dev-entrypoint.sh" args: - - "bin/rails server -p 3000 -b 0.0.0.0" + - "bin/rails server -p 3000 -b ::" resources: requests: memory: "1Gi" @@ -196,7 +199,7 @@ spec: image: freikin/dawarich:0.16.4 imagePullPolicy: Always volumeMounts: - - mountPath: /usr/local/bundle/gems_sidekiq + - mountPath: /usr/local/bundle/gems name: gem-sidekiq - mountPath: /var/app/public name: public diff --git a/postgresql.conf.example b/postgresql.conf.example new file mode 100644 index 00000000..9e1687c3 --- /dev/null +++ b/postgresql.conf.example @@ -0,0 +1,36 @@ +listen_addresses = '*' +max_connections = 50 + +shared_buffers = 512MB + +work_mem = 128MB +maintenance_work_mem = 128MB + + +dynamic_shared_memory_type = posix +checkpoint_timeout = 10min # range 30s-1d +max_wal_size = 2GB +min_wal_size = 80MB +max_parallel_workers_per_gather = 4 + +log_min_duration_statement = 500 # -1 is disabled, 0 logs all statements + # -1 disables, 0 logs all temp files +log_timezone = 'UTC' + + +autovacuum_vacuum_scale_factor = 0.05 # fraction of table size before vacuum +autovacuum_analyze_scale_factor = 0.05 # fraction of table size before analyze + + +datestyle = 'iso, dmy' + +timezone = 'UTC' + +lc_messages = 'en_US.utf8' # locale for system error message + # strings +lc_monetary = 'en_US.utf8' # locale for monetary formatting +lc_numeric = 'en_US.utf8' # locale for number formatting +lc_time = 'en_US.utf8' # locale for time formatting + + +default_text_search_config = 'pg_catalog.english' diff --git a/public/400.html b/public/400.html new file mode 100644 index 00000000..282dbc8c --- /dev/null +++ b/public/400.html @@ -0,0 +1,114 @@ + + + + +
+ +The server cannot process the request due to a client error. Please check the request and try again. If you’re the application owner check the logs for more information.
+The page you were looking for doesn’t exist. You may have mistyped the address or the page may have moved. If you’re the application owner check the logs for more information.
+You may have mistyped the address or the page may have moved.
-If you are the application owner check the logs for more information.
-