Compare commits
No commits in common. 'dd4029710753a539cbabd1047c1c6216866e86e6' and '7144e4d6746a9fe8a3d62fd0ce1ff9cb4d55082c' have entirely different histories.
dd40297107
...
7144e4d674
29 changed files with 723 additions and 8348 deletions
@ -1,47 +0,0 @@ |
|||||||
# Git files |
|
||||||
.git |
|
||||||
.gitignore |
|
||||||
.gitattributes |
|
||||||
|
|
||||||
# Build artifacts |
|
||||||
gitcitadel-online |
|
||||||
server |
|
||||||
*.exe |
|
||||||
*.dll |
|
||||||
*.so |
|
||||||
*.dylib |
|
||||||
|
|
||||||
# Test files |
|
||||||
*_test.go |
|
||||||
*.test |
|
||||||
|
|
||||||
# Documentation |
|
||||||
*.md |
|
||||||
!README.md |
|
||||||
LICENSE.md |
|
||||||
|
|
||||||
# IDE files |
|
||||||
.vscode |
|
||||||
.idea |
|
||||||
*.swp |
|
||||||
*.swo |
|
||||||
*~ |
|
||||||
|
|
||||||
# OS files |
|
||||||
.DS_Store |
|
||||||
Thumbs.db |
|
||||||
|
|
||||||
# Cache and temporary files |
|
||||||
cache/ |
|
||||||
*.log |
|
||||||
*.tmp |
|
||||||
|
|
||||||
# Node modules (will be installed in container) |
|
||||||
node_modules/ |
|
||||||
|
|
||||||
# Config file (should be mounted) |
|
||||||
config.yaml |
|
||||||
|
|
||||||
# Development files |
|
||||||
.env |
|
||||||
.env.local |
|
||||||
@ -1,223 +0,0 @@ |
|||||||
# Docker Setup for GitCitadel Online |
|
||||||
|
|
||||||
This guide explains how to run GitCitadel Online using Docker. |
|
||||||
|
|
||||||
## Prerequisites |
|
||||||
|
|
||||||
- Docker (version 20.10 or later) |
|
||||||
- Docker Compose (version 2.0 or later, optional but recommended) |
|
||||||
- Network access (for downloading dependencies and connecting to Nostr relays) |
|
||||||
|
|
||||||
## Image Details |
|
||||||
|
|
||||||
The Docker image uses **Alpine Linux** for a smaller footprint (~50MB base image vs ~200MB+ for Debian). This works well because: |
|
||||||
|
|
||||||
- The Go binary is statically compiled (`CGO_ENABLED=0`), so no C library dependencies |
|
||||||
- Node.js packages (`@asciidoctor/core`, `marked`) are pure JavaScript with no native bindings |
|
||||||
- Alpine's musl libc is sufficient for our use case |
|
||||||
|
|
||||||
If you encounter any compatibility issues, you can modify the Dockerfile to use Debian-based images (`golang:1.22` and `node:20-slim`), though this will increase the image size. |
|
||||||
|
|
||||||
## Quick Start |
|
||||||
|
|
||||||
### Using Docker Compose (Recommended) |
|
||||||
|
|
||||||
1. **Create your configuration file:** |
|
||||||
```bash |
|
||||||
cp config.yaml.example config.yaml |
|
||||||
# Edit config.yaml with your Nostr indices, relay URLs, and settings |
|
||||||
``` |
|
||||||
|
|
||||||
2. **Build and run:** |
|
||||||
```bash |
|
||||||
docker-compose up -d |
|
||||||
``` |
|
||||||
|
|
||||||
3. **View logs:** |
|
||||||
```bash |
|
||||||
docker-compose logs -f |
|
||||||
``` |
|
||||||
|
|
||||||
4. **Stop the container:** |
|
||||||
```bash |
|
||||||
docker-compose down |
|
||||||
``` |
|
||||||
|
|
||||||
### Using Docker directly |
|
||||||
|
|
||||||
1. **Build the image:** |
|
||||||
```bash |
|
||||||
docker build -t gitcitadel-online . |
|
||||||
``` |
|
||||||
|
|
||||||
2. **Create config file:** |
|
||||||
```bash |
|
||||||
cp config.yaml.example config.yaml |
|
||||||
# Edit config.yaml with your settings |
|
||||||
``` |
|
||||||
|
|
||||||
3. **Run the container:** |
|
||||||
```bash |
|
||||||
docker run -d \ |
|
||||||
--name gitcitadel-online \ |
|
||||||
-p 8080:8080 \ |
|
||||||
-v $(pwd)/config.yaml:/app/config.yaml:ro \ |
|
||||||
-v $(pwd)/cache:/app/cache \ |
|
||||||
--restart unless-stopped \ |
|
||||||
gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
4. **View logs:** |
|
||||||
```bash |
|
||||||
docker logs -f gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
5. **Stop the container:** |
|
||||||
```bash |
|
||||||
docker stop gitcitadel-online |
|
||||||
docker rm gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
## Configuration |
|
||||||
|
|
||||||
### Config File |
|
||||||
|
|
||||||
The `config.yaml` file must be mounted into the container. The default path is `/app/config.yaml`. |
|
||||||
|
|
||||||
You can override the config path using the `--config` flag: |
|
||||||
```bash |
|
||||||
docker run ... gitcitadel-online --config /path/to/config.yaml |
|
||||||
``` |
|
||||||
|
|
||||||
### Port Mapping |
|
||||||
|
|
||||||
By default, the application runs on port 8080. You can change the host port mapping: |
|
||||||
```bash |
|
||||||
# Map to different host port |
|
||||||
docker run -p 3000:8080 ... |
|
||||||
``` |
|
||||||
|
|
||||||
Or update `docker-compose.yml`: |
|
||||||
```yaml |
|
||||||
ports: |
|
||||||
- "3000:8080" |
|
||||||
``` |
|
||||||
|
|
||||||
### Cache Persistence |
|
||||||
|
|
||||||
The cache directory (`cache/`) is persisted as a volume to maintain cached pages and media between container restarts. |
|
||||||
|
|
||||||
### Environment Variables |
|
||||||
|
|
||||||
You can pass environment variables, though most configuration should be in `config.yaml`: |
|
||||||
|
|
||||||
```bash |
|
||||||
docker run -e LOG_LEVEL=debug ... |
|
||||||
``` |
|
||||||
|
|
||||||
## Development Mode |
|
||||||
|
|
||||||
To run in development mode with verbose logging: |
|
||||||
|
|
||||||
```bash |
|
||||||
docker run ... gitcitadel-online --dev |
|
||||||
``` |
|
||||||
|
|
||||||
Or with docker-compose, override the command: |
|
||||||
```yaml |
|
||||||
command: ["--config", "/app/config.yaml", "--dev"] |
|
||||||
``` |
|
||||||
|
|
||||||
## Health Check |
|
||||||
|
|
||||||
The container includes a health check that monitors the `/health` endpoint. You can check the health status: |
|
||||||
|
|
||||||
```bash |
|
||||||
docker ps |
|
||||||
# Look for "healthy" status |
|
||||||
|
|
||||||
# Or inspect directly |
|
||||||
docker inspect --format='{{.State.Health.Status}}' gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
## Troubleshooting |
|
||||||
|
|
||||||
### Container won't start |
|
||||||
|
|
||||||
1. **Check logs:** |
|
||||||
```bash |
|
||||||
docker logs gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
2. **Verify config file:** |
|
||||||
```bash |
|
||||||
docker exec gitcitadel-online cat /app/config.yaml |
|
||||||
``` |
|
||||||
|
|
||||||
3. **Check file permissions:** |
|
||||||
The container runs as a non-root user (UID 1000). Ensure cache directory is writable: |
|
||||||
```bash |
|
||||||
chmod -R 777 cache/ |
|
||||||
``` |
|
||||||
|
|
||||||
### Can't connect to Nostr relays |
|
||||||
|
|
||||||
- Ensure the container has network access |
|
||||||
- Check firewall rules if running on a remote server |
|
||||||
- Verify relay URLs in `config.yaml` are correct |
|
||||||
|
|
||||||
### Cache not persisting |
|
||||||
|
|
||||||
- Ensure the cache volume is properly mounted |
|
||||||
- Check volume permissions |
|
||||||
- Verify the cache directory exists and is writable |
|
||||||
|
|
||||||
## Building for Different Architectures |
|
||||||
|
|
||||||
The Dockerfile builds for `linux/amd64` by default. To build for other architectures: |
|
||||||
|
|
||||||
```bash |
|
||||||
# For ARM64 (e.g., Raspberry Pi, Apple Silicon) |
|
||||||
docker buildx build --platform linux/arm64 -t gitcitadel-online . |
|
||||||
|
|
||||||
# For multiple architectures |
|
||||||
docker buildx build --platform linux/amd64,linux/arm64 -t gitcitadel-online . |
|
||||||
``` |
|
||||||
|
|
||||||
## Production Deployment |
|
||||||
|
|
||||||
For production deployment: |
|
||||||
|
|
||||||
1. **Use a reverse proxy** (nginx, Traefik, etc.) in front of the container |
|
||||||
2. **Set up SSL/TLS** certificates |
|
||||||
3. **Configure proper logging** and monitoring |
|
||||||
4. **Use secrets management** for sensitive configuration |
|
||||||
5. **Set resource limits** in docker-compose.yml: |
|
||||||
```yaml |
|
||||||
deploy: |
|
||||||
resources: |
|
||||||
limits: |
|
||||||
cpus: '1' |
|
||||||
memory: 512M |
|
||||||
``` |
|
||||||
|
|
||||||
## Updating |
|
||||||
|
|
||||||
To update to a new version: |
|
||||||
|
|
||||||
```bash |
|
||||||
# Pull latest code |
|
||||||
git pull |
|
||||||
|
|
||||||
# Rebuild and restart |
|
||||||
docker-compose build |
|
||||||
docker-compose up -d |
|
||||||
``` |
|
||||||
|
|
||||||
Or with Docker directly: |
|
||||||
```bash |
|
||||||
docker build -t gitcitadel-online . |
|
||||||
docker stop gitcitadel-online |
|
||||||
docker rm gitcitadel-online |
|
||||||
docker run ... # (same command as before) |
|
||||||
``` |
|
||||||
@ -1,77 +0,0 @@ |
|||||||
# Multi-stage build for GitCitadel Online |
|
||||||
# Using Alpine Linux for smaller image size (~50MB vs ~200MB+ for Debian) |
|
||||||
# Alpine works well here because: |
|
||||||
# - Go binary is statically compiled (CGO_ENABLED=0) |
|
||||||
# - Node.js packages are pure JavaScript (no native bindings) |
|
||||||
# - No C library dependencies required |
|
||||||
|
|
||||||
# Stage 1: Build Go application |
|
||||||
FROM golang:1.22-alpine AS builder |
|
||||||
|
|
||||||
# Install build dependencies |
|
||||||
RUN apk add --no-cache git |
|
||||||
|
|
||||||
# Set working directory |
|
||||||
WORKDIR /build |
|
||||||
|
|
||||||
# Copy go mod files |
|
||||||
COPY go.mod go.sum ./ |
|
||||||
RUN go mod download |
|
||||||
|
|
||||||
# Copy source code |
|
||||||
COPY . . |
|
||||||
|
|
||||||
# Build the application |
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-w -s' -o gitcitadel-online ./cmd/server |
|
||||||
|
|
||||||
# Stage 2: Runtime with Node.js for AsciiDoc processing |
|
||||||
FROM node:20-alpine |
|
||||||
|
|
||||||
# Install runtime dependencies (wget for health check and nostr-tools download) |
|
||||||
RUN apk add --no-cache ca-certificates tzdata wget |
|
||||||
|
|
||||||
# Set working directory |
|
||||||
WORKDIR /app |
|
||||||
|
|
||||||
# Install Node.js dependencies for AsciiDoc processing |
|
||||||
COPY package.json package-lock.json ./ |
|
||||||
RUN npm ci --only=production |
|
||||||
|
|
||||||
# Copy built binary from builder |
|
||||||
COPY --from=builder /build/gitcitadel-online /app/gitcitadel-online |
|
||||||
|
|
||||||
# Copy static files and templates |
|
||||||
COPY static/ ./static/ |
|
||||||
COPY templates/ ./templates/ |
|
||||||
|
|
||||||
# Download nostr-tools bundle if not present (for contact form) |
|
||||||
RUN if [ ! -f ./static/js/nostr.bundle.js ]; then \ |
|
||||||
mkdir -p ./static/js && \ |
|
||||||
wget -O ./static/js/nostr.bundle.js https://unpkg.com/nostr-tools@latest/lib/nostr.bundle.js || \ |
|
||||||
echo "Warning: Failed to download nostr-tools bundle"; \ |
|
||||||
fi |
|
||||||
|
|
||||||
# Copy example config (user should mount their own config.yaml) |
|
||||||
COPY config.yaml.example ./config.yaml.example |
|
||||||
|
|
||||||
# Create cache directories |
|
||||||
RUN mkdir -p cache/media |
|
||||||
|
|
||||||
# Create non-root user for security |
|
||||||
RUN addgroup -g 1000 appuser && \ |
|
||||||
adduser -D -u 1000 -G appuser appuser && \ |
|
||||||
chown -R appuser:appuser /app |
|
||||||
|
|
||||||
# Switch to non-root user |
|
||||||
USER appuser |
|
||||||
|
|
||||||
# Expose port (default 8080, can be overridden via config) |
|
||||||
EXPOSE 8080 |
|
||||||
|
|
||||||
# Health check |
|
||||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ |
|
||||||
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 |
|
||||||
|
|
||||||
# Run the application |
|
||||||
ENTRYPOINT ["/app/gitcitadel-online"] |
|
||||||
CMD ["--config", "/app/config.yaml"] |
|
||||||
@ -1,312 +1,103 @@ |
|||||||
# GitCitadel Online |
# GitCitadel Online |
||||||
|
|
||||||
A server-generated static website that fetches content from Nostr relays, processes AsciiDoc articles, and serves professional HTML pages with intelligent caching. Built with Go and designed for decentralized content publishing. |
A server-generated website that fetches kind 30818 wiki events from Nostr relays, processes AsciiDoc content, and serves professional HTML pages with caching. |
||||||
|
|
||||||
## Features |
## Features |
||||||
|
|
||||||
- **Wiki System**: Fetches and displays wiki articles (kind 30818) from Nostr relays |
- Fetches wiki content from Nostr relays (kind 30818 events) |
||||||
- **Blog & Articles**: Supports blog posts and longform articles (kind 30023) with full markdown/AsciiDoc processing |
- Processes AsciiDoc content to HTML |
||||||
- **E-Books Library**: Displays e-books and publications (kind 30040) from Nostr |
- Caches all pages for fast serving |
||||||
- **Feed Integration**: Real-time kind 1 feed integration in sidebar |
- Background cache rewarming to keep content fresh |
||||||
- **Contact Form**: Nostr-based contact form with browser extension support and anonymous submission |
- Kind 1 feed integration in sidebar |
||||||
- **AsciiDoc Processing**: Full AsciiDoc to HTML conversion with table of contents support |
- SEO optimized with structured data |
||||||
- **Intelligent Caching**: Multi-layer caching system with background rewarming |
- Responsive design with medium-dark theme |
||||||
- **Media Caching**: Automatic caching of external images and media |
- WCAG AA/AAA compliant accessibility |
||||||
- **SEO Optimized**: Structured data, sitemaps, and meta tags |
- YAML configuration for easy index management |
||||||
- **Responsive Design**: Mobile-first responsive design with medium-dark theme |
|
||||||
- **Accessibility**: WCAG AA/AAA compliant with proper ARIA labels and keyboard navigation |
|
||||||
- **Content Security Policy**: Secure CSP headers for XSS protection |
|
||||||
|
|
||||||
## Requirements |
## Requirements |
||||||
|
|
||||||
- **Go 1.22+** - For building and running the server |
- Go 1.22+ |
||||||
- **Node.js** - For AsciiDoc processing |
- Node.js (for asciidoctor.js) |
||||||
- **@asciidoctor/core** - npm package for AsciiDoc conversion |
- @asciidoctor/core npm package |
||||||
- **Network access** - To connect to Nostr relays |
- Network access to Nostr relays |
||||||
|
|
||||||
## Installation |
## Installation |
||||||
|
|
||||||
1. **Clone the repository:** |
1. Clone the repository |
||||||
```bash |
2. Install Go dependencies: |
||||||
git clone <repository-url> |
|
||||||
cd gitcitadel-online |
|
||||||
``` |
|
||||||
|
|
||||||
2. **Install Go dependencies:** |
|
||||||
```bash |
```bash |
||||||
go mod tidy |
go mod tidy |
||||||
``` |
``` |
||||||
|
3. Install Node.js dependencies: |
||||||
3. **Install Node.js dependencies:** |
|
||||||
```bash |
```bash |
||||||
npm install @asciidoctor/core |
npm install @asciidoctor/core |
||||||
``` |
``` |
||||||
Or install globally: |
Or globally: |
||||||
```bash |
```bash |
||||||
npm install -g @asciidoctor/core |
npm install -g @asciidoctor/core |
||||||
``` |
``` |
||||||
|
4. Copy the example config: |
||||||
4. **Download nostr-tools bundle (for contact form):** |
|
||||||
```bash |
|
||||||
mkdir -p static/js |
|
||||||
curl -L -o static/js/nostr.bundle.js https://unpkg.com/nostr-tools@latest/lib/nostr.bundle.js |
|
||||||
``` |
|
||||||
Note: The nostr-tools library is hosted locally to avoid dependency on external CDNs. |
|
||||||
|
|
||||||
5. **Copy and configure:** |
|
||||||
```bash |
```bash |
||||||
cp config.yaml.example config.yaml |
cp config.yaml.example config.yaml |
||||||
``` |
``` |
||||||
Edit `config.yaml` with your Nostr indices, relay URLs, and settings. |
5. Edit `config.yaml` with your indices and settings |
||||||
|
|
||||||
## Configuration |
## Configuration |
||||||
|
|
||||||
Edit `config.yaml` to configure: |
Edit `config.yaml` to set: |
||||||
|
|
||||||
### Required Settings |
|
||||||
|
|
||||||
- `wiki_index`: naddr for your wiki index (kind 30040) |
- `wiki_index`: naddr for your wiki index (kind 30040) |
||||||
- `blog_index`: naddr for your blog index (kind 30040) |
- `blog_index`: naddr for your blog index (kind 30040) |
||||||
- `repo_announcement`: naddr for repository announcement (for contact form) |
- Relay URLs |
||||||
- `relays.feeds`: Primary relay URL for fetching content |
- Cache refresh intervals |
||||||
- `relays.profiles`: Comma-separated relay URLs for profile data |
- Server port |
||||||
- `relays.contactform`: Comma-separated relay URLs for contact form submissions |
- SEO settings |
||||||
|
|
||||||
### Optional Settings |
|
||||||
|
|
||||||
- `link_base_url`: Base URL for external links (default: Alexandria) |
|
||||||
- `cache.refresh_interval_minutes`: How often to refresh cached pages (default: 30) |
|
||||||
- `feed.poll_interval_minutes`: How often to poll for new feed items (default: 5) |
|
||||||
- `feed.max_events`: Maximum number of feed items to display (default: 30) |
|
||||||
- `server.port`: HTTP server port (default: 8080) |
|
||||||
- `server.enable_compression`: Enable gzip compression (default: true) |
|
||||||
- `seo.site_name`: Site name for SEO |
|
||||||
- `seo.site_url`: Canonical site URL |
|
||||||
- `seo.default_image`: Default OpenGraph image path |
|
||||||
|
|
||||||
### Example Configuration |
|
||||||
|
|
||||||
```yaml |
|
||||||
wiki_index: "naddr1qvzqqqr4tqpzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqyd8wumn8ghj7..." |
|
||||||
blog_index: "naddr1qvzqqqr4tqpzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqyvhwumn8ghj7..." |
|
||||||
repo_announcement: "naddr1qvzqqqrhnypzplfq3m5v3u5r0q9f255fdeyz8nyac6lagssx8zy4wugxjs8ajf7pqq9xw6t5vd5hgctyv4kqde47kt" |
|
||||||
relays: |
|
||||||
feeds: "wss://theforest.nostr1.com" |
|
||||||
profiles: "wss://theforest.nostr1.com,wss://nostr.land" |
|
||||||
contactform: "wss://thecitadel.nostr1.com,wss://relay.damus.io" |
|
||||||
server: |
|
||||||
port: 8080 |
|
||||||
enable_compression: true |
|
||||||
seo: |
|
||||||
site_name: "GitCitadel" |
|
||||||
site_url: "https://gitcitadel.com" |
|
||||||
``` |
|
||||||
|
|
||||||
## Running |
## Running |
||||||
|
|
||||||
### Development Mode |
|
||||||
|
|
||||||
Run with verbose logging: |
|
||||||
```bash |
```bash |
||||||
go run cmd/server/main.go --dev |
go run cmd/server/main.go |
||||||
``` |
``` |
||||||
|
|
||||||
### Production Mode |
Or build and run: |
||||||
|
|
||||||
Build and run: |
|
||||||
```bash |
```bash |
||||||
go build -o gitcitadel-online cmd/server/main.go |
go build -o gitcitadel-online cmd/server/main.go |
||||||
./gitcitadel-online |
./gitcitadel-online |
||||||
``` |
``` |
||||||
|
|
||||||
### Command Line Options |
Development mode with verbose logging: |
||||||
|
```bash |
||||||
- `--config <path>`: Path to configuration file (default: `config.yaml`) |
go run cmd/server/main.go --dev |
||||||
- `--dev`: Enable development mode with verbose logging |
``` |
||||||
- `--log-level <level>`: Set log level (debug, info, warn, error) (default: info) |
|
||||||
|
|
||||||
## Routes & Endpoints |
|
||||||
|
|
||||||
### Public Pages |
|
||||||
|
|
||||||
- `/` - Landing page with feed sidebar |
|
||||||
- `/wiki` - Wiki index page |
|
||||||
- `/wiki/<d-tag>` - Individual wiki article pages |
|
||||||
- `/blog` - Blog index page with article navigation |
|
||||||
- `/articles` - Longform articles index page |
|
||||||
- `/ebooks` - E-books library with sortable table |
|
||||||
- `/feed` - Feed page with relay information |
|
||||||
- `/contact` - Contact form with Nostr integration |
|
||||||
|
|
||||||
### Static Assets |
|
||||||
|
|
||||||
- `/static/` - Static files (CSS, JavaScript, images, icons) |
|
||||||
- `/cache/media/` - Cached external media files |
|
||||||
- `/favicon.ico` - Site favicon |
|
||||||
|
|
||||||
### API Endpoints |
|
||||||
|
|
||||||
- `/api/contact` - POST endpoint for submitting contact form events (JSON) |
|
||||||
|
|
||||||
### System Endpoints |
|
||||||
|
|
||||||
- `/health` - Health check endpoint |
|
||||||
- `/metrics` - Metrics endpoint (Prometheus format) |
|
||||||
- `/sitemap.xml` - XML sitemap for search engines |
|
||||||
- `/robots.txt` - Robots.txt file |
|
||||||
|
|
||||||
## Project Structure |
## Project Structure |
||||||
|
|
||||||
``` |
``` |
||||||
gitcitadel-online/ |
gitcitadel-online/ |
||||||
├── cmd/ |
├── cmd/server/ # Main server application |
||||||
│ └── server/ # Main server application entry point |
|
||||||
├── internal/ |
├── internal/ |
||||||
│ ├── asciidoc/ # AsciiDoc processing with Node.js |
|
||||||
│ ├── cache/ # Multi-layer caching system |
|
||||||
│ │ ├── cache.go # Page cache |
|
||||||
│ │ ├── feed_cache.go # Feed item cache |
|
||||||
│ │ └── media_cache.go # Media file cache |
|
||||||
│ ├── config/ # Configuration management |
|
||||||
│ ├── generator/ # HTML generation and SEO |
|
||||||
│ ├── logger/ # Structured logging |
|
||||||
│ ├── nostr/ # Nostr client and event parsing |
│ ├── nostr/ # Nostr client and event parsing |
||||||
│ │ ├── client.go # Relay connection management |
│ ├── asciidoc/ # AsciiDoc processing |
||||||
│ │ ├── wiki.go # Wiki event parsing |
│ ├── generator/ # HTML generation |
||||||
│ │ ├── profile.go # Profile metadata |
│ ├── cache/ # Caching layer |
||||||
│ │ ├── feed.go # Feed event parsing |
│ ├── server/ # HTTP server |
||||||
│ │ ├── ebooks.go # E-book parsing |
│ └── config/ # Configuration management |
||||||
│ │ └── issues.go # Issue/contact form handling |
|
||||||
│ └── server/ # HTTP server and handlers |
|
||||||
├── static/ # Static assets |
|
||||||
│ ├── css/ # Stylesheets |
|
||||||
│ ├── icons/ # SVG icons |
|
||||||
│ └── js/ # JavaScript libraries |
|
||||||
├── templates/ # HTML templates |
├── templates/ # HTML templates |
||||||
│ ├── base.html # Base template |
├── static/ # Static assets (CSS, images) |
||||||
│ ├── landing.html # Landing page |
└── config.yaml # Configuration file |
||||||
│ ├── wiki.html # Wiki pages |
|
||||||
│ ├── blog.html # Blog pages |
|
||||||
│ ├── articles.html # Article pages |
|
||||||
│ ├── ebooks.html # E-books page |
|
||||||
│ ├── feed.html # Feed page |
|
||||||
│ ├── contact.html # Contact form |
|
||||||
│ └── components.html # Reusable components |
|
||||||
├── cache/ # Runtime cache directory |
|
||||||
│ └── media/ # Cached media files |
|
||||||
├── config.yaml # Configuration file (not in repo) |
|
||||||
├── config.yaml.example # Example configuration |
|
||||||
├── go.mod # Go module dependencies |
|
||||||
├── package.json # Node.js dependencies |
|
||||||
└── README.md # This file |
|
||||||
``` |
``` |
||||||
|
|
||||||
## Development |
## API |
||||||
|
|
||||||
### Building |
|
||||||
|
|
||||||
```bash |
The server provides: |
||||||
go build -o gitcitadel-online cmd/server/main.go |
- `/` - Landing page |
||||||
``` |
- `/wiki/<d-tag>` - Wiki article pages |
||||||
|
- `/blog` - Blog index page |
||||||
### Testing |
- `/static/` - Static assets |
||||||
|
- `/health` - Health check endpoint |
||||||
The server uses a caching system that pre-generates all pages. On first run, pages will be generated and cached. Subsequent requests serve from cache until the refresh interval. |
- `/metrics` - Metrics endpoint |
||||||
|
- `/sitemap.xml` - Sitemap |
||||||
### Cache Management |
- `/robots.txt` - Robots.txt |
||||||
|
|
||||||
- Pages are cached in memory for fast serving |
|
||||||
- Cache rewarming runs in the background at configured intervals |
|
||||||
- Media files are cached to disk in `cache/media/` |
|
||||||
- Cache can be cleared by restarting the server |
|
||||||
|
|
||||||
### Logging |
|
||||||
|
|
||||||
Logs are structured and can be configured via: |
|
||||||
- `--log-level` flag (debug, info, warn, error) |
|
||||||
- `--dev` flag enables debug logging and verbose output |
|
||||||
|
|
||||||
## Content Types Supported |
|
||||||
|
|
||||||
### Wiki Articles (Kind 30818) |
|
||||||
- AsciiDoc content processing |
|
||||||
- Table of contents generation |
|
||||||
- Cross-referencing support |
|
||||||
- Syntax highlighting |
|
||||||
|
|
||||||
### Blog Posts (Kind 30023) |
|
||||||
- Markdown/AsciiDoc content |
|
||||||
- Image support with caching |
|
||||||
- Author profiles |
|
||||||
- Timestamps and metadata |
|
||||||
|
|
||||||
### E-Books (Kind 30040) |
|
||||||
- Publication listings |
|
||||||
- Author information |
|
||||||
- Sortable table interface |
|
||||||
- Links to Alexandria library |
|
||||||
|
|
||||||
### Feed Items (Kind 1) |
|
||||||
- Real-time note display |
|
||||||
- Author badges with profiles |
|
||||||
- Timestamp formatting |
|
||||||
- Content rendering |
|
||||||
|
|
||||||
## Contact Form |
|
||||||
|
|
||||||
The contact form supports two submission methods: |
|
||||||
|
|
||||||
1. **Browser Extension**: Users can sign with their Nostr browser extension (nos2x, Alby, etc.) |
|
||||||
2. **Anonymous**: Server generates a temporary key pair for anonymous submissions |
|
||||||
|
|
||||||
Both methods publish kind 1 events to configured relays with proper tags for issue tracking. |
|
||||||
|
|
||||||
## Security |
|
||||||
|
|
||||||
- Content Security Policy (CSP) headers prevent XSS attacks |
|
||||||
- All external scripts are hosted locally |
|
||||||
- Input validation on contact form |
|
||||||
- Event signature verification for API submissions |
|
||||||
- Secure relay connections (WSS) |
|
||||||
|
|
||||||
## Performance |
|
||||||
|
|
||||||
- Multi-layer caching (memory + disk) |
|
||||||
- Background cache rewarming |
|
||||||
- Gzip compression support |
|
||||||
- Optimized static asset serving |
|
||||||
- Efficient Nostr event parsing |
|
||||||
|
|
||||||
## Docker Deployment |
|
||||||
|
|
||||||
GitCitadel Online can be run using Docker for easy deployment on localhost or remote servers. |
|
||||||
|
|
||||||
### Quick Start with Docker Compose |
|
||||||
|
|
||||||
```bash |
|
||||||
# 1. Create config file |
|
||||||
cp config.yaml.example config.yaml |
|
||||||
# Edit config.yaml with your settings |
|
||||||
|
|
||||||
# 2. Build and run |
|
||||||
docker-compose up -d |
|
||||||
|
|
||||||
# 3. View logs |
|
||||||
docker-compose logs -f |
|
||||||
``` |
|
||||||
|
|
||||||
The application will be available at `http://localhost:8080`. |
|
||||||
|
|
||||||
For detailed Docker instructions, see [DOCKER.md](DOCKER.md). |
|
||||||
|
|
||||||
## License |
## License |
||||||
|
|
||||||
MIT License - see LICENSE.md for details |
MIT License - see LICENSE.md |
||||||
|
|
||||||
## Contributing |
|
||||||
|
|
||||||
Contributions are welcome! Please ensure: |
|
||||||
- Code follows Go conventions |
|
||||||
- Templates are accessible (WCAG AA/AAA) |
|
||||||
- All routes are documented |
|
||||||
- Configuration changes are backward compatible |
|
||||||
|
|||||||
@ -1,33 +0,0 @@ |
|||||||
version: '3.8' |
|
||||||
|
|
||||||
services: |
|
||||||
gitcitadel-online: |
|
||||||
build: |
|
||||||
context: . |
|
||||||
dockerfile: Dockerfile |
|
||||||
container_name: gitcitadel-online |
|
||||||
restart: unless-stopped |
|
||||||
ports: |
|
||||||
- "8080:8080" |
|
||||||
volumes: |
|
||||||
# Mount config file (create from config.yaml.example) |
|
||||||
- ./config.yaml:/app/config.yaml:ro |
|
||||||
# Persist cache directory |
|
||||||
- ./cache:/app/cache |
|
||||||
environment: |
|
||||||
# Optional: override config path |
|
||||||
# - CONFIG_PATH=/app/config.yaml |
|
||||||
# Optional: set log level |
|
||||||
# - LOG_LEVEL=info |
|
||||||
networks: |
|
||||||
- gitcitadel-network |
|
||||||
healthcheck: |
|
||||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] |
|
||||||
interval: 30s |
|
||||||
timeout: 10s |
|
||||||
retries: 3 |
|
||||||
start_period: 40s |
|
||||||
|
|
||||||
networks: |
|
||||||
gitcitadel-network: |
|
||||||
driver: bridge |
|
||||||
@ -0,0 +1,70 @@ |
|||||||
|
package generator |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
) |
||||||
|
|
||||||
|
// GenerateStructuredData generates JSON-LD structured data
|
||||||
|
func GenerateStructuredData(siteName, siteURL, pageType, title, description, url string) string { |
||||||
|
var data map[string]interface{} |
||||||
|
|
||||||
|
switch pageType { |
||||||
|
case "article": |
||||||
|
data = map[string]interface{}{ |
||||||
|
"@context": "https://schema.org", |
||||||
|
"@type": "Article", |
||||||
|
"headline": title, |
||||||
|
"description": description, |
||||||
|
"url": url, |
||||||
|
"publisher": map[string]interface{}{ |
||||||
|
"@type": "Organization", |
||||||
|
"name": siteName, |
||||||
|
}, |
||||||
|
} |
||||||
|
case "website": |
||||||
|
data = map[string]interface{}{ |
||||||
|
"@context": "https://schema.org", |
||||||
|
"@type": "WebSite", |
||||||
|
"name": siteName, |
||||||
|
"url": siteURL, |
||||||
|
} |
||||||
|
default: |
||||||
|
data = map[string]interface{}{ |
||||||
|
"@context": "https://schema.org", |
||||||
|
"@type": "WebPage", |
||||||
|
"name": title, |
||||||
|
"url": url, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(data) |
||||||
|
return string(jsonData) |
||||||
|
} |
||||||
|
|
||||||
|
// GenerateBreadcrumbStructuredData generates breadcrumb structured data
|
||||||
|
func GenerateBreadcrumbStructuredData(items []BreadcrumbItem, siteURL string) string { |
||||||
|
breadcrumbList := make([]map[string]interface{}, len(items)) |
||||||
|
for i, item := range items { |
||||||
|
breadcrumbList[i] = map[string]interface{}{ |
||||||
|
"@type": "ListItem", |
||||||
|
"position": i + 1, |
||||||
|
"name": item.Name, |
||||||
|
"item": siteURL + item.URL, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
data := map[string]interface{}{ |
||||||
|
"@context": "https://schema.org", |
||||||
|
"@type": "BreadcrumbList", |
||||||
|
"itemListElement": breadcrumbList, |
||||||
|
} |
||||||
|
|
||||||
|
jsonData, _ := json.Marshal(data) |
||||||
|
return string(jsonData) |
||||||
|
} |
||||||
|
|
||||||
|
// BreadcrumbItem represents a breadcrumb item
|
||||||
|
type BreadcrumbItem struct { |
||||||
|
Name string |
||||||
|
URL string |
||||||
|
} |
||||||
Loading…
Reference in new issue