aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorV1K1NGbg <victor@ilchev.com>2024-08-22 17:48:56 -0300
committerHisham Muhammad <hisham@gobolinux.org>2024-10-21 13:30:51 -0300
commitc083f3418dbaa8b354febf936fe08037a9514d40 (patch)
treeef3d7964c5219c8125abe240503b20e1143944a1 /src
parent0bbe8cab30ddade580f9773f5cb6303f25fe3913 (diff)
downloadluarocks-c083f3418dbaa8b354febf936fe08037a9514d40.tar.gz
luarocks-c083f3418dbaa8b354febf936fe08037a9514d40.tar.bz2
luarocks-c083f3418dbaa8b354febf936fe08037a9514d40.zip
Teal: convert luarocks.upload.api
Diffstat (limited to 'src')
-rw-r--r--src/luarocks/upload/api.tl313
1 files changed, 174 insertions, 139 deletions
diff --git a/src/luarocks/upload/api.tl b/src/luarocks/upload/api.tl
index e1413702..dc0b568c 100644
--- a/src/luarocks/upload/api.tl
+++ b/src/luarocks/upload/api.tl
@@ -1,5 +1,22 @@
1local record api
2 record Configuration
3 key: string
4 server: string
5 version: string
6 end
1 7
2local api = {} 8 record Api
9 load_config: function(Api): Configuration
10 save_config: function(Api): boolean, string
11 check_version: function(Api): boolean, string
12 method: function(Api, string, ...:Parameters): {string : any}, string
13 raw_method: function(Api, string, ...:Parameters): {string : any}, string
14 request: function(Api, string, ?Parameters, ?Parameters): {string : any}, string
15 config: Configuration
16 debug: boolean
17 _server_tool_version: string
18 end
19end
3 20
4local cfg = require("luarocks.core.cfg") 21local cfg = require("luarocks.core.cfg")
5local fs = require("luarocks.fs") 22local fs = require("luarocks.fs")
@@ -10,43 +27,50 @@ local multipart = require("luarocks.upload.multipart")
10local json = require("luarocks.vendor.dkjson") 27local json = require("luarocks.vendor.dkjson")
11local dir_sep = package.config:sub(1, 1) 28local dir_sep = package.config:sub(1, 1)
12 29
13local Api = {} 30local type Parameters = multipart.Parameters
31local type Api = api.Api
32local type Args = require("luarocks.core.types.args").Args
33local type Configuration = api.Configuration
34local type File = multipart.File
35
36local type PersistableTable = require("luarocks.core.types.persist").PersistableTable
14 37
15local function upload_config_file() 38local function upload_config_file(): string
16 if not cfg.config_files.user.file then 39 if not cfg.config_files.user.file then
17 return nil 40 return nil
18 end 41 end
19 return (cfg.config_files.user.file:gsub("[\\/][^\\/]+$", dir_sep .. "upload_config.lua")) 42 return (cfg.config_files.user.file:gsub("[\\/][^\\/]+$", dir_sep .. "upload_config.lua"))
20end 43end
21 44
22function Api:load_config() 45function api.Api:load_config(): Configuration
23 local upload_conf = upload_config_file() 46 local upload_conf = upload_config_file()
24 if not upload_conf then return nil end 47 if not upload_conf then return nil end
25 local config, err = persist.load_into_table(upload_conf) 48 local config = persist.load_into_table(upload_conf) as Configuration
26 return config 49 return config
27end 50end
28 51
29function Api:save_config() 52function api.Api:save_config(): boolean, string
30 -- Test configuration before saving it. 53 -- Test configuration before saving it.
31 local res, err = self:raw_method("status") 54 local res, errraw = self:raw_method("status")
32 if not res then 55 if not res then
33 return nil, err 56 return nil, errraw
34 end 57 end
35 if res.errors then 58 local reserrors = res.errors
36 util.printerr("Server says: " .. tostring(res.errors[1])) 59 if reserrors is {string} then
37 return 60 return nil, ("Server error: " .. tostring(reserrors[1]))
38 end 61 end
39 local upload_conf = upload_config_file() 62 local upload_conf = upload_config_file()
40 if not upload_conf then return nil end 63 if not upload_conf then return nil end
41 local ok, err = fs.make_dir(dir.dir_name(upload_conf)) 64 local ok, errmake = fs.make_dir(dir.dir_name(upload_conf))
42 if not ok then 65 if not ok then
43 return nil, err 66 return nil, errmake
44 end 67 end
45 persist.save_from_table(upload_conf, self.config) 68 persist.save_from_table(upload_conf, self.config as PersistableTable)
46 fs.set_permissions(upload_conf, "read", "user") 69 fs.set_permissions(upload_conf, "read", "user")
70 return true
47end 71end
48 72
49function Api:check_version() 73function api.Api:check_version(): boolean, string
50 if not self._server_tool_version then 74 if not self._server_tool_version then
51 local tool_version = cfg.upload.tool_version 75 local tool_version = cfg.upload.tool_version
52 local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", { 76 local res, err = self:request(tostring(self.config.server) .. "/api/tool_version", {
@@ -58,7 +82,7 @@ function Api:check_version()
58 if not res.version then 82 if not res.version then
59 return nil, "failed to fetch tool version" 83 return nil, "failed to fetch tool version"
60 end 84 end
61 self._server_tool_version = res.version 85 self._server_tool_version = tostring(res.version)
62 if res.force_update then 86 if res.force_update then
63 return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks." 87 return nil, "Your upload client is too out of date to continue, please upgrade LuaRocks."
64 end 88 end
@@ -69,40 +93,45 @@ function Api:check_version()
69 return true 93 return true
70end 94end
71 95
72function Api:method(...) 96function api.Api:method(path: string, ...: Parameters): {string : any}, string
73 local res, err = self:raw_method(...) 97 local res, err = self:raw_method(path, ...)
74 if not res then 98 if not res then
75 return nil, err 99 return nil, err
76 end 100 end
77 if res.errors then 101 local reserrors = res.errors
78 if res.errors[1] == "Invalid key" then 102 if reserrors is {string} then --! not checking the contents
79 return nil, res.errors[1] .. " (use the --api-key flag to change)" 103 if reserrors[1] == "Invalid key" then
104 return nil, reserrors[1] .. " (use the --api-key flag to change)"
80 end 105 end
81 local msg = table.concat(res.errors, ", ") 106 local msg = table.concat(reserrors, ", ")
82 return nil, "API Failed: " .. msg 107 return nil, "API Failed: " .. msg
83 end 108 end
84 return res 109 return res
85end 110end
86 111
87function Api:raw_method(path, ...) 112function api.Api:raw_method(path: string, ...: Parameters): {string : any}, string
88 self:check_version() 113 self:check_version()
89 local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. tostring(path) 114 local url = tostring(self.config.server) .. "/api/" .. tostring(cfg.upload.api_version) .. "/" .. tostring(self.config.key) .. "/" .. path
90 return self:request(url, ...) 115 return self:request(url, ...)
91end 116end
92 117
93local function encode_query_string(t, sep) 118local function encode_query_string(t: Parameters, sep?: string): string
94 if sep == nil then 119 if sep == nil then
95 sep = "&" 120 sep = "&"
96 end 121 end
97 local i = 0 122 local i = 0
98 local buf = { } 123 local buf: {string} = { }
99 for k, v in pairs(t) do 124 for k, v in pairs(t) do
100 if type(k) == "number" and type(v) == "table" then 125 local ks, vs: string, string
101 k, v = v[1], v[2] 126 local vf = v
127 if vf is File then
128 ks, vs = k, vf:content()
129 else
130 ks, vs = k, vf
102 end 131 end
103 buf[i + 1] = multipart.url_escape(k) 132 buf[i + 1] = multipart.url_escape(ks)
104 buf[i + 2] = "=" 133 buf[i + 2] = "="
105 buf[i + 3] = multipart.url_escape(v) 134 buf[i + 3] = multipart.url_escape(vs)
106 buf[i + 4] = sep 135 buf[i + 4] = sep
107 i = i + 4 136 i = i + 4
108 end 137 end
@@ -110,141 +139,144 @@ local function encode_query_string(t, sep)
110 return table.concat(buf) 139 return table.concat(buf)
111end 140end
112 141
113local function redact_api_url(url) 142local function redact_api_url(url: any): string
114 url = tostring(url) 143 local urls = tostring(url)
115 return (url:gsub(".*/api/[^/]+/[^/]+", "")) or "" 144 return (urls:gsub(".*/api/[^/]+/[^/]+", "")) or ""
116end 145end
117 146
118local ltn12_ok, ltn12 = pcall(require, "ltn12") 147local ltn12_ok, ltn12 = pcall(require, "ltn12")
119if not ltn12_ok then -- If not using LuaSocket and/or LuaSec... 148if not ltn12_ok then -- If not using LuaSocket and/or LuaSec...
120 149
121function Api:request(url, params, post_params) 150 api.Api.request = function(self: Api, url: string, params?: Parameters, post_params?: Parameters): {string : any}, string
122 local vars = cfg.variables 151 local vars = cfg.variables
123
124 if fs.which_tool("downloader") == "wget" then
125 local curl_ok, err = fs.is_tool_available(vars.CURL, "curl")
126 if not curl_ok then
127 return nil, err
128 end
129 end
130 152
131 if not self.config.key then 153 if fs.which_tool("downloader") == "wget" then
132 return nil, "Must have API key before performing any actions." 154 local curl_ok, err = fs.is_tool_available(vars.CURL, "curl")
133 end 155 if not curl_ok then
134 if params and next(params) then 156 return nil, err
135 url = url .. ("?" .. encode_query_string(params))
136 end
137 local method = "GET"
138 local out
139 local tmpfile = fs.tmpname()
140 if post_params then
141 method = "POST"
142 local curl_cmd = vars.CURL.." "..vars.CURLNOCERTFLAG.." -f -L --silent --user-agent \""..cfg.user_agent.." via curl\" "
143 for k,v in pairs(post_params) do
144 local var = v
145 if type(v) == "table" then
146 var = "@"..v.fname
147 end 157 end
148 curl_cmd = curl_cmd .. "--form \""..k.."="..var.."\" "
149 end 158 end
150 if cfg.connection_timeout and cfg.connection_timeout > 0 then 159
151 curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." " 160 if not self.config.key then
161 return nil, "Must have API key before performing any actions."
152 end 162 end
153 local ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile)) 163 if params and next(params) then
154 if not ok then 164 url = url .. ("?" .. encode_query_string(params))
155 return nil, "API failure: " .. redact_api_url(url)
156 end 165 end
157 else 166 local method = "GET"
158 local ok, err = fs.download(url, tmpfile) 167 local out: string
159 if not ok then 168 local tmpfile = fs.tmpname()
160 return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url) 169 if post_params then
170 method = "POST"
171 local curl_cmd = vars.CURL.." "..vars.CURLNOCERTFLAG.." -f -L --silent --user-agent \""..cfg.user_agent.." via curl\" "
172 for k,v in pairs(post_params) do
173 local var: string
174 if v is File then
175 var = "@"..v.fname
176 else
177 var = v
178 end
179 curl_cmd = curl_cmd .. "--form \""..k.."="..var.."\" "
180 end
181 if cfg.connection_timeout and cfg.connection_timeout > 0 then
182 curl_cmd = curl_cmd .. "--connect-timeout "..tonumber(cfg.connection_timeout).." "
183 end
184 local ok = fs.execute_string(curl_cmd..fs.Q(url).." -o "..fs.Q(tmpfile))
185 if not ok then
186 return nil, "API failure: " .. redact_api_url(url)
187 end
188 else
189 local name, err = fs.download(url, tmpfile)
190 if not name then
191 return nil, "API failure: " .. tostring(err) .. " - " .. redact_api_url(url)
192 end
161 end 193 end
162 end
163 194
164 local tmpfd = io.open(tmpfile) 195 local tmpfd = io.open(tmpfile)
165 if not tmpfd then 196 if not tmpfd then
197 os.remove(tmpfile)
198 return nil, "API failure reading temporary file - " .. redact_api_url(url)
199 end
200 out = tmpfd:read("*a")
201 tmpfd:close()
166 os.remove(tmpfile) 202 os.remove(tmpfile)
167 return nil, "API failure reading temporary file - " .. redact_api_url(url)
168 end
169 out = tmpfd:read("*a")
170 tmpfd:close()
171 os.remove(tmpfile)
172 203
173 if self.debug then 204 if self.debug then
174 util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ") 205 util.printout("[" .. tostring(method) .. " via curl] " .. redact_api_url(url) .. " ... ")
175 end 206 end
176 207
177 return json.decode(out) 208 return json.decode(out)
178end 209 end
179 210
180else -- use LuaSocket and LuaSec 211else -- use LuaSocket and LuaSec
181 212
182local warned_luasec = false 213 local warned_luasec = false
183 214
184function Api:request(url, params, post_params) 215 api.Api.request = function(self: Api, url: string, params?: Parameters, post_params?: Parameters): {string : any}, string
185 local server = tostring(self.config.server) 216 local server = tostring(self.config.server)
186 local http_ok, http 217 local type Http = require("socket.http")
187 local via = "luasocket" 218 local http_ok, http: boolean, Http
188 if server:match("^https://") then 219 local via = "luasocket"
189 http_ok, http = pcall(require, "ssl.https") 220 if server:match("^https://") then
190 if http_ok then 221 http_ok, http = pcall(require, "ssl.https") as (boolean, Http)
191 via = "luasec" 222 if http_ok then
192 else 223 via = "luasec"
193 if not warned_luasec then 224 else
194 util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.") 225 if not warned_luasec then
195 warned_luasec = true 226 util.printerr("LuaSec is not available; using plain HTTP. Install 'luasec' to enable HTTPS.")
227 warned_luasec = true
228 end
229 http_ok, http = pcall(require, "socket.http")
230 url = url:gsub("^https", "http")
231 via = "luasocket"
196 end 232 end
233 else
197 http_ok, http = pcall(require, "socket.http") 234 http_ok, http = pcall(require, "socket.http")
198 url = url:gsub("^https", "http")
199 via = "luasocket"
200 end 235 end
201 else 236 if not http_ok then
202 http_ok, http = pcall(require, "socket.http") 237 return nil, "Failed loading socket library!"
203 end 238 end
204 if not http_ok then
205 return nil, "Failed loading socket library!"
206 end
207 239
208 if not self.config.key then 240 if not self.config.key then
209 return nil, "Must have API key before performing any actions." 241 return nil, "Must have API key before performing any actions."
210 end 242 end
211 local body 243 local body: string
212 local headers = {} 244 local headers: {string: string} = {}
213 if params and next(params) then 245 if params and next(params) then
214 url = url .. ("?" .. encode_query_string(params)) 246 url = url .. ("?" .. encode_query_string(params))
215 end 247 end
216 if post_params then 248 if post_params then
217 local boundary 249 local boundary: string
218 body, boundary = multipart.encode(post_params) 250 body, boundary = multipart.encode(post_params)
219 headers["Content-length"] = #body 251 headers["Content-length"] = tostring(#body)
220 headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary) 252 headers["Content-type"] = "multipart/form-data; boundary=" .. tostring(boundary)
221 end 253 end
222 local method = post_params and "POST" or "GET" 254 local method = post_params and "POST" or "GET"
223 if self.debug then 255 if self.debug then
224 util.printout("[" .. tostring(method) .. " via "..via.."] " .. redact_api_url(url) .. " ... ") 256 util.printout("[" .. tostring(method) .. " via "..via.."] " .. redact_api_url(url) .. " ... ")
225 end 257 end
226 local out = {} 258 local out = {}
227 local _, status = http.request({ 259 local _, status = http.request({
228 url = url, 260 url = url,
229 headers = headers, 261 headers = headers,
230 method = method, 262 method = method,
231 sink = ltn12.sink.table(out), 263 sink = ltn12.sink.table(out),
232 source = body and ltn12.source.string(body) 264 source = body and ltn12.source.string(body)
233 }) 265 })
234 if self.debug then 266 if self.debug then
235 util.printout(tostring(status)) 267 util.printout(tostring(status))
236 end 268 end
237 local pok, ret, err = pcall(json.decode, table.concat(out)) 269 local pok, ret = pcall(json.decode, table.concat(out))
238 if pok and ret then 270 if pok and ret then
239 return ret 271 return ret
272 end
273 return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url)
240 end 274 end
241 return nil, "API returned " .. tostring(status) .. " - " .. redact_api_url(url)
242end
243 275
244end 276end
245 277
246function api.new(args) 278function api.new(args: Args): Api, string
247 local self = {} 279 local self: Api = {}
248 setmetatable(self, { __index = Api }) 280 setmetatable(self, { __index = Api })
249 self.config = self:load_config() or {} 281 self.config = self:load_config() or {}
250 self.config.server = args.server or self.config.server or cfg.upload.server 282 self.config.server = args.server or self.config.server or cfg.upload.server
@@ -257,7 +289,10 @@ function api.new(args)
257 "and then pass it through the --api-key=<key> flag." 289 "and then pass it through the --api-key=<key> flag."
258 end 290 end
259 if args.api_key then 291 if args.api_key then
260 self:save_config() 292 local ok, err = self:save_config()
293 if not ok then
294 return nil, err
295 end
261 end 296 end
262 return self 297 return self
263end 298end