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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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.
@@ -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
|
||||
+2
-36
@@ -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
|
||||
|
||||
+27
-27
@@ -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);
|
||||
|
||||
+2
-11
@@ -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;
|
||||
|
||||
@@ -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
|
||||
+48
-31
@@ -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
|
||||
|
||||
+5
-11
@@ -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);
|
||||
|
||||
+225
-190
@@ -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,41 +35,108 @@ 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
|
||||
}
|
||||
|
||||
|
||||
// Get timestamp
|
||||
time_t now = time(NULL);
|
||||
struct tm *tm_info = localtime(&now);
|
||||
char timestamp[64];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
|
||||
// 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
|
||||
fprintf(log_file, "[%s] [PID:%d] [%s] ", timestamp, getpid(), level_str);
|
||||
|
||||
|
||||
// Write formatted message
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(log_file, format, args);
|
||||
va_end(args);
|
||||
|
||||
|
||||
// Ensure newline
|
||||
fprintf(log_file, "\n");
|
||||
|
||||
|
||||
fclose(log_file);
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
// For interactive mode (when stdin is available), offer setup
|
||||
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;
|
||||
|
||||
+1
-10
@@ -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
|
||||
|
||||
|
||||
+22
-11
@@ -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;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>test_html</title>
|
||||
/* payload type: text/html */
|
||||
console.log("test_html");
|
||||
@@ -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