v0.1.32 - Migrate to nostr_core_lib v0.4.13 and unify app/nostr logging via callback API
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/bud08.o
BIN
build/bud08.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
build/main.o
BIN
build/main.o
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -113,7 +113,7 @@ server {
|
||||
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;
|
||||
try_files /blobs/$1.html /blobs/$1.js /blobs/$1.mjs /blobs/$1.css /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";
|
||||
|
||||
Binary file not shown.
142
plans/unified-logging-migration.md
Normal file
142
plans/unified-logging-migration.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# Unified Logging Migration Plan
|
||||
|
||||
## Overview
|
||||
|
||||
Migrate ginxsom to use nostr_core_lib v0.4.13's callback-based logging API and unify all application logging into a single sink (`logs/app/app.log`).
|
||||
|
||||
## Architecture Context
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Current State - v0.4.8
|
||||
A[ginxsom app] -->|fprintf stderr ~122 calls| B[nginx error_log]
|
||||
A -->|app_log ~80+ calls| C[logs/app/app.log]
|
||||
A -->|validator_debug_log ~10 calls| D[logs/app/debug.log]
|
||||
E[nostr_core_lib] -->|direct file write| F[nostr_core_lib/debug.log]
|
||||
G[systemd journald] -->|spawn-fcgi parent only| H[journal]
|
||||
end
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Target State - v0.4.13
|
||||
A2[ginxsom app] -->|stderr: pre-init fatal only ~10 calls| B2[nginx error_log]
|
||||
A2 -->|app_log: all operational logs| C2[logs/app/app.log]
|
||||
E2[nostr_core_lib] -->|callback| A2
|
||||
G2[systemd journald] -->|spawn-fcgi parent only| H2[journal]
|
||||
end
|
||||
```
|
||||
|
||||
## Logging Policy
|
||||
|
||||
| Sink | Purpose | When Used |
|
||||
|------|---------|-----------|
|
||||
| `stderr` -> nginx error_log | Pre-init fatal errors only | Before `app_log` is available, or truly unrecoverable errors during early `main` |
|
||||
| `logs/app/app.log` via `app_log` | All application operational logging | Everything after logger init: app events, nostr_core_lib callback, validator, relay, admin |
|
||||
| `logs/app/debug.log` | **ELIMINATED** | Replaced by `app_log(LOG_DEBUG, ...)` |
|
||||
| `nostr_core_lib/debug.log` | **ELIMINATED** | v0.4.13 removes direct file logging; callback replaces it |
|
||||
|
||||
## Log Levels
|
||||
|
||||
```c
|
||||
typedef enum {
|
||||
LOG_TRACE = 0, // NEW: maps to NOSTR_LOG_LEVEL_TRACE
|
||||
LOG_DEBUG = 1, // maps to NOSTR_LOG_LEVEL_DEBUG
|
||||
LOG_INFO = 2, // maps to NOSTR_LOG_LEVEL_INFO
|
||||
LOG_WARN = 3, // maps to NOSTR_LOG_LEVEL_WARN
|
||||
LOG_ERROR = 4 // maps to NOSTR_LOG_LEVEL_ERROR
|
||||
} log_level_t;
|
||||
```
|
||||
|
||||
**Note:** Existing code uses `LOG_DEBUG=0, LOG_INFO=1, LOG_WARN=2, LOG_ERROR=3`. Adding `LOG_TRACE` at 0 shifts all values up by 1. All existing call sites use the enum names (not raw integers), so this is safe.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Phase 1: Update nostr_core_lib submodule
|
||||
|
||||
1. Update the `nostr_core_lib` git submodule to v0.4.13
|
||||
2. Rebuild `libnostr_core_x64.a` from the updated source
|
||||
3. Verify the new headers exist: `nostr_core/nostr_log.h` (or equivalent in `nostr_core.h`)
|
||||
4. Verify build still compiles with `make clean && make`
|
||||
|
||||
### Phase 2: Enhance app_log in ginxsom.h and main.c
|
||||
|
||||
5. Add `LOG_TRACE` level to `log_level_t` enum in `ginxsom.h`
|
||||
6. Add a global `g_log_level` variable to control minimum log level
|
||||
7. Update `app_log()` in `main.c` to:
|
||||
- Check message level against `g_log_level` before writing
|
||||
- Handle the new `LOG_TRACE` level string
|
||||
- Keep the existing file-based sink (`logs/app/app.log`)
|
||||
|
||||
### Phase 3: Register nostr_core_lib log callback
|
||||
|
||||
8. Add `#include "nostr_core/nostr_core.h"` (or `nostr_log.h`) to `main.c` if not already present
|
||||
9. Create `nostr_log_cb()` callback function that maps nostr log levels to `app_log()` levels and prefixes messages with `[nostr:component]`
|
||||
10. In `main()`, after `nostr_init()` / `nostr_crypto_init()`, call:
|
||||
- `nostr_set_log_callback(nostr_log_cb, NULL)`
|
||||
- `nostr_set_log_level(NOSTR_LOG_LEVEL_INFO)` (or TRACE for debug builds)
|
||||
11. In shutdown path, call `nostr_set_log_callback(NULL, NULL)` before `nostr_cleanup()`
|
||||
|
||||
### Phase 4: Eliminate debug.log
|
||||
|
||||
12. Remove `validator_debug_log()` function from `request_validator.c`
|
||||
13. Replace all `validator_debug_log()` calls with `app_log(LOG_DEBUG, "VALIDATOR: %s", ...)` calls
|
||||
14. Delete stale `logs/app/debug.log` and `nostr_core_lib/debug.log` references from `.gitignore` or docs if present
|
||||
15. Remove `debug.log` from project root if it was generated by nostr_core_lib
|
||||
|
||||
### Phase 5: Migrate fprintf(stderr) calls to app_log
|
||||
|
||||
Migrate post-startup `fprintf(stderr, ...)` calls file-by-file. Keep only pre-init and truly fatal stderr calls in early `main()`.
|
||||
|
||||
**Files to migrate (by priority):**
|
||||
|
||||
16. `src/admin_auth.c` (~28 fprintf calls) - Replace with `app_log(LOG_ERROR, "AUTH: ...")` etc.
|
||||
17. `src/request_validator.c` (~2 fprintf calls) - Replace with `app_log(LOG_DEBUG, "VALIDATOR: ...")`
|
||||
18. `src/main.c` - Categorize ~90 fprintf calls:
|
||||
- **Keep as stderr** (~10): Pre-logger-init errors in early `main()`, help text output, keypair display banner
|
||||
- **Migrate to app_log** (~80): All post-init startup messages, database operations, request logging, upload handling, shutdown messages
|
||||
|
||||
### Phase 6: Remove duplicate log_level_t declarations
|
||||
|
||||
19. Remove the duplicate `typedef enum { LOG_DEBUG... } log_level_t` and `void app_log(...)` forward declarations from `src/relay_client.c` and `src/admin_commands.c` — these files should just `#include "ginxsom.h"` instead
|
||||
|
||||
### Phase 7: Update nostr_crypto_init to nostr_init
|
||||
|
||||
20. Replace `nostr_crypto_init()` calls with `nostr_init()` in `main.c` and `request_validator.c` if v0.4.13 consolidates initialization under `nostr_init()`
|
||||
21. Add `nostr_cleanup()` call in shutdown path if not already present
|
||||
|
||||
### Phase 8: Update Makefile and build
|
||||
|
||||
22. Verify `Makefile` CFLAGS and LIBS are compatible with v0.4.13 (check if new link flags needed)
|
||||
23. Remove any `LOGGING_FLAGS` build-time defines that are no longer needed
|
||||
24. Run `make clean && make` and verify clean build
|
||||
|
||||
### Phase 9: Verify and test
|
||||
|
||||
25. Run the application and verify:
|
||||
- `logs/app/app.log` contains both app logs and `[nostr:websocket]` / `[nostr:nip013]` tagged lines
|
||||
- `logs/app/debug.log` is no longer created
|
||||
- `nostr_core_lib/debug.log` is no longer created
|
||||
- `logs/nginx/error.log` only contains pre-init errors and nginx's own messages
|
||||
26. Run existing test suite to verify no regressions
|
||||
|
||||
## Files Modified
|
||||
|
||||
| File | Changes |
|
||||
|------|---------|
|
||||
| `nostr_core_lib/` | Submodule updated to v0.4.13 |
|
||||
| `src/ginxsom.h` | Add LOG_TRACE, add g_log_level extern |
|
||||
| `src/main.c` | Enhanced app_log, nostr_log_cb, callback registration, fprintf migration |
|
||||
| `src/request_validator.c` | Remove validator_debug_log, replace with app_log |
|
||||
| `src/admin_auth.c` | Replace fprintf(stderr) with app_log |
|
||||
| `src/relay_client.c` | Remove duplicate log_level_t typedef |
|
||||
| `src/admin_commands.c` | Remove duplicate log_level_t typedef |
|
||||
| `Makefile` | Verify/update build flags |
|
||||
| `.gitignore` | Add/update debug.log exclusions |
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
- **Backward compatibility**: Enum names are used everywhere, not raw integers, so shifting values is safe
|
||||
- **Build breakage**: Phase 1 verifies build before any code changes
|
||||
- **Runtime breakage**: Callback registration is additive — if it fails, app still works, just without nostr_core_lib log routing
|
||||
- **stderr removal safety**: Only post-init calls are migrated; pre-init fatal errors stay on stderr for nginx capture
|
||||
@@ -88,43 +88,9 @@ static void admin_nip94_build_blob_url(const char* origin, const char* sha256,
|
||||
snprintf(out, out_size, "%s/%s%s", origin, sha256, extension);
|
||||
}
|
||||
|
||||
// Centralized MIME type to file extension mapping (from main.c)
|
||||
// Centralized MIME type to file extension mapping (delegates to shared BUD-08 mapping)
|
||||
static const char* admin_mime_to_extension(const char* mime_type) {
|
||||
if (!mime_type) {
|
||||
return ".bin";
|
||||
}
|
||||
|
||||
if (strstr(mime_type, "image/jpeg")) {
|
||||
return ".jpg";
|
||||
} else if (strstr(mime_type, "image/webp")) {
|
||||
return ".webp";
|
||||
} else if (strstr(mime_type, "image/png")) {
|
||||
return ".png";
|
||||
} else if (strstr(mime_type, "image/gif")) {
|
||||
return ".gif";
|
||||
} else if (strstr(mime_type, "video/mp4")) {
|
||||
return ".mp4";
|
||||
} else if (strstr(mime_type, "video/webm")) {
|
||||
return ".webm";
|
||||
} else if (strstr(mime_type, "audio/mpeg")) {
|
||||
return ".mp3";
|
||||
} else if (strstr(mime_type, "audio/ogg")) {
|
||||
return ".ogg";
|
||||
} else if (strstr(mime_type, "text/plain")) {
|
||||
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")) {
|
||||
return ".pdf";
|
||||
} else {
|
||||
return ".bin";
|
||||
}
|
||||
return mime_to_extension(mime_type);
|
||||
}
|
||||
|
||||
// Main API request handler
|
||||
|
||||
@@ -28,7 +28,7 @@ static int g_keys_loaded = 0; // Whether keys have been loaded
|
||||
static int ensure_keys_loaded(void) {
|
||||
if (!g_keys_loaded) {
|
||||
if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
|
||||
fprintf(stderr, "ERROR: Cannot load blossom private key for admin auth\n");
|
||||
app_log(LOG_ERROR, "ERROR: Cannot load blossom private key for admin auth\n");
|
||||
return -1;
|
||||
}
|
||||
g_keys_loaded = 1;
|
||||
@@ -83,13 +83,13 @@ int validate_admin_event(cJSON *event) {
|
||||
cJSON *sig = cJSON_GetObjectItem(event, "sig");
|
||||
|
||||
if (!cJSON_IsString(pubkey) || !cJSON_IsString(sig)) {
|
||||
fprintf(stderr, "AUTH: Invalid event format - missing pubkey or sig\n");
|
||||
app_log(LOG_ERROR, "AUTH: Invalid event format - missing pubkey or sig\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if pubkey matches configured admin pubkey
|
||||
if (!validate_admin_pubkey(pubkey->valuestring)) {
|
||||
fprintf(stderr, "AUTH: Pubkey %s is not authorized admin\n", pubkey->valuestring);
|
||||
app_log(LOG_ERROR, "AUTH: Pubkey %s is not authorized admin\n", pubkey->valuestring);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -114,14 +114,14 @@ int decrypt_admin_command(cJSON *event, char **decrypted_command_out) {
|
||||
// Get admin pubkey from event
|
||||
cJSON *admin_pubkey_json = cJSON_GetObjectItem(event, "pubkey");
|
||||
if (!cJSON_IsString(admin_pubkey_json)) {
|
||||
fprintf(stderr, "AUTH: Missing or invalid pubkey in event\n");
|
||||
app_log(LOG_ERROR, "AUTH: Missing or invalid pubkey in event\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get encrypted content
|
||||
cJSON *content = cJSON_GetObjectItem(event, "content");
|
||||
if (!cJSON_IsString(content)) {
|
||||
fprintf(stderr, "AUTH: Missing or invalid content in event\n");
|
||||
app_log(LOG_ERROR, "AUTH: Missing or invalid content in event\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -130,12 +130,12 @@ int decrypt_admin_command(cJSON *event, char **decrypted_command_out) {
|
||||
unsigned char admin_public_key[32];
|
||||
|
||||
if (nostr_hex_to_bytes(g_blossom_seckey, blossom_private_key, 32) != 0) {
|
||||
fprintf(stderr, "AUTH: Failed to parse blossom private key\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse blossom private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nostr_hex_to_bytes(admin_pubkey_json->valuestring, admin_public_key, 32) != 0) {
|
||||
fprintf(stderr, "AUTH: Failed to parse admin public key\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse admin public key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -152,14 +152,14 @@ int decrypt_admin_command(cJSON *event, char **decrypted_command_out) {
|
||||
);
|
||||
|
||||
if (result != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "AUTH: NIP-44 decryption failed with error code %d\n", result);
|
||||
app_log(LOG_ERROR, "AUTH: NIP-44 decryption failed with error code %d\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate and copy decrypted content
|
||||
*decrypted_command_out = malloc(strlen(decrypted_buffer) + 1);
|
||||
if (!*decrypted_command_out) {
|
||||
fprintf(stderr, "AUTH: Failed to allocate memory for decrypted content\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to allocate memory for decrypted content\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(*decrypted_command_out, decrypted_buffer);
|
||||
@@ -176,19 +176,19 @@ int parse_admin_command(const char *decrypted_content, char ***command_array_out
|
||||
// Parse the decrypted content as JSON array
|
||||
cJSON *content_json = cJSON_Parse(decrypted_content);
|
||||
if (!content_json) {
|
||||
fprintf(stderr, "AUTH: Failed to parse decrypted content as JSON\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse decrypted content as JSON\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!cJSON_IsArray(content_json)) {
|
||||
fprintf(stderr, "AUTH: Decrypted content is not a JSON array\n");
|
||||
app_log(LOG_ERROR, "AUTH: Decrypted content is not a JSON array\n");
|
||||
cJSON_Delete(content_json);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int array_size = cJSON_GetArraySize(content_json);
|
||||
if (array_size < 1) {
|
||||
fprintf(stderr, "AUTH: Command array is empty\n");
|
||||
app_log(LOG_ERROR, "AUTH: Command array is empty\n");
|
||||
cJSON_Delete(content_json);
|
||||
return -1;
|
||||
}
|
||||
@@ -196,7 +196,7 @@ int parse_admin_command(const char *decrypted_content, char ***command_array_out
|
||||
// Allocate command array
|
||||
char **command_array = malloc(array_size * sizeof(char *));
|
||||
if (!command_array) {
|
||||
fprintf(stderr, "AUTH: Failed to allocate command array\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to allocate command array\n");
|
||||
cJSON_Delete(content_json);
|
||||
return -1;
|
||||
}
|
||||
@@ -205,7 +205,7 @@ int parse_admin_command(const char *decrypted_content, char ***command_array_out
|
||||
for (int i = 0; i < array_size; i++) {
|
||||
cJSON *item = cJSON_GetArrayItem(content_json, i);
|
||||
if (!cJSON_IsString(item)) {
|
||||
fprintf(stderr, "AUTH: Command array element %d is not a string\n", i);
|
||||
app_log(LOG_ERROR, "AUTH: Command array element %d is not a string\n", i);
|
||||
// Clean up allocated strings
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(command_array[j]);
|
||||
@@ -217,7 +217,7 @@ int parse_admin_command(const char *decrypted_content, char ***command_array_out
|
||||
|
||||
command_array[i] = malloc(strlen(item->valuestring) + 1);
|
||||
if (!command_array[i]) {
|
||||
fprintf(stderr, "AUTH: Failed to allocate command string\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to allocate command string\n");
|
||||
// Clean up allocated strings
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(command_array[j]);
|
||||
@@ -228,7 +228,7 @@ int parse_admin_command(const char *decrypted_content, char ***command_array_out
|
||||
}
|
||||
strcpy(command_array[i], item->valuestring);
|
||||
if (!command_array[i]) {
|
||||
fprintf(stderr, "AUTH: Failed to duplicate command string\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to duplicate command string\n");
|
||||
// Clean up allocated strings
|
||||
for (int j = 0; j < i; j++) {
|
||||
free(command_array[j]);
|
||||
@@ -274,7 +274,7 @@ int process_admin_command(cJSON *event, char ***command_array_out, int *command_
|
||||
sqlite3_close(db);
|
||||
|
||||
if (strlen(blossom_pubkey) != 64) {
|
||||
fprintf(stderr, "ERROR: Cannot determine blossom pubkey for admin auth\n");
|
||||
app_log(LOG_ERROR, "ERROR: Cannot determine blossom pubkey for admin auth\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ int process_admin_command(cJSON *event, char ***command_array_out, int *command_
|
||||
|
||||
*admin_pubkey_out = malloc(strlen(admin_pubkey_json->valuestring) + 1);
|
||||
if (!*admin_pubkey_out) {
|
||||
fprintf(stderr, "AUTH: Failed to allocate admin pubkey string\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to allocate admin pubkey string\n");
|
||||
return -1;
|
||||
}
|
||||
strcpy(*admin_pubkey_out, admin_pubkey_json->valuestring);
|
||||
@@ -386,7 +386,7 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
sqlite3_close(db);
|
||||
|
||||
if (strlen(blossom_pubkey) != 64) {
|
||||
fprintf(stderr, "ERROR: Cannot determine blossom pubkey for response\n");
|
||||
app_log(LOG_ERROR, "ERROR: Cannot determine blossom pubkey for response\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -395,12 +395,12 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
unsigned char admin_public_key[32];
|
||||
|
||||
if (nostr_hex_to_bytes(g_blossom_seckey, blossom_private_key, 32) != 0) {
|
||||
fprintf(stderr, "AUTH: Failed to parse blossom private key\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse blossom private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nostr_hex_to_bytes(admin_pubkey, admin_public_key, 32) != 0) {
|
||||
fprintf(stderr, "AUTH: Failed to parse admin public key\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse admin public key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -415,14 +415,14 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
);
|
||||
|
||||
if (result != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "AUTH: NIP-44 encryption failed with error code %d\n", result);
|
||||
app_log(LOG_ERROR, "AUTH: NIP-44 encryption failed with error code %d\n", result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create Kind 23457 response event
|
||||
cJSON *response_event = cJSON_CreateObject();
|
||||
if (!response_event) {
|
||||
fprintf(stderr, "AUTH: Failed to create response event JSON\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to create response event JSON\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
// Convert private key hex to bytes
|
||||
unsigned char blossom_private_key_bytes[32];
|
||||
if (nostr_hex_to_bytes(g_blossom_seckey, blossom_private_key_bytes, 32) != 0) {
|
||||
fprintf(stderr, "AUTH: Failed to parse blossom private key for signing\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to parse blossom private key for signing\n");
|
||||
cJSON_Delete(response_event);
|
||||
return -1;
|
||||
}
|
||||
@@ -452,7 +452,7 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
// Create a temporary event structure for signing
|
||||
cJSON* temp_event = cJSON_Duplicate(response_event, 1);
|
||||
if (!temp_event) {
|
||||
fprintf(stderr, "AUTH: Failed to create temp event for signing\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to create temp event for signing\n");
|
||||
cJSON_Delete(response_event);
|
||||
return -1;
|
||||
}
|
||||
@@ -467,7 +467,7 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
);
|
||||
|
||||
if (!signed_event) {
|
||||
fprintf(stderr, "AUTH: Failed to sign admin response event\n");
|
||||
app_log(LOG_ERROR, "AUTH: Failed to sign admin response event\n");
|
||||
cJSON_Delete(response_event);
|
||||
cJSON_Delete(temp_event);
|
||||
return -1;
|
||||
@@ -481,7 +481,7 @@ int create_admin_response(const char *response_json, const char *admin_pubkey, c
|
||||
cJSON_AddStringToObject(response_event, "id", cJSON_GetStringValue(signed_id));
|
||||
cJSON_AddStringToObject(response_event, "sig", cJSON_GetStringValue(signed_sig));
|
||||
} else {
|
||||
fprintf(stderr, "AUTH: Signed event missing id or sig\n");
|
||||
app_log(LOG_ERROR, "AUTH: Signed event missing id or sig\n");
|
||||
cJSON_Delete(response_event);
|
||||
cJSON_Delete(signed_event);
|
||||
cJSON_Delete(temp_event);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
*/
|
||||
|
||||
#include "admin_commands.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include "app_log.h"
|
||||
#include "../nostr_core_lib/nostr_core/nip044.h"
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -11,16 +12,6 @@
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
// Forward declare app_log
|
||||
typedef enum {
|
||||
LOG_DEBUG = 0,
|
||||
LOG_INFO = 1,
|
||||
LOG_WARN = 2,
|
||||
LOG_ERROR = 3
|
||||
} log_level_t;
|
||||
|
||||
void app_log(log_level_t level, const char* format, ...);
|
||||
|
||||
// Global state
|
||||
static struct {
|
||||
int initialized;
|
||||
|
||||
24
src/app_log.h
Normal file
24
src/app_log.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef APP_LOG_H
|
||||
#define APP_LOG_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Centralized application logging (writes to logs/app/app.log)
|
||||
typedef enum {
|
||||
LOG_TRACE = 0,
|
||||
LOG_DEBUG = 1,
|
||||
LOG_INFO = 2,
|
||||
LOG_WARN = 3,
|
||||
LOG_ERROR = 4
|
||||
} log_level_t;
|
||||
|
||||
extern log_level_t g_log_level;
|
||||
void app_log(log_level_t level, const char* format, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // APP_LOG_H
|
||||
77
src/bud08.c
77
src/bud08.c
@@ -95,43 +95,60 @@ int nip94_get_origin(char* out, size_t out_size) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define GINXSOM_MIME_EXTENSION_ENTRIES(X) \
|
||||
X("image/jpeg", ".jpg") \
|
||||
X("image/webp", ".webp") \
|
||||
X("image/png", ".png") \
|
||||
X("image/gif", ".gif") \
|
||||
X("video/mp4", ".mp4") \
|
||||
X("video/webm", ".webm") \
|
||||
X("audio/mpeg", ".mp3") \
|
||||
X("audio/ogg", ".ogg") \
|
||||
X("text/plain", ".txt") \
|
||||
X("text/html", ".html") \
|
||||
X("text/css", ".css") \
|
||||
X("application/javascript", ".js") \
|
||||
X("text/javascript", ".mjs") \
|
||||
X("application/pdf", ".pdf")
|
||||
|
||||
typedef struct {
|
||||
const char* mime;
|
||||
const char* extension;
|
||||
} mime_extension_entry_t;
|
||||
|
||||
static const mime_extension_entry_t g_mime_extension_map[] = {
|
||||
#define MAKE_MIME_ENTRY(mime, ext) {mime, ext},
|
||||
GINXSOM_MIME_EXTENSION_ENTRIES(MAKE_MIME_ENTRY)
|
||||
#undef MAKE_MIME_ENTRY
|
||||
};
|
||||
|
||||
static const char* g_supported_mime_types[] = {
|
||||
#define MAKE_MIME_ONLY(mime, ext) mime,
|
||||
GINXSOM_MIME_EXTENSION_ENTRIES(MAKE_MIME_ONLY)
|
||||
#undef MAKE_MIME_ONLY
|
||||
};
|
||||
|
||||
const char* const* ginxsom_supported_mime_types(size_t* count) {
|
||||
if (count) {
|
||||
*count = sizeof(g_supported_mime_types) / sizeof(g_supported_mime_types[0]);
|
||||
}
|
||||
return g_supported_mime_types;
|
||||
}
|
||||
|
||||
// Centralized MIME type to file extension mapping
|
||||
const char* mime_to_extension(const char* mime_type) {
|
||||
if (!mime_type) {
|
||||
return ".bin";
|
||||
}
|
||||
|
||||
if (strstr(mime_type, "image/jpeg")) {
|
||||
return ".jpg";
|
||||
} else if (strstr(mime_type, "image/webp")) {
|
||||
return ".webp";
|
||||
} else if (strstr(mime_type, "image/png")) {
|
||||
return ".png";
|
||||
} else if (strstr(mime_type, "image/gif")) {
|
||||
return ".gif";
|
||||
} else if (strstr(mime_type, "video/mp4")) {
|
||||
return ".mp4";
|
||||
} else if (strstr(mime_type, "video/webm")) {
|
||||
return ".webm";
|
||||
} else if (strstr(mime_type, "audio/mpeg")) {
|
||||
return ".mp3";
|
||||
} else if (strstr(mime_type, "audio/ogg")) {
|
||||
return ".ogg";
|
||||
} else if (strstr(mime_type, "text/plain")) {
|
||||
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")) {
|
||||
return ".pdf";
|
||||
} else {
|
||||
return ".bin";
|
||||
size_t count = sizeof(g_mime_extension_map) / sizeof(g_mime_extension_map[0]);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (strstr(mime_type, g_mime_extension_map[i].mime)) {
|
||||
return g_mime_extension_map[i].extension;
|
||||
}
|
||||
}
|
||||
|
||||
return ".bin";
|
||||
}
|
||||
|
||||
// Build canonical blob URL from origin + sha256 + extension
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 1
|
||||
#define VERSION_PATCH 31
|
||||
#define VERSION "v0.1.31"
|
||||
#define VERSION_PATCH 32
|
||||
#define VERSION "v0.1.32"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -20,6 +20,8 @@
|
||||
#include <sqlite3.h>
|
||||
#include "../nostr_core_lib/cjson/cJSON.h"
|
||||
|
||||
#include "app_log.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -226,6 +228,7 @@ int nip94_get_origin(char* out, size_t out_size);
|
||||
|
||||
// MIME type and file extension handling
|
||||
const char* mime_to_extension(const char* mime_type);
|
||||
const char* const* ginxsom_supported_mime_types(size_t* count);
|
||||
void nip94_build_blob_url(const char* origin, const char* sha256, const char* mime_type, char* out, size_t out_size);
|
||||
|
||||
// Image dimension parsing
|
||||
@@ -250,15 +253,6 @@ void send_json_response(int status_code, const char* json_content);
|
||||
// Logging utilities
|
||||
void log_request(const char* method, const char* uri, const char* auth_status, int status_code);
|
||||
|
||||
// Centralized application logging (writes to logs/app/app.log)
|
||||
typedef enum {
|
||||
LOG_DEBUG = 0,
|
||||
LOG_INFO = 1,
|
||||
LOG_WARN = 2,
|
||||
LOG_ERROR = 3
|
||||
} log_level_t;
|
||||
|
||||
void app_log(log_level_t level, const char* format, ...);
|
||||
|
||||
// SHA-256 validation helper (used by multiple BUDs)
|
||||
int validate_sha256_format(const char* sha256);
|
||||
|
||||
401
src/main.c
401
src/main.c
@@ -8,6 +8,7 @@
|
||||
#include "relay_client.h"
|
||||
#include "admin_commands.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_common.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_log.h"
|
||||
#include "../nostr_core_lib/nostr_core/utils.h"
|
||||
#include <getopt.h>
|
||||
#include <curl/curl.h>
|
||||
@@ -34,8 +35,62 @@ static void shutdown_handler(int signum) {
|
||||
g_shutdown_requested = 1;
|
||||
}
|
||||
|
||||
// Centralized logging level (minimum severity to emit)
|
||||
log_level_t g_log_level = LOG_DEBUG;
|
||||
|
||||
static nostr_log_level_t app_log_level_to_nostr(log_level_t level) {
|
||||
switch (level) {
|
||||
case LOG_TRACE:
|
||||
return NOSTR_LOG_LEVEL_TRACE;
|
||||
case LOG_DEBUG:
|
||||
return NOSTR_LOG_LEVEL_DEBUG;
|
||||
case LOG_INFO:
|
||||
return NOSTR_LOG_LEVEL_INFO;
|
||||
case LOG_WARN:
|
||||
return NOSTR_LOG_LEVEL_WARN;
|
||||
case LOG_ERROR:
|
||||
default:
|
||||
return NOSTR_LOG_LEVEL_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static void nostr_log_cb(int level, const char *component, const char *message,
|
||||
void *user_data) {
|
||||
(void)user_data;
|
||||
const char *comp = (component && component[0]) ? component : "core";
|
||||
const char *msg = message ? message : "";
|
||||
|
||||
log_level_t app_level = LOG_INFO;
|
||||
switch (level) {
|
||||
case NOSTR_LOG_LEVEL_ERROR:
|
||||
app_level = LOG_ERROR;
|
||||
break;
|
||||
case NOSTR_LOG_LEVEL_WARN:
|
||||
app_level = LOG_WARN;
|
||||
break;
|
||||
case NOSTR_LOG_LEVEL_INFO:
|
||||
app_level = LOG_INFO;
|
||||
break;
|
||||
case NOSTR_LOG_LEVEL_DEBUG:
|
||||
app_level = LOG_DEBUG;
|
||||
break;
|
||||
case NOSTR_LOG_LEVEL_TRACE:
|
||||
app_level = LOG_TRACE;
|
||||
break;
|
||||
default:
|
||||
app_level = LOG_INFO;
|
||||
break;
|
||||
}
|
||||
|
||||
app_log(app_level, "[nostr:%s] %s", comp, msg);
|
||||
}
|
||||
|
||||
// Centralized logging system (declaration in ginxsom.h)
|
||||
void app_log(log_level_t level, const char *format, ...) {
|
||||
if (level < g_log_level) {
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *log_file = fopen("logs/app/app.log", "a");
|
||||
if (!log_file) {
|
||||
return; // Silently fail if we can't open log file
|
||||
@@ -50,11 +105,24 @@ void app_log(log_level_t level, const char *format, ...) {
|
||||
// Get log level string
|
||||
const char *level_str;
|
||||
switch (level) {
|
||||
case LOG_DEBUG: level_str = "DEBUG"; break;
|
||||
case LOG_INFO: level_str = "INFO"; break;
|
||||
case LOG_WARN: level_str = "WARN"; break;
|
||||
case LOG_ERROR: level_str = "ERROR"; break;
|
||||
default: level_str = "UNKNOWN"; break;
|
||||
case LOG_TRACE:
|
||||
level_str = "TRACE";
|
||||
break;
|
||||
case LOG_DEBUG:
|
||||
level_str = "DEBUG";
|
||||
break;
|
||||
case LOG_INFO:
|
||||
level_str = "INFO";
|
||||
break;
|
||||
case LOG_WARN:
|
||||
level_str = "WARN";
|
||||
break;
|
||||
case LOG_ERROR:
|
||||
level_str = "ERROR";
|
||||
break;
|
||||
default:
|
||||
level_str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
// Write log prefix with timestamp, PID, and level
|
||||
@@ -130,19 +198,19 @@ int initialize_database(const char *db_path) {
|
||||
rc = sqlite3_open_v2(db_path, &db,
|
||||
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "Cannot open database: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If database was just created, initialize schema
|
||||
if (!db_exists) {
|
||||
fprintf(stderr, "Database not found, initializing schema...\n");
|
||||
app_log(LOG_INFO, "Database not found, initializing schema...\n");
|
||||
|
||||
// Enable foreign key constraints
|
||||
rc = sqlite3_exec(db, "PRAGMA foreign_keys = ON;", NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to enable foreign keys: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to enable foreign keys: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -164,7 +232,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_blobs, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create blobs table: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create blobs table: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -182,7 +250,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_config, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create config table: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create config table: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -198,7 +266,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_system, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create system table: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create system table: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -218,7 +286,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_auth_rules, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create auth_rules table: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create auth_rules table: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -236,7 +304,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_indexes, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create indexes: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create indexes: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -259,7 +327,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, insert_config, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to insert default config: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to insert default config: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -279,7 +347,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_view, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create storage_stats view: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create storage_stats view: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -303,7 +371,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_overview_view, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create blob_overview view: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create blob_overview view: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -323,7 +391,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_type_view, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create blob_type_distribution view: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create blob_type_distribution view: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -340,7 +408,7 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_time_view, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create blob_time_stats view: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create blob_time_stats view: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
@@ -364,13 +432,13 @@ int initialize_database(const char *db_path) {
|
||||
|
||||
rc = sqlite3_exec(db, create_uploaders_view, NULL, NULL, &err_msg);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to create top_uploaders view: %s\n", err_msg);
|
||||
app_log(LOG_ERROR, "Failed to create top_uploaders view: %s\n", err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Database schema initialized successfully\n");
|
||||
app_log(LOG_INFO, "Database schema initialized successfully\n");
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
@@ -409,19 +477,19 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
|
||||
// Derive public key from private key hex string
|
||||
int derive_pubkey_from_privkey(const char *privkey_hex, char *pubkey_hex_out) {
|
||||
if (!privkey_hex || strlen(privkey_hex) != 64) {
|
||||
fprintf(stderr, "ERROR: Invalid private key format\n");
|
||||
app_log(LOG_ERROR, "ERROR: Invalid private key format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char seckey_bytes[32];
|
||||
if (nostr_hex_to_bytes(privkey_hex, seckey_bytes, 32) != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: Failed to parse private key hex\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to parse private key hex\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned char pubkey_bytes[32];
|
||||
if (nostr_ec_public_key_from_private_key(seckey_bytes, pubkey_bytes) != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: Failed to derive public key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to derive public key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -432,12 +500,12 @@ int derive_pubkey_from_privkey(const char *privkey_hex, char *pubkey_hex_out) {
|
||||
// Set database path based on pubkey (db/<pubkey>.db)
|
||||
int set_db_path_from_pubkey(const char *pubkey) {
|
||||
if (!pubkey || strlen(pubkey) != 64) {
|
||||
fprintf(stderr, "ERROR: Invalid pubkey for database naming\n");
|
||||
app_log(LOG_ERROR, "ERROR: Invalid pubkey for database naming\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(g_db_path, sizeof(g_db_path), "db/%s.db", pubkey);
|
||||
fprintf(stderr, "DATABASE: Set database path to %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Set database path to %s\n", g_db_path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -454,7 +522,7 @@ int validate_database_pubkey_match(const char *db_path, const char *expected_pub
|
||||
// Check if filename matches pattern: <pubkey>.db
|
||||
size_t filename_len = strlen(filename);
|
||||
if (filename_len != 67) { // 64 chars + ".db" = 67
|
||||
fprintf(stderr, "WARNING: Database filename doesn't match pubkey pattern: %s\n", filename);
|
||||
app_log(LOG_WARN, "WARNING: Database filename doesn't match pubkey pattern: %s\n", filename);
|
||||
return 0; // Don't enforce for non-standard names (backward compatibility)
|
||||
}
|
||||
|
||||
@@ -465,14 +533,14 @@ int validate_database_pubkey_match(const char *db_path, const char *expected_pub
|
||||
|
||||
// Compare with expected pubkey
|
||||
if (strcasecmp(filename_pubkey, expected_pubkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Database pubkey mismatch!\n");
|
||||
fprintf(stderr, " Database filename: %s\n", filename_pubkey);
|
||||
fprintf(stderr, " Expected pubkey: %s\n", expected_pubkey);
|
||||
fprintf(stderr, " → Database filename doesn't match the keys stored inside\n");
|
||||
app_log(LOG_ERROR, "ERROR: Database pubkey mismatch!\n");
|
||||
app_log(LOG_ERROR, " Database filename: %s\n", filename_pubkey);
|
||||
app_log(LOG_ERROR, " Expected pubkey: %s\n", expected_pubkey);
|
||||
app_log(LOG_ERROR, " → Database filename doesn't match the keys stored inside\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: Pubkey validation passed: %s\n", filename_pubkey);
|
||||
app_log(LOG_INFO, "DATABASE: Pubkey validation passed: %s\n", filename_pubkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -480,7 +548,7 @@ int validate_database_pubkey_match(const char *db_path, const char *expected_pub
|
||||
int generate_random_private_key_bytes(unsigned char *key_bytes, size_t len) {
|
||||
FILE *fp = fopen("/dev/urandom", "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "ERROR: Cannot open /dev/urandom for key generation\n");
|
||||
app_log(LOG_ERROR, "ERROR: Cannot open /dev/urandom for key generation\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -488,7 +556,7 @@ int generate_random_private_key_bytes(unsigned char *key_bytes, size_t len) {
|
||||
fclose(fp);
|
||||
|
||||
if (bytes_read != len) {
|
||||
fprintf(stderr, "ERROR: Failed to read %zu bytes from /dev/urandom\n", len);
|
||||
app_log(LOG_ERROR, "ERROR: Failed to read %zu bytes from /dev/urandom\n", len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -497,21 +565,21 @@ int generate_random_private_key_bytes(unsigned char *key_bytes, size_t len) {
|
||||
|
||||
// Generate server keypair and store in database
|
||||
int generate_server_keypair(void) {
|
||||
fprintf(stderr, "DEBUG: generate_server_keypair() called\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: generate_server_keypair() called\n");
|
||||
unsigned char seckey_bytes[32];
|
||||
char seckey_hex[65];
|
||||
char pubkey_hex[65];
|
||||
|
||||
// Generate random private key
|
||||
fprintf(stderr, "DEBUG: Generating random private key...\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: Generating random private key...\n");
|
||||
if (generate_random_private_key_bytes(seckey_bytes, 32) != 0) {
|
||||
fprintf(stderr, "DEBUG: Failed to generate random bytes\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: Failed to generate random bytes\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Validate the private key
|
||||
if (nostr_ec_private_key_verify(seckey_bytes) != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: Generated invalid private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Generated invalid private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -521,7 +589,7 @@ int generate_server_keypair(void) {
|
||||
// Derive public key
|
||||
unsigned char pubkey_bytes[32];
|
||||
if (nostr_ec_public_key_from_private_key(seckey_bytes, pubkey_bytes) != NOSTR_SUCCESS) {
|
||||
fprintf(stderr, "ERROR: Failed to derive public key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to derive public key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -538,19 +606,19 @@ int generate_server_keypair(void) {
|
||||
|
||||
// Set database path based on pubkey (MUST be done before storing keys)
|
||||
if (set_db_path_from_pubkey(pubkey_hex) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to set database path\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to set database path\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize database with new path
|
||||
if (initialize_database(g_db_path) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to initialize database at %s\n", g_db_path);
|
||||
app_log(LOG_ERROR, "ERROR: Failed to initialize database at %s\n", g_db_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Store private key securely
|
||||
if (store_blossom_private_key(seckey_hex) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to store blossom private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to store blossom private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -561,14 +629,14 @@ int generate_server_keypair(void) {
|
||||
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "ERROR: Can't open database for config: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "ERROR: Can't open database for config: %s\n", sqlite3_errmsg(db));
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *sql = "INSERT OR REPLACE INTO config (key, value, description) VALUES (?, ?, ?)";
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
@@ -582,52 +650,52 @@ int generate_server_keypair(void) {
|
||||
sqlite3_close(db);
|
||||
|
||||
if (rc != SQLITE_DONE) {
|
||||
fprintf(stderr, "ERROR: Failed to store blossom public key in config\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to store blossom public key in config\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Display keys for admin setup
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, "SERVER KEYPAIR GENERATED SUCCESSFULLY\n");
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, "Blossom Public Key: %s\n", pubkey_hex);
|
||||
fprintf(stderr, "Blossom Private Key: %s\n", seckey_hex);
|
||||
fprintf(stderr, "Database Path: %s\n", g_db_path);
|
||||
fprintf(stderr, "========================================\n");
|
||||
fprintf(stderr, "IMPORTANT: Save the private key securely!\n");
|
||||
fprintf(stderr, "This key is used for decrypting admin messages.\n");
|
||||
fprintf(stderr, "========================================\n");
|
||||
app_log(LOG_INFO, "========================================\n");
|
||||
app_log(LOG_INFO, "SERVER KEYPAIR GENERATED SUCCESSFULLY\n");
|
||||
app_log(LOG_INFO, "========================================\n");
|
||||
app_log(LOG_INFO, "Blossom Public Key: %s\n", pubkey_hex);
|
||||
app_log(LOG_INFO, "Blossom Private Key: %s\n", seckey_hex);
|
||||
app_log(LOG_INFO, "Database Path: %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "========================================\n");
|
||||
app_log(LOG_WARN, "IMPORTANT: Save the private key securely!\n");
|
||||
app_log(LOG_INFO, "This key is used for decrypting admin messages.\n");
|
||||
app_log(LOG_INFO, "========================================\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Load server keys from database
|
||||
int load_server_keys(void) {
|
||||
fprintf(stderr, "DEBUG: load_server_keys() called\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: load_server_keys() called\n");
|
||||
sqlite3 *db;
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
// Try to load blossom private key
|
||||
fprintf(stderr, "DEBUG: Trying to load blossom private key...\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: Trying to load blossom private key...\n");
|
||||
if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
|
||||
fprintf(stderr, "DEBUG: No blossom private key found in database\n");
|
||||
app_log(LOG_DEBUG, "DEBUG: No blossom private key found in database\n");
|
||||
return -1; // Keys must exist in database
|
||||
}
|
||||
|
||||
fprintf(stderr, "STARTUP: Blossom private key loaded successfully\n");
|
||||
app_log(LOG_INFO, "STARTUP: Blossom private key loaded successfully\n");
|
||||
|
||||
// Derive public key from private key
|
||||
if (derive_pubkey_from_privkey(g_blossom_seckey, g_blossom_pubkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to derive public key from private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to derive public key from private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "STARTUP: Derived blossom pubkey: %s\n", g_blossom_pubkey);
|
||||
app_log(LOG_INFO, "STARTUP: Derived blossom pubkey: %s\n", g_blossom_pubkey);
|
||||
|
||||
// Validate database filename matches pubkey
|
||||
if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Database pubkey validation failed\n");
|
||||
app_log(LOG_ERROR, "ERROR: Database pubkey validation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -644,7 +712,7 @@ int load_server_keys(void) {
|
||||
const char *pubkey = (const char *)sqlite3_column_text(stmt, 0);
|
||||
if (pubkey && strlen(pubkey) == 64) {
|
||||
strncpy(g_admin_pubkey, pubkey, sizeof(g_admin_pubkey) - 1);
|
||||
fprintf(stderr, "STARTUP: Admin pubkey loaded from config: %s\n", g_admin_pubkey);
|
||||
app_log(LOG_INFO, "STARTUP: Admin pubkey loaded from config: %s\n", g_admin_pubkey);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
@@ -679,14 +747,14 @@ int store_blossom_private_key(const char *seckey) {
|
||||
|
||||
// Validate key format
|
||||
if (!seckey || strlen(seckey) != 64) {
|
||||
fprintf(stderr, "ERROR: Invalid blossom private key format\n");
|
||||
app_log(LOG_ERROR, "ERROR: Invalid blossom private key format\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create blossom_seckey table if it doesn't exist
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "ERROR: Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "ERROR: Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -694,7 +762,7 @@ int store_blossom_private_key(const char *seckey) {
|
||||
const char *create_sql = "CREATE TABLE IF NOT EXISTS blossom_seckey (id INTEGER PRIMARY KEY CHECK (id = 1), seckey TEXT NOT NULL, created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')), CHECK (length(seckey) = 64))";
|
||||
rc = sqlite3_exec(db, create_sql, NULL, NULL, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "ERROR: Failed to create blossom_seckey table: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "ERROR: Failed to create blossom_seckey table: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
@@ -703,7 +771,7 @@ int store_blossom_private_key(const char *seckey) {
|
||||
const char *sql = "INSERT OR REPLACE INTO blossom_seckey (id, seckey) VALUES (1, ?)";
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "ERROR: SQL prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
@@ -714,7 +782,7 @@ int store_blossom_private_key(const char *seckey) {
|
||||
sqlite3_close(db);
|
||||
|
||||
if (rc != SQLITE_DONE) {
|
||||
fprintf(stderr, "ERROR: Failed to store blossom private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to store blossom private key\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -930,7 +998,7 @@ int insert_blob_metadata(const char *sha256, long size, const char *type,
|
||||
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -939,7 +1007,7 @@ int insert_blob_metadata(const char *sha256, long size, const char *type,
|
||||
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
@@ -985,7 +1053,7 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
||||
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_CREATE, NULL);
|
||||
if (rc) {
|
||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -994,7 +1062,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, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
app_log(LOG_ERROR, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
@@ -1039,7 +1107,7 @@ int file_exists_with_type(const char *sha256, const char *mime_type) {
|
||||
size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null
|
||||
|
||||
if (total_len > sizeof(filepath)) {
|
||||
fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
|
||||
app_log(LOG_WARN, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1192,7 +1260,7 @@ void log_request(const char *method, const char *uri, const char *auth_status,
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
// For now, log to stdout - later can be configured to log files
|
||||
fprintf(stderr, "LOG: [%s] %s %s - Auth: %s - Status: %d\r\n", timestamp,
|
||||
app_log(LOG_INFO, "LOG: [%s] %s %s - Auth: %s - Status: %d\r\n", timestamp,
|
||||
method ? method : "NULL", uri ? uri : "NULL",
|
||||
auth_status ? auth_status : "none", status_code);
|
||||
}
|
||||
@@ -1489,36 +1557,7 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
|
||||
}
|
||||
|
||||
// Determine file extension from MIME type and delete physical file
|
||||
const char *extension = "";
|
||||
if (strstr(blob_type_copy, "image/jpeg")) {
|
||||
extension = ".jpg";
|
||||
} else if (strstr(blob_type_copy, "image/webp")) {
|
||||
extension = ".webp";
|
||||
} else if (strstr(blob_type_copy, "image/png")) {
|
||||
extension = ".png";
|
||||
} else if (strstr(blob_type_copy, "image/gif")) {
|
||||
extension = ".gif";
|
||||
} else if (strstr(blob_type_copy, "video/mp4")) {
|
||||
extension = ".mp4";
|
||||
} else if (strstr(blob_type_copy, "video/webm")) {
|
||||
extension = ".webm";
|
||||
} else if (strstr(blob_type_copy, "audio/mpeg")) {
|
||||
extension = ".mp3";
|
||||
} else if (strstr(blob_type_copy, "audio/ogg")) {
|
||||
extension = ".ogg";
|
||||
} else if (strstr(blob_type_copy, "text/plain")) {
|
||||
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 {
|
||||
extension = ".bin";
|
||||
}
|
||||
const char *extension = mime_to_extension(blob_type_copy);
|
||||
|
||||
char filepath[MAX_PATH_LEN];
|
||||
// Construct path safely
|
||||
@@ -1528,7 +1567,7 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
|
||||
size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null
|
||||
|
||||
if (total_len > sizeof(filepath)) {
|
||||
fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
|
||||
app_log(LOG_WARN, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256, extension);
|
||||
// Continue anyway - unlink will fail gracefully
|
||||
} else {
|
||||
// Build path manually to avoid compiler warnings
|
||||
@@ -1664,7 +1703,7 @@ void handle_upload_request(void) {
|
||||
size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null
|
||||
|
||||
if (total_len > sizeof(filepath)) {
|
||||
fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
|
||||
app_log(LOG_WARN, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
|
||||
printf("Status: 500 Internal Server Error\r\n");
|
||||
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
@@ -1695,7 +1734,7 @@ void handle_upload_request(void) {
|
||||
// Set file permissions to 644 (owner read/write, group/others read) -
|
||||
// standard for web files
|
||||
if (chmod(filepath, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
|
||||
fprintf(stderr, "WARNING: Failed to set file permissions for %s\r\n",
|
||||
app_log(LOG_WARN, "WARNING: Failed to set file permissions for %s\r\n",
|
||||
filepath);
|
||||
// Continue anyway - this is not a fatal error
|
||||
} else {
|
||||
@@ -1873,7 +1912,7 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
|
||||
file_data = validation_result->file_data;
|
||||
file_size = validation_result->file_size;
|
||||
should_free_file_data = 0; // Validator owns the memory
|
||||
fprintf(stderr, "UPLOAD: Using file data from validator (%zu bytes)\n", file_size);
|
||||
app_log(LOG_DEBUG, "UPLOAD: Using file data from validator (%zu bytes)\n", file_size);
|
||||
} else {
|
||||
// Fallback: read from stdin (for non-Blossom uploads or when validation didn't provide file data)
|
||||
file_data = malloc(content_length);
|
||||
@@ -1897,7 +1936,7 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
|
||||
|
||||
file_size = bytes_read;
|
||||
should_free_file_data = 1; // We own the memory
|
||||
fprintf(stderr, "UPLOAD: Read file data from stdin (%zu bytes)\n", file_size);
|
||||
app_log(LOG_DEBUG, "UPLOAD: Read file data from stdin (%zu bytes)\n", file_size);
|
||||
}
|
||||
|
||||
// Calculate SHA-256 hash using nostr_core function
|
||||
@@ -1935,7 +1974,7 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
|
||||
size_t total_len = dir_len + 1 + sha_len + ext_len + 1; // +1 for /, +1 for null
|
||||
|
||||
if (total_len > sizeof(filepath)) {
|
||||
fprintf(stderr, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
|
||||
app_log(LOG_WARN, "WARNING: File path too long for buffer: %s/%s%s\n", g_storage_dir, sha256_hex, extension);
|
||||
printf("Status: 500 Internal Server Error\r\n");
|
||||
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
@@ -1966,7 +2005,7 @@ void handle_upload_request_with_validation(nostr_request_result_t* validation_re
|
||||
// Set file permissions to 644 (owner read/write, group/others read) -
|
||||
// standard for web files
|
||||
if (chmod(filepath, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) != 0) {
|
||||
fprintf(stderr, "WARNING: Failed to set file permissions for %s\r\n",
|
||||
app_log(LOG_WARN, "WARNING: Failed to set file permissions for %s\r\n",
|
||||
filepath);
|
||||
// Continue anyway - this is not a fatal error
|
||||
}
|
||||
@@ -2205,24 +2244,28 @@ int main(int argc, char *argv[]) {
|
||||
use_test_keys = 1;
|
||||
} else if (strcmp(argv[i], "--generate-keys") == 0) {
|
||||
g_generate_keys = 1;
|
||||
fprintf(stderr, "WARNING: --generate-keys is deprecated. Keys are generated automatically when needed.\n");
|
||||
app_log(LOG_WARN, "WARNING: --generate-keys is deprecated. Keys are generated automatically when needed.\n");
|
||||
} else {
|
||||
fprintf(stderr, "Unknown option: %s\n", argv[i]);
|
||||
fprintf(stderr, "Use --help for usage information\n");
|
||||
app_log(LOG_ERROR, "Unknown option: %s\n", argv[i]);
|
||||
app_log(LOG_ERROR, "Use --help for usage information\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
app_log(LOG_INFO, "Storage directory: %s", g_storage_dir);
|
||||
|
||||
// CRITICAL: Initialize nostr crypto system BEFORE key operations
|
||||
app_log(LOG_INFO, "Initializing nostr crypto system...");
|
||||
int crypto_init_result = nostr_crypto_init();
|
||||
if (crypto_init_result != 0) {
|
||||
app_log(LOG_ERROR, "Failed to initialize nostr crypto system (result: %d)", crypto_init_result);
|
||||
// Initialize nostr core system BEFORE key operations and relay activity
|
||||
app_log(LOG_INFO, "Initializing nostr core system...");
|
||||
int nostr_init_result = nostr_init();
|
||||
if (nostr_init_result != NOSTR_SUCCESS) {
|
||||
app_log(LOG_ERROR, "Failed to initialize nostr core system (result: %d)",
|
||||
nostr_init_result);
|
||||
return 1;
|
||||
}
|
||||
app_log(LOG_INFO, "Nostr crypto system initialized successfully");
|
||||
|
||||
nostr_set_log_callback(nostr_log_cb, NULL);
|
||||
nostr_set_log_level(app_log_level_to_nostr(g_log_level));
|
||||
app_log(LOG_INFO, "Nostr core system initialized successfully");
|
||||
|
||||
// ========================================================================
|
||||
// DATABASE AND KEY INITIALIZATION - 5 SCENARIOS
|
||||
@@ -2336,15 +2379,15 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// Derive pubkey from provided privkey
|
||||
if (derive_pubkey_from_privkey(test_server_privkey, g_blossom_pubkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Invalid server private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Invalid server private key\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "KEYS: Derived pubkey: %s\n", g_blossom_pubkey);
|
||||
app_log(LOG_INFO, "KEYS: Derived pubkey: %s\n", g_blossom_pubkey);
|
||||
|
||||
// Scenario 5: Both database path and keys specified
|
||||
if (db_path_specified) {
|
||||
fprintf(stderr, "\n=== SCENARIO 5: DATABASE PATH + KEYS ===\n");
|
||||
app_log(LOG_INFO, "\n=== SCENARIO 5: DATABASE PATH + KEYS ===\n");
|
||||
|
||||
// Check if specified path is a directory or file
|
||||
struct stat st;
|
||||
@@ -2361,33 +2404,33 @@ int main(int argc, char *argv[]) {
|
||||
if (is_directory) {
|
||||
// Build database path from directory + derived pubkey
|
||||
snprintf(g_db_path, sizeof(g_db_path), "%s/%s.db", specified_db_path, g_blossom_pubkey);
|
||||
fprintf(stderr, "DATABASE: Using directory path, derived database: %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Using directory path, derived database: %s\n", g_db_path);
|
||||
} else {
|
||||
// Use specified file path directly
|
||||
strncpy(g_db_path, specified_db_path, sizeof(g_db_path) - 1);
|
||||
g_db_path[sizeof(g_db_path) - 1] = '\0';
|
||||
fprintf(stderr, "DATABASE: Using file path: %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Using file path: %s\n", g_db_path);
|
||||
}
|
||||
|
||||
// Check if database exists
|
||||
if (stat(g_db_path, &st) == 0) {
|
||||
// Database exists - validate keys match
|
||||
fprintf(stderr, "DATABASE: Found existing database, validating keys...\n");
|
||||
app_log(LOG_INFO, "DATABASE: Found existing database, validating keys...\n");
|
||||
|
||||
// Load keys from database
|
||||
if (get_blossom_private_key(g_blossom_seckey, sizeof(g_blossom_seckey)) != 0) {
|
||||
fprintf(stderr, "ERROR: Invalid database: missing server keys\n");
|
||||
app_log(LOG_ERROR, "ERROR: Invalid database: missing server keys\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Compare with provided key
|
||||
if (strcmp(g_blossom_seckey, test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Server private key doesn't match database\n");
|
||||
fprintf(stderr, " Provided key and database keys are different\n");
|
||||
app_log(LOG_ERROR, "ERROR: Server private key doesn't match database\n");
|
||||
app_log(LOG_ERROR, " Provided key and database keys are different\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "VALIDATION: Keys match database - continuing\n");
|
||||
app_log(LOG_INFO, "VALIDATION: Keys match database - continuing\n");
|
||||
|
||||
// Validate pubkey matches filename
|
||||
if (validate_database_pubkey_match(g_db_path, g_blossom_pubkey) != 0) {
|
||||
@@ -2395,11 +2438,11 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
} else {
|
||||
// Database doesn't exist - create it with provided keys
|
||||
fprintf(stderr, "DATABASE: No existing database, creating new one...\n");
|
||||
app_log(LOG_INFO, "DATABASE: No existing database, creating new one...\n");
|
||||
|
||||
// Initialize new database
|
||||
if (initialize_database(g_db_path) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to initialize database\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to initialize database\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2408,7 +2451,7 @@ int main(int argc, char *argv[]) {
|
||||
g_blossom_seckey[64] = '\0';
|
||||
|
||||
if (store_blossom_private_key(test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to store private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to store private key\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2440,28 +2483,28 @@ int main(int argc, char *argv[]) {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: New database created successfully\n");
|
||||
app_log(LOG_INFO, "DATABASE: New database created successfully\n");
|
||||
}
|
||||
}
|
||||
// Scenario 3 continued: Create new database with provided keys
|
||||
else {
|
||||
// Set database path based on derived pubkey
|
||||
if (set_db_path_from_pubkey(g_blossom_pubkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to set database path\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to set database path\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check if database already exists
|
||||
struct stat st;
|
||||
if (stat(g_db_path, &st) == 0) {
|
||||
fprintf(stderr, "ERROR: Database already exists for this pubkey: %s\n", g_db_path);
|
||||
fprintf(stderr, " Use --db-path to open existing database or use different keys\n");
|
||||
app_log(LOG_ERROR, "ERROR: Database already exists for this pubkey: %s\n", g_db_path);
|
||||
app_log(LOG_ERROR, " Use --db-path to open existing database or use different keys\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Initialize new database
|
||||
if (initialize_database(g_db_path) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to initialize database\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to initialize database\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2470,7 +2513,7 @@ int main(int argc, char *argv[]) {
|
||||
g_blossom_seckey[64] = '\0';
|
||||
|
||||
if (store_blossom_private_key(test_server_privkey) != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to store private key\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to store private key\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2502,7 +2545,7 @@ int main(int argc, char *argv[]) {
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
fprintf(stderr, "KEYS: New database created successfully\n");
|
||||
app_log(LOG_INFO, "KEYS: New database created successfully\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2510,7 +2553,7 @@ int main(int argc, char *argv[]) {
|
||||
// Note: --db-path should specify a DIRECTORY, not a full file path
|
||||
// The actual database filename will be derived from the server's pubkey
|
||||
else if (db_path_specified) {
|
||||
fprintf(stderr, "\n=== SCENARIO 2: DATABASE DIRECTORY SPECIFIED ===\n");
|
||||
app_log(LOG_INFO, "\n=== SCENARIO 2: DATABASE DIRECTORY SPECIFIED ===\n");
|
||||
|
||||
// Check if specified path is a directory or file
|
||||
struct stat st;
|
||||
@@ -2526,8 +2569,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (is_directory) {
|
||||
// Treat as directory - will derive filename from pubkey after loading keys
|
||||
fprintf(stderr, "DATABASE: Directory specified: %s\n", specified_db_path);
|
||||
fprintf(stderr, "DATABASE: Will derive filename from server pubkey\n");
|
||||
app_log(LOG_INFO, "DATABASE: Directory specified: %s\n", specified_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Will derive filename from server pubkey\n");
|
||||
|
||||
// Look for any .db file that matches the pubkey pattern
|
||||
DIR *dir = opendir(specified_db_path);
|
||||
@@ -2542,7 +2585,7 @@ int main(int argc, char *argv[]) {
|
||||
// Found a potential database file
|
||||
snprintf(g_db_path, sizeof(g_db_path), "%s/%s", specified_db_path, entry->d_name);
|
||||
found_db = 1;
|
||||
fprintf(stderr, "DATABASE: Found existing database: %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Found existing database: %s\n", g_db_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2551,7 +2594,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (!found_db) {
|
||||
// No database found - this is OK, we'll create one if we have keys
|
||||
fprintf(stderr, "DATABASE: No existing database found in directory\n");
|
||||
app_log(LOG_INFO, "DATABASE: No existing database found in directory\n");
|
||||
// g_db_path will be set later based on pubkey
|
||||
}
|
||||
} else {
|
||||
@@ -2562,36 +2605,36 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
// If we found a database file, try to load it
|
||||
if (g_db_path[0] != '\0' && stat(g_db_path, &st) == 0) {
|
||||
fprintf(stderr, "DATABASE: Opening existing database: %s\n", g_db_path);
|
||||
app_log(LOG_INFO, "DATABASE: Opening existing database: %s\n", g_db_path);
|
||||
|
||||
// Load keys from database
|
||||
if (load_server_keys() != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to load keys from database\n");
|
||||
fprintf(stderr, " → Database may be corrupted or not a valid ginxsom database\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to load keys from database\n");
|
||||
app_log(LOG_ERROR, " → Database may be corrupted or not a valid ginxsom database\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "DATABASE: Keys loaded and validated successfully\n");
|
||||
app_log(LOG_INFO, "DATABASE: Keys loaded and validated successfully\n");
|
||||
} else {
|
||||
// No database file exists - we need keys to create one
|
||||
fprintf(stderr, "ERROR: No database found and no --server-privkey provided\n");
|
||||
fprintf(stderr, " → Use --server-privkey to create a new database\n");
|
||||
app_log(LOG_ERROR, "ERROR: No database found and no --server-privkey provided\n");
|
||||
app_log(LOG_ERROR, " → Use --server-privkey to create a new database\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Scenario 1: No Arguments (Fresh Start)
|
||||
else {
|
||||
fprintf(stderr, "\n=== SCENARIO 1: FRESH START (NO ARGUMENTS) ===\n");
|
||||
fprintf(stderr, "FRESH START: Generating new server keypair...\n");
|
||||
app_log(LOG_INFO, "\n=== SCENARIO 1: FRESH START (NO ARGUMENTS) ===\n");
|
||||
app_log(LOG_INFO, "FRESH START: Generating new server keypair...\n");
|
||||
|
||||
// Generate new keypair (this will set g_db_path based on pubkey)
|
||||
if (generate_server_keypair() != 0) {
|
||||
fprintf(stderr, "ERROR: Failed to generate server keypair\n");
|
||||
app_log(LOG_ERROR, "ERROR: Failed to generate server keypair\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "FRESH START: New instance created successfully\n");
|
||||
app_log(LOG_INFO, "FRESH START: New instance created successfully\n");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
@@ -2619,14 +2662,13 @@ int config_loaded = 0;
|
||||
|
||||
// Fall back to database configuration if file config failed
|
||||
if (!config_loaded /* && !initialize_server_config() */) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"STARTUP: No configuration found - server starting in setup mode\n");
|
||||
fprintf(stderr, "STARTUP: Run interactive setup with: ginxsom --setup\n");
|
||||
app_log(LOG_WARN,
|
||||
"STARTUP: No configuration found - server starting in setup mode");
|
||||
app_log(LOG_WARN, "STARTUP: Run interactive setup with: ginxsom --setup");
|
||||
// For interactive mode (when stdin is available), offer setup
|
||||
|
||||
} else if (!config_loaded) {
|
||||
fprintf(stderr, "STARTUP: Database configuration loaded successfully\n");
|
||||
app_log(LOG_INFO, "STARTUP: Database configuration loaded successfully");
|
||||
}
|
||||
|
||||
// Initialize request validator system
|
||||
@@ -2688,10 +2730,8 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
|
||||
int first_request = 1;
|
||||
while (FCGI_Accept() >= 0 && !g_shutdown_requested) {
|
||||
// Test stderr capture on first request
|
||||
if (first_request) {
|
||||
fprintf(stderr, "FCGI: First request received - testing nginx stderr capture\n");
|
||||
fflush(stderr);
|
||||
app_log(LOG_DEBUG, "FCGI: First request received");
|
||||
first_request = 0;
|
||||
}
|
||||
const char *request_method = getenv("REQUEST_METHOD");
|
||||
@@ -2752,16 +2792,12 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
printf(" \"limits\": {\n");
|
||||
printf(" \"max_upload_size\": 104857600,\n");
|
||||
printf(" \"supported_mime_types\": [\n");
|
||||
printf(" \"image/jpeg\",\n");
|
||||
printf(" \"image/png\",\n");
|
||||
printf(" \"image/webp\",\n");
|
||||
printf(" \"image/gif\",\n");
|
||||
printf(" \"video/mp4\",\n");
|
||||
printf(" \"video/webm\",\n");
|
||||
printf(" \"audio/mpeg\",\n");
|
||||
printf(" \"audio/ogg\",\n");
|
||||
printf(" \"text/plain\",\n");
|
||||
printf(" \"application/pdf\"\n");
|
||||
size_t supported_mime_count = 0;
|
||||
const char* const* supported_mimes = ginxsom_supported_mime_types(&supported_mime_count);
|
||||
for (size_t i = 0; i < supported_mime_count; i++) {
|
||||
printf(" \"%s\"%s\n", supported_mimes[i],
|
||||
(i + 1 < supported_mime_count) ? "," : "");
|
||||
}
|
||||
printf(" ]\n");
|
||||
printf(" },\n");
|
||||
printf(" \"authentication\": {\n");
|
||||
@@ -3047,16 +3083,12 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
printf(" \"limits\": {\n");
|
||||
printf(" \"max_upload_size\": 104857600,\n");
|
||||
printf(" \"supported_mime_types\": [\n");
|
||||
printf(" \"image/jpeg\",\n");
|
||||
printf(" \"image/png\",\n");
|
||||
printf(" \"image/webp\",\n");
|
||||
printf(" \"image/gif\",\n");
|
||||
printf(" \"video/mp4\",\n");
|
||||
printf(" \"video/webm\",\n");
|
||||
printf(" \"audio/mpeg\",\n");
|
||||
printf(" \"audio/ogg\",\n");
|
||||
printf(" \"text/plain\",\n");
|
||||
printf(" \"application/pdf\"\n");
|
||||
size_t supported_mime_count = 0;
|
||||
const char* const* supported_mimes = ginxsom_supported_mime_types(&supported_mime_count);
|
||||
for (size_t i = 0; i < supported_mime_count; i++) {
|
||||
printf(" \"%s\"%s\n", supported_mimes[i],
|
||||
(i + 1 < supported_mime_count) ? "," : "");
|
||||
}
|
||||
printf(" ]\n");
|
||||
printf(" },\n");
|
||||
printf(" \"authentication\": {\n");
|
||||
@@ -3110,8 +3142,11 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
}
|
||||
|
||||
// Graceful shutdown - stop relay client thread cleanly
|
||||
app_log(LOG_INFO, "Shutting down ginxsom (shutdown_requested=%d)...", (int)g_shutdown_requested);
|
||||
app_log(LOG_INFO, "Shutting down ginxsom (shutdown_requested=%d)...",
|
||||
(int)g_shutdown_requested);
|
||||
relay_client_stop();
|
||||
nostr_set_log_callback(NULL, NULL);
|
||||
nostr_cleanup();
|
||||
app_log(LOG_INFO, "Ginxsom shutdown complete");
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "relay_client.h"
|
||||
#include "admin_commands.h"
|
||||
#include "app_log.h"
|
||||
#include "../nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include <sqlite3.h>
|
||||
#include <stdio.h>
|
||||
@@ -15,16 +16,6 @@
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
// Forward declare app_log to avoid including ginxsom.h (which has typedef conflicts)
|
||||
typedef enum {
|
||||
LOG_DEBUG = 0,
|
||||
LOG_INFO = 1,
|
||||
LOG_WARN = 2,
|
||||
LOG_ERROR = 3
|
||||
} log_level_t;
|
||||
|
||||
void app_log(log_level_t level, const char* format, ...);
|
||||
|
||||
// Maximum number of relays to connect to
|
||||
#define MAX_RELAYS 10
|
||||
|
||||
|
||||
@@ -92,14 +92,25 @@ struct {
|
||||
} g_last_rule_violation = {0};
|
||||
|
||||
/**
|
||||
* Helper function for consistent debug logging to our debug.log file
|
||||
* Helper function for consistent validator debug logging via app logger
|
||||
*/
|
||||
static void validator_debug_log(const char *message) {
|
||||
FILE *debug_log = fopen("logs/app/debug.log", "a");
|
||||
if (debug_log) {
|
||||
fprintf(debug_log, "%ld %s", (long)time(NULL), message);
|
||||
fclose(debug_log);
|
||||
const char *msg = message ? message : "";
|
||||
size_t msg_len = strlen(msg);
|
||||
|
||||
if (msg_len > 0 && msg[msg_len - 1] == '\n') {
|
||||
char trimmed[1024];
|
||||
size_t copy_len = msg_len - 1;
|
||||
if (copy_len >= sizeof(trimmed)) {
|
||||
copy_len = sizeof(trimmed) - 1;
|
||||
}
|
||||
memcpy(trimmed, msg, copy_len);
|
||||
trimmed[copy_len] = '\0';
|
||||
app_log(LOG_DEBUG, "%s", trimmed);
|
||||
return;
|
||||
}
|
||||
|
||||
app_log(LOG_DEBUG, "%s", msg);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@@ -143,9 +154,9 @@ int ginxsom_request_validator_init(const char *db_path, const char *app_name) {
|
||||
}
|
||||
|
||||
// Initialize nostr_core_lib if not already done
|
||||
if (nostr_crypto_init() != NOSTR_SUCCESS) {
|
||||
if (nostr_init() != NOSTR_SUCCESS) {
|
||||
validator_debug_log(
|
||||
"VALIDATOR: Failed to initialize nostr crypto system\n");
|
||||
"VALIDATOR: Failed to initialize nostr core system\n");
|
||||
return NOSTR_ERROR_CRYPTO_INIT;
|
||||
}
|
||||
|
||||
@@ -1142,15 +1153,15 @@ static int reload_auth_config(void) {
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
fprintf(stderr,
|
||||
app_log(LOG_DEBUG,
|
||||
"VALIDATOR: Configuration loaded from unified config table - "
|
||||
"auth_required: %d, max_file_size: %ld, nip42_mode: %d, "
|
||||
"cache_timeout: %d\n",
|
||||
"cache_timeout: %d",
|
||||
g_auth_cache.auth_required, g_auth_cache.max_file_size,
|
||||
g_auth_cache.nip42_mode, cache_timeout);
|
||||
fprintf(stderr,
|
||||
app_log(LOG_DEBUG,
|
||||
"VALIDATOR: NIP-42 mode details - nip42_mode=%d (0=disabled, "
|
||||
"1=optional/enabled, 2=required)\n",
|
||||
"1=optional/enabled, 2=required)",
|
||||
g_auth_cache.nip42_mode);
|
||||
|
||||
return NOSTR_SUCCESS;
|
||||
|
||||
5
tests/local_webtype_test/test_html.html
Normal file
5
tests/local_webtype_test/test_html.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>test_html</title>
|
||||
/* payload type: text/html */
|
||||
console.log("test_html");
|
||||
57
tests/upload_browser_sample_html.sh
Normal file
57
tests/upload_browser_sample_html.sh
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
BASE_URL="https://blossom.laantungir.net"
|
||||
PRIVKEY="22cc83aa57928a2800234c939240c9a6f0f44a33ea3838a860ed38930b195afd"
|
||||
FILE="/tmp/ginxsom_browser_sample.html"
|
||||
|
||||
cat > "$FILE" <<'EOF'
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Ginxsom Browser Sample</title>
|
||||
<style>
|
||||
body { font-family: system-ui, sans-serif; margin: 2rem; background: #0b1020; color: #e6e9f2; }
|
||||
.card { max-width: 720px; padding: 1.25rem 1.5rem; border: 1px solid #2a3350; border-radius: 12px; background: #121a33; }
|
||||
code { background: #1b2545; padding: 0.1rem 0.35rem; border-radius: 6px; }
|
||||
.ok { color: #69f0ae; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>Ginxsom HTML test</h1>
|
||||
<p class="ok">If you can see this page, remote HTML upload and retrieval are working.</p>
|
||||
<p>Timestamp: <code id="ts"></code></p>
|
||||
</div>
|
||||
<script>
|
||||
document.getElementById("ts").textContent = new Date().toISOString();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
SHA=$(sha256sum "$FILE" | cut -d' ' -f1)
|
||||
EXP=$(date -d '+1 hour' +%s)
|
||||
EVENT=$(nak event -k 24242 -c "" --sec "$PRIVKEY" -t "t=upload" -t "x=$SHA" -t "expiration=$EXP")
|
||||
AUTH="Nostr $(echo -n "$EVENT" | base64 -w 0)"
|
||||
RESP=$(mktemp)
|
||||
|
||||
STATUS=$(curl -s -w "%{http_code}" -o "$RESP" \
|
||||
-X PUT \
|
||||
-H "Authorization: $AUTH" \
|
||||
-H "Content-Type: text/html" \
|
||||
--data-binary "@$FILE" \
|
||||
"$BASE_URL/upload")
|
||||
|
||||
URL=$(jq -r '.url // empty' "$RESP")
|
||||
GET_CODE=0
|
||||
if [ -n "$URL" ]; then
|
||||
GET_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$URL")
|
||||
fi
|
||||
|
||||
echo "UPLOAD_STATUS=$STATUS"
|
||||
echo "SHA256=$SHA"
|
||||
echo "URL=$URL"
|
||||
echo "GET_CODE=$GET_CODE"
|
||||
Reference in New Issue
Block a user