s3arch 0.0.4 → 0.0.6.alpha
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.
Potentially problematic release.
This version of s3arch might be problematic. Click here for more details.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/infrastructure/routes.tf.rb +8 -0
- data/infrastructure/schema.tf.rb +6 -0
- data/lambda/controllers/s3arch_controller.rb +54 -0
- data/lib/s3arch/configuration.rb +5 -0
- data/lib/s3arch/dashboard/application.rb +86 -0
- data/lib/s3arch/dashboard/views/index.html.erb +61 -0
- data/lib/s3arch/dashboard.rb +13 -0
- data/lib/s3arch/holster.rb +7 -0
- data/lib/s3arch/routes.rb +14 -0
- data/lib/s3arch/version.rb +1 -1
- data/lib/s3arch/web.rb +26 -0
- data/lib/s3arch.rb +5 -0
- data.tar.gz.sig +0 -0
- metadata +26 -3
- metadata.gz.sig +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 76cbfbd7db2c11d924729920685cfbd50525fa31e24dda087ab8a2ce4e9c03a4
|
|
4
|
+
data.tar.gz: 6256c7ff9391e7b57d40a9ce7f9751a1960142229480fddcf75fd9bd067bd352
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8cc3ec77fbc57dba507db244b0b67d9810506ae113667ded39e588f8b713440f2ec1e20616592901698bec3d774f38d08a47720d8b1af6b30e6439578dcf17cf
|
|
7
|
+
data.tar.gz: b3f46f36ab31bc07cca101bad5ee10e1d978b4d090ea6911375dfdc9e3a66b2c3984d180133bffd203f2ac2f571b81bd051da056d287d844efdb769fe46492d0
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'aws-sdk-dynamodb'
|
|
4
|
+
|
|
5
|
+
class S3archController < BeltController::Base
|
|
6
|
+
def index
|
|
7
|
+
owners = fetch_owners
|
|
8
|
+
success_response(owners: owners)
|
|
9
|
+
rescue StandardError => e
|
|
10
|
+
error_response("Failed to load s3arch dashboard: #{e.message}", 500)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def rebuild
|
|
14
|
+
owner_id = params['owner_id']&.strip
|
|
15
|
+
return error_response('owner_id is required', 400) if owner_id.nil? || owner_id.empty?
|
|
16
|
+
|
|
17
|
+
handler = S3arch.configuration.rebuild_handler
|
|
18
|
+
if handler
|
|
19
|
+
handler.call(owner_id)
|
|
20
|
+
else
|
|
21
|
+
S3arch::Indexer.new.rebuild(owner_id)
|
|
22
|
+
end
|
|
23
|
+
success_response(status: 'ok', owner_id: owner_id)
|
|
24
|
+
rescue StandardError => e
|
|
25
|
+
error_response("Failed to rebuild index: #{e.message}", 500)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def fetch_owners
|
|
31
|
+
dynamodb = Aws::DynamoDB::Client.new
|
|
32
|
+
config = S3arch.configuration
|
|
33
|
+
items = []
|
|
34
|
+
scan_params = { table_name: config.version_table }
|
|
35
|
+
|
|
36
|
+
loop do
|
|
37
|
+
result = dynamodb.scan(scan_params)
|
|
38
|
+
items.concat(result.items)
|
|
39
|
+
break unless result.last_evaluated_key
|
|
40
|
+
|
|
41
|
+
scan_params[:exclusive_start_key] = result.last_evaluated_key
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
owners = items.map do |item|
|
|
45
|
+
{
|
|
46
|
+
owner_id: item[config.owner_key] || item.values.first,
|
|
47
|
+
version: item['version'],
|
|
48
|
+
record_count: item['record_count'],
|
|
49
|
+
updated_at: item['updated_at']
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
owners.sort_by { |o| o[:updated_at].to_s }.reverse
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/s3arch/configuration.rb
CHANGED
|
@@ -42,6 +42,11 @@ module S3arch
|
|
|
42
42
|
# Searcher settings
|
|
43
43
|
attr_accessor :version_ttl, :max_results, :max_cached_dbs, :ephemeral_storage_mb
|
|
44
44
|
|
|
45
|
+
# Custom rebuild handler — proc/lambda that receives owner_id.
|
|
46
|
+
# When set, the controller uses this instead of calling Indexer.new.rebuild directly.
|
|
47
|
+
# Useful for triggering rebuilds via Lambda invocation instead of in-process.
|
|
48
|
+
attr_accessor :rebuild_handler
|
|
49
|
+
|
|
45
50
|
def initialize
|
|
46
51
|
@owner_key = 'user_id'
|
|
47
52
|
@searchable_fields = %w[name description]
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'erb'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'aws-sdk-dynamodb'
|
|
6
|
+
|
|
7
|
+
module S3arch
|
|
8
|
+
module Dashboard
|
|
9
|
+
class Application
|
|
10
|
+
VIEWS_PATH = File.expand_path('views', __dir__)
|
|
11
|
+
|
|
12
|
+
# Dispatcher mount DSL contract — keep in sync with #call dispatch below.
|
|
13
|
+
def routes
|
|
14
|
+
[
|
|
15
|
+
{ method: :get, path: '/' },
|
|
16
|
+
{ method: :post, path: '/rebuild' }
|
|
17
|
+
]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call(env)
|
|
21
|
+
req = Rack::Request.new(env)
|
|
22
|
+
path = req.path_info.sub(%r{^/}, '')
|
|
23
|
+
|
|
24
|
+
case [req.request_method, path]
|
|
25
|
+
when ['GET', ''], %w[GET index]
|
|
26
|
+
index(req)
|
|
27
|
+
when %w[POST rebuild]
|
|
28
|
+
rebuild(req)
|
|
29
|
+
else
|
|
30
|
+
[404, { 'content-type' => 'text/plain' }, ['Not Found']]
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def index(req)
|
|
37
|
+
@env = req.env
|
|
38
|
+
@owners = fetch_owners
|
|
39
|
+
html = render('index')
|
|
40
|
+
[200, { 'content-type' => 'text/html' }, [html]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def rebuild(req)
|
|
44
|
+
owner_id = req.params['owner_id']&.strip
|
|
45
|
+
if owner_id && !owner_id.empty?
|
|
46
|
+
indexer = S3arch::Indexer.new
|
|
47
|
+
indexer.rebuild(owner_id)
|
|
48
|
+
end
|
|
49
|
+
[303, { 'location' => "#{req.script_name}/" }, []]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def fetch_owners
|
|
53
|
+
config = S3arch.configuration
|
|
54
|
+
dynamodb = Aws::DynamoDB::Client.new
|
|
55
|
+
items = []
|
|
56
|
+
params = { table_name: config.version_table }
|
|
57
|
+
|
|
58
|
+
loop do
|
|
59
|
+
result = dynamodb.scan(params)
|
|
60
|
+
items.concat(result.items)
|
|
61
|
+
break unless result.last_evaluated_key
|
|
62
|
+
|
|
63
|
+
params[:exclusive_start_key] = result.last_evaluated_key
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
owners = items.map do |item|
|
|
67
|
+
{
|
|
68
|
+
owner_id: item[config.owner_key] || item.values.first,
|
|
69
|
+
version: item['version'],
|
|
70
|
+
record_count: item['record_count'],
|
|
71
|
+
updated_at: item['updated_at']
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
owners.sort_by { |o| o[:updated_at].to_s }.reverse
|
|
75
|
+
rescue StandardError => e
|
|
76
|
+
@error = e.message
|
|
77
|
+
[]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def render(template)
|
|
81
|
+
path = File.join(VIEWS_PATH, "#{template}.html.erb")
|
|
82
|
+
ERB.new(File.read(path), trim_mode: '-').result(binding)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>S3arch Dashboard</title>
|
|
5
|
+
<style>
|
|
6
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
7
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f5f5f5; color: #333; padding: 2rem; }
|
|
8
|
+
h1 { margin-bottom: 1.5rem; }
|
|
9
|
+
.error { background: #fee; border: 1px solid #fcc; padding: 0.75rem; border-radius: 4px; margin-bottom: 1rem; color: #c00; }
|
|
10
|
+
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 6px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
|
11
|
+
th, td { padding: 0.75rem 1rem; text-align: left; border-bottom: 1px solid #eee; }
|
|
12
|
+
th { background: #fafafa; font-weight: 600; }
|
|
13
|
+
form.inline { display: inline; }
|
|
14
|
+
button { background: #2563eb; color: #fff; border: none; padding: 0.4rem 0.8rem; border-radius: 4px; cursor: pointer; font-size: 0.85rem; }
|
|
15
|
+
button:hover { background: #1d4ed8; }
|
|
16
|
+
.rebuild-all { margin-bottom: 1rem; }
|
|
17
|
+
.rebuild-all button { background: #dc2626; }
|
|
18
|
+
.rebuild-all button:hover { background: #b91c1c; }
|
|
19
|
+
.empty { padding: 2rem; text-align: center; color: #666; }
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<h1>S3arch Dashboard</h1>
|
|
24
|
+
|
|
25
|
+
<%- if @error -%>
|
|
26
|
+
<div class="error"><strong>Error:</strong> <%= @error %></div>
|
|
27
|
+
<%- end -%>
|
|
28
|
+
|
|
29
|
+
<%- if @owners.any? -%>
|
|
30
|
+
<table>
|
|
31
|
+
<thead>
|
|
32
|
+
<tr>
|
|
33
|
+
<th>Owner</th>
|
|
34
|
+
<th>Version</th>
|
|
35
|
+
<th>Records</th>
|
|
36
|
+
<th>Last Updated</th>
|
|
37
|
+
<th>Actions</th>
|
|
38
|
+
</tr>
|
|
39
|
+
</thead>
|
|
40
|
+
<tbody>
|
|
41
|
+
<%- @owners.each do |owner| -%>
|
|
42
|
+
<tr>
|
|
43
|
+
<td><%= owner[:owner_id] %></td>
|
|
44
|
+
<td><%= owner[:version] %></td>
|
|
45
|
+
<td><%= owner[:record_count] || '—' %></td>
|
|
46
|
+
<td><%= owner[:updated_at] || '—' %></td>
|
|
47
|
+
<td>
|
|
48
|
+
<form class="inline" method="post" action="<%= @env['SCRIPT_NAME'] %>/rebuild">
|
|
49
|
+
<input type="hidden" name="owner_id" value="<%= owner[:owner_id] %>">
|
|
50
|
+
<button type="submit">Rebuild</button>
|
|
51
|
+
</form>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
<%- end -%>
|
|
55
|
+
</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
<%- else -%>
|
|
58
|
+
<div class="empty">No indexed owners found.</div>
|
|
59
|
+
<%- end -%>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module S3arch
|
|
4
|
+
# Lightweight route manifest for Dispatcher mount DSL.
|
|
5
|
+
# No heavy dependencies — safe to load in parse-only contexts.
|
|
6
|
+
module Routes
|
|
7
|
+
def self.routes
|
|
8
|
+
[
|
|
9
|
+
{ method: :get, path: '/' },
|
|
10
|
+
{ method: :post, path: '/rebuild' }
|
|
11
|
+
]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/s3arch/version.rb
CHANGED
data/lib/s3arch/web.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Lightweight S3arch load for web/API contexts (no SQLite dependency).
|
|
4
|
+
# Provides configuration, holster registration, and routes only.
|
|
5
|
+
require_relative 'version'
|
|
6
|
+
require_relative 'configuration'
|
|
7
|
+
require_relative 'routes'
|
|
8
|
+
require_relative 'holster'
|
|
9
|
+
|
|
10
|
+
module S3arch
|
|
11
|
+
class Error < StandardError; end
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
def configuration
|
|
15
|
+
@configuration ||= Configuration.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def configure
|
|
19
|
+
yield(configuration)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def reset_configuration!
|
|
23
|
+
@configuration = Configuration.new
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/s3arch.rb
CHANGED
|
@@ -6,6 +6,11 @@ require_relative 's3arch/tokenizer'
|
|
|
6
6
|
require_relative 's3arch/indexer'
|
|
7
7
|
require_relative 's3arch/searcher'
|
|
8
8
|
require_relative 's3arch/handler'
|
|
9
|
+
require_relative 's3arch/routes'
|
|
10
|
+
require_relative 's3arch/dashboard'
|
|
11
|
+
|
|
12
|
+
# Register as a Belt holster when Belt is loaded
|
|
13
|
+
require_relative 's3arch/holster' if defined?(Belt::Holster)
|
|
9
14
|
|
|
10
15
|
module S3arch
|
|
11
16
|
class Error < StandardError; end
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: s3arch
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.6.alpha
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adam Dalton
|
|
@@ -64,6 +64,20 @@ dependencies:
|
|
|
64
64
|
- - "~>"
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
66
|
version: '1.0'
|
|
67
|
+
- !ruby/object:Gem::Dependency
|
|
68
|
+
name: rack
|
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.0'
|
|
74
|
+
type: :runtime
|
|
75
|
+
prerelease: false
|
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - ">="
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '2.0'
|
|
67
81
|
- !ruby/object:Gem::Dependency
|
|
68
82
|
name: sqlite3
|
|
69
83
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -78,8 +92,8 @@ dependencies:
|
|
|
78
92
|
- - "~>"
|
|
79
93
|
- !ruby/object:Gem::Version
|
|
80
94
|
version: '2.0'
|
|
81
|
-
description: Per-owner SQLite FTS5 indexes stored on S3, queried from Lambda /tmp
|
|
82
|
-
|
|
95
|
+
description: Per-owner SQLite FTS5 indexes stored on S3, queried from Lambda /tmp
|
|
96
|
+
with version tracking.
|
|
83
97
|
email:
|
|
84
98
|
- adam@stowzilla.com
|
|
85
99
|
executables: []
|
|
@@ -90,13 +104,22 @@ files:
|
|
|
90
104
|
- LICENSE.txt
|
|
91
105
|
- README.md
|
|
92
106
|
- certs/stowzilla.pem
|
|
107
|
+
- infrastructure/routes.tf.rb
|
|
108
|
+
- infrastructure/schema.tf.rb
|
|
109
|
+
- lambda/controllers/s3arch_controller.rb
|
|
93
110
|
- lib/s3arch.rb
|
|
94
111
|
- lib/s3arch/configuration.rb
|
|
112
|
+
- lib/s3arch/dashboard.rb
|
|
113
|
+
- lib/s3arch/dashboard/application.rb
|
|
114
|
+
- lib/s3arch/dashboard/views/index.html.erb
|
|
95
115
|
- lib/s3arch/handler.rb
|
|
116
|
+
- lib/s3arch/holster.rb
|
|
96
117
|
- lib/s3arch/indexer.rb
|
|
118
|
+
- lib/s3arch/routes.rb
|
|
97
119
|
- lib/s3arch/searcher.rb
|
|
98
120
|
- lib/s3arch/tokenizer.rb
|
|
99
121
|
- lib/s3arch/version.rb
|
|
122
|
+
- lib/s3arch/web.rb
|
|
100
123
|
homepage: https://github.com/stowzilla/s3arch
|
|
101
124
|
licenses:
|
|
102
125
|
- MIT
|
metadata.gz.sig
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
��
|
|
1
|
+
���ˁ�y����.�T���C�˧������!�(��Dz9alafg�c����
|
|
2
|
+
��8s(�.���0��S�q�EV2/)�! 9�힛�e��v��@��8���.���������]x"��w��F�����]%B����Ơ(\�FIa<����|��N'��ܯ��*V
|