rest-ftp-daemon 0.30.1 → 0.41
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 +8 -8
- data/Gemfile.lock +11 -4
- data/README.md +30 -6
- data/bin/rest-ftp-daemon +16 -9
- data/config.ru +9 -1
- data/lib/rest-ftp-daemon.rb +13 -1
- data/lib/rest-ftp-daemon/api/defaults.rb +58 -0
- data/lib/rest-ftp-daemon/api/jobs.rb +151 -0
- data/lib/rest-ftp-daemon/api/root.rb +79 -0
- data/lib/rest-ftp-daemon/common.rb +49 -0
- data/lib/rest-ftp-daemon/config.rb +9 -2
- data/lib/rest-ftp-daemon/exceptions.rb +6 -1
- data/lib/rest-ftp-daemon/job.rb +69 -23
- data/lib/rest-ftp-daemon/job_queue.rb +105 -0
- data/lib/rest-ftp-daemon/logger.rb +7 -0
- data/lib/rest-ftp-daemon/notification.rb +80 -0
- data/lib/rest-ftp-daemon/worker_pool.rb +63 -0
- data/lib/rest-ftp-daemon/www.rb +6 -0
- data/rest-ftp-daemon.gemspec +2 -2
- metadata +15 -7
- data/lib/rest-ftp-daemon/server.rb +0 -237
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MDQwNWFlNDE3ZGUwZGY2ZTc1N2FiMGE3OWNkODMxN2VhODdkNDNiMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MmM2YmRkMDkwMjM2OTM2YjZlOTcyNjU0NmNmODQwNmJhNjI0YzdlNQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
YzEzZjdmYzEyNWJhZTRkODc0NmY4NjRjNTMxNmZiZWFmMWIwOTg5YmM3YzE5
|
10
|
+
NjIwYTNlMTgzNzhhYTljYTUzNTk2NTk3YzJmOTgzYmNjNzAwY2NiMjFlMDYz
|
11
|
+
MjU3ZmZmNjBhYTA0ZWUzOWJkY2MyNWI1ZmYyM2M5ZDVmYWNiNTA=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTEyZmVkYjIwYzQ2NjVkZjEyNTUzOGFjY2NkNGJjMzkyZWIzZDQ4ZWJiOGNi
|
14
|
+
OGUxMGY5Nzk4YzUxZWEzMjE5Mjg3NDhkMDI1MzFjMWYyNjYxMDk2NmYzZDY2
|
15
|
+
ODQ0NzIwMWY1NGQwYWZjYTE1NjRiMjFhNGZiYzI1Y2U2YjEwMjI=
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rest-ftp-daemon (0.
|
4
|
+
rest-ftp-daemon (0.30.1)
|
5
5
|
grape
|
6
6
|
json
|
7
|
+
thin (~> 1.6)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: http://rubygems.org/
|
@@ -21,10 +22,12 @@ GEM
|
|
21
22
|
builder (3.2.2)
|
22
23
|
coercible (1.0.0)
|
23
24
|
descendants_tracker (~> 0.0.1)
|
25
|
+
daemons (1.1.9)
|
24
26
|
descendants_tracker (0.0.4)
|
25
27
|
thread_safe (~> 0.3, >= 0.3.1)
|
26
28
|
equalizer (0.0.9)
|
27
|
-
|
29
|
+
eventmachine (1.0.3)
|
30
|
+
grape (0.9.0)
|
28
31
|
activesupport
|
29
32
|
builder
|
30
33
|
hashie (>= 2.1.0)
|
@@ -34,11 +37,11 @@ GEM
|
|
34
37
|
rack-accept
|
35
38
|
rack-mount
|
36
39
|
virtus (>= 1.0.0)
|
37
|
-
hashie (3.
|
40
|
+
hashie (3.3.1)
|
38
41
|
i18n (0.6.11)
|
39
42
|
ice_nine (0.11.0)
|
40
43
|
json (1.8.1)
|
41
|
-
minitest (5.4.
|
44
|
+
minitest (5.4.1)
|
42
45
|
multi_json (1.10.1)
|
43
46
|
multi_xml (0.5.5)
|
44
47
|
rack (1.5.2)
|
@@ -47,6 +50,10 @@ GEM
|
|
47
50
|
rack-mount (0.8.3)
|
48
51
|
rack (>= 1.0.0)
|
49
52
|
rake (10.3.2)
|
53
|
+
thin (1.6.2)
|
54
|
+
daemons (>= 1.0.9)
|
55
|
+
eventmachine (>= 1.0.0)
|
56
|
+
rack (>= 1.0.0)
|
50
57
|
thread_safe (0.3.4)
|
51
58
|
tzinfo (1.2.2)
|
52
59
|
thread_safe (~> 0.1)
|
data/README.md
CHANGED
@@ -5,10 +5,32 @@ This is a pretty simple FTP client daemon, controlled through a RESTfull API.
|
|
5
5
|
|
6
6
|
As of today, its main features are :
|
7
7
|
|
8
|
-
* Delegate a transfer job
|
8
|
+
* Delegate a transfer job by ``POST```'ing a simple JSON structure
|
9
9
|
* Spawn a dedicated thread to handle this job in its own context
|
10
|
-
* Report transfer status, progress and errors for each
|
10
|
+
* Report transfer status, progress and errors for each job in realtime
|
11
11
|
* Expose JSON status of workers on ```GET /jobs/``` for automated monitoring
|
12
|
+
* Parralelize jobs as soon as they arrive
|
13
|
+
|
14
|
+
Expected features in a short-time range :
|
15
|
+
|
16
|
+
* Handle job queues
|
17
|
+
* Handle job priorities
|
18
|
+
* Allow change of priorities or other attributes after a job has been started
|
19
|
+
* Provide RESTful notifications to the requesting client
|
20
|
+
* Offer a basic dashboard directly within the daemon HTTP interface
|
21
|
+
* Periodically send an update-notification with transfer status and progress
|
22
|
+
* Allow fallback file source when first file path is unavailable (failover)
|
23
|
+
* Some refactoring may be needed after thos steps
|
24
|
+
* Provide swagger-style API documentation
|
25
|
+
* Authenticate API clients
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
Documentation TODO
|
30
|
+
------------------------------------------------------------------------------------
|
31
|
+
overwrite: any non empty value allows overwriting
|
32
|
+
todo: queues
|
33
|
+
|
12
34
|
|
13
35
|
|
14
36
|
Installation
|
@@ -19,7 +41,7 @@ This project is available as a rubygem, requires on ruby >= 1.9.3 and rubygems i
|
|
19
41
|
Get and install the gem from rubygems.org:
|
20
42
|
|
21
43
|
```
|
22
|
-
# apt-get install
|
44
|
+
# apt-get install ruby1.9.3 ruby-dev rubygems gcc g++
|
23
45
|
gem install rest-ftp-daemon --no-ri --no-rdoc
|
24
46
|
```
|
25
47
|
|
@@ -41,6 +63,8 @@ For now, daemon logs to ```APP_LOGTO``` defined in ```lib/config.rb```
|
|
41
63
|
Usage examples
|
42
64
|
------------------------------------------------------------------------------------
|
43
65
|
|
66
|
+
Requesting notifications is achieved by passing a "notify" key in the request, with a callback URL. This URL will be called at some points, ``POST```'ing a generic JSON structure with progress information.
|
67
|
+
|
44
68
|
Start a job to transfer a file named "file.iso" to a local FTP server
|
45
69
|
|
46
70
|
```
|
@@ -48,17 +72,17 @@ curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
|
48
72
|
'{"source":"~/file.iso","target":"ftp://anonymous@localhost/incoming/dest2.iso"}' "http://localhost:3000/jobs"
|
49
73
|
```
|
50
74
|
|
51
|
-
Start a job to transfer a file
|
75
|
+
Start a job to transfer a file, and request notifications ``POST```'ed on "http://requestb.in/1321axg1"
|
52
76
|
|
53
77
|
```
|
54
78
|
curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
55
|
-
'{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg"}' "http://localhost:3000/jobs"
|
79
|
+
'{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
|
56
80
|
```
|
57
81
|
|
58
82
|
Get status of a specific job based on its name
|
59
83
|
|
60
84
|
```
|
61
|
-
curl -H "Content-Type: application/json" -X
|
85
|
+
curl -H "Content-Type: application/json" -X GET -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
|
62
86
|
```
|
63
87
|
|
64
88
|
Delete a specific job based on its name
|
data/bin/rest-ftp-daemon
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
# Libs and init
|
4
|
-
require
|
4
|
+
require "thin"
|
5
5
|
|
6
6
|
# Initialize some local constants
|
7
7
|
APP_ROOT = File.dirname(__FILE__) + '/../'
|
8
|
-
|
8
|
+
rackup_file = File.expand_path "#{APP_ROOT}/config.ru"
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
APP_LOGTO = "/tmp/#{APP_NAME}.log"
|
10
|
+
# Include config file
|
11
|
+
require File.expand_path "#{APP_ROOT}/lib/rest-ftp-daemon/config.rb"
|
13
12
|
|
14
13
|
# Prepare thin
|
15
|
-
rackup_file = File.expand_path "#{APP_ROOT}/config.ru"
|
16
14
|
argv = ARGV
|
17
15
|
argv << ["-R", rackup_file] unless ARGV.include?("-R")
|
18
|
-
argv << ["-
|
16
|
+
argv << ["-l", "/tmp/thin.log"] unless ARGV.include?("-l")
|
17
|
+
argv << ["--daemonize"] unless ARGV.include?("--daemonize")
|
19
18
|
argv << ["-e", "production"] unless ARGV.include?("-e")
|
20
|
-
argv << ["
|
19
|
+
argv << ["-p", (RestFtpDaemon::PORT).to_s] unless ARGV.include?("-p")
|
21
20
|
#argv << ["--stats", "/stats"] unless ARGV.include?("--stats")
|
22
21
|
|
23
|
-
Thin
|
22
|
+
# Start Thin with this rackup configuration
|
23
|
+
puts "#{RestFtpDaemon::NAME}: using [#{argv.join (' ')}]"
|
24
|
+
begin
|
25
|
+
Thin::Runner.new(argv.flatten).run!
|
26
|
+
rescue Thin::PidFileNotFound
|
27
|
+
puts "#{RestFtpDaemon::NAME}: not running (Thin::PidFileNotFound)"
|
28
|
+
end
|
29
|
+
|
30
|
+
|
data/config.ru
CHANGED
@@ -2,5 +2,13 @@
|
|
2
2
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
|
3
3
|
require 'rest-ftp-daemon'
|
4
4
|
|
5
|
+
# Some extra constants
|
6
|
+
APP_STARTED = Time.now
|
7
|
+
|
8
|
+
# Create worker pool
|
9
|
+
$queue = RestFtpDaemon::JobQueue.new
|
10
|
+
$pool = RestFtpDaemon::WorkerPool.new(1)
|
11
|
+
|
5
12
|
# Start REST FTP Daemon
|
6
|
-
run RestFtpDaemon::API
|
13
|
+
#run Rack::Cascade.new [Rack::File.new("/public"), RestFtpDaemon::API::Root]
|
14
|
+
run Rack::Cascade.new [RestFtpDaemon::API::Root]
|
data/lib/rest-ftp-daemon.rb
CHANGED
@@ -2,9 +2,21 @@
|
|
2
2
|
require 'json'
|
3
3
|
require 'grape'
|
4
4
|
require 'net/ftp'
|
5
|
+
require 'net/http'
|
6
|
+
require 'securerandom'
|
7
|
+
# require 'celluloid/autostart'
|
5
8
|
|
6
9
|
# My libs
|
7
10
|
require 'rest-ftp-daemon/config'
|
8
11
|
require 'rest-ftp-daemon/exceptions'
|
12
|
+
require 'rest-ftp-daemon/common'
|
13
|
+
require 'rest-ftp-daemon/job_queue'
|
14
|
+
require 'rest-ftp-daemon/worker_pool'
|
15
|
+
require 'rest-ftp-daemon/logger'
|
9
16
|
require 'rest-ftp-daemon/job'
|
10
|
-
require 'rest-ftp-daemon/
|
17
|
+
require 'rest-ftp-daemon/notification'
|
18
|
+
require 'rest-ftp-daemon/api/defaults'
|
19
|
+
require 'rest-ftp-daemon/api/jobs'
|
20
|
+
require 'rest-ftp-daemon/api/root'
|
21
|
+
require 'rest-ftp-daemon/www'
|
22
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
module Defaults
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
#version 'v1', using: :header, vendor: 'ftven'
|
8
|
+
format :json
|
9
|
+
do_not_route_head!
|
10
|
+
do_not_route_options!
|
11
|
+
|
12
|
+
# before do
|
13
|
+
# header['Access-Control-Allow-Origin'] = '*'
|
14
|
+
# header['Access-Control-Request-Method'] = '*'
|
15
|
+
# end
|
16
|
+
|
17
|
+
# Handle authentication
|
18
|
+
# http_basic do |username, password|
|
19
|
+
# User.authenticate!(username, password)
|
20
|
+
# end
|
21
|
+
|
22
|
+
# # global handler for simple not found case
|
23
|
+
# rescue_from ActiveRecord::RecordNotFound do |e|
|
24
|
+
# error_response(message: e.message, status: 404)
|
25
|
+
# end
|
26
|
+
|
27
|
+
# # global exception handler, used for error notifications
|
28
|
+
# rescue_from :all do |e|
|
29
|
+
# if Rails.env.development?
|
30
|
+
# raise e
|
31
|
+
# else
|
32
|
+
# Raven.capture_exception(e)
|
33
|
+
# error_response(message: "Internal server error", status: 500)
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
|
37
|
+
# # HTTP header based authentication
|
38
|
+
# before do
|
39
|
+
# error!('Unauthorized', 401) unless headers['Authorization'] == "some token"
|
40
|
+
# end
|
41
|
+
|
42
|
+
helpers do
|
43
|
+
def api_error exception
|
44
|
+
{
|
45
|
+
:error => exception.class,
|
46
|
+
:errmsg => exception.message,
|
47
|
+
:backtrace => exception.backtrace.first,
|
48
|
+
#:backtrace => exception.backtrace,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
|
4
|
+
class Jobs < Grape::API
|
5
|
+
include RestFtpDaemon::API::Defaults
|
6
|
+
logger ActiveSupport::Logger.new APP_LOGTO, 'daily'
|
7
|
+
|
8
|
+
params do
|
9
|
+
optional :overwrite, type: Integer, default: false
|
10
|
+
end
|
11
|
+
|
12
|
+
helpers do
|
13
|
+
|
14
|
+
def threads_with_id job_id
|
15
|
+
$threads.list.select do |thread|
|
16
|
+
next unless thread[:job].is_a? Job
|
17
|
+
thread[:job].id == job_id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def job_describe job_id
|
22
|
+
raise RestFtpDaemon::JobNotFound if ($queue.queued_size==0 && $queue.popped_size==0)
|
23
|
+
|
24
|
+
# Find job with this id
|
25
|
+
found = $queue.all.select { |job| job.id == job_id }.first
|
26
|
+
raise RestFtpDaemon::JobNotFound if found.nil?
|
27
|
+
raise RestFtpDaemon::JobNotFound unless found.is_a? Job
|
28
|
+
|
29
|
+
# Return job description
|
30
|
+
found.describe
|
31
|
+
end
|
32
|
+
|
33
|
+
# def job_delete job_id
|
34
|
+
# end
|
35
|
+
|
36
|
+
def job_list
|
37
|
+
$queue.all.map do |item|
|
38
|
+
next unless item.is_a? Job
|
39
|
+
item.describe
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Get information about a specific job"
|
46
|
+
params do
|
47
|
+
requires :id, type: Integer, desc: "job id"
|
48
|
+
end
|
49
|
+
get ':id' do
|
50
|
+
info "GET /jobs/#{params[:id]}"
|
51
|
+
begin
|
52
|
+
response = job_describe params[:id].to_i
|
53
|
+
rescue RestFtpDaemon::JobNotFound => exception
|
54
|
+
status 404
|
55
|
+
api_error exception
|
56
|
+
rescue RestFtpDaemonException => exception
|
57
|
+
status 500
|
58
|
+
api_error exception
|
59
|
+
rescue Exception => exception
|
60
|
+
status 501
|
61
|
+
api_error exception
|
62
|
+
else
|
63
|
+
status 200
|
64
|
+
response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Delete jobs
|
69
|
+
desc "Kill and remove a specific job"
|
70
|
+
delete ':id' do
|
71
|
+
info "DELETE /jobs/#{params[:name]}"
|
72
|
+
status 501
|
73
|
+
# begin
|
74
|
+
# response = job_delete params[:id].to_i
|
75
|
+
# rescue RestFtpDaemon::JobNotFound => exception
|
76
|
+
# status 404
|
77
|
+
# api_error exception
|
78
|
+
# rescue RestFtpDaemonException => exception
|
79
|
+
# status 500
|
80
|
+
# api_error exception
|
81
|
+
# rescue Exception => exception
|
82
|
+
# status 501
|
83
|
+
# api_error exception
|
84
|
+
# else
|
85
|
+
# status 200
|
86
|
+
# response
|
87
|
+
# end
|
88
|
+
end
|
89
|
+
|
90
|
+
# List jobs
|
91
|
+
desc "Get a list of jobs"
|
92
|
+
get do
|
93
|
+
info "GET /jobs"
|
94
|
+
begin
|
95
|
+
response = job_list
|
96
|
+
rescue RestFtpDaemonException => exception
|
97
|
+
status 501
|
98
|
+
api_error exception
|
99
|
+
rescue Exception => exception
|
100
|
+
status 501
|
101
|
+
api_error exception
|
102
|
+
else
|
103
|
+
status 200
|
104
|
+
response
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
# Spawn a new thread for this new job
|
110
|
+
desc "Create a new job"
|
111
|
+
post do
|
112
|
+
info "POST /jobs: #{request.body.read}"
|
113
|
+
begin
|
114
|
+
# Extract params
|
115
|
+
request.body.rewind
|
116
|
+
params = JSON.parse request.body.read
|
117
|
+
|
118
|
+
# Create a new job
|
119
|
+
job_id = $last_worker_id += 1
|
120
|
+
job = Job.new(job_id, params)
|
121
|
+
|
122
|
+
# And psuh it to the queue
|
123
|
+
$queue.push job
|
124
|
+
|
125
|
+
# Later: start it asynchronously
|
126
|
+
#job.future.process
|
127
|
+
|
128
|
+
rescue JSON::ParserError => exception
|
129
|
+
status 406
|
130
|
+
api_error exception
|
131
|
+
rescue RestFtpDaemonException => exception
|
132
|
+
status 412
|
133
|
+
api_error exception
|
134
|
+
rescue Exception => exception
|
135
|
+
status 501
|
136
|
+
api_error exception
|
137
|
+
else
|
138
|
+
status 201
|
139
|
+
job.describe
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
protected
|
144
|
+
|
145
|
+
def progname
|
146
|
+
"API::Jobs"
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RestFtpDaemon
|
2
|
+
module API
|
3
|
+
|
4
|
+
class Root < Grape::API
|
5
|
+
include RestFtpDaemon::API::Defaults
|
6
|
+
logger ActiveSupport::Logger.new APP_LOGTO, 'daily'
|
7
|
+
#add_swagger_documentation
|
8
|
+
|
9
|
+
mount RestFtpDaemon::API::Jobs => '/jobs'
|
10
|
+
# mount RestFtpDaemon::API::Workers => '/workers'
|
11
|
+
|
12
|
+
helpers do
|
13
|
+
def info message, level = 0
|
14
|
+
Root.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Root")
|
15
|
+
end
|
16
|
+
|
17
|
+
def job_list_by_status
|
18
|
+
statuses = {}
|
19
|
+
alljobs = $queue.all.map do |item|
|
20
|
+
next unless item.is_a? Job
|
21
|
+
statuses[item.get_status] ||= 0
|
22
|
+
statuses[item.get_status] +=1
|
23
|
+
end
|
24
|
+
|
25
|
+
statuses
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
######################################################################
|
30
|
+
####### INIT
|
31
|
+
######################################################################
|
32
|
+
def initialize
|
33
|
+
$last_worker_id = 0
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
######################################################################
|
39
|
+
####### API DEFINITION
|
40
|
+
######################################################################
|
41
|
+
|
42
|
+
# Server global status
|
43
|
+
get '/' do
|
44
|
+
info "GET /"
|
45
|
+
status 200
|
46
|
+
return {
|
47
|
+
name: RestFtpDaemon::NAME,
|
48
|
+
hostname: `hostname`.chomp,
|
49
|
+
version: RestFtpDaemon::VERSION,
|
50
|
+
started: APP_STARTED,
|
51
|
+
uptime: (Time.now - APP_STARTED).round(1),
|
52
|
+
status: job_list_by_status,
|
53
|
+
queue_size: $queue.all_size,
|
54
|
+
jobs_queued: $queue.queued.collect(&:id),
|
55
|
+
jobs_popped: $queue.popped.collect(&:id),
|
56
|
+
routes: RestFtpDaemon::API::Root::routes,
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Server test
|
61
|
+
get '/debug' do
|
62
|
+
info "GET /debug/"
|
63
|
+
begin
|
64
|
+
raise RestFtpDaemon::DummyException
|
65
|
+
rescue RestFtpDaemon::RestFtpDaemonException => exception
|
66
|
+
status 501
|
67
|
+
api_error exception
|
68
|
+
rescue Exception => exception
|
69
|
+
status 501
|
70
|
+
api_error exception
|
71
|
+
else
|
72
|
+
status 200
|
73
|
+
{}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|