diff --git a/Dockerfile b/Dockerfile index eae52ca66..c9a781a8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -64,7 +64,7 @@ ARG BUILD_DATE ARG VCS_REF ARG TAG -ARG ZEALOT_VERSION="5.3.3" +ARG ZEALOT_VERSION="5.3.4" ARG REPLACE_CHINA_MIRROR="true" ARG ORIGINAL_REPO_URL="dl-cdn.alpinelinux.org" ARG MIRROR_REPO_URL="mirrors.ustc.edu.cn" diff --git a/Gemfile b/Gemfile index 7f23f4ed6..5a8b496ed 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem 'lograge', '~> 0.14.0' # API gem 'active_model_serializers', '~> 0.10.14' -gem 'graphql', '~> 2.3.5' +gem 'graphql', '~> 2.3.6' gem 'rack-cors', '~> 2.0.2' gem 'health_check', '~> 3.1.0' gem 'tiny_appstore_connect', '~> 0.1.12' @@ -39,7 +39,7 @@ gem 'webp-ffi', '~> 0.4.0' # Helper gem 'rails-settings-cached', '~> 2.9.4' -gem 'app-info', '~> 3.0.0' +gem 'app-info', '~> 3.1.4' gem 'faraday', '~> 2.9.2' gem 'rqrcode' @@ -54,8 +54,7 @@ gem 'omniauth-google-oauth2', '~> 1.0.1' gem 'omniauth-gitlab', '~> 3.0.0' gem 'omniauth-feishu', '~> 0.1.8' gem 'gitlab_omniauth-ldap', '~> 2.2.0', require: 'omniauth-ldap' -gem 'nkf', '~> 0.2.0' # requires for gitlab_omniauth-ldap in Ruby 3.3 -gem 'omniauth_openid_connect', '0.7.1' +gem 'omniauth_openid_connect', '0.8.0' ## UDID gem 'openssl', '~> 3.2.0' @@ -67,14 +66,14 @@ gem 'vmstat', '~> 2.3.0' gem 'pghero', '~> 3.5.0' ## Exception handler -gem 'sentry-ruby', '~> 5.17.3' -gem 'sentry-rails', '~> 5.17.3' +gem 'sentry-ruby', '~> 5.18.0' +gem 'sentry-rails', '~> 5.18.1' ## Jenkins SDK gem 'improved_jenkins_client', '~> 1.6.7' # Background job -gem 'good_job', '~> 3.29.4' +gem 'good_job', '~> 3.30.0' gem 'activejob-status', '~> 1.0.2' # Assets diff --git a/Gemfile.lock b/Gemfile.lock index 0ba9e5264..973aee3c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,9 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.6) + CFPropertyList (3.0.7) + base64 + nkf rexml actioncable (7.1.3.4) actionpack (= 7.1.3.4) @@ -85,24 +87,26 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) aes_key_wrap (1.1.0) - android_parser (2.6.0) + android_parser (2.7.0) rexml (> 3.0) rubyzip (>= 1.0, < 3.0) - app-info (3.0.0) + app-info (3.1.4) CFPropertyList (>= 2.3.4, < 3.1.0) - android_parser (~> 2.6.0) - google-protobuf (>= 3.19.4, < 3.23.0) + android_parser (>= 2.7, < 3.0) + base64 (~> 0.2.0) + google-protobuf (>= 4.27.1, < 5.0.0) icns (~> 0.2.0) - image_size (>= 1.5, < 3.3) + image_size (>= 1.5, < 3.5) + nkf (~> 0.2.0) pedump (~> 0.6.2) - ruby-macho (>= 1.4, < 4) + ruby-macho (>= 1.4, < 5) rubyzip (>= 1.2, < 3.0) uuidtools (>= 2.1.5, < 2.3.0) ast (2.4.2) - attr_required (1.0.1) + attr_required (1.0.2) awesome_print (1.9.2) base64 (0.2.0) bcrypt (3.1.20) @@ -127,7 +131,7 @@ GEM ssrf_filter (~> 1.0) case_transform (0.2) activesupport - chef-utils (18.0.185) + chef-utils (18.4.12) concurrent-ruby childprocess (5.0.0) chunky_png (1.4.0) @@ -156,6 +160,8 @@ GEM dotenv (= 3.1.2) railties (>= 6.1) drb (2.2.1) + email_validator (2.2.4) + activemodel erubi (1.13.0) et-orbi (1.2.11) tzinfo @@ -176,8 +182,8 @@ GEM ffi (1.17.0-arm64-darwin) ffi (1.17.0-x86_64-darwin) ffi (1.17.0-x86_64-linux-gnu) - ffi-compiler (1.0.1) - ffi (>= 1.0.0) + ffi-compiler (1.3.2) + ffi (>= 1.15.5) rake friendly_id (5.5.1) activerecord (>= 4.0.0) @@ -191,17 +197,31 @@ GEM rubyntlm (~> 0.5) globalid (1.2.1) activesupport (>= 6.1) - good_job (3.29.4) + good_job (3.30.0) activejob (>= 6.0.0) activerecord (>= 6.0.0) concurrent-ruby (>= 1.0.2) fugit (>= 1.1) railties (>= 6.0.0) thor (>= 0.14.1) - google-protobuf (3.22.5) + google-protobuf (4.27.2) + bigdecimal + rake (>= 13) + google-protobuf (4.27.2-aarch64-linux) + bigdecimal + rake (>= 13) + google-protobuf (4.27.2-arm64-darwin) + bigdecimal + rake (>= 13) + google-protobuf (4.27.2-x86_64-darwin) + bigdecimal + rake (>= 13) + google-protobuf (4.27.2-x86_64-linux) + bigdecimal + rake (>= 13) graphiql-rails (1.10.0) railties - graphql (2.3.5) + graphql (2.3.7) base64 hashie (5.0.0) health_check (3.1.0) @@ -212,7 +232,7 @@ GEM image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) - image_size (3.2.0) + image_size (3.4.0) improved_jenkins_client (1.6.7) addressable (~> 2.7) json (>= 1.0) @@ -223,14 +243,14 @@ GEM thor (>= 0.16.0) interception (0.5) io-console (0.7.2) - iostruct (0.0.5) - irb (1.13.2) + iostruct (0.1.2) + irb (1.14.0) rdoc (>= 4.0.0) reline (>= 0.4.2) jb (0.8.2) jsbundling-rails (1.3.0) railties (>= 6.0.0) - json (2.6.3) + json (2.7.2) json-jwt (1.16.6) activesupport (>= 4.2) aes_key_wrap @@ -241,7 +261,8 @@ GEM json-schema (4.3.0) addressable (>= 2.8) jsonapi-renderer (0.2.2) - jwt (2.7.1) + jwt (2.8.2) + base64 kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -256,6 +277,7 @@ GEM kaminari-core (1.2.2) kramdown (2.4.0) rexml + language_server-protocol (3.17.0.3) launchy (3.0.1) addressable (~> 2.8) childprocess (~> 5.0) @@ -284,23 +306,24 @@ GEM net-smtp marcel (1.0.4) method_source (1.1.0) - mini_magick (4.12.0) + mini_magick (4.13.1) mini_mime (1.1.5) mini_portile2 (2.8.7) - minitest (5.24.0) - mixlib-shellout (3.2.7) + minitest (5.24.1) + mixlib-shellout (3.2.8) chef-utils msgpack (1.7.2) multi_json (1.15.0) - multi_xml (0.6.0) - multipart-post (2.3.0) + multi_xml (0.7.1) + bigdecimal (~> 3.1) + multipart-post (2.4.1) mutex_m (0.2.0) net-http (0.4.1) uri - net-imap (0.4.12) + net-imap (0.4.14) date net-protocol - net-ldap (0.17.1) + net-ldap (0.19.0) net-pop (0.1.2) net-protocol net-protocol (0.2.2) @@ -348,28 +371,29 @@ GEM omniauth-rails_csrf_protection (1.0.2) actionpack (>= 4.2) omniauth (~> 2.0) - omniauth_openid_connect (0.7.1) + omniauth_openid_connect (0.8.0) omniauth (>= 1.9, < 3) openid_connect (~> 2.2) - openid_connect (2.2.0) + openid_connect (2.3.0) activemodel attr_required (>= 1.0.0) + email_validator faraday (~> 2.0) faraday-follow_redirects json-jwt (>= 1.16) - net-smtp + mail rack-oauth2 (~> 2.2) swd (~> 2.0) tzinfo - validate_email validate_url webfinger (~> 2.0) openssl (3.2.0) orm_adapter (0.5.0) - parallel (1.22.1) - parser (3.1.3.0) + parallel (1.25.1) + parser (3.3.3.0) ast (~> 2.4.1) - pedump (0.6.6) + racc + pedump (0.6.10) awesome_print iostruct (>= 0.0.4) multipart-post (>= 2.0.0) @@ -394,7 +418,7 @@ GEM pry (>= 0.12.0) psych (5.1.2) stringio - public_suffix (5.0.5) + public_suffix (6.0.0) puma (6.4.2) nio4r (~> 2.0) pundit (2.3.2) @@ -402,10 +426,10 @@ GEM pyu-ruby-sasl (0.0.3.3) raabro (1.4.0) racc (1.8.0) - rack (3.1.3) + rack (3.1.6) rack-cors (2.0.2) rack (>= 2.0.0) - rack-oauth2 (2.2.0) + rack-oauth2 (2.2.1) activesupport attr_required faraday (~> 2.0) @@ -443,7 +467,7 @@ GEM rails-html-sanitizer (1.6.0) loofah (~> 2.21) nokogiri (~> 1.14) - rails-i18n (7.0.6) + rails-i18n (7.0.9) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) rails-settings-cached (2.9.4) @@ -460,21 +484,21 @@ GEM rainbow (3.1.1) rake (13.0.6) rb-fsevent (0.11.2) - rb-inotify (0.10.1) + rb-inotify (0.11.1) ffi (~> 1.0) rdoc (6.7.0) psych (>= 4.0.0) - regexp_parser (2.6.1) + regexp_parser (2.9.2) reline (0.5.9) io-console (~> 0.5) - request_store (1.5.1) + request_store (1.7.0) rack (>= 1.4) responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (4.1.2) + rexml (3.3.1) + strscan + rouge (4.3.0) rqrcode (2.2.0) chunky_png (~> 1.0) rqrcode_core (~> 1.0) @@ -507,38 +531,41 @@ GEM rswag-ui (2.13.0) actionpack (>= 3.1, < 7.2) railties (>= 3.1, < 7.2) - rubocop (1.41.1) + rubocop (1.64.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.23.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.24.0) - parser (>= 3.1.1.0) - rubocop-rails (2.17.4) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) + rubocop-rails (2.25.0) activesupport (>= 4.2.0) rack (>= 1.1) rubocop (>= 1.33.0, < 2.0) - ruby-macho (3.0.0) - ruby-progressbar (1.11.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-macho (4.1.0) + ruby-progressbar (1.13.0) ruby-vips (2.2.1) ffi (~> 1.12) - rubyntlm (0.6.3) + rubyntlm (0.6.5) + base64 rubyzip (2.3.2) - sentry-rails (5.17.3) + sentry-rails (5.18.1) railties (>= 5.0) - sentry-ruby (~> 5.17.3) - sentry-ruby (5.17.3) + sentry-ruby (~> 5.18.1) + sentry-ruby (5.18.1) bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) simple_form (5.3.1) actionpack (>= 5.2) activemodel (>= 5.2) - slim (5.1.1) + slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) slim-rails (3.6.3) @@ -555,7 +582,7 @@ GEM railties (>= 6.0.0) stringio (3.1.1) strscan (3.1.0) - swd (2.0.2) + swd (2.0.3) activesupport (>= 3) attr_required (>= 0.0.5) faraday (~> 2.0) @@ -566,7 +593,7 @@ GEM terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) thor (1.3.1) - tilt (2.3.0) + tilt (2.4.0) timeout (0.4.1) tiny_appstore_connect (0.1.12) CFPropertyList (>= 2.3.4, < 3.1.0) @@ -579,12 +606,9 @@ GEM railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.3.0) + unicode-display_width (2.5.0) uri (0.13.0) uuidtools (2.2.0) - validate_email (0.1.6) - activemodel (>= 3.0) - mail (>= 2.2.5) validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix @@ -596,7 +620,7 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webfinger (2.1.2) + webfinger (2.1.3) activesupport faraday (~> 2.0) faraday-follow_redirects @@ -608,7 +632,7 @@ GEM websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) zeitwerk (2.6.16) - zhexdump (0.0.2) + zhexdump (0.1.1) PLATFORMS aarch64-linux @@ -628,7 +652,7 @@ PLATFORMS DEPENDENCIES active_model_serializers (~> 0.10.14) activejob-status (~> 1.0.2) - app-info (~> 3.0.0) + app-info (~> 3.1.4) awesome_print better_errors binding_of_caller @@ -643,9 +667,9 @@ DEPENDENCIES faraday (~> 2.9.2) friendly_id (~> 5.5.1) gitlab_omniauth-ldap (~> 2.2.0) - good_job (~> 3.29.4) + good_job (~> 3.30.0) graphiql-rails - graphql (~> 2.3.5) + graphql (~> 2.3.6) health_check (~> 3.1.0) improved_jenkins_client (~> 1.6.7) jb (~> 0.8.2) @@ -656,13 +680,12 @@ DEPENDENCIES letter_opener_web (~> 3.0) listen (>= 3.0.5, < 3.10) lograge (~> 0.14.0) - nkf (~> 0.2.0) omniauth (~> 2.1.2) omniauth-feishu (~> 0.1.8) omniauth-gitlab (~> 3.0.0) omniauth-google-oauth2 (~> 1.0.1) omniauth-rails_csrf_protection (~> 1.0.2) - omniauth_openid_connect (= 0.7.1) + omniauth_openid_connect (= 0.8.0) openssl (~> 3.2.0) pg (>= 0.18, < 2.0) pghero (~> 3.5.0) @@ -684,8 +707,8 @@ DEPENDENCIES rswag-ui (~> 2.13.0) rubocop (>= 0.70) rubocop-rails - sentry-rails (~> 5.17.3) - sentry-ruby (~> 5.17.3) + sentry-rails (~> 5.18.1) + sentry-ruby (~> 5.18.0) simple_form (~> 5.3) slim-rails (~> 3.6.3) solid_cache (~> 0.6.0) diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 57821eeb5..643cf8599 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -27,6 +27,7 @@ def edit authorize @user @title = @user.email + @user.send(:generate_confirmation_token!) if @user.send(:confirmation_period_expired?) end def update diff --git a/app/controllers/dashboards_controller.rb b/app/controllers/dashboards_controller.rb index 33c77338f..e41b4c197 100644 --- a/app/controllers/dashboards_controller.rb +++ b/app/controllers/dashboards_controller.rb @@ -24,7 +24,7 @@ def recently_upload def system_analytics general_widgets - admin_panels if current_user&.admin? + admin_panels end def general_widgets @@ -37,6 +37,8 @@ def general_widgets end def admin_panels + return if !!current_user&.admin? + @analytics.merge!({ users: User.count, webhooks: WebHook.count, @@ -58,25 +60,25 @@ def disk_usage end def user_apps - return App.count if Setting.guest_mode || current_user.admin? + return App.count if Setting.guest_mode || !!current_user&.admin? current_user.apps.count end def user_teardowns - return Metadatum.count if Setting.guest_mode || current_user.admin? + return Metadatum.count if Setting.guest_mode || !!current_user&.admin? current_user.metadatum.count end def user_debug_files - return DebugFile.count if Setting.guest_mode || current_user.admin? + return DebugFile.count if Setting.guest_mode || !!current_user&.admin? current_user.apps.sum {|app| app.total_debug_files } end def app_uploads - return Release.count if Setting.guest_mode || current_user.admin? + return Release.count if Setting.guest_mode || !!current_user&.admin? current_user.apps.sum {|app| app.total_releases } end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 5b2cd97a6..018530fb9 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -56,6 +56,8 @@ def platform_name(platform) case platform.downcase when 'ios' 'iOS' + when 'appletv' + 'Apple TV' when 'iphone' 'iPhone' when 'ipad' @@ -83,6 +85,8 @@ def device_name(device) 'iPad' when AppInfo::Device::UNIVERSAL 'Universal' + when AppInfo::Device::APPLETV + 'tvOS' when AppInfo::Device::PHONE 'Phone' when AppInfo::Device::WATCH @@ -122,7 +126,7 @@ def timeline_app_icon(device_type) def device_style(device_type) case device_type.downcase - when 'ios' + when 'ios', 'appletv' ['fa-apple', 'bg-secondary'] when 'android' ['fa-android', 'bg-green'] diff --git a/app/jobs/app_web_hook_job.rb b/app/jobs/app_web_hook_job.rb index 1ae385e3a..7128e73da 100644 --- a/app/jobs/app_web_hook_job.rb +++ b/app/jobs/app_web_hook_job.rb @@ -15,6 +15,15 @@ def perform(event, web_hook, channel, user_id) @release = @channel.releases.last @user = User.find(user_id) + if @release.blank? + logger.error(log_message(t('active_job.webhook.failures.empty_release'))) + return notificate_failure( + user_id: @user.id, + type: 'webhook', + message: t('active_job.webhook.failures.empty_release') + ) + end + logger.info(log_message("trigger event: #{@event}")) logger.info(log_message("trigger url: #{@web_hook.url}")) logger.info(log_message("trigger json body: #{message_body}")) @@ -53,7 +62,7 @@ def build(body) build_version: @release.build_version, bundle_id: @release.bundle_id, changelog: @release.text_changelog, - file_size: @release.file.size, + file_size: @release.file_size, release_url: @release.release_url, install_url: @release.install_url, icon_url: @release.icon_url, @@ -99,4 +108,14 @@ def title def log_message(message) "[Channel] #{@channel.id} #{message}" end + + def build_example_release + @channel.releases.build( + name: 'Example App', + bundle_id: 'im.ews.zealot.example.app', + release_version: '1.0.0', + build_version: '5', + git_commit: '31dbb8497b81e103ecadcab0ca724c3fd87b3ab9' + ) + end end diff --git a/app/models/channel.rb b/app/models/channel.rb index df3785f93..ece245b7e 100755 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -97,6 +97,7 @@ def release_version_count(version) def bundle_id_matched?(value) return true if bundle_id.blank? || bundle_id == '*' + return false if value.blank? # TODO: need verify why it empty(can not parse bundle id or package name or empty) value.match?(bundle_id) end diff --git a/app/models/metadatum.rb b/app/models/metadatum.rb index df470c40f..7d85e46dd 100644 --- a/app/models/metadatum.rb +++ b/app/models/metadatum.rb @@ -6,6 +6,7 @@ class Metadatum < ApplicationRecord enum platform: { ios: 'ios', + appletv: 'appletv', android: 'android', mobileprovision: 'mobileprovision', macos: 'macos', diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 30bda7089..f618ee47c 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -6,5 +6,5 @@ class WebHook < ApplicationRecord delegate :count, to: :channels, prefix: true validates :channel_id, :url, presence: true - validates :body, json: { format: :hash, value_allow_empty: true } + validates :body, presence: true, jb: true end diff --git a/app/services/teardown_service.rb b/app/services/teardown_service.rb index 75215fe41..2cffb4415 100644 --- a/app/services/teardown_service.rb +++ b/app/services/teardown_service.rb @@ -33,7 +33,7 @@ def process process_mobileprovision(parser, metadata) else case parser.platform - when AppInfo::Platform::IOS + when AppInfo::Platform::IOS, AppInfo::Platform::APPLETV process_ios(parser, metadata) when AppInfo::Platform::ANDROID process_android(parser, metadata) @@ -117,9 +117,9 @@ def process_signature_certs(parser, metadata) end end - ########### - # iOS # - ########### + ############### + # iOS/AppleTV # + ############### def process_ios(parser, metadata) process_app_common(parser, metadata) @@ -132,11 +132,11 @@ def process_ios(parser, metadata) metadata.devices = devices end - if schemes = parser.info['CFBundleURLTypes'] - metadata.url_schemes = schemes.each_with_object([]) do |value, obj| - next unless schemes_value = value['CFBundleURLSchemes'] + if url_schemes = parser.url_schemes + metadata.url_schemes = url_schemes.each_with_object([]) do |option, obj| + next unless schemes = option[:schemes] - obj << schemes_value.split(', ') + obj << schemes.split(', ') end end end diff --git a/app/validators/jb_validator.rb b/app/validators/jb_validator.rb new file mode 100644 index 000000000..d88a8347f --- /dev/null +++ b/app/validators/jb_validator.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class JbValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + parsed_value = _parse_jb(record, attribute, value) + validate_value(record, attribute, parsed_value) + validate_format(record, attribute, parsed_value) + rescue StandardError + record.errors.add(attribute, :invaild_jb) + end + + private + + def _parse_jb(record, attribute, value) + ApplicationController.render(inline: value.to_s, type: :jb) + end + + def validate_value(record, attribute, value) + record.errors.add(attribute, :empty_value) if value.blank? || value == 'null' + end + + def validate_format(record, attribute, value) + converted_value = JSON.parse(value) + record.errors.add(attribute, :invaild_jb) unless converted_value.is_a?(Hash) + rescue JSON::ParserError + record.errors.add(attribute, :invaild_jb) + end + +end diff --git a/app/views/layouts/_analytics.html.slim b/app/views/layouts/_analytics.html.slim index eb890a07e..555b1a4a5 100644 --- a/app/views/layouts/_analytics.html.slim +++ b/app/views/layouts/_analytics.html.slim @@ -4,8 +4,7 @@ window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); - - gtag('config', Setting.google_analytics_id); + gtag('config', "#{Setting.google_analytics_id}"); - if Setting.umami_website_id.present? script defer=true src="#{Setting.umami_script_url}" data-website-id="#{Setting.umami_website_id}" @@ -16,4 +15,4 @@ c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)}; t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i; y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y); - })(window, document, "clarity", "script", Setting.clarity_analytics_id); + })(window, document, "clarity", "script", "#{Setting.clarity_analytics_id"); diff --git a/app/views/layouts/mailer.html.slim b/app/views/layouts/mailer.html.slim index 4fe68058a..7431054ef 100644 --- a/app/views/layouts/mailer.html.slim +++ b/app/views/layouts/mailer.html.slim @@ -5,10 +5,11 @@ html link[rel="apple-zealot-icon" sizes="60x60" href="#{asset_path('zealot-icon.png')}"] link[rel="apple-zealot-icon" sizes="120x120" href="#{asset_path('zealot-icon@2x.png')}"] link[rel="apple-zealot-icon" sizes="180x180" href="#{asset_path('zealot-icon@3x.png')}"] - link[rel="icon" type="image/x-icon" href="#{asset_path('zealot-icon@3x.png')}"] + link[rel="icon" type="image/x-icon" href="#{asset_path('zealot-icon@3x.png')}"]\ + meta[name="turbo-prefetch" content="false"] == favicon_link_tag 'zealot-icon.png' - == stylesheet_link_tag 'application', 'data-turbolinks-track': 'reload' - == javascript_include_tag 'application', 'data-turbolinks-track': 'reload', defer: true + == stylesheet_link_tag 'application' + == javascript_include_tag 'application', defer: true == csrf_meta_tags body.hold-transition.layout-top-nav[ diff --git a/app/views/teardowns/_appletv.html.slim b/app/views/teardowns/_appletv.html.slim new file mode 100644 index 000000000..610009bec --- /dev/null +++ b/app/views/teardowns/_appletv.html.slim @@ -0,0 +1,42 @@ +.col-md-6 + .card + .card-header + h3.card-title + i.icon.fas.fa-tv.text-secondary + = t('teardowns.show.metadata') + .card-tools + button.btn.btn-tool data-card-widget="collapse" + i.fas.fa-minus + .card-body.pl-2.pr-2.p-0 + dl.system-info.pb-0 + dt = t('teardowns.show.app_name') + dd + pre = @metadata.name + dt = t('teardowns.show.supported_device') + dd + pre #{device_name(@metadata.device)} (#{platform_name(@metadata.platform)}) + dt = t('teardowns.show.version') + dd + pre = "#{@metadata.release_version} (#{@metadata.build_version})" + dt = t('teardowns.show.package_name') + dd + pre = @metadata.bundle_id + dt = t('teardowns.show.release_type') + dd + pre = release_type_name(@metadata.release_type) + dt = t('teardowns.show.min_tvos_version') + dd + pre = @metadata.min_sdk_version + dt = t('teardowns.show.file_size') + dd + pre = number_to_human_size(@metadata.size) + + == render 'devices_part', devices: @metadata.devices + == render 'card', title: t('teardowns.show.capabilities'), raw: @metadata.capabilities + +.col-md-6 + == render 'developer_certs_part' + == render 'mobileprovision_part' + + == render 'card', title: 'URL Schemes', raw: @metadata.url_schemes + == render 'card', title: 'Entitlements', raw: @metadata.entitlements diff --git a/config/locales/zealot/api.en.yml b/config/locales/zealot/api.en.yml index 727ff7240..d4a33af00 100644 --- a/config/locales/zealot/api.en.yml +++ b/config/locales/zealot/api.en.yml @@ -13,7 +13,7 @@ en: ```json { - "version": "5.3.3", + "version": "5.3.4", "vcs_ref": "effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d", "build_date": "2024-05-23T06:04:48.989Z" } diff --git a/config/locales/zealot/api.zh-CN.yml b/config/locales/zealot/api.zh-CN.yml index 6596a598e..dc28d73ae 100644 --- a/config/locales/zealot/api.zh-CN.yml +++ b/config/locales/zealot/api.zh-CN.yml @@ -13,7 +13,7 @@ zh-CN: ```json { - "version": "5.3.3", + "version": "5.3.4", "vcs_ref": "effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d", "build_date": "2024-05-23T06:04:48.989Z" } diff --git a/config/locales/zealot/en.yml b/config/locales/zealot/en.yml index d03079d3d..6f401223d 100644 --- a/config/locales/zealot/en.yml +++ b/config/locales/zealot/en.yml @@ -35,7 +35,7 @@ en: edit: title: User information active_link: Active link - active_link_tip: If the inviting user does not receive the activation email, use this link to active or %{label}. + active_link_tip: If the inviting user does not receive the activation email, use this link to active or %{label}. resend: resend confirmation email collaborators: Collaborators api: API @@ -637,8 +637,9 @@ en: sdk_version_range: Android min SDK / target SDK file_size: File size release_type: release type - min_ios_version: Min iOS version - min_macos_version: Min macOS version + min_ios_version: Minimum iOS version + min_tvos_version: Minimum tvOS version + min_macos_version: Minimum macOS version capabilities: Capabilities devise_list: 'UDID (%{count})' developer_certs_list: 'Developer Certificates (%{count})' @@ -928,6 +929,9 @@ en: goback_title: Go back homepage_title: homepage active_job: + webhook: + failures: + empty_release: Not found any release to send test webhook. debug_file: success: Debug file (%{id}) pare done, Refresh the web page after 3 seconds. failures: @@ -956,6 +960,7 @@ en: messages: same_value: "%{key} not change, skip to update" invaild_json: Invalid JSON format + invaild_jb: Invalid format incorrect_json_format: Unknown JSON format empty_json_value: JSON value is empty models: diff --git a/config/locales/zealot/zh-CN.yml b/config/locales/zealot/zh-CN.yml index de35299c8..92e0bde26 100644 --- a/config/locales/zealot/zh-CN.yml +++ b/config/locales/zealot/zh-CN.yml @@ -35,7 +35,7 @@ zh-CN: edit: title: 用户信息 active_link: 激活链接 - active_link_tip: 若发送邮件设置错误或邀请用户没有收到激活邮件,可通过本链接激活或%{label}。 + active_link_tip: 若发送邮件设置错误或邀请用户没有收到激活邮件,可通过本链接激活或%{label}。 resend: 重新发送激活邮件 collaborators: 应用成员 api: API @@ -632,6 +632,7 @@ zh-CN: file_size: 文件大小 release_type: 打包类型 min_ios_version: 最低 iOS 版本 + min_tvos_version: 最低 tvOS 版本 min_macos_version: 最低 macOS 版本 capabilities: 启用能力 devise_list: '设备列表 (%{count})' @@ -939,6 +940,9 @@ zh-CN: goback_title: 上级页面 homepage_title: 首页 active_job: + webhook: + failures: + empty_release: 没有找到上传应用版本去发送测试网络钩子 debug_file: success: 调试文件 %{id} 解析完成,3 秒后自动刷新本页面 failures: @@ -970,6 +974,7 @@ zh-CN: messages: same_value: "%{key}没有发生变化,忽略此操作" invaild_json: 无效的 JSON 格式 + invaild_jb: 无效格式 incorrect_json_format: 未知 JSON 格式 empty_json_value: JSON 值为空 models: diff --git a/db/migrate/20240707040747_create_good_job_process_lock_ids.rb b/db/migrate/20240707040747_create_good_job_process_lock_ids.rb new file mode 100644 index 000000000..da43683d4 --- /dev/null +++ b/db/migrate/20240707040747_create_good_job_process_lock_ids.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +class CreateGoodJobProcessLockIds < ActiveRecord::Migration[7.1] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_jobs, :locked_by_id) + end + end + + add_column :good_jobs, :locked_by_id, :uuid + add_column :good_jobs, :locked_at, :datetime + add_column :good_job_executions, :process_id, :uuid + add_column :good_job_processes, :lock_type, :integer, limit: 2 + end +end diff --git a/db/migrate/20240707040748_create_good_job_process_lock_indexes.rb b/db/migrate/20240707040748_create_good_job_process_lock_indexes.rb new file mode 100644 index 000000000..fb98f05a4 --- /dev/null +++ b/db/migrate/20240707040748_create_good_job_process_lock_indexes.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +class CreateGoodJobProcessLockIndexes < ActiveRecord::Migration[7.1] + disable_ddl_transaction! + + def change + reversible do |dir| + dir.up do + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + add_index :good_jobs, [:priority, :scheduled_at], + order: { priority: "ASC NULLS LAST", scheduled_at: :asc }, + where: "finished_at IS NULL AND locked_by_id IS NULL", + name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id) + add_index :good_jobs, :locked_by_id, + where: "locked_by_id IS NOT NULL", + name: :index_good_jobs_on_locked_by_id, + algorithm: :concurrently + end + + unless connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at) + add_index :good_job_executions, [:process_id, :created_at], + name: :index_good_job_executions_on_process_id_and_created_at, + algorithm: :concurrently + end + end + + dir.down do + remove_index(:good_jobs, name: :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_priority_scheduled_at_unfinished_unlocked) + remove_index(:good_jobs, name: :index_good_jobs_on_locked_by_id) if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_locked_by_id) + remove_index(:good_job_executions, name: :index_good_job_executions_on_process_id_and_created_at) if connection.index_name_exists?(:good_job_executions, :index_good_job_executions_on_process_id_and_created_at) + end + end + end +end diff --git a/db/migrate/20240707040749_create_good_job_execution_duration.rb b/db/migrate/20240707040749_create_good_job_execution_duration.rb new file mode 100644 index 000000000..fef37f07b --- /dev/null +++ b/db/migrate/20240707040749_create_good_job_execution_duration.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CreateGoodJobExecutionDuration < ActiveRecord::Migration[7.1] + def change + reversible do |dir| + dir.up do + # Ensure this incremental update migration is idempotent + # with monolithic install migration. + return if connection.column_exists?(:good_job_executions, :duration) + end + end + + add_column :good_job_executions, :duration, :interval + end +end diff --git a/db/schema.rb b/db/schema.rb index 619329cdd..47fa3a7c2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_05_22_065442) do +ActiveRecord::Schema[7.1].define(version: 2024_07_07_040749) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -170,13 +170,17 @@ t.text "error" t.integer "error_event", limit: 2 t.text "error_backtrace", array: true + t.uuid "process_id" + t.interval "duration" t.index ["active_job_id", "created_at"], name: "index_good_job_executions_on_active_job_id_and_created_at" + t.index ["process_id", "created_at"], name: "index_good_job_executions_on_process_id_and_created_at" end create_table "good_job_processes", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.datetime "created_at", null: false t.datetime "updated_at", null: false t.jsonb "state" + t.integer "lock_type", limit: 2 end create_table "good_job_settings", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -209,6 +213,8 @@ t.text "job_class" t.integer "error_event", limit: 2 t.text "labels", array: true + t.uuid "locked_by_id" + t.datetime "locked_at" t.index ["active_job_id", "created_at"], name: "index_good_jobs_on_active_job_id_and_created_at" t.index ["batch_callback_id"], name: "index_good_jobs_on_batch_callback_id", where: "(batch_callback_id IS NOT NULL)" t.index ["batch_id"], name: "index_good_jobs_on_batch_id", where: "(batch_id IS NOT NULL)" @@ -217,8 +223,10 @@ t.index ["cron_key", "cron_at"], name: "index_good_jobs_on_cron_key_and_cron_at_cond", unique: true, where: "(cron_key IS NOT NULL)" t.index ["finished_at"], name: "index_good_jobs_jobs_on_finished_at", where: "((retried_good_job_id IS NULL) AND (finished_at IS NOT NULL))" t.index ["labels"], name: "index_good_jobs_on_labels", where: "(labels IS NOT NULL)", using: :gin + t.index ["locked_by_id"], name: "index_good_jobs_on_locked_by_id", where: "(locked_by_id IS NOT NULL)" t.index ["priority", "created_at"], name: "index_good_job_jobs_for_candidate_lookup", where: "(finished_at IS NULL)" t.index ["priority", "created_at"], name: "index_good_jobs_jobs_on_priority_created_at_when_unfinished", order: { priority: "DESC NULLS LAST" }, where: "(finished_at IS NULL)" + t.index ["priority", "scheduled_at"], name: "index_good_jobs_on_priority_scheduled_at_unfinished_unlocked", where: "((finished_at IS NULL) AND (locked_by_id IS NULL))" t.index ["queue_name", "scheduled_at"], name: "index_good_jobs_on_queue_name_and_scheduled_at", where: "(finished_at IS NULL)" t.index ["scheduled_at"], name: "index_good_jobs_on_scheduled_at", where: "(finished_at IS NULL)" end @@ -353,6 +361,17 @@ t.string "timezone", default: "Asia/Shanghai", null: false end + create_table "visibilities", force: :cascade do |t| + t.string "relationable_type" + t.bigint "relationable_id" + t.integer "level", default: 0, null: false + t.string "vault" + t.datetime "expired_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["relationable_type", "relationable_id"], name: "index_visibilities_on_relationable" + end + create_table "web_hooks", force: :cascade do |t| t.bigint "channel_id" t.string "url" diff --git a/package.json b/package.json index d0737dec5..a7baaa601 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zealot", - "version": "5.3.3", + "version": "5.3.4", "private": true, "license": "MIT", "dependencies": { diff --git a/spec/swagger_helper.rb b/spec/swagger_helper.rb index 311742e7e..e1e075e32 100644 --- a/spec/swagger_helper.rb +++ b/spec/swagger_helper.rb @@ -255,7 +255,7 @@ description: I18n.t('api.schemas.version.description'), type: :object, properties: { - version: { type: :integer, format: :int32, example: '5.3.3' }, + version: { type: :integer, format: :int32, example: '5.3.4' }, vcs_ref: { type: :string, example: 'effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d' }, build_date: { type: :string, example: '2024-05-23T06:04:48.989Z' } } diff --git a/swagger/v1/swagger_en.json b/swagger/v1/swagger_en.json index eae97bafb..2c10c0835 100644 --- a/swagger/v1/swagger_en.json +++ b/swagger/v1/swagger_en.json @@ -3,7 +3,7 @@ "info": { "title": "Zealot API", "version": "v1.3", - "description": "This documentation doesn't provide a way to test our API. In order to facilitate testing, we recommend the following tools:\n\n- [cURL](https://curl.se/) (recommended, command-line)\n- [Bruno](https://www.usebruno.com/)\n- [Postman](https://www.postman.com/downloads/)\n- Your web browser, if you don't need to send headers or a request body\n\nOnce you have a working client, you can test that it works by making a GET request to {host}/version:\n\n```json\n{\n \"version\": \"5.3.3\",\n \"vcs_ref\": \"effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d\",\n \"build_date\": \"2024-05-23T06:04:48.989Z\"\n}\n```\n\n## Authentication\n\nThis API only accepts one options for authentication: Personal access tokens.\nAll tokens are tied to a Zealot user and use the `token` query of the request.\n\nExample:\n\n```\nhttps://tryzealot.ews.im/api/users?token={token}\n```\n\n### Personal access tokens\n\nPersonal access tokens (PATs) can be found in from the [user settings](/docs/user-guide/user_settings).\n" + "description": "This documentation doesn't provide a way to test our API. In order to facilitate testing, we recommend the following tools:\n\n- [cURL](https://curl.se/) (recommended, command-line)\n- [Bruno](https://www.usebruno.com/)\n- [Postman](https://www.postman.com/downloads/)\n- Your web browser, if you don't need to send headers or a request body\n\nOnce you have a working client, you can test that it works by making a GET request to {host}/version:\n\n```json\n{\n \"version\": \"5.3.4\",\n \"vcs_ref\": \"effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d\",\n \"build_date\": \"2024-05-23T06:04:48.989Z\"\n}\n```\n\n## Authentication\n\nThis API only accepts one options for authentication: Personal access tokens.\nAll tokens are tied to a Zealot user and use the `token` query of the request.\n\nExample:\n\n```\nhttps://tryzealot.ews.im/api/users?token={token}\n```\n\n### Personal access tokens\n\nPersonal access tokens (PATs) can be found in from the [user settings](/docs/user-guide/user_settings).\n" }, "servers": [ { @@ -2593,7 +2593,7 @@ "version": { "type": "integer", "format": "int32", - "example": "5.3.3" + "example": "5.3.4" }, "vcs_ref": { "type": "string", diff --git a/swagger/v1/swagger_zh-CN.json b/swagger/v1/swagger_zh-CN.json index f3f373c52..ad92b5b57 100644 --- a/swagger/v1/swagger_zh-CN.json +++ b/swagger/v1/swagger_zh-CN.json @@ -3,7 +3,7 @@ "info": { "title": "Zealot API", "version": "v1.3", - "description": "文档可能提供或没有提供测试接口的工具,便于快速测试,你还可以使用如下工具:\n\n- [cURL](https://curl.se/) (推荐,命令行工具)\n- [Bruno](https://www.usebruno.com/)\n- [Postman](https://www.postman.com/downloads/)\n- 任意浏览器,如果你不需要设置 headers 或请求主体\n\n准备好工具,你可以通过 `GET` 请求 {host}/version 可看到 Zealot 版本信息:\n\n```json\n{\n \"version\": \"5.3.3\",\n \"vcs_ref\": \"effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d\",\n \"build_date\": \"2024-05-23T06:04:48.989Z\"\n}\n```\n\n## 接口认证\n\n接口目前仅提供在用户密钥认证方式,参数是 `token`,请求接口时可在接口 query 或表单字段中传递此字段。\n\n```\nhttps://tryzealot.ews.im/api/users?token={token}\n```\n\n### 用户密钥\n\n用户密钥在[用户详情](/docs/user-guide/user_settings)最底部找到。\n" + "description": "文档可能提供或没有提供测试接口的工具,便于快速测试,你还可以使用如下工具:\n\n- [cURL](https://curl.se/) (推荐,命令行工具)\n- [Bruno](https://www.usebruno.com/)\n- [Postman](https://www.postman.com/downloads/)\n- 任意浏览器,如果你不需要设置 headers 或请求主体\n\n准备好工具,你可以通过 `GET` 请求 {host}/version 可看到 Zealot 版本信息:\n\n```json\n{\n \"version\": \"5.3.4\",\n \"vcs_ref\": \"effe99c25b79fd55d3e1959ea3af0bcb6b75ba1d\",\n \"build_date\": \"2024-05-23T06:04:48.989Z\"\n}\n```\n\n## 接口认证\n\n接口目前仅提供在用户密钥认证方式,参数是 `token`,请求接口时可在接口 query 或表单字段中传递此字段。\n\n```\nhttps://tryzealot.ews.im/api/users?token={token}\n```\n\n### 用户密钥\n\n用户密钥在[用户详情](/docs/user-guide/user_settings)最底部找到。\n" }, "servers": [ { @@ -2593,7 +2593,7 @@ "version": { "type": "integer", "format": "int32", - "example": "5.3.3" + "example": "5.3.4" }, "vcs_ref": { "type": "string",