diff --git a/app/controllers/api/v1/areas_controller.rb b/app/controllers/api/v1/areas_controller.rb index 4ccebd7c..81e20d17 100644 --- a/app/controllers/api/v1/areas_controller.rb +++ b/app/controllers/api/v1/areas_controller.rb @@ -15,7 +15,7 @@ class Api::V1::AreasController < ApiController if @area.save render json: @area, status: :created else - render json: { errors: @area.errors.full_messages }, status: :unprocessable_entity + render json: { errors: @area.errors.full_messages }, status: :unprocessable_content end end @@ -23,7 +23,7 @@ class Api::V1::AreasController < ApiController if @area.update(area_params) render json: @area, status: :ok else - render json: { errors: @area.errors.full_messages }, status: :unprocessable_entity + render json: { errors: @area.errors.full_messages }, status: :unprocessable_content end end diff --git a/app/controllers/api/v1/settings_controller.rb b/app/controllers/api/v1/settings_controller.rb index 10620730..7404ec01 100644 --- a/app/controllers/api/v1/settings_controller.rb +++ b/app/controllers/api/v1/settings_controller.rb @@ -18,7 +18,7 @@ class Api::V1::SettingsController < ApiController status: :ok else render json: { message: 'Something went wrong', errors: current_api_user.errors.full_messages }, - status: :unprocessable_entity + status: :unprocessable_content end end diff --git a/app/controllers/api/v1/subscriptions_controller.rb b/app/controllers/api/v1/subscriptions_controller.rb index 2da2e97d..cc510d67 100644 --- a/app/controllers/api/v1/subscriptions_controller.rb +++ b/app/controllers/api/v1/subscriptions_controller.rb @@ -15,6 +15,6 @@ class Api::V1::SubscriptionsController < ApiController render json: { message: 'Failed to verify subscription update.' }, status: :unauthorized rescue ArgumentError => e ExceptionReporter.call(e) - render json: { message: 'Invalid subscription data received.' }, status: :unprocessable_entity + render json: { message: 'Invalid subscription data received.' }, status: :unprocessable_content end end diff --git a/app/controllers/api/v1/visits_controller.rb b/app/controllers/api/v1/visits_controller.rb index 248e5ea7..4ec4173b 100644 --- a/app/controllers/api/v1/visits_controller.rb +++ b/app/controllers/api/v1/visits_controller.rb @@ -19,7 +19,7 @@ class Api::V1::VisitsController < ApiController render json: Api::VisitSerializer.new(service.visit).call else error_message = service.errors || 'Failed to create visit' - render json: { error: error_message }, status: :unprocessable_entity + render json: { error: error_message }, status: :unprocessable_content end end @@ -34,7 +34,7 @@ class Api::V1::VisitsController < ApiController # Validate that we have at least 2 visit IDs visit_ids = params[:visit_ids] if visit_ids.blank? || visit_ids.length < 2 - return render json: { error: 'At least 2 visits must be selected for merging' }, status: :unprocessable_entity + return render json: { error: 'At least 2 visits must be selected for merging' }, status: :unprocessable_content end # Find all visits that belong to the current user @@ -52,7 +52,7 @@ class Api::V1::VisitsController < ApiController if merged_visit&.persisted? render json: Api::VisitSerializer.new(merged_visit).call, status: :ok else - render json: { error: service.errors.join(', ') }, status: :unprocessable_entity + render json: { error: service.errors.join(', ') }, status: :unprocessable_content end end @@ -71,7 +71,7 @@ class Api::V1::VisitsController < ApiController updated_count: result[:count] }, status: :ok else - render json: { error: service.errors.join(', ') }, status: :unprocessable_entity + render json: { error: service.errors.join(', ') }, status: :unprocessable_content end end @@ -84,7 +84,7 @@ class Api::V1::VisitsController < ApiController render json: { error: 'Failed to delete visit', errors: visit.errors.full_messages - }, status: :unprocessable_entity + }, status: :unprocessable_content end rescue ActiveRecord::RecordNotFound render json: { error: 'Visit not found' }, status: :not_found diff --git a/app/controllers/exports_controller.rb b/app/controllers/exports_controller.rb index efd2d502..0c59e1bf 100644 --- a/app/controllers/exports_controller.rb +++ b/app/controllers/exports_controller.rb @@ -27,7 +27,7 @@ class ExportsController < ApplicationController ExceptionReporter.call(e) - redirect_to exports_url, alert: "Export failed to initiate: #{e.message}", status: :unprocessable_entity + redirect_to exports_url, alert: "Export failed to initiate: #{e.message}", status: :unprocessable_content end def destroy diff --git a/app/controllers/imports_controller.rb b/app/controllers/imports_controller.rb index 3ee75a95..96049978 100644 --- a/app/controllers/imports_controller.rb +++ b/app/controllers/imports_controller.rb @@ -13,9 +13,9 @@ class ImportsController < ApplicationController def index @imports = policy_scope(Import) - .select(:id, :name, :source, :created_at, :processed, :status) - .order(created_at: :desc) - .page(params[:page]) + .select(:id, :name, :source, :created_at, :processed, :status) + .order(created_at: :desc) + .page(params[:page]) end def show; end @@ -43,7 +43,7 @@ class ImportsController < ApplicationController raw_files = Array(files_params).reject(&:blank?) if raw_files.empty? - redirect_to new_import_path, alert: 'No files were selected for upload', status: :unprocessable_entity and return + redirect_to new_import_path, alert: 'No files were selected for upload', status: :unprocessable_content and return end created_imports = [] @@ -62,7 +62,7 @@ class ImportsController < ApplicationController else redirect_to new_import_path, alert: 'No valid file references were found. Please upload files using the file selector.', - status: :unprocessable_entity and return + status: :unprocessable_content and return end rescue StandardError => e if created_imports.present? @@ -74,7 +74,7 @@ class ImportsController < ApplicationController Rails.logger.error e.backtrace.join("\n") ExceptionReporter.call(e) - redirect_to new_import_path, alert: e.message, status: :unprocessable_entity + redirect_to new_import_path, alert: e.message, status: :unprocessable_content end def destroy @@ -117,7 +117,7 @@ class ImportsController < ApplicationController # Extract filename and extension basename = File.basename(original_name, File.extname(original_name)) extension = File.extname(original_name) - + # Add current datetime timestamp = Time.current.strftime('%Y%m%d_%H%M%S') "#{basename}_#{timestamp}#{extension}" @@ -126,6 +126,6 @@ class ImportsController < ApplicationController def validate_points_limit limit_exceeded = PointsLimitExceeded.new(current_user).call - redirect_to imports_path, alert: 'Points limit exceeded', status: :unprocessable_entity if limit_exceeded + redirect_to imports_path, alert: 'Points limit exceeded', status: :unprocessable_content if limit_exceeded end end diff --git a/app/controllers/settings/users_controller.rb b/app/controllers/settings/users_controller.rb index f00a28ce..6545f387 100644 --- a/app/controllers/settings/users_controller.rb +++ b/app/controllers/settings/users_controller.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class Settings::UsersController < ApplicationController - before_action :authenticate_self_hosted!, except: [:export, :import] - before_action :authenticate_admin!, except: [:export, :import] + before_action :authenticate_self_hosted!, except: %i[export import] + before_action :authenticate_admin!, except: %i[export import] before_action :authenticate_user! def index @@ -19,7 +19,7 @@ class Settings::UsersController < ApplicationController if @user.update(user_params) redirect_to settings_users_url, notice: 'User was successfully updated.' else - redirect_to settings_users_url, notice: 'User could not be updated.', status: :unprocessable_entity + redirect_to settings_users_url, notice: 'User could not be updated.', status: :unprocessable_content end end @@ -33,7 +33,7 @@ class Settings::UsersController < ApplicationController if @user.save redirect_to settings_users_url, notice: 'User was successfully created' else - redirect_to settings_users_url, notice: 'User could not be created.', status: :unprocessable_entity + redirect_to settings_users_url, notice: 'User could not be created.', status: :unprocessable_content end end @@ -43,7 +43,7 @@ class Settings::UsersController < ApplicationController if @user.destroy redirect_to settings_url, notice: 'User was successfully deleted.' else - redirect_to settings_url, notice: 'User could not be deleted.', status: :unprocessable_entity + redirect_to settings_url, notice: 'User could not be deleted.', status: :unprocessable_content end end @@ -90,8 +90,7 @@ class Settings::UsersController < ApplicationController end def validate_archive_file(archive_file) - unless archive_file.content_type == 'application/zip' || - archive_file.content_type == 'application/x-zip-compressed' || + unless ['application/zip', 'application/x-zip-compressed'].include?(archive_file.content_type) || File.extname(archive_file.original_filename).downcase == '.zip' redirect_to edit_user_registration_path, alert: 'Please upload a valid ZIP file.' and return diff --git a/app/controllers/shared/stats_controller.rb b/app/controllers/shared/stats_controller.rb new file mode 100644 index 00000000..e660dbcf --- /dev/null +++ b/app/controllers/shared/stats_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class Shared::StatsController < ApplicationController + before_action :authenticate_user!, except: [:show] + before_action :authenticate_active_user!, only: [:update] + + def show + @stat = Stat.find_by(sharing_uuid: params[:uuid]) + + unless @stat&.public_accessible? + return redirect_to root_path, + alert: 'Shared stats not found or no longer available' + end + + @year = @stat.year + @month = @stat.month + @user = @stat.user + @is_public_view = true + @data_bounds = @stat.calculate_data_bounds + + render 'stats/public_month' + end + + def update + @year = params[:year].to_i + @month = params[:month].to_i + @stat = current_user.stats.find_by(year: @year, month: @month) + + return head :not_found unless @stat + + if params[:enabled] == '1' + @stat.enable_sharing!(expiration: params[:expiration] || 'permanent') + sharing_url = shared_stat_url(@stat.sharing_uuid) + + render json: { + success: true, + sharing_url: sharing_url, + message: 'Sharing enabled successfully' + } + else + @stat.disable_sharing! + + render json: { + success: true, + message: 'Sharing disabled successfully' + } + end + rescue StandardError + render json: { + success: false, + message: 'Failed to update sharing settings' + }, status: :unprocessable_content + end +end diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 522cd6f1..f72a6bed 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class StatsController < ApplicationController - before_action :authenticate_user!, except: [:public_show] - before_action :authenticate_active_user!, only: %i[update update_all update_sharing] + before_action :authenticate_user! + before_action :authenticate_active_user!, only: %i[update update_all] def index @stats = build_stats @@ -52,54 +52,6 @@ class StatsController < ApplicationController redirect_to stats_path, notice: 'Stats are being updated', status: :see_other end - def update_sharing - @year = params[:year].to_i - @month = params[:month].to_i - @stat = current_user.stats.find_by(year: @year, month: @month) - - return head :not_found unless @stat - - if params[:enabled] == '1' - @stat.enable_sharing!(expiration: params[:expiration] || 'permanent') - sharing_url = public_stat_url(@stat.sharing_uuid) - - render json: { - success: true, - sharing_url: sharing_url, - message: 'Sharing enabled successfully' - } - else - @stat.disable_sharing! - - render json: { - success: true, - message: 'Sharing disabled successfully' - } - end - rescue StandardError - render json: { - success: false, - message: 'Failed to update sharing settings' - }, status: :unprocessable_entity - end - - def public_show - @stat = Stat.find_by(sharing_uuid: params[:uuid]) - - unless @stat&.public_accessible? - return redirect_to root_path, - alert: 'Shared stats not found or no longer available' - end - - @year = @stat.year - @month = @stat.month - @user = @stat.user - @is_public_view = true - @data_bounds = calculate_data_bounds(@stat) - - render 'public_month' - end - private def assign_points_statistics @@ -132,32 +84,4 @@ class StatsController < ApplicationController stats.sort_by(&:updated_at).reverse end.sort.reverse end - - def calculate_data_bounds(stat) - start_date = Date.new(stat.year, stat.month, 1).beginning_of_day - end_date = start_date.end_of_month.end_of_day - - points_relation = stat.user.points.where(timestamp: start_date.to_i..end_date.to_i) - point_count = points_relation.count - - return nil if point_count.zero? - - bounds_result = ActiveRecord::Base.connection.exec_query( - "SELECT MIN(latitude) as min_lat, MAX(latitude) as max_lat, - MIN(longitude) as min_lng, MAX(longitude) as max_lng - FROM points - WHERE user_id = $1 - AND timestamp BETWEEN $2 AND $3", - 'data_bounds_query', - [stat.user.id, start_date.to_i, end_date.to_i] - ).first - - { - min_lat: bounds_result['min_lat'].to_f, - max_lat: bounds_result['max_lat'].to_f, - min_lng: bounds_result['min_lng'].to_f, - max_lng: bounds_result['max_lng'].to_f, - point_count: point_count - } - end end diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index 1880002b..00764a96 100644 --- a/app/controllers/trips_controller.rb +++ b/app/controllers/trips_controller.rb @@ -16,9 +16,9 @@ class TripsController < ApplicationController end @photo_sources = @trip.photo_sources - if @trip.path.blank? || @trip.distance.blank? || @trip.visited_countries.blank? - Trips::CalculateAllJob.perform_later(@trip.id, current_user.safe_settings.distance_unit) - end + return unless @trip.path.blank? || @trip.distance.blank? || @trip.visited_countries.blank? + + Trips::CalculateAllJob.perform_later(@trip.id, current_user.safe_settings.distance_unit) end def new @@ -34,7 +34,7 @@ class TripsController < ApplicationController if @trip.save redirect_to @trip, notice: 'Trip was successfully created. Data is being calculated in the background.' else - render :new, status: :unprocessable_entity + render :new, status: :unprocessable_content end end @@ -42,7 +42,7 @@ class TripsController < ApplicationController if @trip.update(trip_params) redirect_to @trip, notice: 'Trip was successfully updated.', status: :see_other else - render :edit, status: :unprocessable_entity + render :edit, status: :unprocessable_content end end diff --git a/app/controllers/visits_controller.rb b/app/controllers/visits_controller.rb index a22e60e5..bc8c1d8c 100644 --- a/app/controllers/visits_controller.rb +++ b/app/controllers/visits_controller.rb @@ -22,7 +22,7 @@ class VisitsController < ApplicationController if @visit.update(visit_params) redirect_back(fallback_location: visits_path(status: :suggested)) else - render :edit, status: :unprocessable_entity + render :edit, status: :unprocessable_content end end diff --git a/app/models/stat.rb b/app/models/stat.rb index a8fc1e0a..58ad79f5 100644 --- a/app/models/stat.rb +++ b/app/models/stat.rb @@ -81,6 +81,34 @@ class Stat < ApplicationRecord ) end + def calculate_data_bounds + start_date = Date.new(year, month, 1).beginning_of_day + end_date = start_date.end_of_month.end_of_day + + points_relation = user.points.where(timestamp: start_date.to_i..end_date.to_i) + point_count = points_relation.count + + return nil if point_count.zero? + + bounds_result = ActiveRecord::Base.connection.exec_query( + "SELECT MIN(latitude) as min_lat, MAX(latitude) as max_lat, + MIN(longitude) as min_lng, MAX(longitude) as max_lng + FROM points + WHERE user_id = $1 + AND timestamp BETWEEN $2 AND $3", + 'data_bounds_query', + [user.id, start_date.to_i, end_date.to_i] + ).first + + { + min_lat: bounds_result['min_lat'].to_f, + max_lat: bounds_result['max_lat'].to_f, + min_lng: bounds_result['min_lng'].to_f, + max_lng: bounds_result['max_lng'].to_f, + point_count: point_count + } + end + private def generate_sharing_uuid diff --git a/app/views/stats/public_month.html.erb b/app/views/stats/public_month.html.erb index 15b15532..a9506424 100644 --- a/app/views/stats/public_month.html.erb +++ b/app/views/stats/public_month.html.erb @@ -90,7 +90,9 @@