Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1ea256076 | ||
|
|
09654fa557 | ||
|
|
469d2180c1 | ||
|
|
fa5f5e60cb |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ blobs/
|
||||
c-relay/
|
||||
text_graph/
|
||||
.test_keys
|
||||
ginxsom-local.service
|
||||
ginxsom-local.service
|
||||
*.tar.gz
|
||||
|
||||
@@ -59,12 +59,8 @@ RUN cd /tmp && \
|
||||
make install && \
|
||||
rm -rf /tmp/secp256k1
|
||||
|
||||
# Fetch nostr_core_lib source directly over HTTPS.
|
||||
# This avoids relying on local git metadata/submodule state in the Docker build context.
|
||||
ARG NOSTR_CORE_LIB_REF=2e7eacc02eef0ccf261f655330dc6a43d6cebdfb
|
||||
RUN git clone https://git.laantungir.net/laantungir/nostr_core_lib.git /build/nostr_core_lib && \
|
||||
cd /build/nostr_core_lib && \
|
||||
git checkout "$NOSTR_CORE_LIB_REF"
|
||||
# Copy nostr_core_lib from local submodule (avoids remote git dependency)
|
||||
COPY nostr_core_lib /build/nostr_core_lib
|
||||
|
||||
# Build nostr_core_lib with required NIPs (cached unless submodule changes)
|
||||
# Disable fortification in build.sh to prevent __*_chk symbol issues
|
||||
@@ -72,8 +68,20 @@ RUN git clone https://git.laantungir.net/laantungir/nostr_core_lib.git /build/no
|
||||
RUN cd nostr_core_lib && \
|
||||
chmod +x build.sh && \
|
||||
sed -i 's/CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"/CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"/' build.sh && \
|
||||
if [ "$(uname -m)" = "aarch64" ] && ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \
|
||||
ln -sf /usr/bin/gcc /usr/local/bin/aarch64-linux-gnu-gcc; \
|
||||
fi && \
|
||||
rm -f *.o *.a 2>/dev/null || true && \
|
||||
./build.sh --nips=1,6,13,17,19,42,44,59
|
||||
./build.sh --nips=1,6,13,17,19,42,44,59 && \
|
||||
if [ -f libnostr_core_arm64.a ]; then \
|
||||
cp libnostr_core_arm64.a libnostr_core.a; \
|
||||
elif [ -f libnostr_core_x64.a ]; then \
|
||||
cp libnostr_core_x64.a libnostr_core.a; \
|
||||
else \
|
||||
echo "ERROR: No supported nostr_core static library produced"; \
|
||||
ls -la *.a 2>/dev/null || true; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
# Copy web interface files for embedding
|
||||
# Note: Changes to api/ files will trigger rebuild from this point
|
||||
@@ -110,7 +118,7 @@ RUN if [ "$DEBUG_BUILD" = "true" ]; then \
|
||||
src/request_validator.c src/relay_client.c \
|
||||
nostr_core_lib/nostr_core/core_relay_pool.c \
|
||||
-o /build/ginxsom-fcgi_static \
|
||||
nostr_core_lib/libnostr_core_x64.a \
|
||||
nostr_core_lib/libnostr_core.a \
|
||||
-lfcgi -lsqlite3 -lsecp256k1 -lssl -lcrypto -lcurl \
|
||||
-lnghttp2 -lcares -lidn2 -lunistring -lpsl -lbrotlidec -lbrotlicommon \
|
||||
-lz -lpthread -lm -ldl && \
|
||||
|
||||
BIN
build/ginxsom-fcgi_static_arm64
Executable file
BIN
build/ginxsom-fcgi_static_arm64
Executable file
Binary file not shown.
Binary file not shown.
110
build_static.sh
110
build_static.sh
@@ -11,8 +11,40 @@ DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl"
|
||||
|
||||
# Parse command line arguments
|
||||
DEBUG_BUILD=false
|
||||
if [[ "$1" == "--debug" ]]; then
|
||||
DEBUG_BUILD=true
|
||||
TARGET_ARCH=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--debug)
|
||||
DEBUG_BUILD=true
|
||||
shift
|
||||
;;
|
||||
--arch)
|
||||
if [[ -z "$2" ]]; then
|
||||
echo "ERROR: --arch requires a value"
|
||||
echo "Usage: $0 [--debug] [--arch <arm64|armv7|x86_64>]"
|
||||
exit 1
|
||||
fi
|
||||
case "$2" in
|
||||
arm64|armv7|x86_64)
|
||||
TARGET_ARCH="$2"
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unsupported architecture '$2'"
|
||||
echo "Supported values: arm64, armv7, x86_64"
|
||||
exit 1
|
||||
esac
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: Unknown argument '$1'"
|
||||
echo "Usage: $0 [--debug] [--arch <arm64|armv7|x86_64>]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "$DEBUG_BUILD" = true ]; then
|
||||
echo "=========================================="
|
||||
echo "Ginxsom MUSL Static Binary Builder (DEBUG MODE)"
|
||||
echo "=========================================="
|
||||
@@ -24,6 +56,9 @@ fi
|
||||
echo "Project directory: $SCRIPT_DIR"
|
||||
echo "Build directory: $BUILD_DIR"
|
||||
echo "Debug build: $DEBUG_BUILD"
|
||||
if [[ -n "$TARGET_ARCH" ]]; then
|
||||
echo "Requested target architecture: $TARGET_ARCH"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Create build directory
|
||||
@@ -58,17 +93,43 @@ DOCKER_CMD="docker"
|
||||
echo "✓ Docker is available and running"
|
||||
echo ""
|
||||
|
||||
# Detect architecture
|
||||
# Detect host architecture
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
HOST_ARCH="x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
HOST_ARCH="arm64"
|
||||
;;
|
||||
armv7l|armv7)
|
||||
HOST_ARCH="armv7"
|
||||
;;
|
||||
*)
|
||||
HOST_ARCH="unknown"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Resolve target architecture
|
||||
if [[ -n "$TARGET_ARCH" ]]; then
|
||||
ARCH="$TARGET_ARCH"
|
||||
fi
|
||||
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
PLATFORM="linux/amd64"
|
||||
OUTPUT_NAME="ginxsom-fcgi_static_x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
ARCH="arm64"
|
||||
PLATFORM="linux/arm64"
|
||||
OUTPUT_NAME="ginxsom-fcgi_static_arm64"
|
||||
;;
|
||||
armv7l|armv7)
|
||||
ARCH="armv7"
|
||||
PLATFORM="linux/arm/v7"
|
||||
OUTPUT_NAME="ginxsom-fcgi_static_armv7"
|
||||
;;
|
||||
*)
|
||||
echo "WARNING: Unknown architecture: $ARCH"
|
||||
echo "Defaulting to linux/amd64"
|
||||
@@ -77,10 +138,51 @@ case "$ARCH" in
|
||||
;;
|
||||
esac
|
||||
|
||||
# When cross-building, ensure buildx exists and warn about QEMU/binfmt setup
|
||||
if [[ "$HOST_ARCH" != "unknown" && "$ARCH" != "$HOST_ARCH" ]]; then
|
||||
echo "NOTE: Cross-building from host '$HOST_ARCH' to target '$ARCH'."
|
||||
echo " Docker QEMU/binfmt support is required for this to work."
|
||||
if ! docker buildx version >/dev/null 2>&1; then
|
||||
echo "ERROR: docker buildx is required for cross-architecture builds but is not available."
|
||||
echo "Install/enable buildx and binfmt support, then retry."
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ docker buildx is available for cross-architecture build"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Append _debug suffix to output name for debug builds so production binary is never overwritten
|
||||
if [ "$DEBUG_BUILD" = true ]; then
|
||||
OUTPUT_NAME="${OUTPUT_NAME}_debug"
|
||||
fi
|
||||
|
||||
echo "Building for platform: $PLATFORM"
|
||||
echo "Output binary: $OUTPUT_NAME"
|
||||
echo ""
|
||||
|
||||
# Check if Alpine base image is cached
|
||||
echo "Checking for cached Alpine Docker image..."
|
||||
if ! docker images alpine:3.19 --format "{{.Repository}}:{{.Tag}}" | grep -q "alpine:3.19"; then
|
||||
echo "⚠ Alpine 3.19 image not found in cache"
|
||||
echo "Attempting to pull Alpine 3.19 image..."
|
||||
if ! docker pull alpine:3.19; then
|
||||
echo ""
|
||||
echo "ERROR: Failed to pull Alpine 3.19 image"
|
||||
echo "This is required for the static build."
|
||||
echo ""
|
||||
echo "Possible solutions:"
|
||||
echo " 1. Check your internet connection"
|
||||
echo " 2. Try again later (Docker Hub may be temporarily unavailable)"
|
||||
echo " 3. If you have IPv6 issues, disable IPv6 for Docker"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Alpine 3.19 image pulled successfully"
|
||||
else
|
||||
echo "✓ Alpine 3.19 image found in cache"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Build the Docker image
|
||||
echo "=========================================="
|
||||
echo "Step 1: Building Alpine Docker image"
|
||||
@@ -220,4 +322,4 @@ fi
|
||||
echo ""
|
||||
echo "Deployment:"
|
||||
echo " scp $BUILD_DIR/$OUTPUT_NAME user@server:/path/to/ginxsom/"
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
@@ -8,17 +8,65 @@ YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
print_status() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
||||
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
|
||||
print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||
print_status() { echo -e "${BLUE}[INFO]${NC} $1" >&2; }
|
||||
print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" >&2; }
|
||||
print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" >&2; }
|
||||
print_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
|
||||
|
||||
# Global variables
|
||||
COMMIT_MESSAGE=""
|
||||
RELEASE_MODE=false
|
||||
VERSION_INCREMENT_TYPE="patch" # "patch", "minor", or "major"
|
||||
VERSION_INCREMENT_EXPLICIT=false
|
||||
|
||||
# TODO: Update this URL to match your actual Gitea repository
|
||||
GITEA_REPO_URL="https://git.example.com/api/v1/repos/username/ginxsom"
|
||||
GITEA_REPO_URL="https://git.laantungir.net/api/v1/repos/laantungir/ginxsom"
|
||||
|
||||
show_usage() {
|
||||
echo "Ginxsom Increment and Push Script"
|
||||
echo ""
|
||||
echo "USAGE:"
|
||||
echo " $0 [OPTIONS] \"commit message\""
|
||||
echo ""
|
||||
echo "COMMANDS:"
|
||||
echo " $0 \"commit message\" Default: increment patch, commit & push"
|
||||
echo " $0 -p \"commit message\" Increment patch version"
|
||||
echo " $0 -m \"commit message\" Increment minor version"
|
||||
echo " $0 -M \"commit message\" Increment major version"
|
||||
echo " $0 -r \"commit message\" Create release with assets (no version increment)"
|
||||
echo " $0 -r -p \"commit message\" Create release with patch version increment"
|
||||
echo " $0 -r -m \"commit message\" Create release with minor version increment"
|
||||
echo " $0 -h Show this help message"
|
||||
echo ""
|
||||
echo "OPTIONS:"
|
||||
echo " -p, --patch Increment patch version (default)"
|
||||
echo " -m, --minor Increment minor version"
|
||||
echo " -M, --major Increment major version"
|
||||
echo " -r, --release Create release with assets"
|
||||
echo " -h, --help Show this help message"
|
||||
echo ""
|
||||
echo "EXAMPLES:"
|
||||
echo " $0 \"Fixed authentication bug\""
|
||||
echo " $0 -m \"Added new features\""
|
||||
echo " $0 -M \"Breaking API changes\""
|
||||
echo " $0 -r \"Release current version\""
|
||||
echo " $0 -r -p \"Release with patch increment\""
|
||||
echo " $0 -r -m \"Release with minor increment\""
|
||||
echo ""
|
||||
echo "VERSION INCREMENT MODES:"
|
||||
echo " -p, --patch (default): Increment patch version (v1.2.3 → v1.2.4)"
|
||||
echo " -m, --minor: Increment minor version, zero patch (v1.2.3 → v1.3.0)"
|
||||
echo " -M, --major: Increment major version, zero minor+patch (v1.2.3 → v2.0.0)"
|
||||
echo ""
|
||||
echo "RELEASE MODE (-r flag):"
|
||||
echo " - Build static binaries for x86_64 and ARM64 using build_static.sh"
|
||||
echo " - Create source tarball"
|
||||
echo " - Git add, commit, push, and create Gitea release with assets"
|
||||
echo " - Can be combined with version increment flags"
|
||||
echo ""
|
||||
echo "REQUIREMENTS FOR RELEASE MODE:"
|
||||
echo " - Gitea token in ~/.gitea_token for release uploads"
|
||||
echo " - Docker installed for static binary builds"
|
||||
}
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
@@ -27,6 +75,21 @@ while [[ $# -gt 0 ]]; do
|
||||
RELEASE_MODE=true
|
||||
shift
|
||||
;;
|
||||
-p|--patch)
|
||||
VERSION_INCREMENT_TYPE="patch"
|
||||
VERSION_INCREMENT_EXPLICIT=true
|
||||
shift
|
||||
;;
|
||||
-m|--minor)
|
||||
VERSION_INCREMENT_TYPE="minor"
|
||||
VERSION_INCREMENT_EXPLICIT=true
|
||||
shift
|
||||
;;
|
||||
-M|--major)
|
||||
VERSION_INCREMENT_TYPE="major"
|
||||
VERSION_INCREMENT_EXPLICIT=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
@@ -41,32 +104,6 @@ while [[ $# -gt 0 ]]; do
|
||||
esac
|
||||
done
|
||||
|
||||
show_usage() {
|
||||
echo "Ginxsom Build and Push Script"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $0 \"commit message\" - Default: compile, increment patch, commit & push"
|
||||
echo " $0 -r \"commit message\" - Release: compile, increment minor, create release"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 \"Fixed authentication bug\""
|
||||
echo " $0 --release \"Major release with admin API\""
|
||||
echo ""
|
||||
echo "Default Mode (patch increment):"
|
||||
echo " - Compile Ginxsom FastCGI server"
|
||||
echo " - Increment patch version (v1.2.3 → v1.2.4)"
|
||||
echo " - Git add, commit with message, and push"
|
||||
echo ""
|
||||
echo "Release Mode (-r flag):"
|
||||
echo " - Compile Ginxsom FastCGI server"
|
||||
echo " - Increment minor version, zero patch (v1.2.3 → v1.3.0)"
|
||||
echo " - Git add, commit, push, and create Gitea release"
|
||||
echo ""
|
||||
echo "Requirements for Release Mode:"
|
||||
echo " - Gitea token in ~/.gitea_token for release uploads"
|
||||
echo " - Update GITEA_REPO_URL in script for your repository"
|
||||
}
|
||||
|
||||
# Validate inputs
|
||||
if [[ -z "$COMMIT_MESSAGE" ]]; then
|
||||
print_error "Commit message is required"
|
||||
@@ -85,20 +122,20 @@ check_git_repo() {
|
||||
|
||||
# Function to get current version and increment appropriately
|
||||
increment_version() {
|
||||
local increment_type="$1" # "patch" or "minor"
|
||||
|
||||
local increment_type="$1" # "patch", "minor", or "major"
|
||||
|
||||
print_status "Getting current version..."
|
||||
|
||||
|
||||
# Get the highest version tag (not chronologically latest)
|
||||
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "")
|
||||
if [[ -z "$LATEST_TAG" ]]; then
|
||||
LATEST_TAG="v0.0.0"
|
||||
print_warning "No version tags found, starting from $LATEST_TAG"
|
||||
fi
|
||||
|
||||
|
||||
# Extract version components (remove 'v' prefix)
|
||||
VERSION=${LATEST_TAG#v}
|
||||
|
||||
|
||||
# Parse major.minor.patch using regex
|
||||
if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
MAJOR=${BASH_REMATCH[1]}
|
||||
@@ -109,113 +146,74 @@ increment_version() {
|
||||
print_error "Expected format: v0.1.0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
# Increment version based on type
|
||||
if [[ "$increment_type" == "minor" ]]; then
|
||||
# Minor release: increment minor, zero patch
|
||||
if [[ "$increment_type" == "major" ]]; then
|
||||
NEW_MAJOR=$((MAJOR + 1))
|
||||
NEW_MINOR=0
|
||||
NEW_PATCH=0
|
||||
NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
|
||||
print_status "Major version increment: incrementing major version"
|
||||
elif [[ "$increment_type" == "minor" ]]; then
|
||||
NEW_MAJOR=$MAJOR
|
||||
NEW_MINOR=$((MINOR + 1))
|
||||
NEW_PATCH=0
|
||||
NEW_VERSION="v${MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
|
||||
print_status "Release mode: incrementing minor version"
|
||||
NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
|
||||
print_status "Minor version increment: incrementing minor version"
|
||||
else
|
||||
# Default: increment patch
|
||||
NEW_MAJOR=$MAJOR
|
||||
NEW_MINOR=$MINOR
|
||||
NEW_PATCH=$((PATCH + 1))
|
||||
NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}"
|
||||
print_status "Default mode: incrementing patch version"
|
||||
NEW_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
|
||||
print_status "Patch version increment: incrementing patch version"
|
||||
fi
|
||||
|
||||
|
||||
print_status "Current version: $LATEST_TAG"
|
||||
print_status "New version: $NEW_VERSION"
|
||||
|
||||
|
||||
# Update version in src/ginxsom.h
|
||||
update_version_in_header "$NEW_VERSION" "$NEW_MAJOR" "$NEW_MINOR" "$NEW_PATCH"
|
||||
|
||||
# Export for use in other functions
|
||||
export NEW_VERSION
|
||||
}
|
||||
|
||||
# Function to update version in header file
|
||||
# Function to update version macros in src/ginxsom.h
|
||||
update_version_in_header() {
|
||||
local version="$1"
|
||||
print_status "Updating version in src/ginxsom.h to $version..."
|
||||
local new_version="$1"
|
||||
local major="$2"
|
||||
local minor="$3"
|
||||
local patch="$4"
|
||||
|
||||
# Extract version components (remove 'v' prefix)
|
||||
local version_no_v=${version#v}
|
||||
print_status "Updating version in src/ginxsom.h..."
|
||||
|
||||
# Parse major.minor.patch using regex
|
||||
if [[ $version_no_v =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
|
||||
local major=${BASH_REMATCH[1]}
|
||||
local minor=${BASH_REMATCH[2]}
|
||||
local patch=${BASH_REMATCH[3]}
|
||||
|
||||
# Update the header file
|
||||
sed -i "s/#define VERSION_MAJOR [0-9]\+/#define VERSION_MAJOR $major/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION_MINOR [0-9]\+/#define VERSION_MINOR $minor/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION_PATCH [0-9]\+/#define VERSION_PATCH $patch/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION \"v[0-9]\+\.[0-9]\+\.[0-9]\+\"/#define VERSION \"$version\"/" src/ginxsom.h
|
||||
|
||||
print_success "Updated version in header file"
|
||||
else
|
||||
print_error "Invalid version format: $version"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to build the static Ginxsom binary
|
||||
compile_project() {
|
||||
print_status "Building static Ginxsom FastCGI binary via build_static.sh..."
|
||||
|
||||
if [[ ! -x "./build_static.sh" ]]; then
|
||||
print_error "build_static.sh not found or not executable"
|
||||
if [[ ! -f "src/ginxsom.h" ]]; then
|
||||
print_error "src/ginxsom.h not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ./build_static.sh > /dev/null 2>&1; then
|
||||
print_success "Static build completed successfully"
|
||||
sed -i "s/#define VERSION_MAJOR [0-9]\+/#define VERSION_MAJOR $major/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION_MINOR [0-9]\+/#define VERSION_MINOR $minor/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION_PATCH [0-9]\+/#define VERSION_PATCH $patch/" src/ginxsom.h
|
||||
sed -i "s/#define VERSION \"v[0-9]\+\.[0-9]\+\.[0-9]\+\"/#define VERSION \"$new_version\"/" src/ginxsom.h
|
||||
|
||||
# Verify the static binary was created
|
||||
if [[ -f "build/ginxsom-fcgi_static_x86_64" || -f "build/ginxsom-fcgi_static_arm64" ]]; then
|
||||
print_success "Static binary created in build/"
|
||||
else
|
||||
print_error "Static binary not found after build_static.sh"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_error "Static build failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to build release binary
|
||||
build_release_binary() {
|
||||
print_status "Preparing release binary from static build output..."
|
||||
|
||||
if [[ -f "build/ginxsom-fcgi_static_x86_64" ]]; then
|
||||
cp build/ginxsom-fcgi_static_x86_64 ginxsom-fcgi-linux-x86_64
|
||||
print_success "Release binary created: ginxsom-fcgi-linux-x86_64"
|
||||
elif [[ -f "build/ginxsom-fcgi_static_arm64" ]]; then
|
||||
cp build/ginxsom-fcgi_static_arm64 ginxsom-fcgi-linux-arm64
|
||||
print_success "Release binary created: ginxsom-fcgi-linux-arm64"
|
||||
else
|
||||
print_error "No static build artifact found in build/"
|
||||
exit 1
|
||||
fi
|
||||
print_success "Updated version in src/ginxsom.h to $new_version"
|
||||
}
|
||||
|
||||
# Function to commit and push changes
|
||||
git_commit_and_push() {
|
||||
print_status "Preparing git commit..."
|
||||
|
||||
# Stage all changes
|
||||
|
||||
if git add . > /dev/null 2>&1; then
|
||||
print_success "Staged all changes"
|
||||
else
|
||||
print_error "Failed to stage changes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if there are changes to commit
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
print_warning "No changes to commit"
|
||||
else
|
||||
# Commit changes
|
||||
if git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" > /dev/null 2>&1; then
|
||||
print_success "Committed changes"
|
||||
else
|
||||
@@ -223,15 +221,13 @@ git_commit_and_push() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create new git tag
|
||||
|
||||
if git tag "$NEW_VERSION" > /dev/null 2>&1; then
|
||||
print_success "Created tag: $NEW_VERSION"
|
||||
else
|
||||
print_warning "Tag $NEW_VERSION already exists"
|
||||
fi
|
||||
|
||||
# Push changes and tags
|
||||
|
||||
print_status "Pushing to remote repository..."
|
||||
if git push > /dev/null 2>&1; then
|
||||
print_success "Pushed changes"
|
||||
@@ -239,8 +235,7 @@ git_commit_and_push() {
|
||||
print_error "Failed to push changes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Push only the new tag to avoid conflicts with existing tags
|
||||
|
||||
if git push origin "$NEW_VERSION" > /dev/null 2>&1; then
|
||||
print_success "Pushed tag: $NEW_VERSION"
|
||||
else
|
||||
@@ -257,20 +252,17 @@ git_commit_and_push() {
|
||||
# Function to commit and push changes without creating a tag (tag already created)
|
||||
git_commit_and_push_no_tag() {
|
||||
print_status "Preparing git commit..."
|
||||
|
||||
# Stage all changes
|
||||
|
||||
if git add . > /dev/null 2>&1; then
|
||||
print_success "Staged all changes"
|
||||
else
|
||||
print_error "Failed to stage changes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if there are changes to commit
|
||||
|
||||
if git diff --staged --quiet; then
|
||||
print_warning "No changes to commit"
|
||||
else
|
||||
# Commit changes
|
||||
if git commit -m "$NEW_VERSION - $COMMIT_MESSAGE" > /dev/null 2>&1; then
|
||||
print_success "Committed changes"
|
||||
else
|
||||
@@ -278,8 +270,7 @@ git_commit_and_push_no_tag() {
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Push changes and tags
|
||||
|
||||
print_status "Pushing to remote repository..."
|
||||
if git push > /dev/null 2>&1; then
|
||||
print_success "Pushed changes"
|
||||
@@ -287,8 +278,7 @@ git_commit_and_push_no_tag() {
|
||||
print_error "Failed to push changes"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Push only the new tag to avoid conflicts with existing tags
|
||||
|
||||
if git push origin "$NEW_VERSION" > /dev/null 2>&1; then
|
||||
print_success "Pushed tag: $NEW_VERSION"
|
||||
else
|
||||
@@ -302,42 +292,198 @@ git_commit_and_push_no_tag() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to build release binaries
|
||||
build_release_binary() {
|
||||
print_status "Building release binaries..."
|
||||
|
||||
if [[ ! -f "build_static.sh" ]]; then
|
||||
print_error "build_static.sh not found"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Build x86_64 first (required for success)
|
||||
if ./build_static.sh > /dev/null 2>&1; then
|
||||
print_success "Built x86_64 static binary successfully"
|
||||
else
|
||||
print_error "Failed to build x86_64 static binary"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Build arm64 second (warning-only on failure)
|
||||
if ./build_static.sh --arch arm64 > /dev/null 2>&1; then
|
||||
print_success "Built ARM64 static binary successfully"
|
||||
else
|
||||
print_warning "Failed to build ARM64 static binary (continuing with release)"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Function to create source tarball
|
||||
create_source_tarball() {
|
||||
print_status "Creating source tarball..."
|
||||
|
||||
local tarball_name="ginxsom-${NEW_VERSION#v}.tar.gz"
|
||||
|
||||
# Remove any existing tarball first to avoid self-inclusion
|
||||
rm -f "$tarball_name"
|
||||
|
||||
if tar -czf "$tarball_name" \
|
||||
--exclude='./build' \
|
||||
--exclude='./.git' \
|
||||
--exclude='./.gitmodules' \
|
||||
--exclude='./db/*.db' \
|
||||
--exclude='./db/*.db-*' \
|
||||
--exclude='./*.log' \
|
||||
--exclude="./$tarball_name" \
|
||||
--exclude='./Trash' \
|
||||
--exclude='./*.tar.gz' \
|
||||
. 2>/dev/null; then
|
||||
print_success "Created source tarball: $tarball_name"
|
||||
echo "$tarball_name"
|
||||
return 0
|
||||
else
|
||||
print_error "Failed to create source tarball"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to upload release assets to Gitea
|
||||
upload_release_assets() {
|
||||
local release_id="$1"
|
||||
local binary_path_x86="$2"
|
||||
local tarball_path="$3"
|
||||
local binary_path_arm64="$4"
|
||||
|
||||
print_status "Uploading release assets..."
|
||||
|
||||
if [[ ! -f "$HOME/.gitea_token" ]]; then
|
||||
print_warning "No ~/.gitea_token found. Skipping asset uploads."
|
||||
return 0
|
||||
fi
|
||||
|
||||
local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r')
|
||||
local assets_url="$GITEA_REPO_URL/releases/$release_id/assets"
|
||||
print_status "Assets URL: $assets_url"
|
||||
|
||||
# Upload x86_64 binary
|
||||
if [[ -f "$binary_path_x86" ]]; then
|
||||
print_status "Uploading binary: $(basename "$binary_path_x86")"
|
||||
|
||||
local max_attempts=3
|
||||
local attempt=1
|
||||
while [[ $attempt -le $max_attempts ]]; do
|
||||
print_status "Upload attempt $attempt/$max_attempts"
|
||||
local binary_response=$(curl -fS -X POST "$assets_url" \
|
||||
-H "Authorization: token $token" \
|
||||
-F "attachment=@$binary_path_x86;filename=$(basename "$binary_path_x86")" \
|
||||
-F "name=$(basename "$binary_path_x86")")
|
||||
|
||||
if echo "$binary_response" | grep -q '"id"'; then
|
||||
print_success "Uploaded x86_64 binary successfully"
|
||||
break
|
||||
else
|
||||
print_warning "Upload attempt $attempt failed"
|
||||
if [[ $attempt -lt $max_attempts ]]; then
|
||||
print_status "Retrying in 2 seconds..."
|
||||
sleep 2
|
||||
else
|
||||
print_error "Failed to upload x86_64 binary after $max_attempts attempts"
|
||||
print_error "Response: $binary_response"
|
||||
fi
|
||||
fi
|
||||
((attempt++))
|
||||
done
|
||||
fi
|
||||
|
||||
# Upload ARM64 binary
|
||||
if [[ -f "$binary_path_arm64" ]]; then
|
||||
print_status "Uploading binary: $(basename "$binary_path_arm64")"
|
||||
|
||||
local max_attempts=3
|
||||
local attempt=1
|
||||
while [[ $attempt -le $max_attempts ]]; do
|
||||
print_status "Upload attempt $attempt/$max_attempts"
|
||||
local binary_response=$(curl -fS -X POST "$assets_url" \
|
||||
-H "Authorization: token $token" \
|
||||
-F "attachment=@$binary_path_arm64;filename=$(basename "$binary_path_arm64")" \
|
||||
-F "name=$(basename "$binary_path_arm64")")
|
||||
|
||||
if echo "$binary_response" | grep -q '"id"'; then
|
||||
print_success "Uploaded ARM64 binary successfully"
|
||||
break
|
||||
else
|
||||
print_warning "Upload attempt $attempt failed"
|
||||
if [[ $attempt -lt $max_attempts ]]; then
|
||||
print_status "Retrying in 2 seconds..."
|
||||
sleep 2
|
||||
else
|
||||
print_error "Failed to upload ARM64 binary after $max_attempts attempts"
|
||||
print_error "Response: $binary_response"
|
||||
fi
|
||||
fi
|
||||
((attempt++))
|
||||
done
|
||||
fi
|
||||
|
||||
# Upload source tarball
|
||||
if [[ -f "$tarball_path" ]]; then
|
||||
print_status "Uploading source tarball: $(basename "$tarball_path")"
|
||||
local tarball_response=$(curl -s -X POST "$GITEA_REPO_URL/releases/$release_id/assets" \
|
||||
-H "Authorization: token $token" \
|
||||
-F "attachment=@$tarball_path;filename=$(basename "$tarball_path")")
|
||||
|
||||
if echo "$tarball_response" | grep -q '"id"'; then
|
||||
print_success "Uploaded source tarball successfully"
|
||||
else
|
||||
print_warning "Failed to upload source tarball: $tarball_response"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create Gitea release
|
||||
create_gitea_release() {
|
||||
print_status "Creating Gitea release..."
|
||||
|
||||
# Check for Gitea token
|
||||
|
||||
if [[ ! -f "$HOME/.gitea_token" ]]; then
|
||||
print_warning "No ~/.gitea_token found. Skipping release creation."
|
||||
print_warning "Create ~/.gitea_token with your Gitea access token to enable releases."
|
||||
return 0
|
||||
fi
|
||||
|
||||
|
||||
local token=$(cat "$HOME/.gitea_token" | tr -d '\n\r')
|
||||
|
||||
# Create release
|
||||
|
||||
print_status "Creating release $NEW_VERSION..."
|
||||
local response=$(curl -s -X POST "$GITEA_REPO_URL/releases" \
|
||||
-H "Authorization: token $token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\": \"$NEW_VERSION\", \"name\": \"$NEW_VERSION\", \"body\": \"$COMMIT_MESSAGE\"}")
|
||||
|
||||
-H "Authorization: token $token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\": \"$NEW_VERSION\", \"name\": \"$NEW_VERSION\", \"body\": \"$COMMIT_MESSAGE\"}")
|
||||
|
||||
if echo "$response" | grep -q '"id"'; then
|
||||
print_success "Created release $NEW_VERSION"
|
||||
upload_release_binary "$token"
|
||||
local release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
echo $release_id
|
||||
elif echo "$response" | grep -q "already exists"; then
|
||||
print_warning "Release $NEW_VERSION already exists"
|
||||
upload_release_binary "$token"
|
||||
local check_response=$(curl -s -H "Authorization: token $token" "$GITEA_REPO_URL/releases/tags/$NEW_VERSION")
|
||||
if echo "$check_response" | grep -q '"id"'; then
|
||||
local release_id=$(echo "$check_response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
print_status "Using existing release ID: $release_id"
|
||||
echo $release_id
|
||||
else
|
||||
print_error "Could not find existing release ID"
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
print_error "Failed to create release $NEW_VERSION"
|
||||
print_error "Response: $response"
|
||||
|
||||
# Try to check if the release exists anyway
|
||||
|
||||
print_status "Checking if release exists..."
|
||||
local check_response=$(curl -s -H "Authorization: token $token" "$GITEA_REPO_URL/releases/tags/$NEW_VERSION")
|
||||
if echo "$check_response" | grep -q '"id"'; then
|
||||
print_warning "Release exists but creation response was unexpected"
|
||||
upload_release_binary "$token"
|
||||
local release_id=$(echo "$check_response" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
echo $release_id
|
||||
else
|
||||
print_error "Release does not exist and creation failed"
|
||||
return 1
|
||||
@@ -345,78 +491,26 @@ create_gitea_release() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to upload release binary
|
||||
upload_release_binary() {
|
||||
local token="$1"
|
||||
|
||||
# Get release ID with more robust parsing
|
||||
print_status "Getting release ID for $NEW_VERSION..."
|
||||
local response=$(curl -s -H "Authorization: token $token" "$GITEA_REPO_URL/releases/tags/$NEW_VERSION")
|
||||
local release_id=$(echo "$response" | grep -o '"id":[0-9]*' | head -n1 | cut -d: -f2)
|
||||
|
||||
if [[ -z "$release_id" ]]; then
|
||||
print_error "Could not get release ID for $NEW_VERSION"
|
||||
print_error "API Response: $response"
|
||||
|
||||
# Try to list all releases to debug
|
||||
print_status "Available releases:"
|
||||
curl -s -H "Authorization: token $token" "$GITEA_REPO_URL/releases" | grep -o '"tag_name":"[^"]*"' | head -5
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_success "Found release ID: $release_id"
|
||||
|
||||
# Upload FastCGI binary
|
||||
if [[ -f "ginxsom-fcgi-linux-x86_64" ]]; then
|
||||
print_status "Uploading Ginxsom FastCGI binary..."
|
||||
if curl -s -X POST "$GITEA_REPO_URL/releases/$release_id/assets" \
|
||||
-H "Authorization: token $token" \
|
||||
-F "attachment=@ginxsom-fcgi-linux-x86_64;filename=ginxsom-fcgi-${NEW_VERSION}-linux-x86_64" > /dev/null; then
|
||||
print_success "Uploaded FastCGI binary"
|
||||
else
|
||||
print_warning "Failed to upload FastCGI binary"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "ginxsom-fcgi-linux-arm64" ]]; then
|
||||
print_status "Uploading Ginxsom ARM64 FastCGI binary..."
|
||||
if curl -s -X POST "$GITEA_REPO_URL/releases/$release_id/assets" \
|
||||
-H "Authorization: token $token" \
|
||||
-F "attachment=@ginxsom-fcgi-linux-arm64;filename=ginxsom-fcgi-${NEW_VERSION}-linux-arm64" > /dev/null; then
|
||||
print_success "Uploaded ARM64 FastCGI binary"
|
||||
else
|
||||
print_warning "Failed to upload ARM64 FastCGI binary"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to clean up release binary
|
||||
cleanup_release_binary() {
|
||||
if [[ -f "ginxsom-fcgi-linux-x86_64" ]]; then
|
||||
rm -f ginxsom-fcgi-linux-x86_64
|
||||
print_status "Cleaned up release binary"
|
||||
fi
|
||||
|
||||
if [[ -f "ginxsom-fcgi-linux-arm64" ]]; then
|
||||
rm -f ginxsom-fcgi-linux-arm64
|
||||
print_status "Cleaned up ARM64 release binary"
|
||||
fi
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
print_status "Ginxsom Build and Push Script"
|
||||
|
||||
print_status "Ginxsom Increment and Push Script"
|
||||
|
||||
# Check prerequisites
|
||||
check_git_repo
|
||||
|
||||
|
||||
if [[ "$RELEASE_MODE" == true ]]; then
|
||||
print_status "=== RELEASE MODE ==="
|
||||
|
||||
# Increment minor version for releases
|
||||
increment_version "minor"
|
||||
|
||||
# Create new git tag BEFORE compilation
|
||||
|
||||
# Only increment version if explicitly requested
|
||||
if [[ "$VERSION_INCREMENT_EXPLICIT" == true ]]; then
|
||||
increment_version "$VERSION_INCREMENT_TYPE"
|
||||
else
|
||||
LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.0.0")
|
||||
NEW_VERSION="$LATEST_TAG"
|
||||
export NEW_VERSION
|
||||
fi
|
||||
|
||||
# Create new git tag BEFORE compilation so version is picked up
|
||||
if git tag "$NEW_VERSION" > /dev/null 2>&1; then
|
||||
print_success "Created tag: $NEW_VERSION"
|
||||
else
|
||||
@@ -424,35 +518,63 @@ main() {
|
||||
git tag -d "$NEW_VERSION" > /dev/null 2>&1
|
||||
git tag "$NEW_VERSION" > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Update version in header file
|
||||
update_version_in_header "$NEW_VERSION"
|
||||
|
||||
# Compile project
|
||||
compile_project
|
||||
|
||||
# Build release binary
|
||||
build_release_binary
|
||||
|
||||
# Commit and push (but skip tag creation since we already did it)
|
||||
git_commit_and_push_no_tag
|
||||
|
||||
# Create Gitea release with binary
|
||||
create_gitea_release
|
||||
|
||||
# Cleanup
|
||||
cleanup_release_binary
|
||||
|
||||
print_success "Release $NEW_VERSION completed successfully!"
|
||||
print_status "Binary uploaded to Gitea release"
|
||||
|
||||
|
||||
# Build release binaries
|
||||
local binary_path_x86=""
|
||||
local binary_path_arm64=""
|
||||
if build_release_binary; then
|
||||
if [[ -f "build/ginxsom-fcgi_static_x86_64" ]]; then
|
||||
binary_path_x86="build/ginxsom-fcgi_static_x86_64"
|
||||
fi
|
||||
if [[ -f "build/ginxsom-fcgi_static_arm64" ]]; then
|
||||
binary_path_arm64="build/ginxsom-fcgi_static_arm64"
|
||||
fi
|
||||
else
|
||||
print_warning "x86_64 binary build failed, continuing with release creation"
|
||||
if [[ -f "build/ginxsom-fcgi_static_x86_64" ]]; then
|
||||
print_status "Using existing x86_64 binary from previous build"
|
||||
binary_path_x86="build/ginxsom-fcgi_static_x86_64"
|
||||
fi
|
||||
if [[ -f "build/ginxsom-fcgi_static_arm64" ]]; then
|
||||
print_status "Using existing ARM64 binary from previous build"
|
||||
binary_path_arm64="build/ginxsom-fcgi_static_arm64"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create source tarball
|
||||
local tarball_path=""
|
||||
if tarball_path=$(create_source_tarball); then
|
||||
: # tarball_path is set by the function
|
||||
else
|
||||
print_warning "Source tarball creation failed, continuing with release creation"
|
||||
fi
|
||||
|
||||
# Create Gitea release
|
||||
local release_id=""
|
||||
if release_id=$(create_gitea_release); then
|
||||
if [[ "$release_id" =~ ^[0-9]+$ ]]; then
|
||||
if [[ -n "$release_id" && (-n "$binary_path_x86" || -n "$binary_path_arm64" || -n "$tarball_path") ]]; then
|
||||
upload_release_assets "$release_id" "$binary_path_x86" "$tarball_path" "$binary_path_arm64"
|
||||
fi
|
||||
print_success "Release $NEW_VERSION completed successfully!"
|
||||
else
|
||||
print_error "Invalid release_id: $release_id"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
print_error "Release creation failed"
|
||||
fi
|
||||
|
||||
else
|
||||
print_status "=== DEFAULT MODE ==="
|
||||
|
||||
# Increment patch version for regular commits
|
||||
increment_version "patch"
|
||||
|
||||
# Create new git tag BEFORE compilation
|
||||
|
||||
# Increment version based on type (default to patch)
|
||||
increment_version "$VERSION_INCREMENT_TYPE"
|
||||
|
||||
# Create new git tag BEFORE compilation so version is picked up
|
||||
if git tag "$NEW_VERSION" > /dev/null 2>&1; then
|
||||
print_success "Created tag: $NEW_VERSION"
|
||||
else
|
||||
@@ -460,17 +582,11 @@ main() {
|
||||
git tag -d "$NEW_VERSION" > /dev/null 2>&1
|
||||
git tag "$NEW_VERSION" > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Update version in header file
|
||||
update_version_in_header "$NEW_VERSION"
|
||||
|
||||
# Compile project
|
||||
compile_project
|
||||
|
||||
# Commit and push (but skip tag creation since we already did it)
|
||||
git_commit_and_push_no_tag
|
||||
|
||||
print_success "Build and push completed successfully!"
|
||||
|
||||
print_success "Increment and push completed successfully!"
|
||||
print_status "Version $NEW_VERSION pushed to repository"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
#define GINXSOM_H
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 1
|
||||
#define VERSION_PATCH 38
|
||||
#define VERSION "v0.1.38"
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_PATCH 3
|
||||
#define VERSION "v0.2.3"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
163
src/main.c
163
src/main.c
@@ -314,7 +314,7 @@ int initialize_database(const char *db_path) {
|
||||
const char *insert_config =
|
||||
"INSERT OR IGNORE INTO config (key, value, description) VALUES"
|
||||
" ('max_file_size', '104857600', 'Maximum file size in bytes (100MB)'),"
|
||||
" ('auth_rules_enabled', 'true', 'Whether authentication rules are enabled for uploads'),"
|
||||
" ('auth_rules_enabled', 'false', 'Whether authentication rules are enabled for uploads'),"
|
||||
" ('server_name', 'ginxsom', 'Server name for responses'),"
|
||||
" ('admin_pubkey', '', 'Admin public key for API access'),"
|
||||
" ('admin_enabled', 'true', 'Whether admin API is enabled'),"
|
||||
@@ -471,6 +471,7 @@ void handle_auth_challenge_request(void);
|
||||
|
||||
// Handler function declarations with validation support
|
||||
void handle_delete_request_with_validation(const char *sha256, nostr_request_result_t *validation_result);
|
||||
void handle_get_request(const char *sha256);
|
||||
|
||||
// Key management function implementations
|
||||
|
||||
@@ -1051,7 +1052,7 @@ int get_blob_metadata(const char *sha256, blob_metadata_t *metadata) {
|
||||
sqlite3_stmt *stmt;
|
||||
int rc;
|
||||
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_CREATE, NULL);
|
||||
rc = sqlite3_open_v2(g_db_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
if (rc) {
|
||||
app_log(LOG_ERROR, "Can't open database: %s\n", sqlite3_errmsg(db));
|
||||
return 0;
|
||||
@@ -1170,6 +1171,100 @@ void handle_head_request(const char *sha256) {
|
||||
// HEAD request - no body content
|
||||
}
|
||||
|
||||
// Handle GET request for blob content
|
||||
void handle_get_request(const char *sha256) {
|
||||
blob_metadata_t metadata = {0};
|
||||
|
||||
// Validate SHA-256 format (64 hex characters)
|
||||
if (!sha256 || strlen(sha256) != 64) {
|
||||
printf("Status: 400 Bad Request\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Invalid SHA-256 hash format\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if blob exists in database. If metadata is missing, fall back to
|
||||
// filesystem lookup for anonymous/basic deployments where DB continuity may
|
||||
// be unavailable.
|
||||
int has_metadata = get_blob_metadata(sha256, &metadata);
|
||||
|
||||
const char *extension = NULL;
|
||||
if (has_metadata) {
|
||||
extension = mime_to_extension(metadata.type);
|
||||
} else {
|
||||
// Fallback to default .bin blob path
|
||||
strncpy(metadata.sha256, sha256, sizeof(metadata.sha256) - 1);
|
||||
metadata.sha256[sizeof(metadata.sha256) - 1] = '\0';
|
||||
strncpy(metadata.type, "application/octet-stream", sizeof(metadata.type) - 1);
|
||||
metadata.type[sizeof(metadata.type) - 1] = '\0';
|
||||
metadata.filename[0] = '\0';
|
||||
extension = ".bin";
|
||||
}
|
||||
|
||||
// Resolve file path from extension
|
||||
char filepath[MAX_PATH_LEN];
|
||||
|
||||
size_t dir_len = strlen(g_storage_dir);
|
||||
size_t sha_len = strlen(sha256);
|
||||
size_t ext_len = strlen(extension);
|
||||
size_t total_len = dir_len + 1 + sha_len + ext_len + 1;
|
||||
|
||||
if (total_len > sizeof(filepath)) {
|
||||
send_error_response(500, "path_too_long", "Blob path too long",
|
||||
"Internal path construction overflow prevention triggered");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(filepath, g_storage_dir, dir_len);
|
||||
filepath[dir_len] = '/';
|
||||
memcpy(filepath + dir_len + 1, sha256, sha_len);
|
||||
memcpy(filepath + dir_len + 1 + sha_len, extension, ext_len);
|
||||
filepath[total_len - 1] = '\0';
|
||||
|
||||
FILE *infile = fopen(filepath, "rb");
|
||||
if (!infile) {
|
||||
if (!has_metadata) {
|
||||
printf("Status: 404 Not Found\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Blob not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
send_error_response(404, "blob_not_found", "Blob file not found",
|
||||
"Metadata exists but blob file is missing on disk");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_metadata) {
|
||||
struct stat st;
|
||||
if (stat(filepath, &st) == 0) {
|
||||
metadata.size = st.st_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Return response headers
|
||||
printf("Status: 200 OK\r\n");
|
||||
printf("Content-Type: %s\r\n", metadata.type);
|
||||
printf("Content-Length: %ld\r\n", metadata.size);
|
||||
printf("Cache-Control: public, max-age=31536000, immutable\r\n");
|
||||
printf("ETag: \"%s\"\r\n", metadata.sha256);
|
||||
if (strlen(metadata.filename) > 0) {
|
||||
printf("X-Original-Filename: %s\r\n", metadata.filename);
|
||||
}
|
||||
printf("\r\n");
|
||||
|
||||
// Stream file body
|
||||
unsigned char buffer[8192];
|
||||
size_t nread;
|
||||
while ((nread = fread(buffer, 1, sizeof(buffer), infile)) > 0) {
|
||||
if (fwrite(buffer, 1, nread, stdout) != nread) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
}
|
||||
|
||||
// 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];
|
||||
@@ -1451,16 +1546,12 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
|
||||
}
|
||||
}
|
||||
|
||||
// Get authenticated pubkey from centralized validation result
|
||||
// Authentication is optional for delete when auth bypass is enabled.
|
||||
// If a valid pubkey is present, we preserve ownership enforcement.
|
||||
const char *auth_pubkey = NULL;
|
||||
if (validation_result && validation_result->valid && strlen(validation_result->pubkey) == 64) {
|
||||
if (validation_result && validation_result->valid &&
|
||||
strlen(validation_result->pubkey) == 64) {
|
||||
auth_pubkey = validation_result->pubkey;
|
||||
} else {
|
||||
// No valid authentication - this should have been caught by centralized validation
|
||||
send_error_response(401, "unauthorized", "Authentication required for delete operations",
|
||||
"Valid authentication is required to delete blobs");
|
||||
log_request("DELETE", "/delete", "unauthenticated", 401);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if blob exists in database
|
||||
@@ -1519,15 +1610,16 @@ void handle_delete_request_with_validation(const char *sha256, nostr_request_res
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Check ownership - only the uploader can delete
|
||||
if (!uploader_pubkey_copy[0] ||
|
||||
strcmp(uploader_pubkey_copy, auth_pubkey) != 0) {
|
||||
sqlite3_close(db);
|
||||
send_error_response(403, "access_denied", "Access denied",
|
||||
"You can only delete blobs that you uploaded");
|
||||
log_request("DELETE", "/delete", "ownership_denied", 403);
|
||||
return;
|
||||
} else {
|
||||
// Enforce ownership only when an authenticated pubkey is available.
|
||||
// Anonymous delete is allowed when authentication is bypassed.
|
||||
if (auth_pubkey && auth_pubkey[0]) {
|
||||
if (!uploader_pubkey_copy[0] || strcmp(uploader_pubkey_copy, auth_pubkey) != 0) {
|
||||
sqlite3_close(db);
|
||||
send_error_response(403, "access_denied", "Access denied",
|
||||
"You can only delete blobs that you uploaded");
|
||||
log_request("DELETE", "/delete", "ownership_denied", 403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete from database first
|
||||
@@ -2837,8 +2929,13 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
"Pubkey must be 64 hex characters");
|
||||
log_request("GET", request_uri, "none", 400);
|
||||
}
|
||||
} else if (strcmp(request_method, "GET") == 0 &&
|
||||
strcmp(request_uri, "/") == 0) {
|
||||
} else if (strcmp(request_method, "GET") == 0) {
|
||||
// Handle GET /<sha256> blob retrieval
|
||||
const char *sha256 = extract_sha256_from_uri(request_uri);
|
||||
if (sha256) {
|
||||
handle_get_request(sha256);
|
||||
log_request("GET", request_uri, "public", 200);
|
||||
} else if (strcmp(request_uri, "/") == 0) {
|
||||
// Handle GET / requests - Server info endpoint (NIP-11)
|
||||
printf("Status: 200 OK\r\n");
|
||||
printf("Content-Type: application/nostr+json\r\n\r\n");
|
||||
@@ -2880,18 +2977,16 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
printf(" },\n");
|
||||
printf(" \"authentication\": {\n");
|
||||
printf(" \"required_for_upload\": false,\n");
|
||||
printf(" \"required_for_delete\": true,\n");
|
||||
printf(" \"required_for_delete\": false,\n");
|
||||
printf(" \"required_for_list\": false,\n");
|
||||
printf(" \"nip42_enabled\": true\n");
|
||||
printf(" \"nip42_enabled\": false\n");
|
||||
printf(" }\n");
|
||||
printf("}\n");
|
||||
log_request("GET", "/", "server_info", 200);
|
||||
} else if (strcmp(request_method, "GET") == 0 &&
|
||||
strcmp(request_uri, "/auth") == 0) {
|
||||
// Handle GET /auth requests using the existing handler
|
||||
handle_auth_challenge_request();
|
||||
} else if (strcmp(request_method, "GET") == 0 &&
|
||||
strcmp(request_uri, "/health") == 0) {
|
||||
log_request("GET", "/", "server_info", 200);
|
||||
} else if (strcmp(request_uri, "/auth") == 0) {
|
||||
// Handle GET /auth requests using the existing handler
|
||||
handle_auth_challenge_request();
|
||||
} else if (strcmp(request_uri, "/health") == 0) {
|
||||
// Handle GET /health requests - simple public health response
|
||||
time_t now = time(NULL);
|
||||
long uptime_seconds = 0;
|
||||
@@ -2906,7 +3001,13 @@ if (!config_loaded /* && !initialize_server_config() */) {
|
||||
printf(" \"version\": \"%s\",\n", VERSION);
|
||||
printf(" \"uptime\": %ld\n", uptime_seconds);
|
||||
printf("}\n");
|
||||
log_request("GET", "/health", "public", 200);
|
||||
log_request("GET", "/health", "public", 200);
|
||||
} else {
|
||||
printf("Status: 404 Not Found\r\n");
|
||||
printf("Content-Type: text/plain\r\n\r\n");
|
||||
printf("Not Found\n");
|
||||
log_request("GET", request_uri, "none", 404);
|
||||
}
|
||||
} else if (strcmp(request_method, "DELETE") == 0) {
|
||||
// Handle DELETE /<sha256> requests with pre-validated auth
|
||||
const char *sha256 = extract_sha256_from_uri(request_uri);
|
||||
|
||||
@@ -296,16 +296,23 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
||||
// PHASE 2: NOSTR EVENT VALIDATION (CPU Intensive ~2ms)
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Check if authentication is disabled first (regardless of header presence)
|
||||
if (!g_auth_cache.auth_required) {
|
||||
validator_debug_log("VALIDATOR_DEBUG: STEP 4 PASSED - Authentication "
|
||||
"disabled, allowing request\n");
|
||||
// Global authentication bypass for all non-admin operations.
|
||||
// This makes blob upload/download/list/delete anonymous by default while
|
||||
// keeping admin endpoints gated by the normal validation flow below.
|
||||
int is_admin_operation =
|
||||
(request->operation &&
|
||||
(strcmp(request->operation, "admin") == 0 ||
|
||||
strcmp(request->operation, "admin_event") == 0));
|
||||
|
||||
if (!is_admin_operation) {
|
||||
validator_debug_log(
|
||||
"VALIDATOR_DEBUG: STEP 4 PASSED - Authentication bypassed for non-admin operation\n");
|
||||
result->valid = 1;
|
||||
result->error_code = NOSTR_SUCCESS;
|
||||
strcpy(result->reason, "Authentication disabled");
|
||||
strcpy(result->reason, "Authentication bypassed for non-admin operation");
|
||||
|
||||
// Even when auth is disabled, try to extract uploader pubkey from a provided
|
||||
// auth header so uploads can still be attributed in blob metadata.
|
||||
// Preserve uploader attribution when a valid auth header is optionally
|
||||
// provided by extracting the pubkey without requiring it.
|
||||
if (request->auth_header) {
|
||||
char optional_event_json[4096];
|
||||
int optional_parse = parse_authorization_header(
|
||||
@@ -321,7 +328,7 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
||||
strncpy(result->pubkey, optional_pubkey, 64);
|
||||
result->pubkey[64] = '\0';
|
||||
strcpy(result->reason,
|
||||
"Authentication disabled (pubkey extracted from auth header)");
|
||||
"Authentication bypassed (pubkey extracted from optional auth header)");
|
||||
}
|
||||
}
|
||||
cJSON_Delete(optional_event);
|
||||
@@ -332,6 +339,16 @@ int nostr_validate_unified_request(const nostr_unified_request_t *request,
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
// For admin operations, keep config-driven authentication behavior.
|
||||
if (!g_auth_cache.auth_required) {
|
||||
validator_debug_log("VALIDATOR_DEBUG: STEP 4 PASSED - Authentication "
|
||||
"disabled, allowing request\n");
|
||||
result->valid = 1;
|
||||
result->error_code = NOSTR_SUCCESS;
|
||||
strcpy(result->reason, "Authentication disabled");
|
||||
return NOSTR_SUCCESS;
|
||||
}
|
||||
|
||||
// Check if this is a BUD-09 report request - allow anonymous reporting
|
||||
if (request->operation && strcmp(request->operation, "report") == 0) {
|
||||
// BUD-09 allows anonymous reporting - pass through to bud09.c for validation
|
||||
|
||||
Reference in New Issue
Block a user