mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 09:41:40 -05:00
commit
1a67c6a154
17 changed files with 175 additions and 12 deletions
|
|
@ -1 +1 @@
|
||||||
0.23.3
|
0.23.5
|
||||||
|
|
|
||||||
34
.github/workflows/build_and_push.yml
vendored
34
.github/workflows/build_and_push.yml
vendored
|
|
@ -15,13 +15,16 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.branch || github.ref_name }}
|
ref: ${{ github.event.inputs.branch || github.ref_name }}
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v1
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Cache Docker layers
|
- name: Cache Docker layers
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
|
|
@ -29,20 +32,41 @@ jobs:
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-buildx-
|
${{ runner.os }}-buildx-
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v3.1.0
|
uses: docker/login-action@v3.1.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set Docker tags
|
||||||
|
id: docker_meta
|
||||||
|
run: |
|
||||||
|
VERSION=${GITHUB_REF#refs/tags/}
|
||||||
|
TAGS="freikin/dawarich:${VERSION}"
|
||||||
|
|
||||||
|
# Add :rc tag for pre-releases
|
||||||
|
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
|
||||||
|
TAGS="${TAGS},freikin/dawarich:rc"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add :latest tag only if release is not a pre-release
|
||||||
|
if [ "${{ github.event.release.prerelease }}" != "true" ]; then
|
||||||
|
TAGS="${TAGS},freikin/dawarich:latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@v2
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile.dev
|
file: ./docker/Dockerfile.dev
|
||||||
push: true
|
push: true
|
||||||
tags: freikin/dawarich:latest,freikin/dawarich:${{ github.event.inputs.branch || github.ref_name }}
|
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
cache-from: type=local,src=/tmp/.buildx-cache
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
cache-to: type=local,dest=/tmp/.buildx-cache
|
||||||
|
|
|
||||||
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
# 0.23.5 - 2025-01-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A test for building rc Docker image.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix authentication to `GET /api/v1/countries/visited_cities` with header `Authorization: Bearer YOUR_API_KEY` instead of `api_key` query param. #679
|
||||||
|
- Fix a bug where a gpx file with empty tracks was not being imported. #646
|
||||||
|
- Fix a bug where rc version was being checked as a stable release. #711
|
||||||
|
|
||||||
# 0.23.3 - 2025-01-21
|
# 0.23.3 - 2025-01-21
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
2
Gemfile
2
Gemfile
|
|
@ -30,6 +30,7 @@ gem 'sidekiq-cron'
|
||||||
gem 'sidekiq-limit_fetch'
|
gem 'sidekiq-limit_fetch'
|
||||||
gem 'sprockets-rails'
|
gem 'sprockets-rails'
|
||||||
gem 'stimulus-rails'
|
gem 'stimulus-rails'
|
||||||
|
gem 'strong_migrations'
|
||||||
gem 'tailwindcss-rails'
|
gem 'tailwindcss-rails'
|
||||||
gem 'turbo-rails'
|
gem 'turbo-rails'
|
||||||
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
||||||
|
|
@ -54,6 +55,7 @@ group :test do
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
|
gem 'database_consistency', require: false
|
||||||
gem 'foreman'
|
gem 'foreman'
|
||||||
gem 'rubocop-rails', require: false
|
gem 'rubocop-rails', require: false
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,8 @@ GEM
|
||||||
data_migrate (11.2.0)
|
data_migrate (11.2.0)
|
||||||
activerecord (>= 6.1)
|
activerecord (>= 6.1)
|
||||||
railties (>= 6.1)
|
railties (>= 6.1)
|
||||||
|
database_consistency (2.0.0)
|
||||||
|
activerecord (>= 3.2)
|
||||||
date (3.4.1)
|
date (3.4.1)
|
||||||
debug (1.10.0)
|
debug (1.10.0)
|
||||||
irb (~> 1.10)
|
irb (~> 1.10)
|
||||||
|
|
@ -392,6 +394,8 @@ GEM
|
||||||
stimulus-rails (1.3.4)
|
stimulus-rails (1.3.4)
|
||||||
railties (>= 6.0.0)
|
railties (>= 6.0.0)
|
||||||
stringio (3.1.2)
|
stringio (3.1.2)
|
||||||
|
strong_migrations (2.1.0)
|
||||||
|
activerecord (>= 6.1)
|
||||||
super_diff (0.15.0)
|
super_diff (0.15.0)
|
||||||
attr_extras (>= 6.2.4)
|
attr_extras (>= 6.2.4)
|
||||||
diff-lcs
|
diff-lcs
|
||||||
|
|
@ -442,6 +446,7 @@ DEPENDENCIES
|
||||||
bootsnap
|
bootsnap
|
||||||
chartkick
|
chartkick
|
||||||
data_migrate
|
data_migrate
|
||||||
|
database_consistency
|
||||||
debug
|
debug
|
||||||
devise
|
devise
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
|
|
@ -478,6 +483,7 @@ DEPENDENCIES
|
||||||
simplecov
|
simplecov
|
||||||
sprockets-rails
|
sprockets-rails
|
||||||
stimulus-rails
|
stimulus-rails
|
||||||
|
strong_migrations
|
||||||
super_diff
|
super_diff
|
||||||
tailwindcss-rails
|
tailwindcss-rails
|
||||||
turbo-rails
|
turbo-rails
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,6 @@ class Api::V1::Countries::VisitedCitiesController < ApiController
|
||||||
private
|
private
|
||||||
|
|
||||||
def required_params
|
def required_params
|
||||||
%i[start_at end_at api_key]
|
%i[start_at end_at]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,11 @@ class User < ApplicationRecord
|
||||||
after_create :create_api_key
|
after_create :create_api_key
|
||||||
before_save :strip_trailing_slashes
|
before_save :strip_trailing_slashes
|
||||||
|
|
||||||
|
validates :email, presence: true
|
||||||
|
validates :reset_password_token, uniqueness: true, allow_nil: true
|
||||||
|
|
||||||
|
attribute :admin, :boolean, default: false
|
||||||
|
|
||||||
def countries_visited
|
def countries_visited
|
||||||
stats.pluck(:toponyms).flatten.map { _1['country'] }.uniq.compact
|
stats.pluck(:toponyms).flatten.map { _1['country'] }.uniq.compact
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@ class CheckAppVersion
|
||||||
|
|
||||||
def latest_version
|
def latest_version
|
||||||
Rails.cache.fetch(VERSION_CACHE_KEY, expires_in: 6.hours) do
|
Rails.cache.fetch(VERSION_CACHE_KEY, expires_in: 6.hours) do
|
||||||
JSON.parse(Net::HTTP.get(URI.parse(@repo_url)))[0]['name']
|
versions = JSON.parse(Net::HTTP.get(URI.parse(@repo_url)))
|
||||||
|
# Find first version that contains only numbers and dots
|
||||||
|
release_version = versions.find { |v| v['name'].match?(/^\d+\.\d+\.\d+$/) }
|
||||||
|
release_version ? release_version['name'] : APP_VERSION
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ class Gpx::TrackParser
|
||||||
tracks = json['gpx']['trk']
|
tracks = json['gpx']['trk']
|
||||||
tracks_arr = tracks.is_a?(Array) ? tracks : [tracks]
|
tracks_arr = tracks.is_a?(Array) ? tracks : [tracks]
|
||||||
|
|
||||||
tracks_arr.map { parse_track(_1) }.flatten.each.with_index(1) do |point, index|
|
tracks_arr.map { parse_track(_1) }.flatten.compact.each.with_index(1) do |point, index|
|
||||||
create_point(point, index)
|
create_point(point, index)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -23,6 +23,8 @@ class Gpx::TrackParser
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_track(track)
|
def parse_track(track)
|
||||||
|
return if track['trkseg'].blank?
|
||||||
|
|
||||||
segments = track['trkseg']
|
segments = track['trkseg']
|
||||||
segments_array = segments.is_a?(Array) ? segments : [segments]
|
segments_array = segments.is_a?(Array) ? segments : [segments]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,11 @@ class Tasks::Imports::GoogleRecords
|
||||||
|
|
||||||
def process_file_in_batches(import_id)
|
def process_file_in_batches(import_id)
|
||||||
batch = []
|
batch = []
|
||||||
|
index = 0
|
||||||
|
|
||||||
Oj.load_file(@file_path, mode: :compat) do |record|
|
Oj.load_file(@file_path, mode: :compat) do |record|
|
||||||
next unless record.is_a?(Hash) && record['locations']
|
next unless record.is_a?(Hash) && record['locations']
|
||||||
|
|
||||||
index = 0
|
|
||||||
|
|
||||||
record['locations'].each do |location|
|
record['locations'].each do |location|
|
||||||
batch << location
|
batch << location
|
||||||
|
|
||||||
|
|
@ -47,7 +46,7 @@ class Tasks::Imports::GoogleRecords
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Import::GoogleTakeoutJob.perform_later(import_id, Oj.dump(batch)) if batch.any?
|
Import::GoogleTakeoutJob.perform_later(import_id, Oj.dump(batch), index) if batch.any?
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_start
|
def log_start
|
||||||
|
|
|
||||||
26
config/initializers/strong_migrations.rb
Normal file
26
config/initializers/strong_migrations.rb
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Mark existing migrations as safe
|
||||||
|
StrongMigrations.start_after = 20_250_122_150_500
|
||||||
|
|
||||||
|
# Set timeouts for migrations
|
||||||
|
# If you use PgBouncer in transaction mode, delete these lines and set timeouts on the database user
|
||||||
|
StrongMigrations.lock_timeout = 10.seconds
|
||||||
|
StrongMigrations.statement_timeout = 1.hour
|
||||||
|
|
||||||
|
# Analyze tables after indexes are added
|
||||||
|
# Outdated statistics can sometimes hurt performance
|
||||||
|
StrongMigrations.auto_analyze = true
|
||||||
|
|
||||||
|
# Set the version of the production database
|
||||||
|
# so the right checks are run in development
|
||||||
|
# StrongMigrations.target_version = 10
|
||||||
|
|
||||||
|
# Add custom checks
|
||||||
|
# StrongMigrations.add_check do |method, args|
|
||||||
|
# if method == :add_index && args[0].to_s == "users"
|
||||||
|
# stop! "No more indexes on the users table"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Make some operations safe by default
|
||||||
|
# See https://github.com/ankane/strong_migrations#safe-by-default
|
||||||
|
# StrongMigrations.safe_by_default = true
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddDatabaseUsersConstraints < ActiveRecord::Migration[8.0]
|
||||||
|
def change
|
||||||
|
add_check_constraint :users, 'email IS NOT NULL', name: 'users_email_null', validate: false
|
||||||
|
add_check_constraint :users, 'admin IS NOT NULL', name: 'users_admin_null', validate: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ValidateAddDatabaseUsersConstraints < ActiveRecord::Migration[8.0]
|
||||||
|
def up
|
||||||
|
validate_check_constraint :users, name: 'users_email_null'
|
||||||
|
change_column_null :users, :email, false
|
||||||
|
remove_check_constraint :users, name: 'users_email_null'
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
add_check_constraint :users, 'email IS NOT NULL', name: 'users_email_null', validate: false
|
||||||
|
change_column_null :users, :email, true
|
||||||
|
end
|
||||||
|
end
|
||||||
2
db/schema.rb
generated
2
db/schema.rb
generated
|
|
@ -224,6 +224,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_01_20_154555) do
|
||||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_check_constraint "users", "admin IS NOT NULL", name: "users_admin_null", validate: false
|
||||||
|
|
||||||
create_table "visits", force: :cascade do |t|
|
create_table "visits", force: :cascade do |t|
|
||||||
t.bigint "area_id"
|
t.bigint "area_id"
|
||||||
t.bigint "user_id", null: false
|
t.bigint "user_id", null: false
|
||||||
|
|
|
||||||
41
spec/fixtures/files/gpx/arc_example.gpx
vendored
Normal file
41
spec/fixtures/files/gpx/arc_example.gpx
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<gpx creator="Arc App" version="1.1" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<wpt lat="16.822590884135522" lon="100.26450188975753">
|
||||||
|
<time>2024-12-17T19:40:05+07:00</time>
|
||||||
|
<ele>89.9031832732575</ele>
|
||||||
|
<name>Topland Hotel & Convention Center</name>
|
||||||
|
</wpt>
|
||||||
|
<trk>
|
||||||
|
<type>walking</type>
|
||||||
|
<trkseg />
|
||||||
|
</trk>
|
||||||
|
<trk>
|
||||||
|
<type>taxi</type>
|
||||||
|
<trkseg>
|
||||||
|
<trkpt lat="16.82179723266299" lon="100.26501096574162">
|
||||||
|
<ele>49.96302288016834</ele>
|
||||||
|
<time>2024-12-18T08:44:09+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="16.821804657654933" lon="100.26501263671403">
|
||||||
|
<ele>49.884678590538186</ele>
|
||||||
|
<time>2024-12-18T08:44:16+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="16.821831929143876" lon="100.26500741687741">
|
||||||
|
<ele>49.71960135141746</ele>
|
||||||
|
<time>2024-12-18T08:44:21+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="16.821889949418637" lon="100.26494683052165">
|
||||||
|
<ele>49.91594081568717</ele>
|
||||||
|
<time>2024-12-18T08:44:29+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="16.821914934283804" lon="100.26485762911803">
|
||||||
|
<ele>50.344669848377556</ele>
|
||||||
|
<time>2024-12-18T08:44:38+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="16.821949486294397" lon="100.26482772930362">
|
||||||
|
<ele>50.12800953488726</ele>
|
||||||
|
<time>2024-12-18T08:44:45+07:00</time>
|
||||||
|
</trkpt>
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>
|
||||||
|
|
@ -29,6 +29,15 @@ RSpec.describe CheckAppVersion do
|
||||||
it { is_expected.to be true }
|
it { is_expected.to be true }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when latest version is not a stable release' do
|
||||||
|
before do
|
||||||
|
stub_request(:any, 'https://api.github.com/repos/Freika/dawarich/tags')
|
||||||
|
.to_return(status: 200, body: '[{"name": "1.0.0-rc.1"}]', headers: {})
|
||||||
|
end
|
||||||
|
|
||||||
|
it { is_expected.to be false }
|
||||||
|
end
|
||||||
|
|
||||||
context 'when request fails' do
|
context 'when request fails' do
|
||||||
before do
|
before do
|
||||||
allow(Net::HTTP).to receive(:get).and_raise(StandardError)
|
allow(Net::HTTP).to receive(:get).and_raise(StandardError)
|
||||||
|
|
|
||||||
|
|
@ -74,5 +74,15 @@ RSpec.describe Gpx::TrackParser do
|
||||||
expect(Point.first.velocity).to eq('2.8')
|
expect(Point.first.velocity).to eq('2.8')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when file exported from Arc' do
|
||||||
|
context 'when file has empty tracks' do
|
||||||
|
let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/arc_example.gpx') }
|
||||||
|
|
||||||
|
it 'creates points' do
|
||||||
|
expect { parser }.to change { Point.count }.by(6)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue