added wip CORS proxy
This commit is contained in:
parent
54efe0da3d
commit
dee9d7c9fe
3 changed files with 121 additions and 17 deletions
|
|
@ -24,7 +24,7 @@ $ docker run -t xrforge docker.io/coderofsalvation/xrforge:latest -v ./mnt:/mnt
|
||||||
$ git clone --recurse-submodules --depth 1 https://codeberg.org/coderofsalvation/xrforge.git
|
$ git clone --recurse-submodules --depth 1 https://codeberg.org/coderofsalvation/xrforge.git
|
||||||
$ cd xrforge
|
$ cd xrforge
|
||||||
$ docker load < $(nix-build nix/docker.nix)
|
$ docker load < $(nix-build nix/docker.nix)
|
||||||
$ manyfold/cli/manyfold run
|
$ docker run $(manyfold/cli/manyfold run) # generates a dockercmd with sane env-flags
|
||||||
|
|
||||||
[xrforge] podman detected..starting OCI container
|
[xrforge] podman detected..starting OCI container
|
||||||
+ /run/current-system/sw/bin/podman run -p 8790:3214 -p 8791:3215 --name xrforge -e SECRET_KEY_BASE=lkjwljlkwejrlkjek34k234l -e DATABASE_ADAPTER=sqlite3 -e SUDO_RUN_UNSAFELY=enabled -e MULTIUSER=enabled -e FEDERATION=enabled -e THEME=vapor -e HOMEPAGE=/models -e FEDERATE_DRIVE_CACHE=5s -v ./xrfragment/assets:/mnt/assets/xrfragment/#1 --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse xrforge
|
+ /run/current-system/sw/bin/podman run -p 8790:3214 -p 8791:3215 --name xrforge -e SECRET_KEY_BASE=lkjwljlkwejrlkjek34k234l -e DATABASE_ADAPTER=sqlite3 -e SUDO_RUN_UNSAFELY=enabled -e MULTIUSER=enabled -e FEDERATION=enabled -e THEME=vapor -e HOMEPAGE=/models -e FEDERATE_DRIVE_CACHE=5s -v ./xrfragment/assets:/mnt/assets/xrfragment/#1 --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse xrforge
|
||||||
|
|
@ -36,11 +36,11 @@ sending incremental file list
|
||||||
|
|
||||||
> NOTE: if you don't want the default XR Fragments asset-library, omit `--recurse-submodules` in the git cmd
|
> NOTE: if you don't want the default XR Fragments asset-library, omit `--recurse-submodules` in the git cmd
|
||||||
|
|
||||||
To preserve your uploaded models, add the `experiences` as a docker volume-flag, for example:
|
To preserve your uploaded models and db, add these docker volume-flags, for example:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ mkdir experiences
|
$ mkdir experiences config
|
||||||
$ manyfold/cli/manyfold run -v ./experiences:/mnt/experiences
|
$ docker run -t xrforge docker.io/coderofsalvation/xrforge:latest -v ./config:/config -v ./experiences:/mnt/experiences
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ test -n "$UPLOAD_PATH" || export UPLOAD_PATH=/mnt/experiences
|
||||||
test -n "$THEME" || export THEME=slate
|
test -n "$THEME" || export THEME=slate
|
||||||
test -n "$HOMEPAGE" || export HOMEPAGE=/models
|
test -n "$HOMEPAGE" || export HOMEPAGE=/models
|
||||||
test -n "$GODOT_VERSION" || export GODOT_VERSION=4.4.1-stable
|
test -n "$GODOT_VERSION" || export GODOT_VERSION=4.4.1-stable
|
||||||
|
test -n "$CORS_PROXY" || export CORS_PROXY=1
|
||||||
db=/config/manyfold.sqlite3
|
db=/config/manyfold.sqlite3
|
||||||
|
|
||||||
# utility funcs
|
# utility funcs
|
||||||
|
|
@ -19,14 +20,15 @@ create_config(){
|
||||||
}
|
}
|
||||||
|
|
||||||
run(){
|
run(){
|
||||||
test -x ${oci} || { echo "warning: not running manyfold OCI container [install podman/docker/etc]"; exit; }
|
{
|
||||||
echocolor "[$APPNAME]" "$(basename ${oci}) detected..starting OCI container"
|
test -x ${oci} || { echo "warning: not running manyfold OCI container [install podman/docker/etc]"; exit; }
|
||||||
${oci} rm -f xrforge
|
${oci} rm -f xrforge
|
||||||
#-e NO_OVERLAYFS=true \
|
} 1>&2
|
||||||
#-e NO_DEFAULTDB=true \
|
#-e NO_OVERLAYFS=true \
|
||||||
#-e PUBLIC_HOSTNAME=localhost \
|
#-e NO_DEFAULTDB=true \
|
||||||
#-e PUBLIC_PORT=80 \
|
#-e PUBLIC_HOSTNAME=localhost \
|
||||||
debug ${oci} run "$@" -p 8790:3214 -p 8791:3215 --name xrforge \
|
#-e PUBLIC_PORT=80 \
|
||||||
|
echo ${oci} run "$@" -p 8790:3214 -p 8791:3215 --name xrforge \
|
||||||
-e SECRET_KEY_BASE=lkjwljlkwejrlkjek34k234l \
|
-e SECRET_KEY_BASE=lkjwljlkwejrlkjek34k234l \
|
||||||
-e DATABASE_ADAPTER=sqlite3 \
|
-e DATABASE_ADAPTER=sqlite3 \
|
||||||
-e FEDERATE_DRIVE_HOST=http://localhost:8791 \
|
-e FEDERATE_DRIVE_HOST=http://localhost:8791 \
|
||||||
|
|
@ -124,7 +126,8 @@ set_upload_path(){
|
||||||
test -d $UPLOAD_PATH || mkdir $UPLOAD_PATH
|
test -d $UPLOAD_PATH || mkdir $UPLOAD_PATH
|
||||||
add_lib_to_db $UPLOAD_PATH
|
add_lib_to_db $UPLOAD_PATH
|
||||||
id=$(sqlite3 $db "select id from libraries where path = '$UPLOAD_PATH';")
|
id=$(sqlite3 $db "select id from libraries where path = '$UPLOAD_PATH';")
|
||||||
sqlite3 $db "UPDATE settings set value = $id where var = 'default_library';"
|
sqlite3 $db "INSERT INTO settings VALUES(6,'default_library',replace('--- $id\n','\n',char(10);
|
||||||
|
INSERT INTO sqlite_sequence VALUES('settings',6);"
|
||||||
}
|
}
|
||||||
|
|
||||||
mount_dir(){
|
mount_dir(){
|
||||||
|
|
@ -192,8 +195,7 @@ set_homepage(){
|
||||||
rename_app(){
|
rename_app(){
|
||||||
echocolor "[$APPNAME]" "renaming manyfold to $APPNAME"
|
echocolor "[$APPNAME]" "renaming manyfold to $APPNAME"
|
||||||
sed -i 's/title: Manyfold/title: '$APPNAME'/g' /usr/src/app/config/locales/*.yml
|
sed -i 's/title: Manyfold/title: '$APPNAME'/g' /usr/src/app/config/locales/*.yml
|
||||||
sed -i 's|powered_by_html:.*|powered_by_html: Radically opensource-powered by <a href="https://manifold.app" target="_blank">Manyfold</a>, <a href="https://xrfragment.org">XR Fragments</a> and <a href="https://nixos.org" target="_blank">NIX</a>|g' /usr/src/app/config/locales/*.yml
|
sed -i 's|powered_by_html:.*|powered_by_html: Radically opensource-powered by <a href="https://manifold.app" target="_blank">Manyfold</a>, <a href="https://xrfragment.org">XR Fragments</a>, <a href="https://github.com/jbaicoianu/janusweb" target="_blank">JanusWeb</a> and <a href="https://nixos.org" target="_blank">NIX</a>|g' /usr/src/app/config/locales/*.yml
|
||||||
|
|
||||||
sed -i 's|Models|Experiences|g' /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml
|
sed -i 's|Models|Experiences|g' /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml
|
||||||
sed -i 's|Model|Experience|g' /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml
|
sed -i 's|Model|Experience|g' /usr/src/app/config/locales/*.yml /usr/src/app/config/locales/*/*.yml
|
||||||
}
|
}
|
||||||
|
|
@ -208,7 +210,8 @@ start_syslog(){
|
||||||
scan_libraries(){
|
scan_libraries(){
|
||||||
cd /usr/src/app
|
cd /usr/src/app
|
||||||
echocolor "scanning libraries"
|
echocolor "scanning libraries"
|
||||||
bin/manyfold libraries scan
|
test -f ${db}.startupscan || bin/manyfold libraries scan
|
||||||
|
touch ${db}.startupscan # only do once
|
||||||
}
|
}
|
||||||
|
|
||||||
scan_experience(){
|
scan_experience(){
|
||||||
|
|
@ -250,7 +253,7 @@ init_database(){
|
||||||
set_admin
|
set_admin
|
||||||
set_global site_name "'$APPNAME'"
|
set_global site_name "'$APPNAME'"
|
||||||
set_global site_tagline "'$TAGLINE'"
|
set_global site_tagline "'$TAGLINE'"
|
||||||
set_global model_tags_auto_tag_new "replace('--- \"build \"\\n','\\n',char(10))"
|
set_global model_tags_auto_tag_new "replace('--- \"\"\\n','\\n',char(10))"
|
||||||
set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))"
|
set_global model_path_template "replace('--- \"{creator}/{modelId} \"\\n','\\n',char(10))"
|
||||||
#set_global about "$ABOUT"
|
#set_global about "$ABOUT"
|
||||||
mount_rclone
|
mount_rclone
|
||||||
|
|
|
||||||
101
manyfold/usr/src/app/controllers/cors_proxy.rb
Normal file
101
manyfold/usr/src/app/controllers/cors_proxy.rb
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
require 'net/http'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
# Defines the controller logic for the CORS proxy endpoint.
|
||||||
|
# It handles all HTTP methods and forwards the request to the target URL
|
||||||
|
# provided as a path segment (e.g., /cors/https://example.com/data?q=1).
|
||||||
|
class CorsProxyController < ApplicationController
|
||||||
|
# Skip CSRF protection for this action, as it's intended to handle external requests
|
||||||
|
# that might not have a valid CSRF token.
|
||||||
|
skip_before_action :verify_authenticity_token
|
||||||
|
|
||||||
|
# The main proxy action.
|
||||||
|
def proxy
|
||||||
|
# Assuming the route is configured as: match '/cors/*target_url_segment', ...
|
||||||
|
target_url_segment = params[:target_url_segment]
|
||||||
|
|
||||||
|
# Reconstruct the full target URL. The globbing parameter captures the base path
|
||||||
|
# (e.g., "https://example.com/data"), and we re-attach the raw query string
|
||||||
|
# (e.g., "?q=1¶m=2") that the Rails router parsed separately.
|
||||||
|
query_string = request.query_string.present? ? "?#{request.query_string}" : ""
|
||||||
|
target_url = target_url_segment.to_s + query_string
|
||||||
|
|
||||||
|
# 1. Input Validation
|
||||||
|
unless target_url.present?
|
||||||
|
return render json: { error: 'Target URL is required as a path segment after /cors/.' }, status: :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
uri = URI.parse(target_url)
|
||||||
|
# Security check: Only allow standard HTTP/HTTPS schemes.
|
||||||
|
unless ['http', 'https'].include?(uri.scheme&.downcase)
|
||||||
|
return render json: { error: 'Invalid URL scheme. Only http and https are supported.' }, status: :bad_request
|
||||||
|
end
|
||||||
|
rescue URI::InvalidURIError
|
||||||
|
return render json: { error: 'Invalid URL format.' }, status: :bad_request
|
||||||
|
end
|
||||||
|
|
||||||
|
# 2. Determine the appropriate Net::HTTP request class based on the incoming method
|
||||||
|
request_class = case request.method
|
||||||
|
when 'GET' then Net::HTTP::Get
|
||||||
|
when 'POST' then Net::HTTP::Post
|
||||||
|
when 'PUT' then Net::HTTP::Put
|
||||||
|
when 'DELETE' then Net::HTTP::Delete
|
||||||
|
when 'PATCH' then Net::HTTP::Patch
|
||||||
|
when 'HEAD' then Net::HTTP::Head
|
||||||
|
else
|
||||||
|
return render json: { error: "Unsupported HTTP method: #{request.method}" }, status: :method_not_allowed
|
||||||
|
end
|
||||||
|
|
||||||
|
# 3. Initialize the outgoing request
|
||||||
|
# Use uri.request_uri which includes the path and query parameters of the target
|
||||||
|
outgoing_request = request_class.new(uri.request_uri)
|
||||||
|
|
||||||
|
# 4. Copy relevant headers from the incoming request to the outgoing request
|
||||||
|
# We explicitly skip certain headers that are managed by the HTTP client or should not be proxied.
|
||||||
|
request.headers.each do |key, value|
|
||||||
|
header_key = key.sub(/^HTTP_/, '').underscore.dasherize.split('-').map(&:capitalize).join('-')
|
||||||
|
|
||||||
|
# Skip internal/sensitive/managed headers
|
||||||
|
next if ['Host', 'Content-Length', 'Connection', 'Transfer-Encoding', 'X-Request-Id', 'X-Forwarded-For'].include?(header_key)
|
||||||
|
next if header_key.start_with?('X-') # Generally skip custom Rails internal headers
|
||||||
|
|
||||||
|
# Pass all other headers (including Authorization, Content-Type, etc.)
|
||||||
|
outgoing_request[header_key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
# 5. Handle request body for methods that carry a payload
|
||||||
|
if request.body.present? && !['GET', 'HEAD'].include?(request.method)
|
||||||
|
request.body.rewind # Ensure we read from the beginning of the stream
|
||||||
|
outgoing_request.body = request.body.read
|
||||||
|
end
|
||||||
|
|
||||||
|
# 6. Execute the request
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = (uri.scheme == 'https')
|
||||||
|
|
||||||
|
# Set reasonable timeouts
|
||||||
|
http.read_timeout = 15
|
||||||
|
http.open_timeout = 5
|
||||||
|
|
||||||
|
target_response = http.request(outgoing_request)
|
||||||
|
|
||||||
|
# 7. Proxy the response back to the client
|
||||||
|
# Copy headers from the target response to the client response
|
||||||
|
target_response.each_header do |key, value|
|
||||||
|
# Skip internal headers but ensure CORS headers (Access-Control-*) are passed
|
||||||
|
next if ['Transfer-Encoding', 'Connection', 'Content-Length'].include?(key.downcase)
|
||||||
|
response.headers[key] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set status and send back the body and content type
|
||||||
|
render plain: target_response.body,
|
||||||
|
status: target_response.code.to_i,
|
||||||
|
content_type: target_response['Content-Type'] || 'text/plain'
|
||||||
|
|
||||||
|
rescue StandardError => e
|
||||||
|
# Catch network errors (DNS failures, timeouts, etc.)
|
||||||
|
Rails.logger.error "CORS Proxy Error: #{e.class}: #{e.message}"
|
||||||
|
render json: { error: "Proxy request failed due to a server error: #{e.message}" }, status: :internal_server_error
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue