Class: Neocities::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/neocities/client.rb

Constant Summary collapse

API_URI =
'https://neocities.org/api/'

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Client

Returns a new instance of Client.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/neocities/client.rb', line 20

def initialize(opts={})
  @uri = URI.parse API_URI
  @http = HTTPClient.new force_basic_auth: true
  @opts = opts
  @pastel = Pastel.new eachline: "\n"

  unless opts[:api_key] || (opts[:sitename] && opts[:password])
    raise ArgumentError, 'client requires a login (sitename/password) or an api_key'
  end

  if opts[:api_key]
    @http.default_header = {'Authorization' => "Bearer #{opts[:api_key]}"}
  else
    @http.set_auth API_URI, opts[:sitename], opts[:password]
  end
end

Instance Method Details

#delete(*paths) ⇒ Object



161
162
163
# File 'lib/neocities/client.rb', line 161

def delete(*paths)
  post 'delete', 'filenames[]' => paths
end

#delete_wrapper_with_dry_run(paths, dry_run = false) ⇒ Object



153
154
155
156
157
158
159
# File 'lib/neocities/client.rb', line 153

def delete_wrapper_with_dry_run(paths, dry_run=false)
  if dry_run
    return {result: 'success'}
  else
    delete(paths)
  end
end

#get(path, params = {}) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/neocities/client.rb', line 169

def get(path, params={})
  uri = @uri+path
  uri.query = URI.encode_www_form params
  resp = @http.get uri
  
  JSON.parse resp.body, symbolize_names: true
end

#info(sitename) ⇒ Object



165
166
167
# File 'lib/neocities/client.rb', line 165

def info(sitename)
  get 'info', sitename: sitename
end

#keyObject



121
122
123
# File 'lib/neocities/client.rb', line 121

def key
  get 'key'
end

#list(path = nil) ⇒ Object



37
38
39
# File 'lib/neocities/client.rb', line 37

def list(path=nil)
  get 'list', :path => path
end

#post(path, args = {}) ⇒ Object



177
178
179
180
181
# File 'lib/neocities/client.rb', line 177

def post(path, args={})
  uri = @uri+path
  resp = @http.post uri, args
  JSON.parse resp.body, symbolize_names: true
end

#pull(sitename, last_pull_time = nil, last_pull_loc = nil, quiet = true) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/neocities/client.rb', line 41

def pull(sitename, last_pull_time=nil, last_pull_loc=nil, quiet=true)
  site_info = get 'info', sitename: sitename

  if site_info[:result] == 'error'
    raise ArgumentError, site_info[:message]
  end

  # handle custom domains for supporter accounts
  if site_info[:info][:domain] && site_info[:info][:domain] != ""
    domain = "https://#{site_info[:info][:domain]}/"
  else
    domain = "https://#{sitename}.neocities.org/"
  end

  # start stats
  success_loaded = 0
  start_time = Time.now
  curr_dir = Dir.pwd

  # get list of files
  resp = get 'list'

  if resp[:result] == 'error'
    raise ArgumentError, resp[:message]
  end
  
  # fetch each file
  uri_parser = URI::Parser.new
  resp[:files].each do |file|
    if !file[:is_directory]
      print @pastel.bold("Loading #{file[:path]} ... ") if !quiet
      
      if 
        last_pull_time && \
        last_pull_loc && \
        Time.parse(file[:updated_at]) <= Time.parse(last_pull_time) && \
        last_pull_loc == curr_dir && \
        File.exist?(file[:path]) # case when user deletes file
        # case when file hasn't been updated since last 
        print "#{@pastel.yellow.bold "NO NEW UPDATES"}\n" if !quiet
        next
      end
      
      pathtotry = uri_parser.escape(domain + file[:path])
      fileconts = @http.get pathtotry
      
      # follow redirects
      while fileconts.status == 301
        new_path = fileconts.header['location'][0]
        print "\n#{@pastel.red "Fetch from #{pathtotry} failed."}\nTrying #{new_path} instead..." if !quiet

        pathtotry = new_path
        fileconts = @http.get pathtotry
      end

      if fileconts.ok?
        print "#{@pastel.green.bold 'SUCCESS'}\n" if !quiet
        success_loaded += 1

        File.open("#{file[:path]}", "w") do |f|
          f.write(fileconts.body)
        end
      else
        print "#{@pastel.red.bold 'FAIL'}\n" if !quiet
      end
    else
      FileUtils.mkdir_p "#{file[:path]}"
    end
  end

  # calculate time command took
  total_time = Time.now - start_time

  # stop the spinner, if there is one
  Whirly.stop if quiet

  # display stats
  puts @pastel.green "\nSuccessfully fetched #{success_loaded} files in #{total_time} seconds"
end

#upload(path, remote_path = nil, dry_run = false) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/neocities/client.rb', line 129

def upload(path, remote_path=nil, dry_run=false)
  path = Pathname path

  unless path.exist?
    raise ArgumentError, "#{path.to_s} does not exist."
  end

  rpath = (remote_path || path.basename)

  res = upload_hash rpath, Digest::SHA1.file(path.to_s).hexdigest

  if res[:files] && res[:files][remote_path.to_s.to_sym] == true
    return {result: 'error', error_type: 'file_exists', message: 'file already exists and matches local file, not uploading'}
  else
    if dry_run
      return {result: 'success'}
    else
      File.open(path.to_s) do |file|
        post 'upload', rpath => file
      end
    end
  end
end

#upload_hash(remote_path, sha1_hash) ⇒ Object



125
126
127
# File 'lib/neocities/client.rb', line 125

def upload_hash(remote_path, sha1_hash)
  post 'upload_hash', remote_path => sha1_hash
end