aspera-cli 4.27.2 → 4.27.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +133 -0
  4. data/CONTRIBUTING.md +5 -2
  5. data/bin/ascli +3 -1
  6. data/docs/README.md +1006 -766
  7. data/lib/aspera/agent/base.rb +7 -2
  8. data/lib/aspera/agent/connect.rb +6 -8
  9. data/lib/aspera/agent/desktop.rb +2 -6
  10. data/lib/aspera/agent/direct.rb +52 -22
  11. data/lib/aspera/agent/node.rb +9 -8
  12. data/lib/aspera/agent/transferd.rb +2 -2
  13. data/lib/aspera/api/alee.rb +1 -1
  14. data/lib/aspera/api/aoc.rb +14 -12
  15. data/lib/aspera/api/ats.rb +1 -1
  16. data/lib/aspera/api/cos_node.rb +2 -2
  17. data/lib/aspera/api/faspex.rb +11 -7
  18. data/lib/aspera/api/httpgw.rb +38 -35
  19. data/lib/aspera/api/node.rb +39 -34
  20. data/lib/aspera/ascmd.rb +3 -1
  21. data/lib/aspera/ascp/installation.rb +63 -28
  22. data/lib/aspera/ascp/management.rb +1 -0
  23. data/lib/aspera/assert.rb +4 -0
  24. data/lib/aspera/cli/ascp_actions.rb +20 -41
  25. data/lib/aspera/cli/async_transfer_store.rb +12 -11
  26. data/lib/aspera/cli/bootstrapper.rb +14 -16
  27. data/lib/aspera/cli/command_line.rb +252 -0
  28. data/lib/aspera/cli/command_registry.rb +215 -37
  29. data/lib/aspera/cli/command_spec.rb +104 -15
  30. data/lib/aspera/cli/completion/ascli.bash +12 -0
  31. data/lib/aspera/cli/completion/ascli.fish +16 -0
  32. data/lib/aspera/cli/completion/ascli.zsh +19 -0
  33. data/lib/aspera/cli/context.rb +3 -0
  34. data/lib/aspera/cli/deprecation.rb +37 -0
  35. data/lib/aspera/cli/extended_value.rb +6 -3
  36. data/lib/aspera/cli/formatter.rb +94 -80
  37. data/lib/aspera/cli/gem_checker.rb +1 -1
  38. data/lib/aspera/cli/hints.rb +7 -6
  39. data/lib/aspera/cli/http.rb +22 -34
  40. data/lib/aspera/cli/info.rb +3 -0
  41. data/lib/aspera/cli/mcp_tool.rb +47 -83
  42. data/lib/aspera/cli/option_declarator.rb +33 -42
  43. data/lib/aspera/cli/option_registry.rb +69 -0
  44. data/lib/aspera/cli/option_types.rb +105 -0
  45. data/lib/aspera/cli/option_value.rb +278 -0
  46. data/lib/aspera/cli/options.schema.yaml +124 -15
  47. data/lib/aspera/cli/parser.rb +333 -862
  48. data/lib/aspera/cli/plugins/alee.rb +7 -4
  49. data/lib/aspera/cli/plugins/aoc.rb +545 -518
  50. data/lib/aspera/cli/plugins/ats.rb +59 -80
  51. data/lib/aspera/cli/plugins/base.rb +221 -265
  52. data/lib/aspera/cli/plugins/basic_auth.rb +2 -10
  53. data/lib/aspera/cli/plugins/config.rb +263 -184
  54. data/lib/aspera/cli/plugins/console.rb +103 -39
  55. data/lib/aspera/cli/plugins/cos.rb +6 -23
  56. data/lib/aspera/cli/plugins/factory.rb +3 -0
  57. data/lib/aspera/cli/plugins/faspex5.rb +204 -182
  58. data/lib/aspera/cli/plugins/faspio.rb +6 -11
  59. data/lib/aspera/cli/plugins/httpgw.rb +8 -11
  60. data/lib/aspera/cli/plugins/mcp.rb +20 -55
  61. data/lib/aspera/cli/plugins/node.rb +300 -327
  62. data/lib/aspera/cli/plugins/orchestrator.rb +152 -110
  63. data/lib/aspera/cli/plugins/preview.rb +96 -105
  64. data/lib/aspera/cli/plugins/server.rb +78 -53
  65. data/lib/aspera/cli/plugins/shares.rb +80 -131
  66. data/lib/aspera/cli/preset_actions.rb +44 -27
  67. data/lib/aspera/cli/preset_manager.rb +44 -19
  68. data/lib/aspera/cli/prompt.rb +36 -0
  69. data/lib/aspera/cli/result.rb +42 -36
  70. data/lib/aspera/cli/runner.rb +32 -59
  71. data/lib/aspera/cli/special_values.rb +5 -0
  72. data/lib/aspera/cli/sync_actions.rb +51 -46
  73. data/lib/aspera/cli/terminal_formatter.rb +9 -3
  74. data/lib/aspera/cli/transfer_actions.rb +14 -9
  75. data/lib/aspera/cli/transfer_agent.rb +34 -38
  76. data/lib/aspera/cli/transfer_progress.rb +290 -55
  77. data/lib/aspera/cli/vault_manager.rb +0 -17
  78. data/lib/aspera/cli/version.rb +1 -1
  79. data/lib/aspera/cli/wizard.rb +5 -3
  80. data/lib/aspera/coverage.rb +1 -1
  81. data/lib/aspera/environment.rb +35 -5
  82. data/lib/aspera/faspex_gw.rb +2 -1
  83. data/lib/aspera/faspex_postproc.rb +1 -0
  84. data/lib/aspera/graphql.rb +5 -5
  85. data/lib/aspera/json_rpc/client.rb +5 -5
  86. data/lib/aspera/keychain/encrypted_hash.rb +2 -2
  87. data/lib/aspera/keychain/factory.rb +2 -1
  88. data/lib/aspera/keychain/one_password_api.rb +1 -1
  89. data/lib/aspera/link_header.rb +2 -2
  90. data/lib/aspera/log.rb +47 -27
  91. data/lib/aspera/markdown.rb +2 -0
  92. data/lib/aspera/mime.rb +25 -0
  93. data/lib/aspera/node_emulator.rb +759 -0
  94. data/lib/aspera/oauth/base.rb +37 -26
  95. data/lib/aspera/oauth/factory.rb +7 -3
  96. data/lib/aspera/oauth/generic.rb +1 -1
  97. data/lib/aspera/oauth/json_credentials.rb +34 -0
  98. data/lib/aspera/oauth/jwt.rb +4 -5
  99. data/lib/aspera/oauth/web.rb +9 -8
  100. data/lib/aspera/oauth.rb +1 -0
  101. data/lib/aspera/persistency_folder.rb +1 -3
  102. data/lib/aspera/preview/file_types.rb +4 -4
  103. data/lib/aspera/preview/generator.rb +11 -1
  104. data/lib/aspera/preview/options.schema.yaml +119 -0
  105. data/lib/aspera/preview/terminal.rb +4 -3
  106. data/lib/aspera/preview/utils.rb +9 -6
  107. data/lib/aspera/products/connect.rb +1 -1
  108. data/lib/aspera/rainbow.rb +7 -0
  109. data/lib/aspera/rest/aspera_errors.rb +72 -0
  110. data/lib/aspera/rest/call_error.rb +27 -0
  111. data/lib/aspera/rest/client.rb +523 -0
  112. data/lib/aspera/rest/error_analyzer.rb +113 -0
  113. data/lib/aspera/rest/list.rb +149 -0
  114. data/lib/aspera/rest/parameters.rb +55 -0
  115. data/lib/aspera/rest/util.rb +176 -0
  116. data/lib/aspera/rest.rb +7 -621
  117. data/lib/aspera/schema/IBM Aspera Console-enhanced.yaml +1125 -0
  118. data/lib/aspera/schema/IBM Aspera Faspex API-5.0-enhanced.yaml +0 -20
  119. data/lib/aspera/schema/IBM Aspera Orchestrator API-v1.yaml +1784 -0
  120. data/lib/aspera/schema/IBM Aspera on Cloud API-0.2.6-enhanced.yaml +1230 -137
  121. data/lib/aspera/schema/IBM Aspera on Cloud Automation API-1.0.5-enhanced.yaml +2395 -0
  122. data/lib/aspera/schema/IBM_Aspera_Shares.yaml +14 -13
  123. data/lib/aspera/schema/documentation.rb +13 -3
  124. data/lib/aspera/schema/reader.rb +12 -18
  125. data/lib/aspera/schema/registry.rb +23 -1
  126. data/lib/aspera/schema/validator.rb +92 -0
  127. data/lib/aspera/secret_hider.rb +36 -25
  128. data/lib/aspera/string_ext.rb +15 -0
  129. data/lib/aspera/temp_file_manager.rb +6 -5
  130. data/lib/aspera/transfer/parameters.rb +2 -0
  131. data/lib/aspera/transfer/spec.rb +1 -0
  132. data/lib/aspera/uri_reader.rb +11 -11
  133. data/lib/aspera/web_auth/index.html +147 -0
  134. data/lib/aspera/web_auth/server.rb +81 -0
  135. data.tar.gz.sig +0 -0
  136. metadata +43 -9
  137. metadata.gz.sig +0 -0
  138. data/lib/aspera/colors.rb +0 -79
  139. data/lib/aspera/node_simulator.rb +0 -344
  140. data/lib/aspera/preview/options.rb +0 -45
  141. data/lib/aspera/rest_call_error.rb +0 -25
  142. data/lib/aspera/rest_error_analyzer.rb +0 -111
  143. data/lib/aspera/rest_errors_aspera.rb +0 -58
  144. data/lib/aspera/rest_list.rb +0 -136
  145. data/lib/aspera/web_auth.rb +0 -211
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aspera
4
- # List and lookup methods for Rest
5
- # To be included in classes inheriting Rest that require those methods.
6
- module RestList
7
- # `max`: special query parameter: max number of items for list command
8
- MAX_ITEMS = 'max'
9
- # `pmax`: special query parameter: max number of pages for list command
10
- MAX_PAGES = 'pmax'
11
-
12
- # Query entity by general search (read with parameter `q`)
13
- #
14
- # @param entity [String] Path of entity in API
15
- # @param value [String] Value of field of searched entity
16
- # @param field [String] Field of searched entity
17
- # @param query [Hash] Optional additional search query parameters
18
- # @return [Hash] A single entity matching the search, or an exception if not found or multiple found
19
- def lookup_with_q(entity, value:, field: 'name', query: {})
20
- Aspera.assert_type(query, Hash) { 'query' }
21
- Aspera.assert_type(field, String) { 'field' }
22
- # returns entities matching the query (it matches against several fields in case insensitive way)
23
- # We don't do paging, as anyway, we look for only one match
24
- matching_items = read(entity, query.merge({'q' => value}))
25
- # API style: {totalcount:, ...} cspell: disable-line: TODO: is that total_count ?
26
- # @type [Array<Hash{String => String}>]
27
- matching_items = matching_items[entity] if matching_items.is_a?(Hash)
28
- Aspera.assert_type(matching_items, Array)
29
- case matching_items.length
30
- when 1 then return matching_items.first
31
- when 0 then raise EntityNotFound, %Q{No such #{entity}: "#{value}"}
32
- else
33
- # multiple case insensitive partial matches, try case insensitive full match
34
- # (anyway AoC does not allow creation of 2 entities with same case insensitive field value)
35
- value_matches = matching_items.select { |i| i[field].casecmp?(value) }
36
- case value_matches.length
37
- when 1 then return value_matches.first
38
- when 0 then raise Error, "#{entity}: Multiple case insensitive partial match for: \"#{value}\" in #{matching_items.map { |i| i[field] }.join(', ')} but no case insensitive full match. Please be more specific or give exact #{field}."
39
- else raise Error, "Two entities cannot have the same case insensitive #{field}: #{value_matches.map { |i| i[field] }}"
40
- end
41
- end
42
- end
43
-
44
- # Get a (full or partial) list of all entities of a given type.
45
- # Using query: `offset` + `limit`
46
- # And response `total_count`
47
- # @param entity [String,Symbol] API endpoint of entity to list
48
- # @param items_key [String] Key in the result to get the list of items (Default: same as `entity`)
49
- # @param query [Hash,nil] Additional query parameters
50
- # @return [Array<(Array<Hash>, Integer)>] items, total_count
51
- def list_entities_limit_offset_total_count(
52
- entity:,
53
- operation: 'GET',
54
- items_key: nil,
55
- query: nil
56
- )
57
- entity = entity.to_s if entity.is_a?(Symbol)
58
- items_key = entity.split('/').last if items_key.nil?
59
- query = {} if query.nil?
60
- Aspera.assert_type(entity, String)
61
- Aspera.assert_type(items_key, String)
62
- Aspera.assert_type(query, Hash)
63
- Log.log.debug { "list_entities t=#{entity} k=#{items_key}" }
64
- Log.dump(:query, query)
65
- result = []
66
- offset = 0
67
- max_items = query.delete(MAX_ITEMS)
68
- remain_pages = query.delete(MAX_PAGES)
69
- # Merge default parameters, by default 100 per page
70
- query = {'limit'=> PER_PAGE_DEFAULT}.merge(query)
71
- total_count = nil
72
- call_args = {
73
- operation: operation,
74
- subpath: entity,
75
- query: query,
76
- headers: {'Accept' => Mime::JSON}
77
- }
78
- loop do
79
- query['offset'] = offset
80
- page_result = call(**call_args)
81
- Aspera.assert_type(page_result[items_key], Array)
82
- result.concat(page_result[items_key])
83
- # Reach the limit set by user ?
84
- if !max_items.nil? && (result.length >= max_items)
85
- result = result.slice(0, max_items)
86
- break
87
- end
88
- total_count ||= page_result['total_count']
89
- break if result.length >= total_count
90
- remain_pages -= 1 unless remain_pages.nil?
91
- break if remain_pages == 0
92
- offset += page_result[items_key].length
93
- RestParameters.instance.spinner_cb.call("#{result.length} / #{total_count || '?'}")
94
- end
95
- RestParameters.instance.spinner_cb.call(action: :success)
96
- return result, total_count
97
- end
98
-
99
- # Lookup an entity id from its name.
100
- # Uses query `q` if `query` is `:default` and `field` is `name`.
101
- # @param entity [String] Type of entity to lookup, by default it is the path, and it is also the field name in result
102
- # @param value [String] Value to lookup
103
- # @param field [String] Field to match, by default it is `'name'`
104
- # @param items_key [String] Key in the result to get the list of items (override entity)
105
- # @param query [Hash] Additional query parameters (Default: `:default`)
106
- def lookup_entity_by_field(entity:, value:, field: 'name', items_key: nil, query: :default)
107
- if query.eql?(:default)
108
- Aspera.assert_values(field, ['name']) { 'Default query field' }
109
- query = {'q'=> value}
110
- end
111
- lookup_entity_generic(entity: entity, field: field, value: value) { list_entities_limit_offset_total_count(entity: entity, items_key: items_key, query: query).first }
112
- end
113
-
114
- # Lookup entity by field and value.
115
- # Extracts a single result from the list returned by the block.
116
- #
117
- # @param entity [String] Type of entity to lookup (path, and by default it is also the field name in result)
118
- # @param value [String] Value to match against the field.
119
- # @param field [String] Field to match in the hashes (defaults to 'name').
120
- # @yield [] A mandatory block that returns an Array of Hashes.
121
- # @return [Hash] The unique matching object.
122
- # @raise [Cli::BadIdentifier] If 0 or >1 matches are found.
123
- def lookup_entity_generic(entity:, value:, field: 'name')
124
- Aspera.assert(block_given?, 'block required for lookup_entity_generic')
125
- found = yield
126
- Aspera.assert_array_all(found, Hash)
127
- found = found.select { |i| i[field].eql?(value) }
128
- return found.first if found.length.eql?(1)
129
- raise Cli::BadIdentifier.new(entity, value, field: field, count: found.length)
130
- end
131
-
132
- PER_PAGE_DEFAULT = 1000
133
- private_constant :PER_PAGE_DEFAULT
134
- module_function :lookup_entity_generic
135
- end
136
- end
@@ -1,211 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'aspera/web_server_simple'
4
- require 'aspera/assert'
5
- require 'erb'
6
-
7
- module Aspera
8
- # servlet called on callback: it records the callback request
9
- class WebAuthServlet < WEBrick::HTTPServlet::AbstractServlet
10
- # @param server [WEBrick::HTTPServer] the HTTP server instance
11
- # @param web_auth [WebAuth] the WebAuth instance to record the callback
12
- def initialize(server, web_auth)
13
- Log.log.debug('WebAuthServlet initialize')
14
- super(server)
15
- @web_auth = web_auth
16
- end
17
-
18
- def service(request, response)
19
- Log.log.debug { "received request from browser #{request.request_method} #{request.path}" }
20
- Aspera.assert_values(request.request_method, ['GET'], type: WEBrick::HTTPStatus::MethodNotAllowed) { 'HTTP verb' }
21
- additional_info = @web_auth.signal_request(request)
22
- response.status = 200
23
- response.content_type = 'text/html'
24
- response.body = <<~HTML
25
- <!DOCTYPE html>
26
- <html lang="en">
27
- <head>
28
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
29
- <link rel="icon" href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzMiIgaGVpZ2h0PSIzMiIgdmlld0JveD0iMCAwIDMyIDMyIiBmaWxsPSJub25lIiBzdHJva2U9IiMyMjIiIHN0cm9rZS13aWR0aD0iMyI+CiAgPGxpbmUgeDE9IjMiIHkxPSIzIiB4Mj0iMjkiIHkyPSIyOSIgIHN0cm9rZT0icmVkIi8+CiAgPGxpbmUgeDE9IjI5IiB5MT0iMyIgeDI9IjMiIHkyPSIyOSIgc3Ryb2tlPSJyZWQiIC8+Cjwvc3ZnPg==" type="image/svg+xml">
30
- <title>Close Now</title>
31
- <style>
32
- body {
33
- font-family: Arial, sans-serif;
34
- text-align: center;
35
- padding: 2rem;
36
- margin: 0;
37
- background: linear-gradient(135deg, #f0f4f8, #d9e2ec);
38
- color: #333;
39
- overflow: hidden; /* Ensure no scrollbars for the background animation */
40
- position: relative;
41
- }
42
- h1 {
43
- font-size: 2.5rem;
44
- color: #0078d4;
45
- }
46
- p {
47
- font-size: 1.2rem;
48
- margin-top: 1rem;
49
- }
50
-
51
- /* Styling for animated IBM logos */
52
- .logo {
53
- position: absolute;
54
- bottom: -100px;
55
- width: 40px;
56
- height: 40px;
57
- background: none;
58
- display: flex;
59
- justify-content: center;
60
- align-items: center;
61
- animation: rise 10s infinite ease-in-out;
62
- }
63
-
64
- .logo svg {
65
- width: 100%;
66
- height: 100%;
67
- }
68
-
69
- .logo:nth-child(odd) {
70
- animation-duration: 8s;
71
- }
72
-
73
- .logo:nth-child(even) {
74
- animation-duration: 12s;
75
- }
76
-
77
- @keyframes rise {
78
- 0% {
79
- transform: translateY(0) scale(1);
80
- opacity: 1;
81
- }
82
- 50% {
83
- opacity: 0.7;
84
- }
85
- 100% {
86
- transform: translateY(-120vh) scale(0.7);
87
- opacity: 0;
88
- }
89
- }
90
- </style>
91
- </head>
92
- <body>
93
- <h1>Thank You!</h1>
94
- <p>You can close this window.</p>
95
- <p>#{ERB::Util.html_escape(additional_info) unless additional_info.nil?}</p>
96
-
97
- <!-- JavaScript to generate IBM logos -->
98
- <script>
99
- // Function to create logos dynamically
100
- function createLogos() {
101
- const body = document.body;
102
- const svgContent = `
103
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000px" height="401.149px" viewBox="0 0 1000 401.149" xml:space="preserve">
104
- <g>
105
- <rect fill="#1F70C1" x="0" y="373.217" width="194.433" height="27.833"/>
106
- <rect fill="#1F70C1" x="0" y="319.83" width="194.433" height="27.931"/>
107
- <rect fill="#1F70C1" x="55.468" y="266.541" width="83.399" height="27.932"/>
108
- <rect fill="#1F70C1" x="55.468" y="213.253" width="83.399" height="27.932"/>
109
- <rect fill="#1F70C1" x="55.468" y="159.964" width="83.399" height="27.932"/>
110
- <rect fill="#1F70C1" x="55.468" y="106.577" width="83.399" height="27.932"/>
111
- <rect fill="#1F70C1" x="0" y="53.288" width="194.433" height="27.932"/>
112
- <rect fill="#1F70C1" x="0" y="0" width="194.433" height="27.932"/>
113
- <path fill="#1F70C1" d="M222.167,347.761h299.029c5.051-8.617,8.815-18.027,11.094-27.932H222.167V347.761z"/>
114
- <path fill="#1F70C1" d="M497.92,213.253H277.734v27.932h243.463C514.857,230.487,507.032,221.078,497.92,213.253z"/>
115
- <path fill="#1F70C1" d="M277.734,159.964v27.932H497.92c9.311-7.825,17.135-17.235,23.277-27.932H277.734z"/>
116
- <path fill="#1F70C1" d="M521.197,53.288H222.167V81.22H532.29C529.715,71.315,525.951,61.906,521.197,53.288z"/>
117
- <path fill="#1F70C1" d="M429.279,0H222.167v27.932h278.526C482.072,10.697,456.815,0,429.279,0z"/>
118
- <rect fill="#1F70C1" x="277.734" y="106.577" width="83.3" height="27.932"/>
119
- <path fill="#1F70C1" d="M444.433,134.509h87.163c2.476-8.914,3.764-18.324,3.764-27.932h-90.927z"/>
120
- <rect fill="#1F70C1" x="277.734" y="266.541" width="83.3" height="27.932"/>
121
- <path fill="#1F70C1" d="M444.433,266.541v27.932h90.927c0-9.608-1.288-19.017-3.764-27.932H444.433z"/>
122
- <path fill="#1F70C1" d="M222.167,400.852h207.112c27.734,0,52.793-10.697,71.513-27.932H222.167V400.852z"/>
123
- <rect fill="#1F70C1" x="555.567" y="373.217" width="138.866" height="27.833"/>
124
- <rect fill="#1F70C1" x="555.567" y="319.83" width="138.866" height="27.931"/>
125
- <rect fill="#1F70C1" x="611.034" y="266.541" width="83.399" height="27.932"/>
126
- <rect fill="#1F70C1" x="611.034" y="213.253" width="83.399" height="27.932"/>
127
- <polygon fill="#1F70C1" points="733.063,53.288 555.567,53.288 555.567,81.22 742.67,81.22"/>
128
- <polygon fill="#1F70C1" points="714.639,0 555.567,0 555.567,27.932 724.247,27.932"/>
129
- <rect fill="#1F70C1" x="861.034" y="373.217" width="138.866" height="27.833"/>
130
- <rect fill="#1F70C1" x="861.034" y="319.83" width="138.866" height="27.931"/>
131
- <rect fill="#1F70C1" x="861.034" y="266.541" width="83.399" height="27.932"/>
132
- <rect fill="#1F70C1" x="861.034" y="213.253" width="83.399" height="27.932"/>
133
- <polygon fill="#1F70C1" points="861.034,187.896 944.433,187.896 944.433,159.964 861.034,159.964 694.433,159.964 611.034,159.964 611.034,187.896 694.433,187.896 852.219,187.896"/>
134
- <polygon fill="#1F70C1" points="944.433,106.577 803.982,106.577 794.374,134.509 944.433,134.509"/>
135
- <polygon fill="#1F70C1" points="840.927,0 831.319,27.932 1000,27.932 1000,0"/>
136
- <polygon fill="#1F70C1" points="777.734,400.852 787.341,373.217 768.126,373.217"/>
137
- <polygon fill="#1F70C1" points="759.311,347.761 796.157,347.761 806.062,319.83 749.505,319.83"/>
138
- <polygon fill="#1F70C1" points="740.59,294.473 814.877,294.473 824.683,266.541 730.784,266.541"/>
139
- <polygon fill="#1F70C1" points="721.969,241.185 833.597,241.185 843.106,213.253 712.361,213.253"/>
140
- <polygon fill="#1F70C1" points="611.034,134.509 761.093,134.509 751.486,106.577 611.034,106.577"/>
141
- <polygon fill="#1F70C1" points="812.896,81.22 1000,81.22 1000,53.288 822.405,53.288"/>
142
- </g>
143
- </svg>
144
- `;
145
- for (let i = 0; i < 20; i++) {
146
- const logo = document.createElement('div');
147
- logo.className = 'logo';
148
- const size = Math.random() * 30 + 20; // Random size between 20px and 50px
149
- logo.style.width = `${size}px`;
150
- logo.style.height = `${size}px`;
151
- logo.style.left = `${Math.random() * 100}vw`;
152
- logo.style.animationDelay = `${Math.random() * 5}s`;
153
- logo.innerHTML = svgContent;
154
- body.appendChild(logo);
155
- }
156
- }
157
-
158
- // Call the function to create logos on load
159
- createLogos();
160
- </script>
161
- </body>
162
- </html>
163
- HTML
164
-
165
- return
166
- end
167
- end
168
-
169
- # start a local web server
170
- # then start a browser that will callback the local server upon authentication
171
- # store the final query
172
- class WebAuth < WebServerSimple
173
- # @param endpoint_url [String] e.g. 'https://127.0.0.1:12345'
174
- # @param additional_info [String] Information in web page
175
- def initialize(endpoint_url, additional_info = nil)
176
- uri = URI.parse(endpoint_url)
177
- super(uri)
178
- @mutex = Mutex.new
179
- @cond = ConditionVariable.new
180
- @expected_path = uri.path.empty? ? '/' : uri.path
181
- @query = nil
182
- @additional_info = additional_info
183
- # last argument (self) is provided to constructor of servlet
184
- mount(@expected_path, WebAuthServlet, self)
185
- # server runs in thread
186
- Thread.new { start }
187
- end
188
-
189
- # Called by web server thread on received request
190
- # @return [String] additional information for web page
191
- def signal_request(request)
192
- raise WEBrick::HTTPStatus::NotFound, "unexpected path: #{request.path}" unless request.path.eql?(@expected_path)
193
- # acquire lock and signal change
194
- @mutex.synchronize do
195
- @query = request.query
196
- @cond.signal
197
- end
198
- return @additional_info
199
- end
200
-
201
- # wait for request on web server (main thread)
202
- # @return [Hash] the query
203
- def received_request
204
- # wait for signal from thread
205
- @mutex.synchronize { @cond.wait(@mutex) }
206
- # tell server thread to stop
207
- shutdown
208
- return @query
209
- end
210
- end
211
- end