v0.1.31 - Add MIME/extension + nginx fallback support for html/js/mjs/css blob uploads and retrieval

This commit is contained in:
Your Name
2026-03-11 18:21:53 -04:00
parent b9e9a08e9e
commit ba8b51d1c6
14 changed files with 435 additions and 6 deletions

View File

@@ -2,4 +2,4 @@
description: "increment and push" description: "increment and push"
--- ---
Run increment_and_push.sh adding a good comment on the command line following the command. Run increment_and_push.sh adding a good comment on the command line following the command. Don't run any other git commands.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -298,7 +298,7 @@ http {
} }
# GET requests - serve files directly with extension fallback # GET requests - serve files directly with extension fallback
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404; try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
# Cache headers for blob content # Cache headers for blob content
add_header Cache-Control "public, max-age=31536000, immutable"; add_header Cache-Control "public, max-age=31536000, immutable";
@@ -678,7 +678,7 @@ http {
} }
# GET requests - serve files directly with extension fallback # GET requests - serve files directly with extension fallback
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404; try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
# Cache headers for blob content # Cache headers for blob content
add_header Cache-Control "public, max-age=31536000, immutable"; add_header Cache-Control "public, max-age=31536000, immutable";

View File

@@ -184,7 +184,7 @@ server {
return 405; return 405;
} }
try_files /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404; try_files /$1.html /$1.js /$1.mjs /$1.css /$1.txt /$1.jpg /$1.jpeg /$1.png /$1.webp /$1.gif /$1.pdf /$1.mp4 /$1.mp3 /$1.md =404;
# Cache headers # Cache headers
add_header Cache-Control "public, max-age=31536000, immutable"; add_header Cache-Control "public, max-age=31536000, immutable";

View File

@@ -112,6 +112,14 @@ static const char* admin_mime_to_extension(const char* mime_type) {
return ".ogg"; return ".ogg";
} else if (strstr(mime_type, "text/plain")) { } else if (strstr(mime_type, "text/plain")) {
return ".txt"; return ".txt";
} else if (strstr(mime_type, "text/html")) {
return ".html";
} else if (strstr(mime_type, "text/css")) {
return ".css";
} else if (strstr(mime_type, "application/javascript")) {
return ".js";
} else if (strstr(mime_type, "text/javascript")) {
return ".mjs";
} else if (strstr(mime_type, "application/pdf")) { } else if (strstr(mime_type, "application/pdf")) {
return ".pdf"; return ".pdf";
} else { } else {

View File

@@ -119,6 +119,14 @@ const char* mime_to_extension(const char* mime_type) {
return ".ogg"; return ".ogg";
} else if (strstr(mime_type, "text/plain")) { } else if (strstr(mime_type, "text/plain")) {
return ".txt"; return ".txt";
} else if (strstr(mime_type, "text/html")) {
return ".html";
} else if (strstr(mime_type, "text/css")) {
return ".css";
} else if (strstr(mime_type, "application/javascript")) {
return ".js";
} else if (strstr(mime_type, "text/javascript")) {
return ".mjs";
} else if (strstr(mime_type, "application/pdf")) { } else if (strstr(mime_type, "application/pdf")) {
return ".pdf"; return ".pdf";
} else { } else {

View File

@@ -10,8 +10,8 @@
// Version information (auto-updated by build system) // Version information (auto-updated by build system)
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 1 #define VERSION_MINOR 1
#define VERSION_PATCH 30 #define VERSION_PATCH 31
#define VERSION "v0.1.30" #define VERSION "v0.1.31"
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>

View File

@@ -1508,6 +1508,14 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
extension = ".ogg"; extension = ".ogg";
} else if (strstr(blob_type_copy, "text/plain")) { } else if (strstr(blob_type_copy, "text/plain")) {
extension = ".txt"; extension = ".txt";
} else if (strstr(blob_type_copy, "text/html")) {
extension = ".html";
} else if (strstr(blob_type_copy, "text/css")) {
extension = ".css";
} else if (strstr(blob_type_copy, "application/javascript")) {
extension = ".js";
} else if (strstr(blob_type_copy, "text/javascript")) {
extension = ".mjs";
} else { } else {
extension = ".bin"; extension = ".bin";
} }

View File

@@ -0,0 +1,266 @@
#!/bin/bash
# file_put_production.sh - Test script for production Ginxsom Blossom server
# Tests upload functionality on blossom.laantungir.net
set -e # Exit on any error
# Configuration
SERVER_URL="https://blossom.laantungir.net"
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
TEST_FILE="test_blob_$(date +%s).html"
CLEANUP_FILES=()
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Cleanup function
cleanup() {
echo -e "${YELLOW}Cleaning up temporary files...${NC}"
for file in "${CLEANUP_FILES[@]}"; do
if [[ -f "$file" ]]; then
rm -f "$file"
echo "Removed: $file"
fi
done
}
# Set up cleanup on exit
trap cleanup EXIT
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Check prerequisites
check_prerequisites() {
log_info "Checking prerequisites..."
# Check if nak is installed
if ! command -v nak &> /dev/null; then
log_error "nak command not found. Please install nak first."
log_info "Install with: go install github.com/fiatjaf/nak@latest"
exit 1
fi
log_success "nak is installed"
# Check if curl is available
if ! command -v curl &> /dev/null; then
log_error "curl command not found. Please install curl."
exit 1
fi
log_success "curl is available"
# Check if sha256sum is available
if ! command -v sha256sum &> /dev/null; then
log_error "sha256sum command not found."
exit 1
fi
log_success "sha256sum is available"
# Check if base64 is available
if ! command -v base64 &> /dev/null; then
log_error "base64 command not found."
exit 1
fi
log_success "base64 is available"
}
# Check if server is running
check_server() {
log_info "Checking if server is running..."
if curl -s -f "${SERVER_URL}/health" > /dev/null 2>&1; then
log_success "Server is running at ${SERVER_URL}"
else
log_error "Server is not responding at ${SERVER_URL}"
exit 1
fi
}
# Create test file
create_test_file() {
log_info "Creating test file: ${TEST_FILE}"
# Create test content with timestamp and random data
cat > "${TEST_FILE}" << EOF
Test blob content for Ginxsom Blossom server (PRODUCTION)
Timestamp: $(date -Iseconds)
Random data: $(openssl rand -hex 32)
Test message: Hello from production test!
This file is used to test the upload functionality
of the Ginxsom Blossom server on blossom.laantungir.net
EOF
CLEANUP_FILES+=("${TEST_FILE}")
log_success "Created test file with $(wc -c < "${TEST_FILE}") bytes"
}
# Calculate file hash
calculate_hash() {
log_info "Calculating SHA-256 hash..."
HASH=$(sha256sum "${TEST_FILE}" | cut -d' ' -f1)
log_success "Data to hash: ${TEST_FILE}"
log_success "File hash: ${HASH}"
}
# Generate nostr event
generate_nostr_event() {
log_info "Generating kind 24242 nostr event with nak using WSB's private key..."
# Calculate expiration time (1 hour from now)
EXPIRATION=$(date -d '+1 hour' +%s)
# Generate the event using nak with WSB's private key
EVENT_JSON=$(nak event -k 24242 -c "" \
--sec "22cc83aa57928a2800234c939240c9a6f0f44a33ea3838a860ed38930b195afd" \
-t "t=upload" \
-t "x=${HASH}" \
-t "expiration=${EXPIRATION}")
if [[ -z "$EVENT_JSON" ]]; then
log_error "Failed to generate nostr event"
exit 1
fi
log_success "Generated nostr event"
echo "Event JSON: $EVENT_JSON"
}
# Create authorization header
create_auth_header() {
log_info "Creating authorization header..."
# Base64 encode the event (without newlines)
AUTH_B64=$(echo -n "$EVENT_JSON" | base64 -w 0)
AUTH_HEADER="Nostr ${AUTH_B64}"
log_success "Created authorization header"
echo "Auth header length: ${#AUTH_HEADER} characters"
}
# Perform upload
perform_upload() {
log_info "Performing upload to ${UPLOAD_ENDPOINT}..."
# Create temporary file for response
RESPONSE_FILE=$(mktemp)
CLEANUP_FILES+=("${RESPONSE_FILE}")
# Perform the upload with verbose output
HTTP_STATUS=$(curl -s -w "%{http_code}" \
-X PUT \
-H "Authorization: ${AUTH_HEADER}" \
-H "Content-Type: text/plain" \
-H "Content-Disposition: attachment; filename=\"${TEST_FILE}\"" \
--data-binary "@${TEST_FILE}" \
"${UPLOAD_ENDPOINT}" \
-o "${RESPONSE_FILE}")
echo "HTTP Status: ${HTTP_STATUS}"
echo "Response body:"
cat "${RESPONSE_FILE}"
echo
# Check response
case "${HTTP_STATUS}" in
200)
log_success "Upload successful!"
;;
201)
log_success "Upload successful (created)!"
;;
400)
log_error "Bad request - check the event format"
;;
401)
log_error "Unauthorized - authentication failed"
;;
405)
log_error "Method not allowed - check nginx configuration"
;;
413)
log_error "Payload too large"
;;
501)
log_warning "Upload endpoint not yet implemented (expected for now)"
;;
*)
log_error "Upload failed with HTTP status: ${HTTP_STATUS}"
;;
esac
}
# Test file retrieval
test_retrieval() {
log_info "Testing file retrieval..."
RETRIEVAL_URL="${SERVER_URL}/${HASH}"
if curl -s -f "${RETRIEVAL_URL}" > /dev/null 2>&1; then
log_success "File can be retrieved at: ${RETRIEVAL_URL}"
# Download and verify
DOWNLOADED_FILE=$(mktemp)
CLEANUP_FILES+=("${DOWNLOADED_FILE}")
curl -s "${RETRIEVAL_URL}" -o "${DOWNLOADED_FILE}"
DOWNLOADED_HASH=$(sha256sum "${DOWNLOADED_FILE}" | cut -d' ' -f1)
if [[ "${DOWNLOADED_HASH}" == "${HASH}" ]]; then
log_success "Downloaded file hash matches! Verification successful."
else
log_error "Hash mismatch! Expected: ${HASH}, Got: ${DOWNLOADED_HASH}"
fi
else
log_warning "File not yet available for retrieval"
fi
}
# Main execution
main() {
echo "=== Ginxsom Blossom Production Upload Test ==="
echo "Server: ${SERVER_URL}"
echo "Timestamp: $(date -Iseconds)"
echo
check_prerequisites
check_server
create_test_file
calculate_hash
generate_nostr_event
create_auth_header
perform_upload
test_retrieval
echo
log_info "Test completed!"
echo "Summary:"
echo " Test file: ${TEST_FILE}"
echo " File hash: ${HASH}"
echo " Server: ${SERVER_URL}"
echo " Upload endpoint: ${UPLOAD_ENDPOINT}"
echo " Retrieval URL: ${SERVER_URL}/${HASH}"
}
# Run main function
main "$@"

11
tests/test_upload.html Normal file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>Ginxsom Test</title>
</head>
<body>
<h1>Ginxsom HTML Upload Test</h1>
<p>This is a small test file to verify HTML uploads on blossom.laantungir.net.</p>
<p>Timestamp: 2026-03-11T21:55:00Z</p>
</body>
</html>

128
tests/upload_html_test.sh Executable file
View File

@@ -0,0 +1,128 @@
#!/bin/bash
# upload_html_test.sh - Test script for uploading HTML to production Ginxsom Blossom server
# Tests upload functionality on blossom.laantungir.net
set -e # Exit on any error
# Configuration
SERVER_URL="https://blossom.laantungir.net"
UPLOAD_ENDPOINT="${SERVER_URL}/upload"
TEST_FILE="tests/test_upload.html"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Helper functions
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
# Calculate file hash
calculate_hash() {
log_info "Calculating SHA-256 hash..."
HASH=$(sha256sum "${TEST_FILE}" | cut -d' ' -f1)
log_success "File hash: ${HASH}"
}
# Generate nostr event
generate_nostr_event() {
log_info "Generating kind 24242 nostr event..."
EXPIRATION=$(date -d '+1 hour' +%s)
EVENT_JSON=$(nak event -k 24242 -c "" \
--sec "22cc83aa57928a2800234c939240c9a6f0f44a33ea3838a860ed38930b195afd" \
-t "t=upload" \
-t "x=${HASH}" \
-t "expiration=${EXPIRATION}")
if [[ -z "$EVENT_JSON" ]]; then
log_error "Failed to generate nostr event"
exit 1
fi
log_success "Generated nostr event"
}
# Create authorization header
create_auth_header() {
log_info "Creating authorization header..."
AUTH_B64=$(echo -n "$EVENT_JSON" | base64 -w 0)
AUTH_HEADER="Nostr ${AUTH_B64}"
log_success "Created authorization header"
}
# Perform upload
perform_upload() {
log_info "Performing upload to ${UPLOAD_ENDPOINT}..."
RESPONSE_FILE=$(mktemp)
HTTP_STATUS=$(curl -s -w "%{http_code}" \
-X PUT \
-H "Authorization: ${AUTH_HEADER}" \
-H "Content-Type: text/html" \
--data-binary "@${TEST_FILE}" \
"${UPLOAD_ENDPOINT}" \
-o "${RESPONSE_FILE}")
echo "HTTP Status: ${HTTP_STATUS}"
echo "Response body:"
cat "${RESPONSE_FILE}"
echo
rm -f "${RESPONSE_FILE}"
if [[ "${HTTP_STATUS}" == "200" || "${HTTP_STATUS}" == "201" ]]; then
log_success "Upload successful!"
else
log_error "Upload failed with HTTP status: ${HTTP_STATUS}"
exit 1
fi
}
# Test file retrieval
test_retrieval() {
log_info "Testing file retrieval..."
# Try with .html extension
RETRIEVAL_URL_EXT="${SERVER_URL}/${HASH}.html"
log_info "Trying retrieval with extension: ${RETRIEVAL_URL_EXT}"
if curl -s -f -I "${RETRIEVAL_URL_EXT}" > /dev/null 2>&1; then
log_success "File found with .html extension!"
else
log_warning "File NOT found with .html extension"
fi
# Try without extension
RETRIEVAL_URL="${SERVER_URL}/${HASH}"
log_info "Trying retrieval without extension: ${RETRIEVAL_URL}"
if curl -s -f -I "${RETRIEVAL_URL}" > /dev/null 2>&1; then
log_success "File found without extension!"
else
log_warning "File NOT found without extension"
fi
}
# Main execution
main() {
calculate_hash
generate_nostr_event
create_auth_header
perform_upload
test_retrieval
}
main "$@"