spree_dashboard 5.6.0.rc1
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 +7 -0
- data/README.md +41 -0
- data/Rakefile +15 -0
- data/app/controllers/spree/dashboard/app_controller.rb +78 -0
- data/config/routes.rb +8 -0
- data/lib/spree/dashboard/engine.rb +10 -0
- data/lib/spree/dashboard.rb +19 -0
- data/lib/spree_dashboard.rb +1 -0
- metadata +69 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 00ab70dc5556a8ef8e044c41b7b1b420b99cdfb1677e743832886c4aba4f07e7
|
|
4
|
+
data.tar.gz: f29e4be23eaa1f634cdb29cda7c971c5397aa22f13bbce6e4285a73b8f9fe60f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2853926c77a77f4a9f58efb12fc9ccf189a7dd7000596cb712ccc56c1f7471fb6e205ce35e545f53b2b23d5e77d7e8b6e81b8697f858a7c101b55f4da0af02a8
|
|
7
|
+
data.tar.gz: ce2e2d80e83f35e8f6fd9815a409549818818601b807039065e1d4ddd3798421852eeab6c8fcf944933b3dbf53c4d064fff9294a356c2419aaf4ab8da2b860b2
|
data/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# spree_dashboard
|
|
2
|
+
|
|
3
|
+
Hosts the [Spree React Dashboard](https://spreecommerce.org/docs/developer/dashboard/overview) from your Spree server — the **single-node topology**: the dashboard and the Admin API share one origin, so there are no CORS entries to maintain and auth cookies stay `SameSite=Lax`.
|
|
4
|
+
|
|
5
|
+
> **Developer Preview.** In Spree 6 this gem becomes the default admin delivery. The path is permanent: the dashboard lives at `/dashboard`, and `/admin` stays reserved for the classic `spree_admin`.
|
|
6
|
+
|
|
7
|
+
## What it does
|
|
8
|
+
|
|
9
|
+
Serves a built dashboard (`vite build` output) at **`/dashboard`** with SPA semantics: every dashboard route falls back to `index.html` (no-cache), Vite's content-hashed `assets/` get immutable caching, and paths are traversal-guarded. The bundle is public client code, so there's no authentication on the route — the SPA authenticates its API calls itself.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
# Gemfile
|
|
15
|
+
gem 'spree_dashboard'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Point it at a built dashboard, either via env (what the official Docker image and the Render Blueprint use):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
SPREE_DASHBOARD_DIST_PATH=/rails/dashboard
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
or an initializer:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
# config/initializers/spree.rb
|
|
28
|
+
Spree::Dashboard.dist_path = Rails.root.join('dashboard/dist').to_s
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Unset, `/dashboard` responds 404.
|
|
32
|
+
|
|
33
|
+
The bundle must be built for sub-path mounting with a relative API base: `VITE_BASE_PATH=/dashboard/ pnpm build` (and `VITE_SPREE_API_URL` unset) — see the [deployment docs](https://spreecommerce.org/docs/developer/dashboard/deployment) for the full picture, including baking it into a Docker image.
|
|
34
|
+
|
|
35
|
+
## Testing
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bundle install
|
|
39
|
+
bundle exec rake test_app # generates the dummy app (skip if present)
|
|
40
|
+
bundle exec rspec
|
|
41
|
+
```
|
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
require 'spree/testing_support/common_rake'
|
|
6
|
+
|
|
7
|
+
RSpec::Core::RakeTask.new
|
|
8
|
+
|
|
9
|
+
task default: :spec
|
|
10
|
+
|
|
11
|
+
desc "Generates a dummy app for testing"
|
|
12
|
+
task :test_app do
|
|
13
|
+
ENV['LIB_NAME'] = 'spree/dashboard'
|
|
14
|
+
Rake::Task['common:test_app'].invoke
|
|
15
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Dashboard
|
|
3
|
+
# Serves a built React Dashboard (`vite build` output) at /dashboard — the
|
|
4
|
+
# single-node topology: the SPA and the Admin API share one origin, so no
|
|
5
|
+
# CORS entries and Lax cookies. The dist directory comes from
|
|
6
|
+
# `Spree::Dashboard.dist_path` (the official Docker image sets it via
|
|
7
|
+
# SPREE_DASHBOARD_DIST_PATH); when unset, /dashboard 404s.
|
|
8
|
+
#
|
|
9
|
+
# SPA semantics: real files are served with long-lived caching (Vite's
|
|
10
|
+
# `assets/` are content-hashed, so they're immutable); every other path
|
|
11
|
+
# falls back to index.html with no-cache so deploys take effect on the
|
|
12
|
+
# next navigation. No authentication — the bundle is public client code;
|
|
13
|
+
# the SPA authenticates its API calls itself.
|
|
14
|
+
class AppController < ActionController::Base
|
|
15
|
+
# GET-only file serving mutates nothing, but enable forgery protection
|
|
16
|
+
# anyway so the controller stays safe if an action is ever added.
|
|
17
|
+
protect_from_forgery with: :exception
|
|
18
|
+
# `verify_same_origin_request` blocks JavaScript responses to plain GET
|
|
19
|
+
# requests — protection against JSONP-style data leaks from dynamically
|
|
20
|
+
# generated JS. The Vite bundle is static public code that the SPA's own
|
|
21
|
+
# <script> tags must load, which is exactly that request shape.
|
|
22
|
+
skip_after_action :verify_same_origin_request
|
|
23
|
+
|
|
24
|
+
def show
|
|
25
|
+
root = dist_root
|
|
26
|
+
return head :not_found unless root
|
|
27
|
+
|
|
28
|
+
if (file = resolve_file(root, params[:path].to_s))
|
|
29
|
+
response.headers['Cache-Control'] = cache_control_for(params[:path].to_s)
|
|
30
|
+
send_file file, disposition: 'inline'
|
|
31
|
+
elsif (index = root.join('index.html')).file?
|
|
32
|
+
response.headers['Cache-Control'] = 'no-cache'
|
|
33
|
+
send_file index, type: 'text/html', disposition: 'inline'
|
|
34
|
+
else
|
|
35
|
+
head :not_found
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def dist_root
|
|
42
|
+
path = Spree::Dashboard.dist_path
|
|
43
|
+
return if path.blank?
|
|
44
|
+
|
|
45
|
+
root = Pathname.new(path).expand_path
|
|
46
|
+
root if root.directory?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Resolve a request path to a real file inside the dist directory.
|
|
50
|
+
# Defense in depth against traversal: reject null bytes, absolute
|
|
51
|
+
# paths, and any `.`/`..` segment before touching the filesystem, then
|
|
52
|
+
# verify the expanded path still lives under the dist root (which also
|
|
53
|
+
# covers symlinks pointing outside it).
|
|
54
|
+
def resolve_file(root, relative_path)
|
|
55
|
+
return if relative_path.blank? || relative_path.include?("\0")
|
|
56
|
+
|
|
57
|
+
relative = Pathname.new(relative_path)
|
|
58
|
+
return if relative.absolute?
|
|
59
|
+
|
|
60
|
+
segments = relative.each_filename.to_a
|
|
61
|
+
return if segments.empty? || segments.any? { |segment| segment == '.' || segment == '..' }
|
|
62
|
+
|
|
63
|
+
candidate = root.join(*segments).expand_path
|
|
64
|
+
return unless candidate.to_s.start_with?("#{root}#{File::SEPARATOR}")
|
|
65
|
+
|
|
66
|
+
candidate if candidate.file?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def cache_control_for(relative_path)
|
|
70
|
+
if relative_path.start_with?('assets/')
|
|
71
|
+
'public, max-age=31536000, immutable'
|
|
72
|
+
else
|
|
73
|
+
'public, max-age=3600'
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Spree::Core::Engine.add_routes do
|
|
2
|
+
# Hosted React Dashboard (single-node topology). Serves the built SPA from
|
|
3
|
+
# `Spree::Dashboard.dist_path`; 404s when unconfigured. `format: false`
|
|
4
|
+
# keeps asset extensions (.js, .css, .svg) inside the splat instead of
|
|
5
|
+
# being parsed as a response format.
|
|
6
|
+
get '/dashboard', to: 'dashboard/app#show', as: :dashboard_app
|
|
7
|
+
get '/dashboard/*path', to: 'dashboard/app#show', format: false
|
|
8
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'spree'
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Dashboard
|
|
5
|
+
class << self
|
|
6
|
+
# Directory holding a built React Dashboard (`vite build` output),
|
|
7
|
+
# served at /dashboard. Set from an initializer, or via the
|
|
8
|
+
# SPREE_DASHBOARD_DIST_PATH env var (which the official Docker image
|
|
9
|
+
# and the Render Blueprint use). Unset, /dashboard responds 404.
|
|
10
|
+
attr_writer :dist_path
|
|
11
|
+
|
|
12
|
+
def dist_path
|
|
13
|
+
@dist_path.presence || ENV.fetch('SPREE_DASHBOARD_DIST_PATH', nil)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
require 'spree/dashboard/engine'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'spree/dashboard'
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: spree_dashboard
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 5.6.0.rc1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Vendo Connect Inc.
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-07-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: spree
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 5.6.0.rc1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 5.6.0.rc1
|
|
27
|
+
description: Serves a built Spree React Dashboard at /dashboard with SPA semantics
|
|
28
|
+
— the single-node topology where the dashboard and the Admin API share one origin.
|
|
29
|
+
Developer Preview; becomes the default admin delivery in Spree 6.
|
|
30
|
+
email: hello@spreecommerce.org
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- README.md
|
|
36
|
+
- Rakefile
|
|
37
|
+
- app/controllers/spree/dashboard/app_controller.rb
|
|
38
|
+
- config/routes.rb
|
|
39
|
+
- lib/spree/dashboard.rb
|
|
40
|
+
- lib/spree/dashboard/engine.rb
|
|
41
|
+
- lib/spree_dashboard.rb
|
|
42
|
+
homepage: https://spreecommerce.org
|
|
43
|
+
licenses:
|
|
44
|
+
- BSD-3-Clause
|
|
45
|
+
metadata:
|
|
46
|
+
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
47
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.rc1
|
|
48
|
+
documentation_uri: https://spreecommerce.org/docs/developer/dashboard/deployment
|
|
49
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.6.0.rc1
|
|
50
|
+
post_install_message:
|
|
51
|
+
rdoc_options: []
|
|
52
|
+
require_paths:
|
|
53
|
+
- lib
|
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
|
+
requirements:
|
|
56
|
+
- - ">="
|
|
57
|
+
- !ruby/object:Gem::Version
|
|
58
|
+
version: '3.2'
|
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: '0'
|
|
64
|
+
requirements: []
|
|
65
|
+
rubygems_version: 3.5.22
|
|
66
|
+
signing_key:
|
|
67
|
+
specification_version: 4
|
|
68
|
+
summary: Hosts the Spree React Dashboard from your Spree server
|
|
69
|
+
test_files: []
|