Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5b1556a80 |
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
@@ -26,51 +26,122 @@ server {
|
||||
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;
|
||||
# Root directory for blob storage
|
||||
root /home/teknari/Storage/Blossom;
|
||||
|
||||
# Maximum upload size
|
||||
client_max_body_size 100M;
|
||||
|
||||
# OPTIONS preflight handler
|
||||
if ($request_method = OPTIONS) {
|
||||
return 204;
|
||||
}
|
||||
|
||||
# All dynamic endpoints go through FastCGI
|
||||
location ~ ^/(upload|list|delete|mirror|media|report|health|admin) {
|
||||
# PUT /upload - File uploads
|
||||
location = /upload {
|
||||
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_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
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})" {
|
||||
# GET /list/<pubkey> - List user blobs
|
||||
location ~ "^/list/([a-f0-9]{64})$" {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# PUT /mirror - Mirror content
|
||||
location = /mirror {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# PUT /report - Report content
|
||||
location = /report {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# GET /auth - NIP-42 challenges
|
||||
location = /auth {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# GET /health - Health check
|
||||
location = /health {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# Admin API
|
||||
location /api/ {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# Admin interface
|
||||
location /admin {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# Blob serving - SHA256 patterns (serve directly from filesystem)
|
||||
location ~ "^/([a-f0-9]{64})(\.[a-zA-Z0-9]+)?$" {
|
||||
# Handle DELETE via rewrite to internal location
|
||||
if ($request_method = DELETE) {
|
||||
rewrite ^/(.*)$ /fcgi-delete/$1 last;
|
||||
}
|
||||
|
||||
# Enable range requests for video streaming
|
||||
add_header Accept-Ranges bytes always;
|
||||
# Handle HEAD via rewrite to internal location
|
||||
if ($request_method = HEAD) {
|
||||
rewrite ^/(.*)$ /fcgi-head/$1 last;
|
||||
}
|
||||
|
||||
# Cache static blobs
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable" always;
|
||||
# GET requests - serve directly from filesystem
|
||||
if ($request_method != GET) {
|
||||
return 405;
|
||||
}
|
||||
|
||||
try_files /blobs/$1.txt /blobs/$1.jpg /blobs/$1.jpeg /blobs/$1.png /blobs/$1.webp /blobs/$1.gif /blobs/$1.pdf /blobs/$1.mp4 /blobs/$1.mp3 /blobs/$1.md =404;
|
||||
|
||||
# Cache control for blobs
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# Internal FastCGI handler for DELETE
|
||||
location ~ "^/fcgi-delete/([a-f0-9]{64}).*$" {
|
||||
internal;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
# Internal FastCGI handler for HEAD
|
||||
location ~ "^/fcgi-head/([a-f0-9]{64}).*$" {
|
||||
internal;
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
}
|
||||
|
||||
# Root endpoint - server info
|
||||
location = / {
|
||||
include fastcgi_params;
|
||||
fastcgi_pass unix:/tmp/ginxsom.sock;
|
||||
fastcgi_param SCRIPT_FILENAME /home/teknari/Storage/Blossom/ginxsom;
|
||||
}
|
||||
|
||||
# Deny access to sensitive files
|
||||
|
||||
@@ -87,6 +87,10 @@ if [ -f "$SCRIPT_DIR/.admin_keys" ]; then
|
||||
chmod 600 "$DEPLOY_DIR/.admin_keys"
|
||||
echo " - .admin_keys"
|
||||
fi
|
||||
if [ -f "$SCRIPT_DIR/config/fastcgi_params" ]; then
|
||||
cp "$SCRIPT_DIR/config/fastcgi_params" "$DEPLOY_DIR/"
|
||||
echo " - fastcgi_params"
|
||||
fi
|
||||
echo -e "${GREEN}✓ Configuration files copied${NC}"
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 1
|
||||
#define VERSION_PATCH 26
|
||||
#define VERSION "v0.1.26"
|
||||
#define VERSION_PATCH 27
|
||||
#define VERSION "v0.1.27"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
88
src/main.c
88
src/main.c
@@ -973,11 +973,9 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
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);
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_CREATE, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "get_blob_metadata: Can't open database %s: %s\n", g_db_path, sqlite3_errmsg(db));
|
||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -986,7 +984,7 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
||||
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "get_blob_metadata: SQL error: %s\n", sqlite3_errmsg(db));
|
||||
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
@@ -994,10 +992,8 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
||||
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "get_blob_metadata: Found blob in database\n");
|
||||
strncpy(metadata->sha256, (char *)sqlite3_column_text(stmt, 0),
|
||||
MAX_SHA256_LEN - 1);
|
||||
metadata->size = sqlite3_column_int64(stmt, 1);
|
||||
@@ -1096,70 +1092,6 @@ void handle_head_request(const char *sha256) {
|
||||
// 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)
|
||||
const char *extract_sha256_from_uri(const char *uri) {
|
||||
static char sha256_buffer[MAX_SHA256_LEN];
|
||||
@@ -2862,9 +2794,6 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
operation = "list";
|
||||
} else if (strcmp(request_method, "GET") == 0 && strcmp(request_uri, "/auth") == 0) {
|
||||
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) {
|
||||
operation = "delete";
|
||||
resource_hash = extract_sha256_from_uri(request_uri);
|
||||
@@ -3115,17 +3044,6 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
strcmp(request_uri, "/auth") == 0) {
|
||||
// Handle GET /auth requests using the existing handler
|
||||
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) {
|
||||
// Handle DELETE /<sha256> requests with pre-validated auth
|
||||
const char *sha256 = extract_sha256_from_uri(request_uri);
|
||||
|
||||
@@ -305,25 +305,7 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Check if authentication header is provided (required for non-report operations)
|
||||
if (!request->auth_header) {
|
||||
|
||||
result->valid = 0;
|
||||
|
||||
Reference in New Issue
Block a user