Finished BUD 1
This commit is contained in:
39
.clinerules/workspace_rules.md
Normal file
39
.clinerules/workspace_rules.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Workspace Rules for Ginxsom Development
|
||||
|
||||
## Important Reminders for Future Assistants
|
||||
|
||||
### SQLite Configuration
|
||||
- **SQLite is compiled locally** in this project, not using system package
|
||||
- Location: `sqlite3-build/` directory contains local SQLite 3.37.2 build
|
||||
- The system SQLite package had version conflicts, so we built from source
|
||||
- Database file: `db/ginxsom.db` (local to project)
|
||||
- Always use local SQLite binary: `./sqlite3-build/sqlite3`
|
||||
|
||||
### Development Setup
|
||||
- **All development is LOCAL** - work within the project directory
|
||||
- Local nginx runs on port 9001 (not system nginx on port 80)
|
||||
- Configuration: `config/local-nginx.conf` (NOT system nginx configs)
|
||||
- FastCGI socket: `/tmp/ginxsom-fcgi.sock`
|
||||
- Never modify system nginx files in `/etc/nginx/` during development
|
||||
|
||||
### File Structure
|
||||
- Blob files stored in: `blobs/` directory
|
||||
- Database: `db/ginxsom.db`
|
||||
- Built binary: `build/ginxsom-fcgi`
|
||||
- Logs: `logs/` directory (nginx access/error logs)
|
||||
|
||||
### Testing
|
||||
- Test files are already in `blobs/`:
|
||||
- `708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp`
|
||||
- `f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.jpg`
|
||||
- Test URLs: `http://localhost:9001/<sha256>` or `http://localhost:9001/<sha256>.<ext>`
|
||||
|
||||
### Build Process
|
||||
- Compile with: `make`
|
||||
- Start FastCGI: `./scripts/start-fcgi.sh`
|
||||
- Start nginx: `nginx -p . -c config/local-nginx.conf`
|
||||
|
||||
### Common Issues
|
||||
- If nginx fails to start, check for port conflicts or kill existing processes
|
||||
- If FastCGI fails, check socket permissions and that binary exists
|
||||
- Always use local paths, never assume system installations
|
||||
368
FASTCGI.md
Normal file
368
FASTCGI.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# FastCGI Protocol Flow Documentation
|
||||
|
||||
This document provides ASCII flow charts to understand how FastCGI works with nginx for the ginxsom blossom server.
|
||||
|
||||
## FastCGI Overview
|
||||
|
||||
FastCGI is a binary protocol that allows web servers (like nginx) to communicate with application servers efficiently. Unlike CGI which spawns a new process per request, FastCGI applications are persistent and can handle multiple concurrent requests.
|
||||
|
||||
## 1. FastCGI Connection Setup Flow
|
||||
|
||||
```
|
||||
┌─────────────────┐ Socket ┌─────────────────┐
|
||||
│ nginx │◄──────────────►│ FastCGI App │
|
||||
│ Web Server │ Connection │ (ginxsom) │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
│ │
|
||||
│ 1. Create Unix socket │
|
||||
│ /tmp/ginxsom.sock │
|
||||
│ │
|
||||
│ 2. FastCGI app binds & listens │
|
||||
│◄───────────────────────────────────│
|
||||
│ │
|
||||
│ 3. nginx connects when needed │
|
||||
│───────────────────────────────────►│
|
||||
│ │
|
||||
│ 4. Connection established │
|
||||
│◄──────────────────────────────────►│
|
||||
```
|
||||
|
||||
## 2. HTTP Request Processing Flow
|
||||
|
||||
```
|
||||
┌─────────┐ HTTP ┌─────────┐ FastCGI ┌─────────┐
|
||||
│ Client │────────────►│ nginx │─────────────►│ ginxsom │
|
||||
│Browser │ │ │ │ FastCGI │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │
|
||||
│ HTTP Request │ │
|
||||
│ PUT /upload │ │
|
||||
│────────────────────────► │
|
||||
│ │ │
|
||||
│ │ FCGI_BEGIN_REQUEST │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ FCGI_PARAMS │
|
||||
│ │ (HTTP headers, etc) │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ FCGI_STDIN │
|
||||
│ │ (Request body) │
|
||||
│ │───────────────────────►│
|
||||
│ │ │
|
||||
│ │ Process │
|
||||
│ │◄───────Request─────────│
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ (Response headers) │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ (Response body) │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ │ FCGI_END_REQUEST │
|
||||
│ │◄───────────────────────│
|
||||
│ │ │
|
||||
│ HTTP Response │ │
|
||||
│◄──────────────────────│ │
|
||||
```
|
||||
|
||||
## 3. FastCGI Record Structure
|
||||
|
||||
```
|
||||
FastCGI Record Format:
|
||||
┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Version │ Type │RequestID│RequestID│ Length │ Length │ Padding │Reserved │
|
||||
│ (1) │ (1) │ Hi │ Lo │ Hi │ Lo │ Length │ (1) │
|
||||
│ │ │ (1) │ (1) │ (1) │ (1) │ (1) │ │
|
||||
└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
│◄────────────────── 8 bytes header ──────────────────────►│
|
||||
│ │
|
||||
│◄─────────────────── Content (Length bytes) ─────────────►│
|
||||
│ │
|
||||
│◄───── Padding (Padding Length bytes) ────►│
|
||||
|
||||
Record Types:
|
||||
- FCGI_BEGIN_REQUEST (1) - Start new request
|
||||
- FCGI_PARAMS (4) - Environment variables
|
||||
- FCGI_STDIN (5) - Request body data
|
||||
- FCGI_STDOUT (6) - Response data
|
||||
- FCGI_END_REQUEST (3) - End of request
|
||||
```
|
||||
|
||||
## 4. Ginxsom Endpoint Handling Flow
|
||||
|
||||
### 4a. Static File Request (Direct nginx)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │
|
||||
└─────────┘ └─────────┘
|
||||
│ │
|
||||
│ GET /{sha256hash} │
|
||||
│───────────────────────►│
|
||||
│ │
|
||||
│ │ Check file exists:
|
||||
│ │ /var/lib/ginxsom/files/
|
||||
│ │ {first2}/{remaining}
|
||||
│ │
|
||||
│ │ ┌─ File exists? ─┐
|
||||
│ │ │ YES │
|
||||
│ │ └────────────────┘
|
||||
│ │ │
|
||||
│ HTTP 200 + File │ │ Serve directly
|
||||
│◄──────────────────────│◄──────┘
|
||||
│ │
|
||||
|
||||
Alternative flow:
|
||||
│ │ ┌─ File exists? ─┐
|
||||
│ │ │ NO │
|
||||
│ │ └────────────────┘
|
||||
│ │ │
|
||||
│ HTTP 404 Not Found │ │
|
||||
│◄──────────────────────│◄──────┘
|
||||
```
|
||||
|
||||
### 4b. Upload Request (FastCGI)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │ │ ginxsom │ │ File │
|
||||
│ │ │ │ │ FastCGI │ │ System │
|
||||
└─────────┘ └─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │ │
|
||||
│ PUT /upload │ │ │
|
||||
│ Auth: xyz │ │ │
|
||||
│ Body: file │ │ │
|
||||
│─────────────►│ │ │
|
||||
│ │ │ │
|
||||
│ │ FCGI_PARAMS │ │
|
||||
│ │ METHOD=PUT │ │
|
||||
│ │ URI=/upload │ │
|
||||
│ │ AUTH=xyz │ │
|
||||
│ │─────────────►│ │
|
||||
│ │ │ │
|
||||
│ │ FCGI_STDIN │ │
|
||||
│ │ (file data) │ │
|
||||
│ │─────────────►│ │
|
||||
│ │ │ │
|
||||
│ │ │ Verify Auth │
|
||||
│ │ │ (nostr sig) │
|
||||
│ │ │ │
|
||||
│ │ │ Calculate │
|
||||
│ │ │ SHA-256 │
|
||||
│ │ │ │
|
||||
│ │ │ Write file │
|
||||
│ │ │─────────────►│
|
||||
│ │ │ │
|
||||
│ │ │ Store │
|
||||
│ │ │ metadata │
|
||||
│ │ │────────────────────►
|
||||
│ │ │ │ DB
|
||||
│ │ │ │
|
||||
│ │ FCGI_STDOUT │ │
|
||||
│ │ 200 OK │ │
|
||||
│ │ {sha256} │ │
|
||||
│ │◄─────────────│ │
|
||||
│ │ │ │
|
||||
│ HTTP 200 OK │ │ │
|
||||
│ {sha256} │ │ │
|
||||
│◄─────────────│ │ │
|
||||
```
|
||||
|
||||
### 4c. HEAD Request for Metadata (FastCGI)
|
||||
|
||||
```
|
||||
┌─────────┐ ┌─────────┐ ┌─────────┐
|
||||
│ Client │ │ nginx │ │ ginxsom │
|
||||
│ │ │ │ │ FastCGI │
|
||||
└─────────┘ └─────────┘ └─────────┘
|
||||
│ │ │
|
||||
│ HEAD /{sha256} │ │
|
||||
│──────────────────►│ │
|
||||
│ │ │
|
||||
│ │ FCGI_PARAMS │
|
||||
│ │ METHOD=HEAD │
|
||||
│ │ BLOSSOM_HASH=... │
|
||||
│ │──────────────────►│
|
||||
│ │ │
|
||||
│ │ │ Query DB for
|
||||
│ │ │ file metadata
|
||||
│ │ │
|
||||
│ │ FCGI_STDOUT │
|
||||
│ │ Content-Length: X │
|
||||
│ │ Content-Type: Y │
|
||||
│ │ x-sha256: hash │
|
||||
│ │◄──────────────────│
|
||||
│ │ │
|
||||
│ HTTP Headers Only │ │
|
||||
│◄──────────────────│ │
|
||||
```
|
||||
|
||||
## 5. Development vs Production Deployment
|
||||
|
||||
### Development Mode
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ Terminal │ │ nginx │
|
||||
│ │ │ │
|
||||
│ ./ginxsom │◄──────────────►│ Config: │
|
||||
│ --fastcgi │ Unix Socket │ fastcgi_pass│
|
||||
│ --socket │ /tmp/ginxsom │ unix:/tmp/ │
|
||||
│ /tmp/... │ .sock │ ginxsom.sock│
|
||||
└─────────────┘ └─────────────┘
|
||||
│ │
|
||||
│ Direct execution │ sudo systemctl
|
||||
│ Easy debugging │ reload nginx
|
||||
│ Ctrl+C to stop │ (config changes)
|
||||
│ Real-time logs │
|
||||
```
|
||||
|
||||
### Production Mode
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐
|
||||
│ systemd │ │ nginx │
|
||||
│ │ │ │
|
||||
│ ginxsom │◄──────────────►│ Config: │
|
||||
│ .service │ Unix Socket │ fastcgi_pass│
|
||||
│ │ /run/ginxsom/ │ unix:/run/ │
|
||||
│ Auto-start │ ginxsom.sock │ ginxsom/... │
|
||||
│ Auto-restart│ │ │
|
||||
│ Log to file │ │ │
|
||||
└─────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
## 6. Complete nginx + FastCGI Request Flow
|
||||
|
||||
```
|
||||
┌───────┐ ┌─────────────────────────────────────────────────┐ ┌─────────┐
|
||||
│Client │ │ nginx │ │ginxsom │
|
||||
└───────┘ └─────────────────────────────────────────────────┘ │FastCGI │
|
||||
│ │ └─────────┘
|
||||
│ GET /abc123...def │ │
|
||||
│───────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ location ~ ^/([a-f0-9]{64})$ { │
|
||||
│ │ # Check if static file exists │
|
||||
│ │ try_files /$prefix/$suffix =404 │
|
||||
│ │ } │
|
||||
│ │ │
|
||||
│ │ ┌─ File exists? ─┐ │
|
||||
│ │ │ YES │ │
|
||||
│ │ └────────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ │ Serve directly (fast!) │
|
||||
│ HTTP 200 + File │ │ │
|
||||
│◄──────────────────────│◄──────┘ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ PUT /upload │ │
|
||||
│ Authorization: xyz │ │
|
||||
│───────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ location /upload { │
|
||||
│ │ fastcgi_pass unix:/tmp/ │
|
||||
│ │ ginxsom.sock; │
|
||||
│ │ } │
|
||||
│ │ │
|
||||
│ │ ┌─ FastCGI Protocol ─┐ │
|
||||
│ │ │ FCGI_BEGIN_REQUEST │─────────────►│
|
||||
│ │ │ FCGI_PARAMS │─────────────►│
|
||||
│ │ │ FCGI_STDIN │─────────────►│
|
||||
│ │ └─────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ │ Verify
|
||||
│ │ │ nostr
|
||||
│ │ │ signature
|
||||
│ │ │
|
||||
│ │ │ Calculate
|
||||
│ │ │ SHA-256
|
||||
│ │ │
|
||||
│ │ │ Store file
|
||||
│ │ │ & metadata
|
||||
│ │ │
|
||||
│ │ ┌─ FastCGI Response ─┐ │
|
||||
│ │ │ FCGI_STDOUT │◄─────────────│
|
||||
│ │ │ FCGI_END_REQUEST │◄─────────────│
|
||||
│ │ └─────────────────────┘ │
|
||||
│ │ │
|
||||
│ HTTP 200 OK │ │
|
||||
│ {"sha256": "..."} │ │
|
||||
│◄──────────────────────│ │
|
||||
```
|
||||
|
||||
## 7. libfcgi Library Usage
|
||||
|
||||
### Basic FastCGI Application Structure
|
||||
|
||||
```c
|
||||
#include <fcgiapp.h>
|
||||
|
||||
int main() {
|
||||
FCGX_Request request;
|
||||
|
||||
// Initialize FastCGI library
|
||||
FCGX_Init();
|
||||
FCGX_InitRequest(&request, 0, 0);
|
||||
|
||||
// Main request processing loop
|
||||
while (FCGX_Accept_r(&request) == 0) {
|
||||
|
||||
// Read environment variables (HTTP headers, etc)
|
||||
char* method = FCGX_GetParam("REQUEST_METHOD", request.envp);
|
||||
char* uri = FCGX_GetParam("REQUEST_URI", request.envp);
|
||||
char* auth = FCGX_GetParam("HTTP_AUTHORIZATION", request.envp);
|
||||
|
||||
// Route requests
|
||||
if (strcmp(uri, "/health") == 0) {
|
||||
handle_health(&request);
|
||||
} else if (strcmp(uri, "/upload") == 0) {
|
||||
handle_upload(&request);
|
||||
} else if (strncmp(uri, "/head/", 6) == 0) {
|
||||
handle_head(&request);
|
||||
}
|
||||
|
||||
// Finish this request
|
||||
FCGX_Finish_r(&request);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Reading Request Data
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ nginx sends: │ │ ginxsom reads: │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGI_PARAMS │────────►│ FCGX_GetParam() │
|
||||
│ REQUEST_METHOD │ │ "PUT" │
|
||||
│ REQUEST_URI │ │ "/upload" │
|
||||
│ CONTENT_LENGTH │ │ "1024" │
|
||||
│ HTTP_* │ │ headers │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGI_STDIN │────────►│ FCGX_GetStr() │
|
||||
│ (request body) │ │ file data │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
### Writing Response Data
|
||||
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ ginxsom writes: │ │ nginx sends: │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGX_PutS() │────────►│ HTTP Response │
|
||||
│ "Content-Type: │ │ Headers │
|
||||
│ application/ │ │ │
|
||||
│ json\r\n\r\n" │ │ │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ FCGX_PutS() │────────►│ HTTP Response │
|
||||
│ '{"status": │ │ Body │
|
||||
│ "ok"}' │ │ │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
This documentation shows how FastCGI enables nginx to efficiently serve static blossom files directly while delegating authenticated operations to the ginxsom FastCGI application.
|
||||
@@ -14,39 +14,42 @@ This document outlines the implementation plan for ginxsom, a FastCGI-based Blos
|
||||
## Phase 1: Basic File Serving & Retrieval (BUD-01)
|
||||
|
||||
### 1.1 Infrastructure Setup
|
||||
- [ ] Create basic directory structure
|
||||
- [ ] Create `blobs/` directory for file storage
|
||||
- [ ] Create `db/` directory for SQLite database
|
||||
- [ ] Create `logs/` directory for application logs
|
||||
- [ ] Set up proper permissions (nginx readable, app writable)
|
||||
- [x] Create basic directory structure
|
||||
- [x] Create `blobs/` directory for file storage
|
||||
- [x] Create `db/` directory for SQLite database
|
||||
- [x] Create `logs/` directory for application logs
|
||||
- [x] Set up proper permissions (nginx readable, app writable)
|
||||
|
||||
### 1.2 Database Schema
|
||||
- [ ] Design SQLite schema for blob metadata
|
||||
- [ ] `blobs` table: sha256, size, type, uploaded_at, uploader_pubkey
|
||||
- [ ] `server_config` table: key-value pairs for server settings
|
||||
- [ ] Create database initialization script
|
||||
- [ ] Add proper indexes on sha256 hash
|
||||
- [x] Design SQLite schema for blob metadata
|
||||
- [x] `blobs` table: sha256, size, type, uploaded_at, uploader_pubkey, filename
|
||||
- [x] `server_config` table: key-value pairs for server settings
|
||||
- [x] Create database initialization script
|
||||
- [x] Add proper indexes on sha256 hash
|
||||
|
||||
### 1.3 nginx Configuration
|
||||
- [ ] Configure nginx for static file serving
|
||||
- [ ] Set up location block for `GET /<sha256>` pattern
|
||||
- [ ] Configure proper MIME type detection
|
||||
- [ ] Add proper headers (Cache-Control, ETag, etc.)
|
||||
- [ ] Handle 404s gracefully when blob doesn't exist
|
||||
- [ ] Configure FastCGI pass-through for non-GET requests
|
||||
- [x] Configure nginx for static file serving
|
||||
- [x] Set up location block for `GET /<sha256>` pattern with extension support
|
||||
- [x] Configure try_files directive for multiple extension fallbacks
|
||||
- [x] Configure proper MIME type detection
|
||||
- [x] Add proper headers (Cache-Control, ETag, etc.)
|
||||
- [x] Handle 404s gracefully when blob doesn't exist
|
||||
- [x] Configure FastCGI pass-through for HEAD and non-GET requests
|
||||
|
||||
### 1.4 Basic HEAD Endpoint
|
||||
- [ ] Implement FastCGI handler for `HEAD /<sha256>`
|
||||
- [ ] Query database for blob metadata
|
||||
- [ ] Return proper headers (Content-Type, Content-Length, etc.)
|
||||
- [ ] Return 404 if blob doesn't exist
|
||||
- [ ] Add server timing headers for debugging
|
||||
- [x] Implement FastCGI handler for `HEAD /<sha256>`
|
||||
- [x] Query database for blob metadata (single source of truth)
|
||||
- [x] Extract SHA-256 from URL (strip extensions)
|
||||
- [x] Return proper headers (Content-Type, Content-Length, etc.)
|
||||
- [x] Return 404 if blob doesn't exist in database
|
||||
- [x] Add server timing headers for debugging
|
||||
|
||||
### 1.5 Testing & Validation
|
||||
- [ ] Create test blobs with known SHA-256 hashes
|
||||
- [ ] Verify nginx serves files correctly
|
||||
- [ ] Verify HEAD requests return proper metadata
|
||||
- [ ] Test with missing files (404 responses)
|
||||
- [x] Create test blobs with known SHA-256 hashes
|
||||
- [x] Verify nginx serves files correctly with extension support
|
||||
- [x] Verify HEAD requests return proper metadata
|
||||
- [x] Test with missing files (404 responses)
|
||||
- [x] Test HEAD requests with and without extensions
|
||||
- [ ] Performance test with large files
|
||||
|
||||
---
|
||||
@@ -177,10 +180,10 @@ This document outlines the implementation plan for ginxsom, a FastCGI-based Blos
|
||||
|
||||
## Development Milestones
|
||||
|
||||
### Milestone 1: Basic Functionality ✓ (Phase 1 Complete)
|
||||
- nginx serves files by hash
|
||||
- HEAD requests return metadata
|
||||
- Database stores blob information
|
||||
### Milestone 1: Basic Functionality (Phase 1 Complete)
|
||||
- [x] nginx serves files by hash with extension support
|
||||
- [x] HEAD requests return metadata from database
|
||||
- [x] Database stores blob information with proper schema
|
||||
|
||||
### Milestone 2: Full Upload Support (Phase 2 Complete)
|
||||
- Authenticated uploads working
|
||||
@@ -262,4 +265,3 @@ This document outlines the implementation plan for ginxsom, a FastCGI-based Blos
|
||||
- [ ] Backup procedures
|
||||
- [ ] Security hardening guide
|
||||
- [ ] Documentation and examples
|
||||
|
||||
|
||||
50
Makefile
Normal file
50
Makefile
Normal file
@@ -0,0 +1,50 @@
|
||||
# Ginxsom Blossom Server Makefile
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -O2
|
||||
LIBS = -lfcgi -lsqlite3
|
||||
SRCDIR = src
|
||||
BUILDDIR = build
|
||||
TARGET = $(BUILDDIR)/ginxsom-fcgi
|
||||
|
||||
# Source files
|
||||
SOURCES = $(SRCDIR)/main.c
|
||||
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(BUILDDIR)/%.o)
|
||||
|
||||
# Default target
|
||||
all: $(TARGET)
|
||||
|
||||
# Create build directory
|
||||
$(BUILDDIR):
|
||||
mkdir -p $(BUILDDIR)
|
||||
|
||||
# Compile object files
|
||||
$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR)
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Link final executable
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(OBJECTS) $(LIBS) -o $@
|
||||
|
||||
# Clean build files
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
# Install (copy to system location)
|
||||
install: $(TARGET)
|
||||
sudo cp $(TARGET) /usr/local/bin/
|
||||
sudo chmod 755 /usr/local/bin/ginxsom-fcgi
|
||||
|
||||
# Uninstall
|
||||
uninstall:
|
||||
sudo rm -f /usr/local/bin/ginxsom-fcgi
|
||||
|
||||
# Run the FastCGI application
|
||||
run: $(TARGET)
|
||||
./$(TARGET)
|
||||
|
||||
# Debug build
|
||||
debug: CFLAGS += -g -DDEBUG
|
||||
debug: $(TARGET)
|
||||
|
||||
.PHONY: all clean install uninstall run debug
|
||||
115
README.md
115
README.md
@@ -231,9 +231,122 @@ Successful uploads return blob descriptors:
|
||||
│ └── b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf
|
||||
├── a8/
|
||||
│ └── 47/
|
||||
│ └── a8472f6d93e42c1e5b4e9f3a7b2c8d4e6f9a1b3c5d7e8f0a1b2c3d4e5f6a7b8.png
|
||||
│ └── a8472f6d93e42c1e5b4e6f9a1b3c5d7e8f0a1b2c3d4e6f9a1b3c5d7e8f0a1b2c3d4e5f6a7b8.png
|
||||
```
|
||||
|
||||
## File and Extension Handling
|
||||
|
||||
ginxsom implements a sophisticated file and extension handling strategy that optimizes both performance and flexibility:
|
||||
|
||||
### Database-Driven Architecture
|
||||
|
||||
The system uses the SQLite database as the **single source of truth** for blob existence and metadata:
|
||||
|
||||
```sql
|
||||
-- Database schema (clean SHA-256 hashes, no extensions)
|
||||
CREATE TABLE blobs (
|
||||
sha256 TEXT PRIMARY KEY, -- Clean hash: b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553
|
||||
size INTEGER NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
uploaded_at INTEGER NOT NULL,
|
||||
uploader_pubkey TEXT,
|
||||
filename TEXT -- Original filename: document.pdf
|
||||
);
|
||||
```
|
||||
|
||||
**Key Benefits:**
|
||||
- **Database Lookup vs Filesystem**: FastCGI queries the database instead of checking filesystem
|
||||
- **Single Source of Truth**: Database definitively knows if a blob exists
|
||||
- **Extension Irrelevant**: Database uses clean SHA-256 hashes without extensions
|
||||
- **Performance**: Database queries are faster than filesystem stat() calls
|
||||
|
||||
### URL and Extension Support
|
||||
|
||||
ginxsom supports flexible URL patterns for maximum client compatibility:
|
||||
|
||||
```
|
||||
# Both forms work identically:
|
||||
GET /b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553
|
||||
GET /b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf
|
||||
|
||||
# HEAD requests work with or without extensions:
|
||||
HEAD /b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553
|
||||
HEAD /b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf
|
||||
```
|
||||
|
||||
### nginx File Storage Strategy
|
||||
|
||||
Files are stored on disk **with extensions** for nginx MIME type detection and optimal performance:
|
||||
|
||||
```nginx
|
||||
# Blossom-compliant nginx configuration
|
||||
location ~ ^/([a-f0-9]{64}).*$ {
|
||||
root /var/lib/ginxsom/blobs;
|
||||
try_files /$1* =404;
|
||||
}
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **Hash-only lookup**: nginx extracts the 64-character SHA-256 hash from the URL, ignoring any extension
|
||||
2. **Wildcard matching**: `try_files /$1*` finds any file starting with that hash
|
||||
3. **Blossom compliance**: Serves correct file regardless of URL extension
|
||||
|
||||
**Examples:**
|
||||
|
||||
Client requests: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf`
|
||||
- nginx extracts hash: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553`
|
||||
- nginx looks for: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553*`
|
||||
- nginx finds: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf`
|
||||
- nginx serves the PDF with correct `Content-Type: application/pdf`
|
||||
|
||||
Client requests: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.mp3`
|
||||
- nginx extracts same hash: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553`
|
||||
- nginx looks for: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553*`
|
||||
- nginx finds: `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf`
|
||||
- nginx serves the PDF (not 404) with correct `Content-Type: application/pdf`
|
||||
|
||||
**Key Benefits:**
|
||||
|
||||
- **Blossom Protocol Compliance**: Accepts any extension, returns correct MIME type
|
||||
- **Performance**: nginx serves files directly without FastCGI involvement
|
||||
- **Flexibility**: URLs work with correct extension, wrong extension, or no extension
|
||||
- **MIME Detection**: nginx determines Content-Type from actual file extension on disk
|
||||
|
||||
This approach ensures that files are always found by their SHA-256 hash regardless of what extension (if any) is used in the request URL, while maintaining nginx's excellent static file serving performance.
|
||||
|
||||
### HEAD Request Handling
|
||||
|
||||
HEAD requests are processed differently to ensure accuracy:
|
||||
|
||||
1. **nginx receives HEAD request**: `/b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553.pdf`
|
||||
2. **nginx forwards to FastCGI**: All HEAD requests go to ginxsom for metadata
|
||||
3. **ginxsom extracts clean hash**: Strips `.pdf` → `b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553`
|
||||
4. **ginxsom queries database**: `SELECT size, type FROM blobs WHERE sha256 = ?`
|
||||
5. **ginxsom returns headers**: Content-Length, Content-Type, etc.
|
||||
|
||||
### Why This Approach?
|
||||
|
||||
**Performance:**
|
||||
- GET requests: nginx serves directly from disk (no database hit)
|
||||
- HEAD requests: Single database query (no filesystem checking)
|
||||
- Extension mismatches: Eliminated by database-driven approach
|
||||
|
||||
**Reliability:**
|
||||
- Database is authoritative source of blob existence
|
||||
- No race conditions between filesystem and metadata
|
||||
- Consistent behavior regardless of URL format
|
||||
|
||||
**Flexibility:**
|
||||
- Clients can use URLs with or without extensions
|
||||
- Browser-friendly URLs with proper file extensions
|
||||
- API-friendly clean hash URLs
|
||||
|
||||
**Scalability:**
|
||||
- nginx handles the heavy lifting (file serving)
|
||||
- FastCGI only processes metadata operations
|
||||
- Database queries scale better than filesystem operations
|
||||
|
||||
## Development
|
||||
|
||||
### Project Structure
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 199 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
build/ginxsom-fcgi
Executable file
BIN
build/ginxsom-fcgi
Executable file
Binary file not shown.
BIN
build/main.o
Normal file
BIN
build/main.o
Normal file
Binary file not shown.
233
config/deploy.sh
Executable file
233
config/deploy.sh
Executable file
@@ -0,0 +1,233 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# Ginxsom Deployment Script
|
||||
# This script sets up the ginxsom blossom server configuration
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
log_info() {
|
||||
echo -e "${GREEN}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_warn() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1"
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
log_error "This script should not be run as root. Please run as a regular user with sudo access."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_dependencies() {
|
||||
log_info "Checking dependencies..."
|
||||
|
||||
if ! command -v nginx &> /dev/null; then
|
||||
log_error "nginx is not installed. Please install nginx first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v systemctl &> /dev/null; then
|
||||
log_error "systemctl is not available. This script requires systemd."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_info "Dependencies check passed."
|
||||
}
|
||||
|
||||
create_directories() {
|
||||
log_info "Creating required directories..."
|
||||
|
||||
# Create data directories
|
||||
sudo mkdir -p /var/lib/ginxsom/files
|
||||
sudo mkdir -p /var/lib/ginxsom/db
|
||||
sudo mkdir -p /run/ginxsom
|
||||
sudo mkdir -p /var/log/ginxsom
|
||||
sudo mkdir -p /etc/ginxsom
|
||||
|
||||
log_info "Directories created."
|
||||
}
|
||||
|
||||
create_user() {
|
||||
log_info "Creating ginxsom user..."
|
||||
|
||||
if ! id "ginxsom" &>/dev/null; then
|
||||
sudo useradd --system --no-create-home --shell /bin/false ginxsom
|
||||
log_info "User 'ginxsom' created."
|
||||
else
|
||||
log_info "User 'ginxsom' already exists."
|
||||
fi
|
||||
}
|
||||
|
||||
set_permissions() {
|
||||
log_info "Setting file permissions..."
|
||||
|
||||
# Set ownership of data directories
|
||||
sudo chown -R ginxsom:ginxsom /var/lib/ginxsom
|
||||
sudo chown -R ginxsom:ginxsom /run/ginxsom
|
||||
sudo chown -R ginxsom:ginxsom /var/log/ginxsom
|
||||
sudo chown -R ginxsom:ginxsom /etc/ginxsom
|
||||
|
||||
# Set proper permissions
|
||||
sudo chmod 755 /var/lib/ginxsom
|
||||
sudo chmod 755 /var/lib/ginxsom/files
|
||||
sudo chmod 755 /var/lib/ginxsom/db
|
||||
sudo chmod 755 /run/ginxsom
|
||||
sudo chmod 755 /var/log/ginxsom
|
||||
sudo chmod 755 /etc/ginxsom
|
||||
|
||||
log_info "Permissions set."
|
||||
}
|
||||
|
||||
deploy_nginx_config() {
|
||||
log_info "Deploying nginx configuration..."
|
||||
|
||||
# Copy nginx configuration
|
||||
sudo cp "$SCRIPT_DIR/nginx/ginxsom.conf" /etc/nginx/sites-available/ginxsom
|
||||
|
||||
# Enable the site
|
||||
if [[ ! -L /etc/nginx/sites-enabled/ginxsom ]]; then
|
||||
sudo ln -sf /etc/nginx/sites-available/ginxsom /etc/nginx/sites-enabled/ginxsom
|
||||
log_info "Nginx site enabled."
|
||||
else
|
||||
log_info "Nginx site already enabled."
|
||||
fi
|
||||
|
||||
# Test nginx configuration
|
||||
if sudo nginx -t; then
|
||||
log_info "Nginx configuration test passed."
|
||||
else
|
||||
log_error "Nginx configuration test failed. Please check the configuration."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
deploy_systemd_service() {
|
||||
log_info "Deploying systemd service..."
|
||||
|
||||
# Copy systemd service file
|
||||
sudo cp "$SCRIPT_DIR/systemd/ginxsom.service" /etc/systemd/system/ginxsom.service
|
||||
|
||||
# Reload systemd
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
log_info "Systemd service installed."
|
||||
}
|
||||
|
||||
create_sample_config() {
|
||||
log_info "Creating sample configuration..."
|
||||
|
||||
if [[ ! -f /etc/ginxsom/config.toml ]]; then
|
||||
sudo tee /etc/ginxsom/config.toml > /dev/null << 'EOF'
|
||||
# Ginxsom Blossom Server Configuration
|
||||
|
||||
[server]
|
||||
# FastCGI socket path
|
||||
socket = "/run/ginxsom/ginxsom.sock"
|
||||
|
||||
# Data directory for files
|
||||
data_dir = "/var/lib/ginxsom"
|
||||
|
||||
# Maximum file size in bytes (100MB)
|
||||
max_file_size = 104857600
|
||||
|
||||
[logging]
|
||||
level = "info"
|
||||
file = "/var/log/ginxsom/ginxsom.log"
|
||||
|
||||
[storage]
|
||||
# File storage directory
|
||||
files_dir = "/var/lib/ginxsom/files"
|
||||
|
||||
# Database file for metadata
|
||||
database = "/var/lib/ginxsom/db/ginxsom.db"
|
||||
|
||||
[auth]
|
||||
# Enable authentication for uploads
|
||||
require_auth = true
|
||||
|
||||
# Enable list endpoint
|
||||
enable_list = false
|
||||
|
||||
# Enable mirror endpoint
|
||||
enable_mirror = false
|
||||
EOF
|
||||
sudo chown ginxsom:ginxsom /etc/ginxsom/config.toml
|
||||
sudo chmod 640 /etc/ginxsom/config.toml
|
||||
log_info "Sample configuration created at /etc/ginxsom/config.toml"
|
||||
else
|
||||
log_info "Configuration file already exists at /etc/ginxsom/config.toml"
|
||||
fi
|
||||
}
|
||||
|
||||
show_status() {
|
||||
log_info "Deployment complete! Next steps:"
|
||||
echo ""
|
||||
echo "1. Build and install the ginxsom binary:"
|
||||
echo " cd $PROJECT_ROOT"
|
||||
echo " make build"
|
||||
echo " sudo make install"
|
||||
echo ""
|
||||
echo "2. Start the services:"
|
||||
echo " sudo systemctl enable ginxsom"
|
||||
echo " sudo systemctl start ginxsom"
|
||||
echo " sudo systemctl reload nginx"
|
||||
echo ""
|
||||
echo "3. Check status:"
|
||||
echo " sudo systemctl status ginxsom"
|
||||
echo " sudo systemctl status nginx"
|
||||
echo ""
|
||||
echo "4. Test the server:"
|
||||
echo " curl http://localhost/health"
|
||||
echo ""
|
||||
echo "Configuration files:"
|
||||
echo " - Nginx: /etc/nginx/sites-available/ginxsom"
|
||||
echo " - SystemD: /etc/systemd/system/ginxsom.service"
|
||||
echo " - Config: /etc/ginxsom/config.toml"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main() {
|
||||
log_info "Starting ginxsom deployment..."
|
||||
|
||||
check_root
|
||||
check_dependencies
|
||||
create_user
|
||||
create_directories
|
||||
set_permissions
|
||||
deploy_nginx_config
|
||||
deploy_systemd_service
|
||||
create_sample_config
|
||||
show_status
|
||||
|
||||
log_info "Deployment script completed successfully!"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
case "${1:-}" in
|
||||
--help|-h)
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --help, -h Show this help message"
|
||||
echo ""
|
||||
echo "This script deploys the ginxsom blossom server configuration."
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
main "$@"
|
||||
;;
|
||||
esac
|
||||
109
config/local-nginx.conf
Normal file
109
config/local-nginx.conf
Normal file
@@ -0,0 +1,109 @@
|
||||
# Local Ginxsom Development Server Configuration
|
||||
# This configuration serves files directly from the local repo directory
|
||||
|
||||
# Main context - specify error log here to override system default
|
||||
error_log logs/error.log;
|
||||
pid logs/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
# HTTP context
|
||||
http {
|
||||
# Basic settings
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
# MIME types
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
# Logging (relative to prefix directory)
|
||||
access_log logs/access.log;
|
||||
|
||||
# FastCGI upstream configuration
|
||||
upstream fastcgi_backend {
|
||||
server unix:/tmp/ginxsom-fcgi.sock;
|
||||
}
|
||||
|
||||
# Local development server
|
||||
server {
|
||||
listen 9001;
|
||||
server_name localhost;
|
||||
|
||||
# Root directory for blossom files (local blobs directory)
|
||||
root blobs;
|
||||
|
||||
# Maximum upload size (adjust as needed)
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Security headers
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# Handle GET and HEAD requests for blob files - Blossom compliant
|
||||
location ~ "^/([a-f0-9]{64}).*$" {
|
||||
limit_except HEAD GET {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Route HEAD requests to FastCGI via rewrite
|
||||
if ($request_method = HEAD) {
|
||||
rewrite ^/(.*)$ /fcgi-head/$1 last;
|
||||
}
|
||||
|
||||
# GET requests served directly with hash-only lookup
|
||||
try_files /$1* =404;
|
||||
|
||||
# Set appropriate headers for blobs
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
}
|
||||
|
||||
# FastCGI handler for HEAD requests
|
||||
location ~ "^/fcgi-head/([a-f0-9]{64}).*$" {
|
||||
internal;
|
||||
fastcgi_pass fastcgi_backend;
|
||||
fastcgi_param REQUEST_METHOD HEAD;
|
||||
fastcgi_param REQUEST_URI /$1;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param QUERY_STRING $query_string;
|
||||
fastcgi_param CONTENT_TYPE $content_type;
|
||||
fastcgi_param CONTENT_LENGTH $content_length;
|
||||
fastcgi_param SERVER_PROTOCOL $server_protocol;
|
||||
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
|
||||
fastcgi_param REMOTE_ADDR $remote_addr;
|
||||
fastcgi_param REMOTE_PORT $remote_port;
|
||||
fastcgi_param SERVER_ADDR $server_addr;
|
||||
fastcgi_param SERVER_PORT $server_port;
|
||||
fastcgi_param SERVER_NAME $server_name;
|
||||
}
|
||||
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "OK\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
|
||||
# List files endpoint for debugging
|
||||
location /debug/list {
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
}
|
||||
|
||||
# Root redirect
|
||||
location = / {
|
||||
return 200 "Ginxsom Local Development Server\nTry: GET /<sha256>\nHealth: GET /health\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
config/nginx/ginxsom.conf
Normal file
131
config/nginx/ginxsom.conf
Normal file
@@ -0,0 +1,131 @@
|
||||
# Ginxsom Blossom Server Configuration
|
||||
# This configuration serves files directly via nginx for maximum performance
|
||||
# while handling authenticated operations through FastCGI
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost; # Change this to your domain
|
||||
|
||||
# Root directory for blossom files (organized by SHA-256 hash)
|
||||
root /var/lib/ginxsom/files;
|
||||
|
||||
# Maximum upload size (adjust as needed)
|
||||
client_max_body_size 100M;
|
||||
|
||||
# Security headers
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/ginxsom_access.log;
|
||||
error_log /var/log/nginx/ginxsom_error.log;
|
||||
|
||||
# Static file serving - nginx handles this directly for maximum performance
|
||||
# Files are stored as: /var/lib/ginxsom/files/{first2chars}/{remaining_hash}
|
||||
location ~ ^/([a-f0-9]{64})$ {
|
||||
set $hash $1;
|
||||
set $prefix $1; # First 2 characters
|
||||
set $suffix $1; # Remaining characters
|
||||
|
||||
# Extract first 2 chars and remaining
|
||||
if ($hash ~ ^([a-f0-9]{2})([a-f0-9]{62})$) {
|
||||
set $prefix $1;
|
||||
set $suffix $2;
|
||||
}
|
||||
|
||||
try_files /$prefix/$suffix =404;
|
||||
|
||||
# Set proper content type based on file extension in metadata
|
||||
# This will be enhanced when we add metadata lookup
|
||||
add_header Content-Type application/octet-stream;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
}
|
||||
|
||||
# HEAD requests for file existence checking
|
||||
# This endpoint checks if a file exists and returns metadata
|
||||
location ~ ^/head/([a-f0-9]{64})$ {
|
||||
# Pass to FastCGI application for metadata lookup
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param REQUEST_METHOD HEAD;
|
||||
fastcgi_param BLOSSOM_HASH $1;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
}
|
||||
|
||||
# Upload endpoint - requires authentication
|
||||
location /upload {
|
||||
# Pass to FastCGI application for processing
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
|
||||
# Only allow PUT method for uploads
|
||||
if ($request_method !~ ^(PUT)$ ) {
|
||||
return 405;
|
||||
}
|
||||
}
|
||||
|
||||
# List endpoint - returns list of files (if enabled)
|
||||
location /list {
|
||||
# Pass to FastCGI application
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
|
||||
# Only allow GET method
|
||||
if ($request_method !~ ^(GET)$ ) {
|
||||
return 405;
|
||||
}
|
||||
}
|
||||
|
||||
# Mirror endpoint - for mirroring files from other servers
|
||||
location /mirror {
|
||||
# Pass to FastCGI application
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
|
||||
# Only allow PUT method
|
||||
if ($request_method !~ ^(PUT)$ ) {
|
||||
return 405;
|
||||
}
|
||||
}
|
||||
|
||||
# Delete endpoint - requires authentication
|
||||
location ~ ^/([a-f0-9]{64})$ {
|
||||
# Handle DELETE requests through FastCGI
|
||||
if ($request_method = DELETE) {
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_param BLOSSOM_HASH $1;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
}
|
||||
|
||||
# For GET/HEAD, fall through to static file serving above
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
# Pass to FastCGI application
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/ginxsom.fcgi;
|
||||
fastcgi_pass unix:/run/ginxsom/ginxsom.sock;
|
||||
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Deny access to hidden files and directories
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Deny access to backup and temporary files
|
||||
location ~ ~$ {
|
||||
deny all;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
}
|
||||
47
config/systemd/ginxsom.service
Normal file
47
config/systemd/ginxsom.service
Normal file
@@ -0,0 +1,47 @@
|
||||
[Unit]
|
||||
Description=Ginxsom Blossom Server FastCGI Application
|
||||
After=network.target
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
User=ginxsom
|
||||
Group=ginxsom
|
||||
WorkingDirectory=/var/lib/ginxsom
|
||||
ExecStart=/usr/local/bin/ginxsom --fastcgi --socket /run/ginxsom/ginxsom.sock
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
KillMode=process
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
# Security settings
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/var/lib/ginxsom /run/ginxsom /var/log/ginxsom
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
ProtectHostname=true
|
||||
ProtectClock=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectKernelModules=true
|
||||
ProtectKernelLogs=true
|
||||
ProtectControlGroups=true
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
RestrictRealtime=true
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
MemoryDenyWriteExecute=true
|
||||
|
||||
# Resource limits
|
||||
LimitNOFILE=65536
|
||||
LimitNPROC=4096
|
||||
|
||||
# Environment
|
||||
Environment=GINXSOM_CONFIG=/etc/ginxsom/config.toml
|
||||
Environment=GINXSOM_DATA_DIR=/var/lib/ginxsom
|
||||
Environment=GINXSOM_LOG_LEVEL=info
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
BIN
db/ginxsom.db
Normal file
BIN
db/ginxsom.db
Normal file
Binary file not shown.
BIN
db/ginxsom.db.backup.1755562070
Normal file
BIN
db/ginxsom.db.backup.1755562070
Normal file
Binary file not shown.
BIN
db/ginxsom.db.backup.1755563905
Normal file
BIN
db/ginxsom.db.backup.1755563905
Normal file
Binary file not shown.
BIN
db/ginxsom.db.backup.1755565735
Normal file
BIN
db/ginxsom.db.backup.1755565735
Normal file
Binary file not shown.
52
db/init.sh
Executable file
52
db/init.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Ginxsom Database Initialization Script
|
||||
# This script creates the SQLite database with the proper schema
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Configuration
|
||||
DB_DIR="$(dirname "$0")"
|
||||
DB_FILE="$DB_DIR/ginxsom.db"
|
||||
SCHEMA_FILE="$DB_DIR/schema.sql"
|
||||
|
||||
echo "Initializing Ginxsom database..."
|
||||
|
||||
# Check if schema file exists
|
||||
if [ ! -f "$SCHEMA_FILE" ]; then
|
||||
echo "Error: Schema file not found at $SCHEMA_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create database directory if it doesn't exist
|
||||
mkdir -p "$DB_DIR"
|
||||
|
||||
# Remove existing database if it exists (for clean initialization)
|
||||
if [ -f "$DB_FILE" ]; then
|
||||
echo "Warning: Existing database found. Creating backup..."
|
||||
cp "$DB_FILE" "$DB_FILE.backup.$(date +%s)"
|
||||
rm "$DB_FILE"
|
||||
fi
|
||||
|
||||
# Create new database with schema
|
||||
echo "Creating database at $DB_FILE..."
|
||||
sqlite3 "$DB_FILE" < "$SCHEMA_FILE"
|
||||
|
||||
# Set proper permissions
|
||||
chmod 644 "$DB_FILE"
|
||||
chmod 755 "$DB_DIR"
|
||||
|
||||
echo "Database initialized successfully!"
|
||||
echo "Database location: $DB_FILE"
|
||||
|
||||
# Show database info
|
||||
echo ""
|
||||
echo "Database tables:"
|
||||
sqlite3 "$DB_FILE" ".tables"
|
||||
|
||||
echo ""
|
||||
echo "Server configuration:"
|
||||
sqlite3 "$DB_FILE" "SELECT key, value, description FROM server_config;"
|
||||
|
||||
echo ""
|
||||
echo "Database ready for use."
|
||||
67
db/schema.sql
Normal file
67
db/schema.sql
Normal file
@@ -0,0 +1,67 @@
|
||||
-- Ginxsom Blossom Server Database Schema
|
||||
-- SQLite database for blob metadata and server configuration
|
||||
|
||||
-- Enable foreign key constraints
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
-- Main blobs table for storing blob metadata
|
||||
CREATE TABLE IF NOT EXISTS blobs (
|
||||
sha256 TEXT PRIMARY KEY NOT NULL, -- SHA-256 hash (64 hex chars)
|
||||
size INTEGER NOT NULL, -- File size in bytes
|
||||
type TEXT NOT NULL, -- MIME type
|
||||
uploaded_at INTEGER NOT NULL, -- Unix timestamp
|
||||
uploader_pubkey TEXT, -- Nostr public key (optional)
|
||||
filename TEXT, -- Original filename (optional)
|
||||
CHECK (length(sha256) = 64), -- Ensure valid SHA-256 hash length
|
||||
CHECK (size >= 0), -- Ensure non-negative size
|
||||
CHECK (uploaded_at > 0) -- Ensure valid timestamp
|
||||
);
|
||||
|
||||
-- Server configuration table for key-value settings
|
||||
CREATE TABLE IF NOT EXISTS server_config (
|
||||
key TEXT PRIMARY KEY NOT NULL, -- Configuration key
|
||||
value TEXT NOT NULL, -- Configuration value
|
||||
description TEXT, -- Human-readable description
|
||||
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')) -- Last update timestamp
|
||||
);
|
||||
|
||||
-- Indexes for performance optimization
|
||||
CREATE INDEX IF NOT EXISTS idx_blobs_uploaded_at ON blobs(uploaded_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_blobs_uploader_pubkey ON blobs(uploader_pubkey);
|
||||
CREATE INDEX IF NOT EXISTS idx_blobs_type ON blobs(type);
|
||||
CREATE INDEX IF NOT EXISTS idx_blobs_size ON blobs(size);
|
||||
|
||||
-- Insert default server configuration
|
||||
INSERT OR IGNORE INTO server_config (key, value, description) VALUES
|
||||
('max_file_size', '104857600', 'Maximum file size in bytes (100MB)'),
|
||||
('require_auth', 'false', 'Whether authentication is required for uploads'),
|
||||
('allowed_types', '*', 'Allowed MIME types (* for all)'),
|
||||
('server_name', 'ginxsom', 'Server name for responses'),
|
||||
('storage_quota', '10737418240', 'Total storage quota in bytes (10GB)'),
|
||||
('cleanup_interval', '86400', 'Cleanup interval in seconds (daily)'),
|
||||
('max_upload_rate', '1048576', 'Max upload rate per client in bytes/sec (1MB/s)');
|
||||
|
||||
-- View for storage statistics
|
||||
CREATE VIEW IF NOT EXISTS storage_stats AS
|
||||
SELECT
|
||||
COUNT(*) as total_blobs,
|
||||
SUM(size) as total_bytes,
|
||||
AVG(size) as avg_blob_size,
|
||||
MIN(uploaded_at) as first_upload,
|
||||
MAX(uploaded_at) as last_upload,
|
||||
COUNT(DISTINCT uploader_pubkey) as unique_uploaders
|
||||
FROM blobs;
|
||||
|
||||
-- View for recent uploads (last 24 hours)
|
||||
CREATE VIEW IF NOT EXISTS recent_uploads AS
|
||||
SELECT
|
||||
sha256,
|
||||
size,
|
||||
type,
|
||||
uploaded_at,
|
||||
uploader_pubkey,
|
||||
filename,
|
||||
datetime(uploaded_at, 'unixepoch') as uploaded_datetime
|
||||
FROM blobs
|
||||
WHERE uploaded_at > (strftime('%s', 'now') - 86400)
|
||||
ORDER BY uploaded_at DESC;
|
||||
64
logs/access.log
Normal file
64
logs/access.log
Normal file
@@ -0,0 +1,64 @@
|
||||
127.0.0.1 - - [18/Aug/2025:19:08:28 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:10:04 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:10:15 -0400] "GET / HTTP/1.1" 200 72 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:10:39 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:05 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:11 -0400] "HEAD /70/8d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:37 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:44 -0400] "HEAD /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:59 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 203886 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:11:59 -0400] "GET /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e HTTP/1.1" 200 30236 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:12:11 -0400] "GET /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e HTTP/1.1" 200 30236 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:19:12:44 -0400] "GET /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:19:12:44 -0400] "GET /favicon.ico HTTP/1.1" 404 564 "http://localhost:9001/f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.webp" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:05 -0400] "GET /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.jpg HTTP/1.1" 200 30236 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:06 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:13 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:38 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:45 -0400] "HEAD /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:17:50 -0400] "HEAD /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.jpg HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:18:20 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:20:27 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:19:23:40 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:15:17 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:16:34 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:16:50 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:17:01 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 400 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:18:01 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:18:06 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 400 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:18:26 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:29:58 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:31:09 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 162 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:31:41 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:31:42 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 564 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:31:49 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 162 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:32:15 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:32:41 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:33:48 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:15 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 400 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:20 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:22 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:25 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:25 -0400] "GET /favicon.ico HTTP/1.1" 404 564 "http://localhost:9001/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:34:53 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 400 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:36:59 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:37:06 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:37:32 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:40:39 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:41:52 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:42:04 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:42:51 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:42:56 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:42:59 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 203886 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:43:08 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:48:43 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:20:52:51 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:53:44 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:20:54:04 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:10:59 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:11:07 -0400] "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:11:13 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
127.0.0.1 - - [18/Aug/2025:21:11:13 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1" 200 203886 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:11:21 -0400] "HEAD /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e HTTP/1.1" 200 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:11:28 -0400] "HEAD /0000000000000000000000000000000000000000000000000000000000000000 HTTP/1.1" 404 0 "-" "curl/8.15.0"
|
||||
127.0.0.1 - - [18/Aug/2025:21:49:44 -0400] "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
|
||||
92
logs/error.log
Normal file
92
logs/error.log
Normal file
@@ -0,0 +1,92 @@
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: bind() to 0.0.0.0:8080 failed (98: Unknown error)
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: bind() to 0.0.0.0:8080 failed (98: Unknown error)
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: bind() to 0.0.0.0:8080 failed (98: Unknown error)
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: bind() to 0.0.0.0:8080 failed (98: Unknown error)
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: bind() to 0.0.0.0:8080 failed (98: Unknown error)
|
||||
2025/08/18 19:05:40 [emerg] 212857#212857: still could not bind()
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: bind() to 0.0.0.0:9000 failed (98: Unknown error)
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: bind() to 0.0.0.0:9000 failed (98: Unknown error)
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: bind() to 0.0.0.0:9000 failed (98: Unknown error)
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: bind() to 0.0.0.0:9000 failed (98: Unknown error)
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: bind() to 0.0.0.0:9000 failed (98: Unknown error)
|
||||
2025/08/18 19:08:10 [emerg] 213180#213180: still could not bind()
|
||||
2025/08/18 19:08:28 [error] 213123#213123: *1 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1", host: "localhost:9000"
|
||||
2025/08/18 19:08:33 [alert] 212996#212996: unlink() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/logs/nginx.pid" failed (2: No such file or directory)
|
||||
2025/08/18 19:10:04 [error] 213452#213452: *1 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 19:10:39 [error] 213452#213452: *4 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 19:11:05 [error] 215075#215075: *5 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "HEAD /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 19:12:44 [error] 215220#215220: *11 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.webp" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 19:12:44 [error] 215220#215220: *11 open() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/favicon.ico" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:9001", referrer: "http://localhost:9001/f8b5b4904c79bb53b2b417bc9c939268ac2871f194e95523b7b66113862da15e.webp"
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 20:12:11 [emerg] 244527#244527: still could not bind()
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:18:26 [crit] 246736#246736: *11 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:29:58 [crit] 246736#246736: *12 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:09 [crit] 246736#246736: *13 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:41 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:42 [crit] 246736#246736: *14 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.jpeg" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.png" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.gif" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.pdf" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.txt" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp4" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:31:49 [crit] 246736#246736: *15 stat() "/home/teknari/Sync/Programming/VibeCoding/ginxsom/blobs/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.mp3" failed (13: Permission denied), client: 127.0.0.1, server: localhost, request: "GET /708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp HTTP/1.1", host: "localhost:9001"
|
||||
2025/08/18 20:34:25 [error] 252785#252785: *6 open() "./blobs/favicon.ico" failed (2: No such file or directory), client: 127.0.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:9001", referrer: "http://localhost:9001/708d0e8226ec17b0585417c0ec9352ce5f52c3820c904b7066fe20b00f2d9cfe.webp"
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: bind() to 0.0.0.0:9001 failed (98: Unknown error)
|
||||
2025/08/18 21:07:53 [emerg] 265258#265258: still could not bind()
|
||||
1
logs/nginx.pid
Normal file
1
logs/nginx.pid
Normal file
@@ -0,0 +1 @@
|
||||
265507
|
||||
68
scripts/start-fcgi.sh
Executable file
68
scripts/start-fcgi.sh
Executable file
@@ -0,0 +1,68 @@
|
||||
#!/bin/bash
|
||||
# Start Ginxsom FastCGI Application
|
||||
|
||||
# Configuration
|
||||
FCGI_BINARY="./build/ginxsom-fcgi"
|
||||
SOCKET_PATH="/tmp/ginxsom-fcgi.sock"
|
||||
PID_FILE="/tmp/ginxsom-fcgi.pid"
|
||||
|
||||
# Function to cleanup on exit
|
||||
cleanup() {
|
||||
echo "Cleaning up..."
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
PID=$(cat "$PID_FILE")
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
echo "Stopping FastCGI process $PID"
|
||||
kill "$PID"
|
||||
sleep 1
|
||||
# Force kill if still running
|
||||
if kill -0 "$PID" 2>/dev/null; then
|
||||
kill -9 "$PID"
|
||||
fi
|
||||
fi
|
||||
rm -f "$PID_FILE"
|
||||
fi
|
||||
rm -f "$SOCKET_PATH"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Setup signal handlers
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# Check if binary exists
|
||||
if [ ! -f "$FCGI_BINARY" ]; then
|
||||
echo "Error: FastCGI binary not found at $FCGI_BINARY"
|
||||
echo "Please run 'make' to build the application first"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up old socket and pid file
|
||||
rm -f "$SOCKET_PATH" "$PID_FILE"
|
||||
|
||||
# Start the FastCGI application
|
||||
echo "Starting Ginxsom FastCGI application..."
|
||||
echo "Socket: $SOCKET_PATH"
|
||||
echo "Binary: $FCGI_BINARY"
|
||||
|
||||
# Use spawn-fcgi to start the application
|
||||
spawn-fcgi -s "$SOCKET_PATH" -M 666 -u "$USER" -g "$USER" -f "$FCGI_BINARY" -P "$PID_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "FastCGI application started successfully"
|
||||
echo "PID: $(cat $PID_FILE 2>/dev/null || echo 'unknown')"
|
||||
|
||||
# Wait for the process to exit or be killed
|
||||
if [ -f "$PID_FILE" ]; then
|
||||
PID=$(cat "$PID_FILE")
|
||||
echo "Monitoring process $PID (Press Ctrl+C to stop)"
|
||||
while kill -0 "$PID" 2>/dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
echo "FastCGI process exited"
|
||||
fi
|
||||
else
|
||||
echo "Failed to start FastCGI application"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cleanup
|
||||
261
src/main.c
Normal file
261
src/main.c
Normal file
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Ginxsom Blossom Server - FastCGI Application
|
||||
* Handles HEAD requests and other dynamic operations
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcgi_stdio.h>
|
||||
#include <sqlite3.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#define MAX_SHA256_LEN 65
|
||||
#define MAX_PATH_LEN 512
|
||||
#define MAX_MIME_LEN 128
|
||||
|
||||
// Database path
|
||||
#define DB_PATH "db/ginxsom.db"
|
||||
|
||||
// Blob metadata structure
|
||||
typedef struct {
|
||||
char sha256[MAX_SHA256_LEN];
|
||||
long size;
|
||||
char type[MAX_MIME_LEN];
|
||||
long uploaded_at;
|
||||
char filename[256];
|
||||
int found;
|
||||
} blob_metadata_t;
|
||||
|
||||
// Get blob metadata from database
|
||||
int get_blob_metadata(const char* sha256, blob_metadata_t* metadata) {
|
||||
sqlite3* db;
|
||||
sqlite3_stmt* stmt;
|
||||
int rc;
|
||||
|
||||
printf("DEBUG: get_blob_metadata() called with sha256='%s'\r\n", sha256);
|
||||
printf("DEBUG: Opening database at path: %s\r\n", DB_PATH);
|
||||
|
||||
rc = sqlite3_open_v2(DB_PATH, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (rc) {
|
||||
printf("DEBUG: Database open FAILED: %s\r\n", sqlite3_errmsg(db));
|
||||
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("DEBUG: Database opened successfully\r\n");
|
||||
|
||||
const char* sql = "SELECT sha256, size, type, uploaded_at, filename FROM blobs WHERE sha256 = ?";
|
||||
printf("DEBUG: Preparing SQL: %s\r\n", sql);
|
||||
|
||||
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
printf("DEBUG: SQL prepare FAILED: %s\r\n", sqlite3_errmsg(db));
|
||||
fprintf(stderr, "SQL error: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("DEBUG: SQL prepared successfully\r\n");
|
||||
printf("DEBUG: Binding parameter sha256='%s'\r\n", sha256);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, sha256, -1, SQLITE_STATIC);
|
||||
|
||||
printf("DEBUG: Executing SQL query...\r\n");
|
||||
rc = sqlite3_step(stmt);
|
||||
printf("DEBUG: sqlite3_step() returned: %d (SQLITE_ROW=%d, SQLITE_DONE=%d)\r\n",
|
||||
rc, SQLITE_ROW, SQLITE_DONE);
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
printf("DEBUG: Row found! Extracting metadata...\r\n");
|
||||
strncpy(metadata->sha256, (char*)sqlite3_column_text(stmt, 0), MAX_SHA256_LEN-1);
|
||||
metadata->size = sqlite3_column_int64(stmt, 1);
|
||||
strncpy(metadata->type, (char*)sqlite3_column_text(stmt, 2), MAX_MIME_LEN-1);
|
||||
metadata->uploaded_at = sqlite3_column_int64(stmt, 3);
|
||||
const char* filename = (char*)sqlite3_column_text(stmt, 4);
|
||||
if (filename) {
|
||||
strncpy(metadata->filename, filename, 255);
|
||||
} else {
|
||||
metadata->filename[0] = '\0';
|
||||
}
|
||||
metadata->found = 1;
|
||||
printf("DEBUG: Metadata extracted - size=%ld, type='%s'\r\n",
|
||||
metadata->size, metadata->type);
|
||||
} else {
|
||||
printf("DEBUG: No row found for sha256='%s'\r\n", sha256);
|
||||
metadata->found = 0;
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_close(db);
|
||||
printf("DEBUG: Database closed, returning %d\r\n", metadata->found);
|
||||
return metadata->found;
|
||||
}
|
||||
|
||||
// Check if physical file exists (with extension based on MIME type)
|
||||
int file_exists_with_type(const char* sha256, const char* mime_type) {
|
||||
char filepath[MAX_PATH_LEN];
|
||||
const char* extension = "";
|
||||
|
||||
// Determine file extension based on MIME type
|
||||
if (strstr(mime_type, "image/jpeg")) {
|
||||
extension = ".jpg";
|
||||
} else if (strstr(mime_type, "image/webp")) {
|
||||
extension = ".webp";
|
||||
} else if (strstr(mime_type, "image/png")) {
|
||||
extension = ".png";
|
||||
} else if (strstr(mime_type, "image/gif")) {
|
||||
extension = ".gif";
|
||||
} else if (strstr(mime_type, "video/mp4")) {
|
||||
extension = ".mp4";
|
||||
} else if (strstr(mime_type, "video/webm")) {
|
||||
extension = ".webm";
|
||||
} else if (strstr(mime_type, "audio/mpeg")) {
|
||||
extension = ".mp3";
|
||||
} else if (strstr(mime_type, "audio/ogg")) {
|
||||
extension = ".ogg";
|
||||
} else if (strstr(mime_type, "text/plain")) {
|
||||
extension = ".txt";
|
||||
}
|
||||
|
||||
snprintf(filepath, sizeof(filepath), "blobs/%s%s", sha256, extension);
|
||||
|
||||
printf("DEBUG: file_exists_with_type() checking path: '%s' (MIME: %s)\r\n", filepath, mime_type);
|
||||
|
||||
struct stat st;
|
||||
int result = stat(filepath, &st);
|
||||
printf("DEBUG: stat() returned: %d (0=success, -1=fail)\r\n", result);
|
||||
|
||||
if (result == 0) {
|
||||
printf("DEBUG: File exists! Size: %ld bytes\r\n", st.st_size);
|
||||
return 1;
|
||||
} else {
|
||||
printf("DEBUG: File does not exist or stat failed\r\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle HEAD request for blob
|
||||
void handle_head_request(const char* sha256) {
|
||||
blob_metadata_t metadata = {0};
|
||||
|
||||
printf("DEBUG: handle_head_request called with sha256=%s\r\n", sha256);
|
||||
|
||||
// Validate SHA-256 format (64 hex characters)
|
||||
if (strlen(sha256) != 64) {
|
||||
printf("DEBUG: SHA-256 length validation failed: %zu\r\n", strlen(sha256));
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid SHA-256 hash format\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("DEBUG: SHA-256 length validation passed\r\n");
|
||||
|
||||
// Check if blob exists in database - this is the single source of truth
|
||||
if (!get_blob_metadata(sha256, &metadata)) {
|
||||
printf("DEBUG: Database lookup failed for sha256=%s\r\n", sha256);
|
||||
printf("Status: 404 Not Found\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Blob not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("DEBUG: Database lookup succeeded - blob exists\r\n");
|
||||
|
||||
// Return successful HEAD response with metadata from database
|
||||
printf("Status: 200 OK\r\n");
|
||||
printf("Content-Type: %s\r\n", metadata.type);
|
||||
printf("Content-Length: %ld\r\n", metadata.size);
|
||||
printf("Cache-Control: public, max-age=31536000, immutable\r\n");
|
||||
printf("ETag: \"%s\"\r\n", metadata.sha256);
|
||||
|
||||
// Add timing header for debugging
|
||||
printf("X-Ginxsom-Server: FastCGI\r\n");
|
||||
printf("X-Ginxsom-Timestamp: %ld\r\n", time(NULL));
|
||||
|
||||
if (strlen(metadata.filename) > 0) {
|
||||
printf("X-Original-Filename: %s\r\n", metadata.filename);
|
||||
}
|
||||
|
||||
printf("\r\n");
|
||||
// HEAD request - no body content
|
||||
}
|
||||
|
||||
// Extract SHA-256 from request URI (Blossom compliant - ignores any extension)
|
||||
const char* extract_sha256_from_uri(const char* uri) {
|
||||
static char sha256_buffer[MAX_SHA256_LEN];
|
||||
|
||||
if (!uri || uri[0] != '/') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* start = uri + 1; // Skip leading '/'
|
||||
|
||||
// Extract exactly 64 hex characters, ignoring anything after (extensions, etc.)
|
||||
int len = 0;
|
||||
for (int i = 0; i < 64 && start[i] != '\0'; i++) {
|
||||
char c = start[i];
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
|
||||
// If we hit a non-hex character before 64 chars, it's invalid
|
||||
if (len < 64) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
sha256_buffer[i] = c;
|
||||
len = i + 1;
|
||||
}
|
||||
|
||||
// Must be exactly 64 hex characters
|
||||
if (len != 64) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sha256_buffer[64] = '\0';
|
||||
return sha256_buffer;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
while (FCGI_Accept() >= 0) {
|
||||
// DEBUG: Log every request received
|
||||
printf("DEBUG: FastCGI received request\r\n");
|
||||
|
||||
const char* request_method = getenv("REQUEST_METHOD");
|
||||
const char* request_uri = getenv("REQUEST_URI");
|
||||
|
||||
// DEBUG: Log request details
|
||||
printf("DEBUG: METHOD=%s, URI=%s\r\n",
|
||||
request_method ? request_method : "NULL",
|
||||
request_uri ? request_uri : "NULL");
|
||||
|
||||
if (!request_method || !request_uri) {
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid request\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle HEAD requests for blob metadata
|
||||
if (strcmp(request_method, "HEAD") == 0) {
|
||||
const char* sha256 = extract_sha256_from_uri(request_uri);
|
||||
printf("DEBUG: Extracted SHA256=%s\r\n", sha256 ? sha256 : "NULL");
|
||||
if (sha256) {
|
||||
handle_head_request(sha256);
|
||||
} else {
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid SHA-256 hash in URI\n");
|
||||
}
|
||||
} else {
|
||||
// Other methods not implemented yet
|
||||
printf("Status: 501 Not Implemented\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Method %s not implemented\n", request_method);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user