v0.1.26 - Add local deployment system with deploy_local.sh, ginxsom-local.service, nginx SSL config, and fix GET blob retrieval
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -4,3 +4,5 @@ nostr_core_lib/
|
|||||||
blobs/
|
blobs/
|
||||||
c-relay/
|
c-relay/
|
||||||
text_graph/
|
text_graph/
|
||||||
|
.test_keys/
|
||||||
|
ginxsom-local.service
|
||||||
5
.roo/commands/push.md
Normal file
5
.roo/commands/push.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
description: "increment and push"
|
||||||
|
---
|
||||||
|
|
||||||
|
Run increment_and_push.sh adding a good comment on the command line following the command.
|
||||||
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
80
config/ginxsom-local.conf
Normal file
80
config/ginxsom-local.conf
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Ginxsom Blossom Server nginx configuration
|
||||||
|
# Local deployment configuration
|
||||||
|
# Port: 9443 with SSL
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 9443 ssl http2;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
# SSL configuration
|
||||||
|
ssl_certificate /home/teknari/.ssl_for_local_servers/cert.pem;
|
||||||
|
ssl_certificate_key /home/teknari/.ssl_for_local_servers/key.pem;
|
||||||
|
|
||||||
|
# SSL settings
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
add_header X-Frame-Options DENY always;
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# CORS headers for all responses (including error responses)
|
||||||
|
add_header Access-Control-Allow-Origin * always;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, HEAD, PUT, DELETE, OPTIONS" always;
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-SHA-256, X-Content-Type, X-Content-Length" always;
|
||||||
|
add_header Access-Control-Max-Age 86400 always;
|
||||||
|
|
||||||
|
# Handle preflight requests
|
||||||
|
location / {
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
add_header Access-Control-Allow-Methods "GET, HEAD, PUT, DELETE, OPTIONS";
|
||||||
|
add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-SHA-256, X-Content-Type, X-Content-Length";
|
||||||
|
add_header Access-Control-Max-Age 86400;
|
||||||
|
add_header Content-Length 0;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pass all requests to FastCGI
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
# All dynamic endpoints go through FastCGI
|
||||||
|
location ~ ^/(upload|list|delete|mirror|media|report|health|admin) {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
|
||||||
|
# Handle large uploads
|
||||||
|
client_max_body_size 100M;
|
||||||
|
fastcgi_read_timeout 300s;
|
||||||
|
|
||||||
|
# Disable buffering for large uploads
|
||||||
|
fastcgi_request_buffering off;
|
||||||
|
fastcgi_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Blob serving - SHA256 hashes
|
||||||
|
location ~ "^/([a-fA-F0-9]{64})" {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
|
||||||
|
# Enable range requests for video streaming
|
||||||
|
add_header Accept-Ranges bytes always;
|
||||||
|
|
||||||
|
# Cache static blobs
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable" always;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Deny access to sensitive files
|
||||||
|
location ~ /\.(ht|git|svn) {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
199
deploy_local.sh
Executable file
199
deploy_local.sh
Executable file
@@ -0,0 +1,199 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Deploy Ginxsom to local installation directory
|
||||||
|
# Builds static binary and deploys to ~/Storage/Blossom
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DEPLOY_DIR="$HOME/Storage/Blossom"
|
||||||
|
SERVICE_NAME="ginxsom.service"
|
||||||
|
NGINX_SITE_NAME="ginxsom"
|
||||||
|
NGINX_PORT="9443"
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
echo -e "${YELLOW}=== Ginxsom Local Deployment ===${NC}"
|
||||||
|
echo "Source: $SCRIPT_DIR"
|
||||||
|
echo "Target: $DEPLOY_DIR"
|
||||||
|
echo "Nginx Port: $NGINX_PORT"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 1: Build static binary
|
||||||
|
echo -e "${YELLOW}Step 1: Building static binary...${NC}"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
./build_static.sh
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo -e "${RED}Build failed! Cannot continue.${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ Build complete${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 2: Create deployment directory
|
||||||
|
echo -e "${YELLOW}Step 2: Preparing deployment directory...${NC}"
|
||||||
|
mkdir -p "$DEPLOY_DIR"
|
||||||
|
mkdir -p "$DEPLOY_DIR/blobs"
|
||||||
|
mkdir -p "$DEPLOY_DIR/db"
|
||||||
|
mkdir -p "$DEPLOY_DIR/logs"
|
||||||
|
echo -e "${GREEN}✓ Directories created${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 3: Stop service before copying binary
|
||||||
|
echo -e "${YELLOW}Step 3: Stopping service before binary update...${NC}"
|
||||||
|
if systemctl list-unit-files | grep -q "$SERVICE_NAME"; then
|
||||||
|
sudo systemctl stop "$SERVICE_NAME" || true
|
||||||
|
sleep 1
|
||||||
|
echo -e "${GREEN}✓ Service stopped${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 4: Copy binary
|
||||||
|
echo -e "${YELLOW}Step 4: Copying binary...${NC}"
|
||||||
|
|
||||||
|
# Detect architecture
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64) BINARY_NAME="ginxsom-fcgi_static_x86_64" ;;
|
||||||
|
aarch64|arm64) BINARY_NAME="ginxsom-fcgi_static_arm64" ;;
|
||||||
|
*) BINARY_NAME="ginxsom-fcgi_static_${ARCH}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
SOURCE_BINARY="$SCRIPT_DIR/build/$BINARY_NAME"
|
||||||
|
TARGET_BINARY="$DEPLOY_DIR/ginxsom"
|
||||||
|
|
||||||
|
if [ ! -f "$SOURCE_BINARY" ]; then
|
||||||
|
echo -e "${RED}Error: Binary not found at $SOURCE_BINARY${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cp "$SOURCE_BINARY" "$TARGET_BINARY"
|
||||||
|
chmod +x "$TARGET_BINARY"
|
||||||
|
echo -e "${GREEN}✓ Binary copied to $TARGET_BINARY${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 5: Copy configuration files if they exist
|
||||||
|
echo -e "${YELLOW}Step 5: Copying configuration files...${NC}"
|
||||||
|
if [ -f "$SCRIPT_DIR/mime.types" ]; then
|
||||||
|
cp "$SCRIPT_DIR/mime.types" "$DEPLOY_DIR/"
|
||||||
|
echo " - mime.types"
|
||||||
|
fi
|
||||||
|
if [ -f "$SCRIPT_DIR/.admin_keys" ]; then
|
||||||
|
cp "$SCRIPT_DIR/.admin_keys" "$DEPLOY_DIR/"
|
||||||
|
chmod 600 "$DEPLOY_DIR/.admin_keys"
|
||||||
|
echo " - .admin_keys"
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}✓ Configuration files copied${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 6: Setup nginx configuration
|
||||||
|
echo -e "${YELLOW}Step 6: Setting up nginx configuration...${NC}"
|
||||||
|
|
||||||
|
# Copy nginx config to sites-available
|
||||||
|
if [ -f "$SCRIPT_DIR/config/ginxsom-local.conf" ]; then
|
||||||
|
sudo cp "$SCRIPT_DIR/config/ginxsom-local.conf" "/etc/nginx/sites-available/$NGINX_SITE_NAME"
|
||||||
|
echo " - Copied nginx config to sites-available"
|
||||||
|
|
||||||
|
# Create symlink in sites-enabled if it doesn't exist
|
||||||
|
if [ ! -L "/etc/nginx/sites-enabled/$NGINX_SITE_NAME" ]; then
|
||||||
|
sudo ln -s "/etc/nginx/sites-available/$NGINX_SITE_NAME" "/etc/nginx/sites-enabled/$NGINX_SITE_NAME"
|
||||||
|
echo " - Created symlink in sites-enabled"
|
||||||
|
else
|
||||||
|
echo " - Symlink already exists in sites-enabled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Test nginx configuration
|
||||||
|
echo " - Testing nginx configuration..."
|
||||||
|
if sudo nginx -t; then
|
||||||
|
echo -e "${GREEN}✓ Nginx configuration valid${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Nginx configuration test failed${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Warning: nginx config not found at $SCRIPT_DIR/config/ginxsom-local.conf${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 7: Restart service
|
||||||
|
echo -e "${YELLOW}Step 7: Restarting service...${NC}"
|
||||||
|
|
||||||
|
# Check if service exists
|
||||||
|
if systemctl list-unit-files | grep -q "$SERVICE_NAME"; then
|
||||||
|
echo "Stopping service..."
|
||||||
|
sudo systemctl stop "$SERVICE_NAME" || true
|
||||||
|
|
||||||
|
echo "Reloading systemd daemon..."
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
|
||||||
|
echo "Starting service..."
|
||||||
|
sudo systemctl start "$SERVICE_NAME"
|
||||||
|
|
||||||
|
# Wait a moment for service to start
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
||||||
|
echo -e "${GREEN}✓ Service started successfully${NC}"
|
||||||
|
systemctl status "$SERVICE_NAME" --no-pager -l
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Service failed to start${NC}"
|
||||||
|
echo "Check logs with: journalctl -u $SERVICE_NAME -n 50"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Service not found. Install with:${NC}"
|
||||||
|
echo " sudo cp ginxsom-local.service /etc/systemd/system/ginxsom.service"
|
||||||
|
echo " sudo systemctl daemon-reload"
|
||||||
|
echo " sudo systemctl enable ginxsom.service"
|
||||||
|
echo " sudo systemctl start ginxsom.service"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Step 8: Start/restart nginx
|
||||||
|
echo -e "${YELLOW}Step 8: Starting nginx...${NC}"
|
||||||
|
if systemctl is-active --quiet nginx; then
|
||||||
|
echo "Reloading nginx..."
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
else
|
||||||
|
echo "Starting nginx..."
|
||||||
|
sudo systemctl start nginx
|
||||||
|
fi
|
||||||
|
|
||||||
|
if systemctl is-active --quiet nginx; then
|
||||||
|
echo -e "${GREEN}✓ Nginx is running${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}✗ Nginx failed to start${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Deployment complete ===${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Service Information:"
|
||||||
|
echo " FastCGI Service: $SERVICE_NAME"
|
||||||
|
echo " Nginx Port: $NGINX_PORT"
|
||||||
|
echo " Socket: /tmp/ginxsom.sock"
|
||||||
|
echo " Storage: $DEPLOY_DIR/blobs"
|
||||||
|
echo " Database: $DEPLOY_DIR/db"
|
||||||
|
echo ""
|
||||||
|
echo "Service commands:"
|
||||||
|
echo " Status: systemctl status $SERVICE_NAME"
|
||||||
|
echo " Stop: sudo systemctl stop $SERVICE_NAME"
|
||||||
|
echo " Start: sudo systemctl start $SERVICE_NAME"
|
||||||
|
echo " Restart: sudo systemctl restart $SERVICE_NAME"
|
||||||
|
echo " Logs: journalctl -u $SERVICE_NAME -f"
|
||||||
|
echo ""
|
||||||
|
echo "Nginx commands:"
|
||||||
|
echo " Status: systemctl status nginx"
|
||||||
|
echo " Reload: sudo systemctl reload nginx"
|
||||||
|
echo " Logs: sudo tail -f /var/log/nginx/error.log"
|
||||||
|
echo ""
|
||||||
|
echo "Test the server:"
|
||||||
|
echo " curl http://localhost:$NGINX_PORT/health"
|
||||||
|
echo " curl http://localhost:$NGINX_PORT/"
|
||||||
@@ -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 25
|
#define VERSION_PATCH 26
|
||||||
#define VERSION "v0.1.25"
|
#define VERSION "v0.1.26"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|||||||
88
src/main.c
88
src/main.c
@@ -973,9 +973,11 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
|||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_CREATE, NULL);
|
fprintf(stderr, "get_blob_metadata: Looking for %s in database %s\n", sha256, g_db_path);
|
||||||
|
|
||||||
|
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
|
fprintf(stderr, "get_blob_metadata: Can't open database %s: %s\n", g_db_path, sqlite3_errmsg(db));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -984,7 +986,7 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
|||||||
|
|
||||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||||
if (rc != SQLITE_OK) {
|
if (rc != SQLITE_OK) {
|
||||||
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
fprintf(stderr, "get_blob_metadata: SQL error: %s\n", sqlite3_errmsg(db));
|
||||||
sqlite3_close(db);
|
sqlite3_close(db);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -992,8 +994,10 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
|||||||
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
|
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
|
||||||
|
|
||||||
rc = sqlite3_step(stmt);
|
rc = sqlite3_step(stmt);
|
||||||
|
fprintf(stderr, "get_blob_metadata: sqlite3_step returned %d (SQLITE_ROW=%d, SQLITE_DONE=%d)\n", rc, SQLITE_ROW, SQLITE_DONE);
|
||||||
|
|
||||||
if (rc == SQLITE_ROW) {
|
if (rc == SQLITE_ROW) {
|
||||||
|
fprintf(stderr, "get_blob_metadata: Found blob in database\n");
|
||||||
strncpy(metadata->sha256, (char *)sqlite3_column_text(stmt, 0),
|
strncpy(metadata->sha256, (char *)sqlite3_column_text(stmt, 0),
|
||||||
MAX_SHA256_LEN - 1);
|
MAX_SHA256_LEN - 1);
|
||||||
metadata->size = sqlite3_column_int64(stmt, 1);
|
metadata->size = sqlite3_column_int64(stmt, 1);
|
||||||
@@ -1092,6 +1096,70 @@ void handle_head_request(const char *sha256) {
|
|||||||
// HEAD request - no body content
|
// HEAD request - no body content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle GET request for blob - serve the actual file
|
||||||
|
void handle_get_request(const char *sha256) {
|
||||||
|
blob_metadata_t metadata = {0};
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
fprintf(stderr, "GET: Requesting blob %s\n", sha256);
|
||||||
|
fprintf(stderr, "GET: Database path: %s\n", g_db_path);
|
||||||
|
fprintf(stderr, "GET: Storage dir: %s\n", g_storage_dir);
|
||||||
|
|
||||||
|
// Validate SHA-256 format (64 hex characters)
|
||||||
|
if (strlen(sha256) != 64) {
|
||||||
|
printf("Status: 400 Bad Request\r\n");
|
||||||
|
printf("Content-Type: text/plain\r\n\r\n");
|
||||||
|
printf("Invalid SHA-256 hash format\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if blob exists in database
|
||||||
|
if (!get_blob_metadata(sha256, &metadata)) {
|
||||||
|
fprintf(stderr, "GET: Blob not found in database\n");
|
||||||
|
printf("Status: 404 Not Found\r\n");
|
||||||
|
printf("Content-Type: text/plain\r\n\r\n");
|
||||||
|
printf("Blob not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct file path
|
||||||
|
char filepath[4096];
|
||||||
|
const char *extension = mime_to_extension(metadata.type);
|
||||||
|
snprintf(filepath, sizeof(filepath), "%s/%s%s", g_storage_dir, sha256, extension);
|
||||||
|
|
||||||
|
// Open and serve the file
|
||||||
|
FILE *file = fopen(filepath, "rb");
|
||||||
|
if (!file) {
|
||||||
|
printf("Status: 404 Not Found\r\n");
|
||||||
|
printf("Content-Type: text/plain\r\n\r\n");
|
||||||
|
printf("Blob file not found on disk\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send headers
|
||||||
|
printf("Status: 200 OK\r\n");
|
||||||
|
printf("Content-Type: %s\r\n", metadata.type);
|
||||||
|
printf("Content-Length: %ld\r\n", metadata.size);
|
||||||
|
printf("Cache-Control: public, max-age=31536000, immutable\r\n");
|
||||||
|
printf("ETag: \"%s\"\r\n", metadata.sha256);
|
||||||
|
printf("X-Ginxsom-Server: FastCGI\r\n");
|
||||||
|
|
||||||
|
if (strlen(metadata.filename) > 0) {
|
||||||
|
printf("X-Original-Filename: %s\r\n", metadata.filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\r\n");
|
||||||
|
|
||||||
|
// Send file content
|
||||||
|
char buffer[8192];
|
||||||
|
size_t bytes_read;
|
||||||
|
while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
|
||||||
|
fwrite(buffer, 1, bytes_read, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
// Extract SHA-256 from request URI (Blossom compliant - ignores any extension)
|
// Extract SHA-256 from request URI (Blossom compliant - ignores any extension)
|
||||||
const char *extract_sha256_from_uri(const char *uri) {
|
const char *extract_sha256_from_uri(const char *uri) {
|
||||||
static char sha256_buffer[MAX_SHA256_LEN];
|
static char sha256_buffer[MAX_SHA256_LEN];
|
||||||
@@ -2794,6 +2862,9 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
|||||||
operation = "list";
|
operation = "list";
|
||||||
} else if (strcmp(request_method, "GET") == 0 && strcmp(request_uri, "/auth") == 0) {
|
} else if (strcmp(request_method, "GET") == 0 && strcmp(request_uri, "/auth") == 0) {
|
||||||
operation = "challenge";
|
operation = "challenge";
|
||||||
|
} else if (strcmp(request_method, "GET") == 0 && extract_sha256_from_uri(request_uri) != NULL) {
|
||||||
|
operation = "get_blob"; // Public blob retrieval - no auth required
|
||||||
|
resource_hash = extract_sha256_from_uri(request_uri);
|
||||||
} else if (strcmp(request_method, "DELETE") == 0) {
|
} else if (strcmp(request_method, "DELETE") == 0) {
|
||||||
operation = "delete";
|
operation = "delete";
|
||||||
resource_hash = extract_sha256_from_uri(request_uri);
|
resource_hash = extract_sha256_from_uri(request_uri);
|
||||||
@@ -3044,6 +3115,17 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
|||||||
strcmp(request_uri, "/auth") == 0) {
|
strcmp(request_uri, "/auth") == 0) {
|
||||||
// Handle GET /auth requests using the existing handler
|
// Handle GET /auth requests using the existing handler
|
||||||
handle_auth_challenge_request();
|
handle_auth_challenge_request();
|
||||||
|
} else if (strcmp(request_method, "GET") == 0) {
|
||||||
|
// Handle GET /<sha256> requests for blob retrieval
|
||||||
|
const char *sha256 = extract_sha256_from_uri(request_uri);
|
||||||
|
if (sha256) {
|
||||||
|
handle_get_request(sha256);
|
||||||
|
log_request("GET", request_uri, "public", 200);
|
||||||
|
} else {
|
||||||
|
send_error_response(400, "invalid_hash", "Invalid SHA-256 hash in URI",
|
||||||
|
"URI must contain a valid 64-character hex hash");
|
||||||
|
log_request("GET", request_uri, "none", 400);
|
||||||
|
}
|
||||||
} else if (strcmp(request_method, "DELETE") == 0) {
|
} else if (strcmp(request_method, "DELETE") == 0) {
|
||||||
// Handle DELETE /<sha256> requests with pre-validated auth
|
// Handle DELETE /<sha256> requests with pre-validated auth
|
||||||
const char *sha256 = extract_sha256_from_uri(request_uri);
|
const char *sha256 = extract_sha256_from_uri(request_uri);
|
||||||
|
|||||||
@@ -305,7 +305,25 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
|||||||
return NOSTR_SUCCESS;
|
return NOSTR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if authentication header is provided (required for non-report operations)
|
// Check if this is a GET blob request - allow public blob retrieval
|
||||||
|
if (request->operation && strcmp(request->operation, "get_blob") == 0) {
|
||||||
|
result->valid = 1;
|
||||||
|
result->error_code = NOSTR_SUCCESS;
|
||||||
|
strcpy(result->reason, "Public blob retrieval - no authentication required");
|
||||||
|
validator_debug_log("VALIDATOR_DEBUG: GET blob detected, bypassing authentication\n");
|
||||||
|
return NOSTR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a HEAD request - allow public metadata retrieval
|
||||||
|
if (request->operation && (strcmp(request->operation, "head") == 0 || strcmp(request->operation, "head_upload") == 0)) {
|
||||||
|
result->valid = 1;
|
||||||
|
result->error_code = NOSTR_SUCCESS;
|
||||||
|
strcpy(result->reason, "Public HEAD request - no authentication required");
|
||||||
|
validator_debug_log("VALIDATOR_DEBUG: HEAD request detected, bypassing authentication\n");
|
||||||
|
return NOSTR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if authentication header is provided (required for non-public operations)
|
||||||
if (!request->auth_header) {
|
if (!request->auth_header) {
|
||||||
|
|
||||||
result->valid = 0;
|
result->valid = 0;
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ main() {
|
|||||||
generate_nostr_event
|
generate_nostr_event
|
||||||
create_auth_header
|
create_auth_header
|
||||||
perform_upload
|
perform_upload
|
||||||
# test_retrieval
|
test_retrieval
|
||||||
|
|
||||||
echo
|
echo
|
||||||
log_info "Test completed!"
|
log_info "Test completed!"
|
||||||
|
|||||||
Reference in New Issue
Block a user