ZMediumToMarkdown 3.4.0 → 3.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41871cb0a57ded38ba5b49cbe98b19ffa8f963b4425d0bf6a0e8597f305fcbc3
4
- data.tar.gz: 555122d0f62b4434462200240910ed5fed97db9bebc7f54bc1b2b73a308b6702
3
+ metadata.gz: 047cd0234c7a856da3d912b93d419ddcd7ad1396079ba49d3c494807ca37a8de
4
+ data.tar.gz: c4a96904909f9885c48bff408200a886191858be9c9f8746a01db065e66aaa8f
5
5
  SHA512:
6
- metadata.gz: 8f4b1e85672bbb3f24b2fcc7ab7f4e21455eb76dced1abefde45479fc5cce31a0ad928c73a08e34733ea8c0f1913073f547fa3bfaceeab10d3e17aed4d6e0711
7
- data.tar.gz: 915db5d26bc3f78a67fd1d58cc858364f69f4cb5830527213ccefbc7d48911469c6a326367a476ea6d8ecb7da69978ac76fccd2f72f600e0fbcbcc6707615f09
6
+ metadata.gz: 07f6681e620d463a849ccff8e8d497c10114073d8aad3792f0d2beb6cf730145dfb36ac6e304a3ad805de56aad5f24e3f028c5e2613da4f38228657bbc9f70f7
7
+ data.tar.gz: 6e8056232851f1f2656a5b6340b6972bdc603f8ff4aedd18e101c9ec8eb5fe10d7b4ca4d6376086e533daddd180d169a4db7b3c2ead4bd69ba1b175cf73661d9
data/lib/CLI.rb CHANGED
@@ -14,7 +14,6 @@ module CLI
14
14
  COOKIE_SETUP_URL = 'https://github.com/ZhgChgLi/ZMediumToMarkdown/wiki/Setting-Up-Medium-Cookies-and-a-Cloudflare-Worker-Proxy'.freeze
15
15
 
16
16
  DEFAULT_MEDIUM_HOST = 'https://medium.com/_/graphql'.freeze
17
- DEFAULT_MIRO_MEDIUM_HOST = 'https://miro.medium.com'.freeze
18
17
 
19
18
  module_function
20
19
 
@@ -53,10 +52,6 @@ module CLI
53
52
  ENV['MEDIUM_HOST'] = v
54
53
  end
55
54
 
56
- opts.on('--miro_medium_host URL', 'Cloudflare Worker proxy URL for Medium image CDN (or set $MIRO_MEDIUM_HOST). Optional companion to --medium_host.') do |v|
57
- ENV['MIRO_MEDIUM_HOST'] = v
58
- end
59
-
60
55
  opts.on('-u', '--username USERNAME', 'Download all posts from a Medium username') do |v|
61
56
  options[:username] = v
62
57
  end
@@ -167,25 +162,17 @@ module CLI
167
162
  !host.empty? && host != DEFAULT_MEDIUM_HOST
168
163
  end
169
164
 
170
- def imageProxyConfigured?
171
- host = ENV['MIRO_MEDIUM_HOST'].to_s
172
- !host.empty? && host != DEFAULT_MIRO_MEDIUM_HOST
173
- end
174
-
175
-
176
165
  # Only warn when the invocation will actually hit Medium — skip for
177
166
  # --version, --clean, --help, --new.
178
167
  def warnAboutMissingSetup(options, errput: $stderr)
179
168
  return unless willHitMedium?(options)
180
169
 
181
- missingCookies = !cookiesPresent?
182
- missingProxy = !proxyConfigured?
183
- missingImageProxy = !imageProxyConfigured?
184
- return if !missingCookies && !missingProxy && !missingImageProxy
170
+ missingCookies = !cookiesPresent?
171
+ missingProxy = !proxyConfigured?
172
+ return if !missingCookies && !missingProxy
185
173
 
186
174
  errput.puts buildSetupBanner(missingCookies: missingCookies,
187
- missingProxy: missingProxy,
188
- missingImageProxy: missingImageProxy)
175
+ missingProxy: missingProxy)
189
176
  end
190
177
 
191
178
  def willHitMedium?(options)
@@ -194,11 +181,10 @@ module CLI
194
181
 
195
182
  # One-line warning. The wiki has the actual setup steps; we just
196
183
  # nudge the user toward it instead of dumping a wall of guidance.
197
- def buildSetupBanner(missingCookies:, missingProxy:, missingImageProxy:)
184
+ def buildSetupBanner(missingCookies:, missingProxy:)
198
185
  missing = []
199
186
  missing << 'Medium cookies (sid / uid)' if missingCookies
200
187
  missing << 'Cloudflare Worker proxy (MEDIUM_HOST)' if missingProxy
201
- missing << 'Cloudflare image proxy (MIRO_MEDIUM_HOST)' if missingImageProxy
202
188
  return '' if missing.empty?
203
189
 
204
190
  "⚠ Missing #{missing.join(' / ')}. Medium / Cloudflare may block the run. Setup guide: #{COOKIE_SETUP_URL}"
@@ -3,6 +3,7 @@ require 'Models/Paragraph'
3
3
 
4
4
  require 'ImageDownloader'
5
5
  require 'PathPolicy'
6
+ require 'Request'
6
7
 
7
8
  class IMGParser < Parser
8
9
  attr_accessor :nextParser, :pathPolicy, :isForJekyll
@@ -20,7 +21,7 @@ class IMGParser < Parser
20
21
 
21
22
  fileName = paragraph.metadata.id #d*fsafwfe.jpg
22
23
 
23
- miro_host = ENV.fetch('MIRO_MEDIUM_HOST', 'https://miro.medium.com')
24
+ miro_host = Request.miroHost
24
25
  imageURL = "#{miro_host}/#{fileName}"
25
26
 
26
27
  result = ""
data/lib/Post.rb CHANGED
@@ -67,7 +67,7 @@ class Post
67
67
  imagePathPolicy = PathPolicy.new(pathPolicy.getAbsolutePath(postID), pathPolicy.getRelativePath(postID))
68
68
  absolutePath = imagePathPolicy.getAbsolutePath(previewImageFileName)
69
69
 
70
- miro_host = ENV.fetch('MIRO_MEDIUM_HOST', 'https://miro.medium.com')
70
+ miro_host = Request.miroHost
71
71
  imageURL = "#{miro_host}/#{previewImageFileName}"
72
72
 
73
73
  if ImageDownloader.download(absolutePath, imageURL)
data/lib/Request.rb CHANGED
@@ -227,6 +227,14 @@ class Request
227
227
  request['Cookie'] = cookiesString;
228
228
  end
229
229
 
230
+ # When the request is going to a configured Worker proxy (and only
231
+ # then), attach the user's MEDIUM_HOST_SECRET as a header so the
232
+ # Worker can authenticate the caller. Skipped for upstream
233
+ # medium.com / miro.medium.com so the secret never leaks to Medium.
234
+ if proxyURI?(uri) && (proxySecret = ENV['MEDIUM_HOST_SECRET'].to_s) && !proxySecret.empty?
235
+ request['X-Medium-Proxy-Secret'] = proxySecret
236
+ end
237
+
230
238
  response = https.request(request);
231
239
 
232
240
  setCookieString = response.get_fields('set-cookie');
@@ -305,6 +313,28 @@ class Request
305
313
  nil
306
314
  end
307
315
 
316
+ # Resolve the host the gem should use for miro.medium.com image fetches.
317
+ # Single-Worker setups: the same MEDIUM_HOST proxy handles both medium.com
318
+ # and miro.medium.com via path dispatch, so we always derive miro from
319
+ # MEDIUM_HOST's origin. No proxy → upstream miro.medium.com.
320
+ def self.miroHost
321
+ mediumProxyOrigin || 'https://miro.medium.com'
322
+ end
323
+
324
+ # True iff `uri` is hosted by the configured Worker proxy — i.e. its
325
+ # host matches MEDIUM_HOST and MEDIUM_HOST is set to something other
326
+ # than upstream medium.com. Used to gate the MEDIUM_HOST_SECRET auth
327
+ # header so the secret only leaves the process when heading to the
328
+ # user's own proxy.
329
+ def self.proxyURI?(uri)
330
+ return false if uri.nil? || uri.host.nil?
331
+ envValue = ENV['MEDIUM_HOST'].to_s
332
+ return false if envValue.empty?
333
+ parsed = URI.parse(envValue) rescue nil
334
+ return false if parsed.nil? || parsed.host.nil?
335
+ parsed.host != 'medium.com' && parsed.host == uri.host
336
+ end
337
+
308
338
  # Cloudflare tags blocked responses via either the cf-mitigated header
309
339
  # or the standard "Just a moment..." challenge HTML. We check both
310
340
  # so we catch challenges even on Cloudflare deployments that don't
@@ -178,8 +178,8 @@ class ZMediumFetcher
178
178
 
179
179
  # Stdout fast path: render markdown directly to `stdoutIO` without
180
180
  # touching the filesystem and without downloading any images. Image
181
- # references stay as remote miro.medium.com URLs (or MIRO_MEDIUM_HOST
182
- # proxy if set).
181
+ # references stay as remote URLs on miro.medium.com (or the configured
182
+ # MEDIUM_HOST proxy origin when set).
183
183
  def downloadPostToStdout(postURL, isPin)
184
184
  postID = Post.getPostIDFromPostURLString(postURL)
185
185
  postPath = Post.getPostPathFromPostURLString(postURL)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ZMediumToMarkdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ZhgChgLi