mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 09:41:40 -05:00
commit
db5f6ee9e7
17 changed files with 202 additions and 72 deletions
|
|
@ -1 +1 @@
|
||||||
0.9.2
|
0.9.3
|
||||||
|
|
|
||||||
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -5,6 +5,20 @@ 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.9.3] — 2024-07-19
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Admin flag to the database. Now not only the first user in the system can create new users, but also users with the admin flag set to true. This will make easier introduction of more admin functions in the future.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Route hover distance is now being rendered in kilometers, not in meters, if route distance is more than 1 km.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [0.9.2] — 2024-07-19
|
## [0.9.2] — 2024-07-19
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -13,10 +13,10 @@ class ApplicationController < ActionController::Base
|
||||||
@unread_notifications ||= Notification.where(user: current_user).unread
|
@unread_notifications ||= Notification.where(user: current_user).unread
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_first_user!
|
def authenticate_admin!
|
||||||
return if current_user == User.first
|
return if current_user.admin?
|
||||||
|
|
||||||
redirect_to root_path, notice: 'You are not authorized to perform this action.', status: :unauthorized
|
redirect_to root_path, notice: 'You are not authorized to perform this action.', status: :see_other
|
||||||
end
|
end
|
||||||
|
|
||||||
def authenticate_api_key
|
def authenticate_api_key
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class Settings::BackgroundJobsController < ApplicationController
|
class Settings::BackgroundJobsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :authenticate_first_user!
|
before_action :authenticate_admin!
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@queues = Sidekiq::Queue.all
|
@queues = Sidekiq::Queue.all
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class Settings::UsersController < ApplicationController
|
class Settings::UsersController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :authenticate_first_user!
|
before_action :authenticate_admin!
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@user = User.new(
|
@user = User.new(
|
||||||
|
|
@ -12,7 +12,8 @@ class Settings::UsersController < ApplicationController
|
||||||
)
|
)
|
||||||
|
|
||||||
if @user.save
|
if @user.save
|
||||||
redirect_to settings_url, notice: "User was successfully created, email is #{@user.email}, password is \"password\"."
|
redirect_to settings_url,
|
||||||
|
notice: "User was successfully created, email is #{@user.email}, password is \"password\"."
|
||||||
else
|
else
|
||||||
redirect_to settings_url, notice: 'User could not be created.', status: :unprocessable_entity
|
redirect_to settings_url, notice: 'User could not be created.', status: :unprocessable_entity
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ export default class extends Controller {
|
||||||
<b>Start:</b> ${firstTimestamp}<br>
|
<b>Start:</b> ${firstTimestamp}<br>
|
||||||
<b>End:</b> ${lastTimestamp}<br>
|
<b>End:</b> ${lastTimestamp}<br>
|
||||||
<b>Duration:</b> ${timeOnRoute}<br>
|
<b>Duration:</b> ${timeOnRoute}<br>
|
||||||
<b>Distance:</b> ${Math.round(distance)}m<br>
|
<b>Distance:</b> ${this.formatDistance(distance)}<br>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
if (isDebugMode) {
|
if (isDebugMode) {
|
||||||
|
|
@ -335,4 +335,12 @@ export default class extends Controller {
|
||||||
})
|
})
|
||||||
).addTo(map);
|
).addTo(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formatDistance(distance) {
|
||||||
|
if (distance >= 1000) {
|
||||||
|
return (distance / 1000).toFixed(2) + ' km';
|
||||||
|
} else {
|
||||||
|
return distance.toFixed(0) + ' meters';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,9 @@
|
||||||
<details>
|
<details>
|
||||||
<summary>
|
<summary>
|
||||||
<%= "#{current_user.email}" %>
|
<%= "#{current_user.email}" %>
|
||||||
|
<% if current_user.admin? %>
|
||||||
|
<span class='tooltip tooltip-bottom' data-tip="You're an admin, Harry!">⭐️</span>
|
||||||
|
<% end %>
|
||||||
</summary>
|
</summary>
|
||||||
<ul class="p-2 bg-base-100 rounded-t-none z-10">
|
<ul class="p-2 bg-base-100 rounded-t-none z-10">
|
||||||
<li><%= link_to 'Account', edit_user_registration_path %></li>
|
<li><%= link_to 'Account', edit_user_registration_path %></li>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,15 @@ require 'sidekiq/web'
|
||||||
Rails.application.routes.draw do
|
Rails.application.routes.draw do
|
||||||
mount Rswag::Api::Engine => '/api-docs'
|
mount Rswag::Api::Engine => '/api-docs'
|
||||||
mount Rswag::Ui::Engine => '/api-docs'
|
mount Rswag::Ui::Engine => '/api-docs'
|
||||||
mount Sidekiq::Web => '/sidekiq'
|
authenticate :user, ->(u) { u.admin? } do
|
||||||
|
mount Sidekiq::Web => '/sidekiq'
|
||||||
|
end
|
||||||
|
|
||||||
|
# We want to return a nice error message if the user is not authorized to access Sidekiq
|
||||||
|
match '/sidekiq' => redirect { |_, request|
|
||||||
|
request.flash[:error] = 'You are not authorized to perform this action.'
|
||||||
|
'/'
|
||||||
|
}, via: :get
|
||||||
|
|
||||||
resources :settings, only: :index
|
resources :settings, only: :index
|
||||||
namespace :settings do
|
namespace :settings do
|
||||||
|
|
|
||||||
13
db/data/20240713103122_make_first_user_admin.rb
Normal file
13
db/data/20240713103122_make_first_user_admin.rb
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MakeFirstUserAdmin < ActiveRecord::Migration[7.1]
|
||||||
|
def up
|
||||||
|
user = User.first
|
||||||
|
user.update!(admin: true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
user = User.first
|
||||||
|
user.update!(admin: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
7
db/migrate/20240713103051_add_admin_to_users.rb
Normal file
7
db/migrate/20240713103051_add_admin_to_users.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddAdminToUsers < ActiveRecord::Migration[7.1]
|
||||||
|
def change
|
||||||
|
add_column :users, :admin, :boolean, default: false
|
||||||
|
end
|
||||||
|
end
|
||||||
3
db/schema.rb
generated
3
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.1].define(version: 2024_07_12_141303) do
|
ActiveRecord::Schema[7.1].define(version: 2024_07_13_103051) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
||||||
|
|
@ -150,6 +150,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_07_12_141303) do
|
||||||
t.string "api_key", default: "", null: false
|
t.string "api_key", default: "", null: false
|
||||||
t.string "theme", default: "dark", null: false
|
t.string "theme", default: "dark", null: false
|
||||||
t.jsonb "settings", default: {"fog_of_war_meters"=>"200", "meters_between_routes"=>"1000", "minutes_between_routes"=>"60"}
|
t.jsonb "settings", default: {"fog_of_war_meters"=>"200", "meters_between_routes"=>"1000", "minutes_between_routes"=>"60"}
|
||||||
|
t.boolean "admin", default: false
|
||||||
t.index ["email"], name: "index_users_on_email", unique: true
|
t.index ["email"], name: "index_users_on_email", unique: true
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :user do
|
factory :user do
|
||||||
sequence :email do |n|
|
sequence :email do |n|
|
||||||
|
|
@ -5,5 +7,9 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
|
|
||||||
password { SecureRandom.hex(8) }
|
password { SecureRandom.hex(8) }
|
||||||
|
|
||||||
|
trait :admin do
|
||||||
|
admin { true }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -91,11 +91,10 @@ RSpec.describe User, type: :model do
|
||||||
describe '#total_reverse_geocoded' do
|
describe '#total_reverse_geocoded' do
|
||||||
subject { user.total_reverse_geocoded }
|
subject { user.total_reverse_geocoded }
|
||||||
|
|
||||||
let(:import) { create(:import, user:) }
|
|
||||||
let!(:reverse_geocoded_point) do
|
let!(:reverse_geocoded_point) do
|
||||||
create(:point, country: 'Country', city: 'City', geodata: { some: 'data' }, import:)
|
create(:point, country: 'Country', city: 'City', geodata: { some: 'data' }, user:)
|
||||||
end
|
end
|
||||||
let!(:not_reverse_geocoded_point) { create(:point, country: 'Country', city: 'City', import:) }
|
let!(:not_reverse_geocoded_point) { create(:point, country: 'Country', city: 'City', user:) }
|
||||||
|
|
||||||
it 'returns number of reverse geocoded points' do
|
it 'returns number of reverse geocoded points' do
|
||||||
expect(subject).to eq(1)
|
expect(subject).to eq(1)
|
||||||
|
|
|
||||||
|
|
@ -17,53 +17,62 @@ RSpec.describe '/settings/background_jobs', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when user is authenticated' do
|
context 'when user is authenticated' do
|
||||||
let(:user) { create(:user) }
|
before { sign_in create(:user) }
|
||||||
|
|
||||||
before do
|
context 'when user is not an admin' do
|
||||||
sign_in user
|
it 'redirects to root page' do
|
||||||
end
|
|
||||||
|
|
||||||
describe 'GET /index' do
|
|
||||||
it 'renders a successful response' do
|
|
||||||
get settings_background_jobs_url
|
get settings_background_jobs_url
|
||||||
|
|
||||||
expect(response).to be_successful
|
expect(response).to redirect_to(root_url)
|
||||||
|
expect(flash[:notice]).to eq('You are not authorized to perform this action.')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST /create' do
|
context 'when user is an admin' do
|
||||||
let(:params) { { job_name: 'start_reverse_geocoding' } }
|
before { sign_in create(:user, :admin) }
|
||||||
|
|
||||||
context 'with valid parameters' do
|
describe 'GET /index' do
|
||||||
it 'enqueues a new job' do
|
it 'renders a successful response' do
|
||||||
expect do
|
get settings_background_jobs_url
|
||||||
post settings_background_jobs_url, params:
|
|
||||||
end.to have_enqueued_job(EnqueueReverseGeocodingJob)
|
expect(response).to be_successful
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'POST /create' do
|
||||||
|
let(:params) { { job_name: 'start_reverse_geocoding' } }
|
||||||
|
|
||||||
|
context 'with valid parameters' do
|
||||||
|
it 'enqueues a new job' do
|
||||||
|
expect do
|
||||||
|
post settings_background_jobs_url, params:
|
||||||
|
end.to have_enqueued_job(EnqueueReverseGeocodingJob)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'redirects to the created settings_background_job' do
|
||||||
|
post(settings_background_jobs_url, params:)
|
||||||
|
|
||||||
|
expect(response).to redirect_to(settings_background_jobs_url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'DELETE /destroy' do
|
||||||
|
it 'clears the Sidekiq queue' do
|
||||||
|
queue = instance_double(Sidekiq::Queue)
|
||||||
|
allow(Sidekiq::Queue).to receive(:new).and_return(queue)
|
||||||
|
|
||||||
|
expect(queue).to receive(:clear)
|
||||||
|
|
||||||
|
delete settings_background_job_url('queue_name')
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'redirects to the created settings_background_job' do
|
it 'redirects to the settings_background_jobs list' do
|
||||||
post(settings_background_jobs_url, params:)
|
delete settings_background_job_url('queue_name')
|
||||||
|
|
||||||
expect(response).to redirect_to(settings_background_jobs_url)
|
expect(response).to redirect_to(settings_background_jobs_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'DELETE /destroy' do
|
|
||||||
it 'clears the Sidekiq queue' do
|
|
||||||
queue = instance_double(Sidekiq::Queue)
|
|
||||||
allow(Sidekiq::Queue).to receive(:new).and_return(queue)
|
|
||||||
|
|
||||||
expect(queue).to receive(:clear)
|
|
||||||
|
|
||||||
delete settings_background_job_url('queue_name')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'redirects to the settings_background_jobs list' do
|
|
||||||
delete settings_background_job_url('queue_name')
|
|
||||||
|
|
||||||
expect(response).to redirect_to(settings_background_jobs_url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,41 +3,61 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe '/settings/users', type: :request do
|
RSpec.describe '/settings/users', type: :request do
|
||||||
before do
|
let(:valid_attributes) { { email: 'user@domain.com' } }
|
||||||
sign_in create(:user)
|
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
post settings_users_url, params: { user: valid_attributes }
|
||||||
|
|
||||||
|
expect(response).to redirect_to(new_user_session_url)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'POST /create' do
|
context 'when user is authenticated' do
|
||||||
context 'with valid parameters' do
|
context 'when user is not an admin' do
|
||||||
let(:valid_attributes) { { email: 'user@domain.com' } }
|
before { sign_in create(:user) }
|
||||||
|
|
||||||
it 'creates a new User' do
|
it 'redirects to root page' do
|
||||||
expect do
|
|
||||||
post settings_users_url, params: { user: valid_attributes }
|
|
||||||
end.to change(User, :count).by(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'redirects to the created settings_user' do
|
|
||||||
post settings_users_url, params: { user: valid_attributes }
|
post settings_users_url, params: { user: valid_attributes }
|
||||||
|
|
||||||
expect(response).to redirect_to(settings_url)
|
expect(response).to redirect_to(root_url)
|
||||||
expect(flash[:notice]).to eq("User was successfully created, email is #{valid_attributes[:email]}, password is \"password\".")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with invalid parameters' do
|
context 'when user is an admin' do
|
||||||
let(:invalid_attributes) { { email: nil } }
|
before { sign_in create(:user, :admin) }
|
||||||
|
|
||||||
it 'does not create a new User' do
|
describe 'POST /create' do
|
||||||
expect do
|
context 'with valid parameters' do
|
||||||
post settings_users_url, params: { user: invalid_attributes }
|
it 'creates a new User' do
|
||||||
end.to change(User, :count).by(0)
|
expect do
|
||||||
end
|
post settings_users_url, params: { user: valid_attributes }
|
||||||
|
end.to change(User, :count).by(1)
|
||||||
|
end
|
||||||
|
|
||||||
it 'renders a response with 422 status (i.e. to display the "new" template)' do
|
it 'redirects to the created settings_user' do
|
||||||
post settings_users_url, params: { user: invalid_attributes }
|
post settings_users_url, params: { user: valid_attributes }
|
||||||
|
|
||||||
expect(response).to have_http_status(:unprocessable_entity)
|
expect(response).to redirect_to(settings_url)
|
||||||
|
expect(flash[:notice]).to eq("User was successfully created, email is #{valid_attributes[:email]}, password is \"password\".")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with invalid parameters' do
|
||||||
|
let(:invalid_attributes) { { email: nil } }
|
||||||
|
|
||||||
|
it 'does not create a new User' do
|
||||||
|
expect do
|
||||||
|
post settings_users_url, params: { user: invalid_attributes }
|
||||||
|
end.to change(User, :count).by(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'renders a response with 422 status (i.e. to display the "new" template)' do
|
||||||
|
post settings_users_url, params: { user: invalid_attributes }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:unprocessable_entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
41
spec/requests/sidekiq_spec.rb
Normal file
41
spec/requests/sidekiq_spec.rb
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe '/sidekiq', type: :request do
|
||||||
|
context 'when user is not authenticated' do
|
||||||
|
it 'redirects to sign in page' do
|
||||||
|
get sidekiq_url
|
||||||
|
|
||||||
|
expect(response).to redirect_to('/users/sign_in')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is authenticated' do
|
||||||
|
context 'when user is not admin' do
|
||||||
|
before { sign_in create(:user) }
|
||||||
|
|
||||||
|
it 'redirects to root page' do
|
||||||
|
get sidekiq_url
|
||||||
|
|
||||||
|
expect(response).to redirect_to(root_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'shows flash message' do
|
||||||
|
get sidekiq_url
|
||||||
|
|
||||||
|
expect(flash[:error]).to eq('You are not authorized to perform this action.')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when user is admin' do
|
||||||
|
before { sign_in create(:user, :admin) }
|
||||||
|
|
||||||
|
it 'renders a successful response' do
|
||||||
|
get sidekiq_url
|
||||||
|
|
||||||
|
expect(response).to be_successful
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in a new issue