diff --git a/.app_version b/.app_version index 473f1fb3..bca57db5 100644 --- a/.app_version +++ b/.app_version @@ -1 +1 @@ -0.30.8 +0.30.10 diff --git a/CHANGELOG.md b/CHANGELOG.md index e505d48b..e8741ecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ 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.30.9] - 2025-08-10 + +# [0.30.10] - 2025-08-19 + ## Added @@ -13,6 +15,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [ ] Add tracker_id index to points table + +# [0.30.9] - 2025-08-19 + +## Changed + +- Countries, visited during a trip, are now being calculated from points to improve performance. + +## Added + +- QR code for API key is implemented but hidden under feature flag until the iOS app supports it. +- X-Dawarich-Response and X-Dawarich-Version headers are now returned for all API responses. +- Trial version for cloud users is now available. + + + # [0.30.8] - 2025-08-01 ## Fixed diff --git a/Gemfile b/Gemfile index 614a2e95..c7145245 100644 --- a/Gemfile +++ b/Gemfile @@ -5,6 +5,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby File.read('.ruby-version').strip +gem 'activerecord-postgis-adapter' # https://meta.discourse.org/t/cant-rebuild-due-to-aws-sdk-gem-bump-and-new-aws-data-integrity-protections/354217/40 gem 'aws-sdk-s3', '~> 1.177.0', require: false gem 'aws-sdk-core', '~> 3.215.1', require: false @@ -24,7 +25,7 @@ gem 'oj' gem 'parallel' gem 'pg' gem 'prometheus_exporter' -gem 'activerecord-postgis-adapter' +gem 'rqrcode', '~> 3.0' gem 'puma' gem 'pundit' gem 'rails', '~> 8.0' diff --git a/Gemfile.lock b/Gemfile.lock index 4b955b5a..74af4a35 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,29 +10,29 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (8.0.2) - actionpack (= 8.0.2) - activesupport (= 8.0.2) + actioncable (8.0.2.1) + actionpack (= 8.0.2.1) + activesupport (= 8.0.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.2) - actionpack (= 8.0.2) - activejob (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + actionmailbox (8.0.2.1) + actionpack (= 8.0.2.1) + activejob (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) mail (>= 2.8.0) - actionmailer (8.0.2) - actionpack (= 8.0.2) - actionview (= 8.0.2) - activejob (= 8.0.2) - activesupport (= 8.0.2) + actionmailer (8.0.2.1) + actionpack (= 8.0.2.1) + actionview (= 8.0.2.1) + activejob (= 8.0.2.1) + activesupport (= 8.0.2.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2) - actionview (= 8.0.2) - activesupport (= 8.0.2) + actionpack (8.0.2.1) + actionview (= 8.0.2.1) + activesupport (= 8.0.2.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -40,38 +40,38 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2) - actionpack (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + actiontext (8.0.2.1) + actionpack (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2) - activesupport (= 8.0.2) + actionview (8.0.2.1) + activesupport (= 8.0.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.2) - activesupport (= 8.0.2) + activejob (8.0.2.1) + activesupport (= 8.0.2.1) globalid (>= 0.3.6) - activemodel (8.0.2) - activesupport (= 8.0.2) - activerecord (8.0.2) - activemodel (= 8.0.2) - activesupport (= 8.0.2) + activemodel (8.0.2.1) + activesupport (= 8.0.2.1) + activerecord (8.0.2.1) + activemodel (= 8.0.2.1) + activesupport (= 8.0.2.1) timeout (>= 0.4.0) activerecord-postgis-adapter (11.0.0) activerecord (~> 8.0.0) rgeo-activerecord (~> 8.0.0) - activestorage (8.0.2) - actionpack (= 8.0.2) - activejob (= 8.0.2) - activerecord (= 8.0.2) - activesupport (= 8.0.2) + activestorage (8.0.2.1) + actionpack (= 8.0.2.1) + activejob (= 8.0.2.1) + activerecord (= 8.0.2.1) + activesupport (= 8.0.2.1) marcel (~> 1.0) - activesupport (8.0.2) + activesupport (8.0.2.1) base64 benchmark (>= 0.3) bigdecimal @@ -127,6 +127,7 @@ GEM regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) chartkick (5.2.0) + chunky_png (1.4.0) coderay (1.1.3) concurrent-ruby (1.3.5) connection_pool (2.5.3) @@ -297,7 +298,7 @@ GEM date stringio public_suffix (6.0.1) - puma (6.6.0) + puma (6.6.1) nio4r (~> 2.0) pundit (2.5.0) activesupport (>= 3.0.0) @@ -311,20 +312,20 @@ GEM rack (>= 1.3) rackup (2.2.1) rack (>= 3) - rails (8.0.2) - actioncable (= 8.0.2) - actionmailbox (= 8.0.2) - actionmailer (= 8.0.2) - actionpack (= 8.0.2) - actiontext (= 8.0.2) - actionview (= 8.0.2) - activejob (= 8.0.2) - activemodel (= 8.0.2) - activerecord (= 8.0.2) - activestorage (= 8.0.2) - activesupport (= 8.0.2) + rails (8.0.2.1) + actioncable (= 8.0.2.1) + actionmailbox (= 8.0.2.1) + actionmailer (= 8.0.2.1) + actionpack (= 8.0.2.1) + actiontext (= 8.0.2.1) + actionview (= 8.0.2.1) + activejob (= 8.0.2.1) + activemodel (= 8.0.2.1) + activerecord (= 8.0.2.1) + activestorage (= 8.0.2.1) + activesupport (= 8.0.2.1) bundler (>= 1.15.0) - railties (= 8.0.2) + railties (= 8.0.2.1) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -332,9 +333,9 @@ GEM rails-html-sanitizer (1.6.2) loofah (~> 2.21) 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.2) - actionpack (= 8.0.2) - activesupport (= 8.0.2) + railties (8.0.2.1) + actionpack (= 8.0.2.1) + activesupport (= 8.0.2.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -365,6 +366,10 @@ GEM rgeo-geojson (2.2.0) multi_json (~> 1.15) rgeo (>= 1.0.0) + rqrcode (3.1.0) + chunky_png (~> 1.0) + rqrcode_core (~> 2.0) + rqrcode_core (2.0.0) rspec-core (3.13.3) rspec-support (~> 3.13.0) rspec-expectations (3.13.4) @@ -553,6 +558,7 @@ DEPENDENCIES rgeo rgeo-activerecord rgeo-geojson + rqrcode (~> 3.0) rspec-rails rswag-api rswag-specs diff --git a/README.md b/README.md index 789bd889..8ee904bf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# π Dawarich: Your Self-Hosted Location History Tracker +# π Dawarich: Your Self-Hostable Location History Tracker [](https://discord.gg/pHsBjpt5J8) | [](https://ko-fi.com/H2H3IDYDD) | [](https://www.patreon.com/freika) @@ -21,9 +21,14 @@ ## πΊοΈ About Dawarich -**Dawarich** is a self-hosted web app designed to replace Google Timeline (aka Google Location History). It enables you to: +If you're looking for Dawarich Cloud, where everything is managed for you, check out [Dawarich Cloud](https://dawarich.app). + +**Dawarich** is a self-hostable web app designed to replace Google Timeline (aka Google Location History). +It enables you to: + +- Track your location history. - Visualize your data on an interactive map. -- Import your location history from Google Maps Timeline and Owntracks. +- Import your location history from Google Maps Timeline, OwnTracks, GPX, GeoJSON and some other sources - Explore statistics like the number of countries and cities visited, total distance traveled, and more! π **Changelog**: Find the latest updates [here](CHANGELOG.md). @@ -62,7 +67,7 @@ Simply install one of the supported apps on your device and configure it to send 1. Clone the repository. 2. Run the following command to start the app: ```bash - docker-compose up + docker-compose -f docker/docker-compose.yml up ``` 3. Access the app at `http://localhost:3000`. diff --git a/app/controllers/api/v1/health_controller.rb b/app/controllers/api/v1/health_controller.rb index 8e13d165..1e5ab2f1 100644 --- a/app/controllers/api/v1/health_controller.rb +++ b/app/controllers/api/v1/health_controller.rb @@ -4,14 +4,6 @@ class Api::V1::HealthController < ApiController skip_before_action :authenticate_api_key def index - if current_api_user - response.set_header('X-Dawarich-Response', 'Hey, I\'m alive and authenticated!') - else - response.set_header('X-Dawarich-Response', 'Hey, I\'m alive!') - end - - response.set_header('X-Dawarich-Version', APP_VERSION) - render json: { status: 'ok' } end end diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb index 4d13bdaf..d53f57ae 100644 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@ -2,10 +2,18 @@ class ApiController < ApplicationController skip_before_action :verify_authenticity_token + before_action :set_version_header before_action :authenticate_api_key private + def set_version_header + message = "Hey, I\'m alive#{current_api_user ? ' and authenticated' : ''}!" + + response.set_header('X-Dawarich-Response', message) + response.set_header('X-Dawarich-Version', APP_VERSION) + end + def authenticate_api_key return head :unauthorized unless current_api_user diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 4bff870e..710f9b60 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -59,11 +59,11 @@ class StatsController < ApplicationController @stats.each do |year, stats| stats_by_month = stats.index_by(&:month) - + year_distances[year] = (1..12).map do |month| month_name = Date::MONTHNAMES[month] distance = stats_by_month[month]&.distance || 0 - + [month_name, distance] end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index dfd93042..5fdcd917 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -48,11 +48,11 @@ module ApplicationHelper grouped_by_country[country] ||= [] - if toponym['cities'].present? - toponym['cities'].each do |city_data| - city = city_data['city'] - grouped_by_country[country] << city if city.present? - end + next unless toponym['cities'].present? + + toponym['cities'].each do |city_data| + city = city_data['city'] + grouped_by_country[country] << city if city.present? end end end @@ -172,4 +172,21 @@ module ApplicationHelper data: { tip: "Expires on #{active_until.iso8601}" } ) end + + def onboarding_modal_showable?(user) + user.trial_state? + end + + def trial_button_class(user) + case (user.active_until.to_date - Time.current.to_date).to_i + when 5..8 + 'btn-info' + when 2...5 + 'btn-warning' + when 0...2 + 'btn-error' + else + 'btn-success' + end + end end diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb new file mode 100644 index 00000000..b28f55b9 --- /dev/null +++ b/app/helpers/user_helper.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module UserHelper + def api_key_qr_code(user) + qrcode = RQRCode::QRCode.new(user.api_key) + svg = qrcode.as_svg( + color: "000", + fill: "fff", + shape_rendering: "crispEdges", + module_size: 11, + standalone: true, + use_path: true, + offset: 5 + ) + svg.html_safe + end +end diff --git a/app/javascript/controllers/direct_upload_controller.js b/app/javascript/controllers/direct_upload_controller.js index 5be5b921..cc58436e 100644 --- a/app/javascript/controllers/direct_upload_controller.js +++ b/app/javascript/controllers/direct_upload_controller.js @@ -5,7 +5,8 @@ import { showFlashMessage } from "../maps/helpers" export default class extends Controller { static targets = ["input", "progress", "progressBar", "submit", "form"] static values = { - url: String + url: String, + userTrial: Boolean } connect() { @@ -50,6 +51,22 @@ export default class extends Controller { const files = this.inputTarget.files if (files.length === 0) return + // Check file size limits for trial users + if (this.userTrialValue) { + const MAX_FILE_SIZE = 11 * 1024 * 1024 // 11MB in bytes + const oversizedFiles = Array.from(files).filter(file => file.size > MAX_FILE_SIZE) + + if (oversizedFiles.length > 0) { + const fileNames = oversizedFiles.map(f => f.name).join(', ') + const message = `File size limit exceeded. Trial users can only upload files up to 10MB. Oversized files: ${fileNames}` + showFlashMessage('error', message) + + // Clear the file input + this.inputTarget.value = '' + return + } + } + console.log(`Uploading ${files.length} files`) this.isUploading = true diff --git a/app/javascript/controllers/onboarding_modal_controller.js b/app/javascript/controllers/onboarding_modal_controller.js new file mode 100644 index 00000000..5a20e1c2 --- /dev/null +++ b/app/javascript/controllers/onboarding_modal_controller.js @@ -0,0 +1,42 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["modal"] + static values = { showable: Boolean } + + connect() { + if (this.showableValue) { + // Listen for Turbo page load events to show modal after navigation completes + document.addEventListener('turbo:load', this.handleTurboLoad.bind(this)) + } + } + + disconnect() { + // Clean up event listener when controller is removed + document.removeEventListener('turbo:load', this.handleTurboLoad.bind(this)) + } + + handleTurboLoad() { + if (this.showableValue) { + this.checkAndShowModal() + } + } + + checkAndShowModal() { + const MODAL_STORAGE_KEY = 'dawarich_onboarding_shown' + const hasShownModal = localStorage.getItem(MODAL_STORAGE_KEY) + + if (!hasShownModal && this.hasModalTarget) { + // Show the modal + this.modalTarget.showModal() + + // Mark as shown in local storage + localStorage.setItem(MODAL_STORAGE_KEY, 'true') + + // Add event listener to handle when modal is closed + this.modalTarget.addEventListener('close', () => { + // Modal closed - state already saved + }) + } + } +} diff --git a/app/jobs/users/mailer_sending_job.rb b/app/jobs/users/mailer_sending_job.rb new file mode 100644 index 00000000..bbce993f --- /dev/null +++ b/app/jobs/users/mailer_sending_job.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class Users::MailerSendingJob < ApplicationJob + queue_as :mailers + + def perform(user_id, email_type, **options) + user = User.find(user_id) + + if trial_related_email?(email_type) && user.active? + Rails.logger.info "Skipping #{email_type} email for user #{user_id} - user is already subscribed" + return + end + + params = { user: user }.merge(options) + + UsersMailer.with(params).public_send(email_type).deliver_later + end + + private + + def trial_related_email?(email_type) + %w[trial_expires_soon trial_expired].include?(email_type.to_s) + end +end diff --git a/app/jobs/users/trial_webhook_job.rb b/app/jobs/users/trial_webhook_job.rb new file mode 100644 index 00000000..512dd075 --- /dev/null +++ b/app/jobs/users/trial_webhook_job.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class Users::TrialWebhookJob < ApplicationJob + queue_as :default + + def perform(user_id) + user = User.find(user_id) + + payload = { + user_id: user.id, + email: user.email, + active_until: user.active_until, + status: user.status, + action: 'create_user' + } + + token = Subscription::EncodeJwtToken.new(payload, ENV['JWT_SECRET_KEY']).call + + request_url = "#{ENV['MANAGER_URL']}/api/v1/users" + headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + } + + HTTParty.post(request_url, headers: headers, body: { token: token }.to_json) + end +end diff --git a/app/mailers/users_mailer.rb b/app/mailers/users_mailer.rb new file mode 100644 index 00000000..c7293a75 --- /dev/null +++ b/app/mailers/users_mailer.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class UsersMailer < ApplicationMailer + def welcome + @user = params[:user] + + mail(to: @user.email, subject: 'Welcome to Dawarich!') + end + + def explore_features + @user = params[:user] + + mail(to: @user.email, subject: 'Explore Dawarich features!') + end + + def trial_expires_soon + @user = params[:user] + + mail(to: @user.email, subject: 'β οΈ Your Dawarich trial expires in 2 days') + end + + def trial_expired + @user = params[:user] + + mail(to: @user.email, subject: 'π Your Dawarich trial expired') + end +end diff --git a/app/models/concerns/calculateable.rb b/app/models/concerns/calculateable.rb index 31e4ff53..12caeac2 100644 --- a/app/models/concerns/calculateable.rb +++ b/app/models/concerns/calculateable.rb @@ -10,6 +10,7 @@ module Calculateable def calculate_distance calculated_distance_meters = calculate_distance_from_coordinates + self.distance = convert_distance_for_storage(calculated_distance_meters) end diff --git a/app/models/import.rb b/app/models/import.rb index d22d5174..74024798 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -13,6 +13,7 @@ class Import < ApplicationRecord after_commit :remove_attached_file, on: :destroy validates :name, presence: true, uniqueness: { scope: :user_id } + validate :file_size_within_limit, if: -> { user.trial? } enum :status, { created: 0, processing: 1, completed: 2, failed: 3 } @@ -58,4 +59,12 @@ class Import < ApplicationRecord def remove_attached_file file.purge_later end + + def file_size_within_limit + return unless file.attached? + + if file.blob.byte_size > 11.megabytes + errors.add(:file, 'is too large. Trial users can only upload files up to 10MB.') + end + end end diff --git a/app/models/trip.rb b/app/models/trip.rb index 7ba14ad5..e409a47b 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -21,12 +21,6 @@ class Trip < ApplicationRecord user.tracked_points.where(timestamp: started_at.to_i..ended_at.to_i).order(:timestamp) end - def countries - return points.pluck(:country).uniq.compact if DawarichSettings.store_geodata? - - visited_countries - end - def photo_previews @photo_previews ||= select_dominant_orientation(photos).sample(12) end @@ -35,13 +29,8 @@ class Trip < ApplicationRecord @photo_sources ||= photos.map { _1[:source] }.uniq end - - def calculate_countries - countries = - Country.where(id: points.pluck(:country_id).compact.uniq).pluck(:name) - - self.visited_countries = countries + self.visited_countries = points.pluck(:country_name).uniq.compact end private diff --git a/app/models/user.rb b/app/models/user.rb index 13c5cee6..accfc486 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class User < ApplicationRecord +class User < ApplicationRecord # rubocop:disable Metrics/ClassLength devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :trackable @@ -19,6 +19,8 @@ class User < ApplicationRecord after_create :create_api_key after_commit :activate, on: :create, if: -> { DawarichSettings.self_hosted? } + after_commit :start_trial, on: :create, if: -> { !DawarichSettings.self_hosted? } + after_commit :schedule_welcome_emails, on: :create, if: -> { !DawarichSettings.self_hosted? } before_save :sanitize_input validates :email, presence: true @@ -27,7 +29,7 @@ class User < ApplicationRecord attribute :admin, :boolean, default: false - enum :status, { inactive: 0, active: 1 } + enum :status, { inactive: 0, active: 1, trial: 2 } def safe_settings Users::SafeSettings.new(settings) @@ -97,7 +99,7 @@ class User < ApplicationRecord end def can_subscribe? - (active_until.nil? || active_until&.past?) && !DawarichSettings.self_hosted? + (trial? || !active_until&.future?) && !DawarichSettings.self_hosted? end def generate_subscription_token @@ -116,6 +118,10 @@ class User < ApplicationRecord Users::ExportDataJob.perform_later(id) end + def trial_state? + tracked_points.none? && trial? + end + private def create_api_key @@ -125,7 +131,6 @@ class User < ApplicationRecord end def activate - # TODO: Remove the `status` column in the future. update(status: :active, active_until: 1000.years.from_now) end @@ -134,4 +139,17 @@ class User < ApplicationRecord settings['photoprism_url']&.gsub!(%r{/+\z}, '') settings.try(:[], 'maps')&.try(:[], 'url')&.strip! end + + def start_trial + update(status: :trial, active_until: 7.days.from_now) + + Users::TrialWebhookJob.perform_later(id) + end + + def schedule_welcome_emails + Users::MailerSendingJob.perform_later(id, 'welcome') + Users::MailerSendingJob.set(wait: 2.days).perform_later(id, 'explore_features') + Users::MailerSendingJob.set(wait: 5.days).perform_later(id, 'trial_expires_soon') + Users::MailerSendingJob.set(wait: 7.days).perform_later(id, 'trial_expired') + end end diff --git a/app/policies/import_policy.rb b/app/policies/import_policy.rb index 0d1ceddf..fcaa2347 100644 --- a/app/policies/import_policy.rb +++ b/app/policies/import_policy.rb @@ -11,13 +11,13 @@ class ImportPolicy < ApplicationPolicy user.present? && record.user == user end - # Users can create new imports if they are active + # Users can create new imports if they are active or trial def new? create? end def create? - user.present? && user.active? + user.present? && (user.active? || user.trial?) end # Users can only edit their own imports diff --git a/app/services/imports/create.rb b/app/services/imports/create.rb index d86fe337..d7ad2323 100644 --- a/app/services/imports/create.rb +++ b/app/services/imports/create.rb @@ -56,7 +56,12 @@ class Imports::Create end def schedule_visit_suggesting(user_id, import) + return unless user.safe_settings.visits_suggestions_enabled? + points = import.points.order(:timestamp) + + return if points.none? + start_at = Time.zone.at(points.first.timestamp) end_at = Time.zone.at(points.last.timestamp) diff --git a/app/services/subscription/encode_jwt_token.rb b/app/services/subscription/encode_jwt_token.rb new file mode 100644 index 00000000..77c9e898 --- /dev/null +++ b/app/services/subscription/encode_jwt_token.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +class Subscription::EncodeJwtToken + def initialize(payload, secret_key) + @payload = payload + @secret_key = secret_key + end + + def call + JWT.encode( + @payload, + @secret_key, + 'HS256' + ) + end +end diff --git a/app/views/devise/registrations/_api_key.html.erb b/app/views/devise/registrations/_api_key.html.erb index c04b7b85..aeba5bfd 100644 --- a/app/views/devise/registrations/_api_key.html.erb +++ b/app/views/devise/registrations/_api_key.html.erb @@ -1,6 +1,14 @@
Use this API key to authenticate your requests.
<%= current_user.api_key %>
+
+ <% if ENV['QR_CODE_ENABLED'] == 'true' %>
+ + Or you can scan it in your Dawarich iOS app: + <%= api_key_qr_code(current_user) %> +
+ <% end %> +
Docs: <%= link_to "API documentation", '/api-docs', class: 'underline hover:no-underline' %>
@@ -20,7 +28,6 @@<%= api_v1_overland_batches_url(api_key: current_user.api_key) %>
<%= link_to "Generate new API key", generate_api_key_path, data: { confirm: "Are you sure? This will invalidate the current API key.", turbo_confirm: "Are you sure?", turbo_method: :post }, class: 'btn btn-primary' %> diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 5fb84f95..23be077a 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -4,7 +4,13 @@
+ <%= link_to 'Subscribe', "#{MANAGER_URL}/auth/dawarich?token=#{current_user.generate_subscription_token}", class: 'btn btn-sm btn-success glass' %> to access your API key and start tracking your location. +
+ <% end %> <% if !DawarichSettings.self_hosted? %> <%= render 'devise/registrations/points_usage' %> <% end %> diff --git a/app/views/imports/_form.html.erb b/app/views/imports/_form.html.erb index 35d2ec34..3f2857fb 100644 --- a/app/views/imports/_form.html.erb +++ b/app/views/imports/_form.html.erb @@ -1,6 +1,7 @@ <%= form_with model: import, class: "contents", data: { controller: "direct-upload", direct_upload_url_value: rails_direct_uploads_url, + direct_upload_user_trial_value: current_user.trial?, direct_upload_target: "form" } do |form| %>