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",