bard-api 0.2.0 → 0.3.1
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 +4 -4
- data/README.md +9 -2
- data/lib/bard/api/app.rb +12 -28
- data/lib/bard/api/version.rb +1 -1
- data/lib/bard/api.rb +0 -2
- metadata +34 -4
- data/lib/bard/api/backup.rb +0 -104
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70f38ef8f674aefa2cdcfba3586e87d644c10026943625bd82cb2be180f0035b
|
|
4
|
+
data.tar.gz: 1fd66c7e938efe753797d99e594c03f802948c004adafe0991b2b5577a00fb73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ce89073234248cf2a6ec1cc628bb97fcbf8f8d96e8be9d46dd117f54b1f18277e4c81cd9ec3ba2fb7fbd6fc1ef53bed84532f603db84ffda376cbd87b08f28e
|
|
7
|
+
data.tar.gz: 1119c0bc33153aeb74d53ec585c59b7bade8c410eda343962e9c2d5cf04bb017e8d2cb7e441c31c89c1dd4b687dcccc0ed1121e5fc7de2d8fbe158ce7ac34e8e
|
data/README.md
CHANGED
|
@@ -80,17 +80,24 @@ Authorization: Bearer <jwt-token>
|
|
|
80
80
|
```json
|
|
81
81
|
{
|
|
82
82
|
"timestamp": "2025-12-06T10:30:00Z",
|
|
83
|
-
"size": 123456789,
|
|
84
83
|
"destinations": [
|
|
85
84
|
{
|
|
86
85
|
"name": "primary",
|
|
87
86
|
"type": "s3",
|
|
88
|
-
"
|
|
87
|
+
"path": "bard-backup/my-app",
|
|
88
|
+
"region": "us-west-2"
|
|
89
89
|
}
|
|
90
90
|
]
|
|
91
91
|
}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
**Response (404 Not Found):**
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"error": "No backups found"
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
94
101
|
### Authentication
|
|
95
102
|
|
|
96
103
|
The API uses JWT with asymmetric RSA keys for authentication. The public key is embedded in the gem, and only BARD Tracker with the private key can create valid tokens.
|
data/lib/bard/api/app.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "rack"
|
|
4
4
|
require "json"
|
|
5
5
|
require_relative "auth"
|
|
6
|
-
|
|
6
|
+
require "bard/backup"
|
|
7
7
|
|
|
8
8
|
module Bard
|
|
9
9
|
module Api
|
|
@@ -24,7 +24,7 @@ module Bard
|
|
|
24
24
|
not_found
|
|
25
25
|
end
|
|
26
26
|
rescue => e
|
|
27
|
-
|
|
27
|
+
json_response(500, { error: e.message })
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
private
|
|
@@ -35,34 +35,22 @@ module Bard
|
|
|
35
35
|
|
|
36
36
|
def create_backup(request)
|
|
37
37
|
with_auth(request) do |payload|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
backup
|
|
44
|
-
result = backup.perform(urls)
|
|
45
|
-
|
|
46
|
-
json_response(200, result)
|
|
38
|
+
backup = Bard::Backup.create!({
|
|
39
|
+
name: "bard",
|
|
40
|
+
type: :upload,
|
|
41
|
+
urls: payload["urls"]
|
|
42
|
+
})
|
|
43
|
+
json_response(200, backup.as_json)
|
|
47
44
|
end
|
|
48
|
-
rescue => e
|
|
49
|
-
json_response(500, { error: "Backup failed: #{e.message}" })
|
|
50
45
|
end
|
|
51
46
|
|
|
52
47
|
def latest_backup(request)
|
|
53
48
|
with_auth(request) do
|
|
54
|
-
|
|
55
|
-
backup
|
|
56
|
-
result = backup.latest
|
|
57
|
-
|
|
58
|
-
if result
|
|
59
|
-
json_response(200, result)
|
|
60
|
-
else
|
|
61
|
-
json_response(404, { error: "No backups found" })
|
|
62
|
-
end
|
|
49
|
+
backup = Bard::Backup.latest
|
|
50
|
+
json_response(200, backup.as_json)
|
|
63
51
|
end
|
|
64
|
-
rescue => e
|
|
65
|
-
json_response(
|
|
52
|
+
rescue Bard::Backup::NotFound => e
|
|
53
|
+
json_response(404, { error: e.message })
|
|
66
54
|
end
|
|
67
55
|
|
|
68
56
|
def with_auth(request)
|
|
@@ -79,10 +67,6 @@ module Bard
|
|
|
79
67
|
def not_found
|
|
80
68
|
json_response(404, { error: "Not found" })
|
|
81
69
|
end
|
|
82
|
-
|
|
83
|
-
def internal_error(e)
|
|
84
|
-
json_response(500, { error: "Internal server error: #{e.message}" })
|
|
85
|
-
end
|
|
86
70
|
end
|
|
87
71
|
end
|
|
88
72
|
end
|
data/lib/bard/api/version.rb
CHANGED
data/lib/bard/api.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bard-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: exe
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date: 2025-12-
|
|
11
|
+
date: 2025-12-11 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: jwt
|
|
@@ -51,6 +52,34 @@ dependencies:
|
|
|
51
52
|
- - ">="
|
|
52
53
|
- !ruby/object:Gem::Version
|
|
53
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: bard
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: bard-backup
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
54
83
|
- !ruby/object:Gem::Dependency
|
|
55
84
|
name: rack-test
|
|
56
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -82,7 +111,6 @@ files:
|
|
|
82
111
|
- lib/bard/api.rb
|
|
83
112
|
- lib/bard/api/app.rb
|
|
84
113
|
- lib/bard/api/auth.rb
|
|
85
|
-
- lib/bard/api/backup.rb
|
|
86
114
|
- lib/bard/api/version.rb
|
|
87
115
|
homepage: https://github.com/botandrose/bard-api
|
|
88
116
|
licenses:
|
|
@@ -90,6 +118,7 @@ licenses:
|
|
|
90
118
|
metadata:
|
|
91
119
|
homepage_uri: https://github.com/botandrose/bard-api
|
|
92
120
|
source_code_uri: https://github.com/botandrose/bard-api
|
|
121
|
+
post_install_message:
|
|
93
122
|
rdoc_options: []
|
|
94
123
|
require_paths:
|
|
95
124
|
- lib
|
|
@@ -104,7 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
104
133
|
- !ruby/object:Gem::Version
|
|
105
134
|
version: '0'
|
|
106
135
|
requirements: []
|
|
107
|
-
rubygems_version: 3.
|
|
136
|
+
rubygems_version: 3.5.11
|
|
137
|
+
signing_key:
|
|
108
138
|
specification_version: 4
|
|
109
139
|
summary: REST API for BARD-managed Rails projects
|
|
110
140
|
test_files: []
|
data/lib/bard/api/backup.rb
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "backhoe"
|
|
4
|
-
require "net/http"
|
|
5
|
-
require "uri"
|
|
6
|
-
require "time"
|
|
7
|
-
require "fileutils"
|
|
8
|
-
|
|
9
|
-
module Bard
|
|
10
|
-
module Api
|
|
11
|
-
class Backup
|
|
12
|
-
class BackupError < StandardError; end
|
|
13
|
-
|
|
14
|
-
# Perform a backup to the specified URLs
|
|
15
|
-
def perform(urls)
|
|
16
|
-
raise BackupError, "No URLs provided" if urls.nil? || urls.empty?
|
|
17
|
-
|
|
18
|
-
timestamp = Time.now.utc
|
|
19
|
-
destinations = []
|
|
20
|
-
errors = []
|
|
21
|
-
|
|
22
|
-
# Create temp file with timestamp
|
|
23
|
-
filename = "#{timestamp.iso8601}.sql.gz"
|
|
24
|
-
temp_path = "/tmp/#{filename}"
|
|
25
|
-
|
|
26
|
-
begin
|
|
27
|
-
# Dump database to temp file using Backhoe
|
|
28
|
-
Backhoe.dump(temp_path)
|
|
29
|
-
|
|
30
|
-
# Get file size
|
|
31
|
-
file_size = File.size(temp_path)
|
|
32
|
-
|
|
33
|
-
# Upload to all URLs in parallel
|
|
34
|
-
threads = urls.map do |url|
|
|
35
|
-
Thread.new do
|
|
36
|
-
begin
|
|
37
|
-
upload_to_url(url, temp_path)
|
|
38
|
-
{
|
|
39
|
-
name: "bard",
|
|
40
|
-
type: "bard",
|
|
41
|
-
status: "success"
|
|
42
|
-
}
|
|
43
|
-
rescue => e
|
|
44
|
-
errors << e
|
|
45
|
-
{
|
|
46
|
-
name: "bard",
|
|
47
|
-
type: "bard",
|
|
48
|
-
status: "failed",
|
|
49
|
-
error: e.message
|
|
50
|
-
}
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Wait for all uploads to complete
|
|
56
|
-
destinations = threads.map(&:value)
|
|
57
|
-
ensure
|
|
58
|
-
# Clean up temp file
|
|
59
|
-
FileUtils.rm_f(temp_path)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Store backup metadata
|
|
63
|
-
@last_backup = {
|
|
64
|
-
timestamp: timestamp.iso8601,
|
|
65
|
-
size: file_size,
|
|
66
|
-
destinations: destinations
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
# Raise error if any destination failed
|
|
70
|
-
unless errors.empty?
|
|
71
|
-
raise BackupError, "Some destinations failed: #{errors.map(&:message).join(", ")}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
@last_backup
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Get the latest backup status
|
|
78
|
-
def latest
|
|
79
|
-
# TODO: Retrieve from database instead of instance variable
|
|
80
|
-
@last_backup
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
|
|
85
|
-
def upload_to_url(url, file_path)
|
|
86
|
-
uri = URI.parse(url)
|
|
87
|
-
|
|
88
|
-
File.open(file_path, "rb") do |file|
|
|
89
|
-
request = Net::HTTP::Put.new(uri)
|
|
90
|
-
request.body = file.read
|
|
91
|
-
request.content_type = "application/octet-stream"
|
|
92
|
-
|
|
93
|
-
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
|
|
94
|
-
http.request(request)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
unless response.is_a?(Net::HTTPSuccess)
|
|
98
|
-
raise BackupError, "Upload failed with status #{response.code}: #{response.body}"
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|