nasg/README.md

166 lines
6.2 KiB
Markdown

# NASG (Not Another Statig Generator)
This is a tiny static site generator, written in Python, to scratch my own itches.
It is most probably not suitable for anyone else.
## Why not [insert static generator here]?
- DRY -Don't Repeat Yourself - is good, so instead of sidefiles for images, I'm using XMP metadata, which most of the ones availabe don't handle well;
- writing a proper plugin to existing generators - Pelican, Nicola, etc - might have taken longer and I wanted to extend my Python knowledge
- I wanted to use the best available utilities for some tasks, like `Pandoc` and `exiftool` instead of Python libraries trying to achive the same
- I needed to handle webmentions and comments
Don't expect anything fancy: my Python Fu has much to learn.
## How content is organized
The directory structure of the "source" is something like this:
```
├── content
│   ├── category1 (containing YAML + MD files)
│   ├── category2 (containing YAML + MD files)
│   ├── photo (containing jpg files)
│   ├── _category_excluded_from_listing_1 (containing YAML + MD files)
├── files
│   ├── image (my own pictures)
│   ├── photo -> ../content/photo
│   └── pic (random images)
├── nasg
│   ├── archive.py
│   ├── config.ini
│   ├── db.py
│   ├── LICENSE
│   ├── nasg.py
│   ├── README.md
│   ├── requirements.txt
│   ├── router.py
│   ├── shared.py
│   └── templates
├── static
│   ├── favicon.ico
│   ├── favicon.png
│   └── pgp.asc
└── var
├── gone.tsv
├── redirects.tsv
├── s.sqlite
├── tokens.json
└── webmention.sqlite
```
Content files can be in either YAML and Markdown, with `.md` extension, or JPG with metadata, with `.jpg` extension.
Inline images in the content are checked against all subdirectories in `files` ; they get their EXIF read and displayed as well if they match the regex in the configuration for the Artist and/or Copyright EXIF fields.
`gone.tsv` is a simple list of URIs that should return a `410 Gone` message while `redirect.tsv` is a tab separated file of `from to` entries that should be `301` redirected. These go into a magic.php file, so if the host supports executing PHP, it will take care of this.
## Output
`nasg.py` generates a `build` directory which will have an directory per entry, with an `index.html`, so urls can be `https://domain.com/filename/`.
Categories are rendered into `category/category_name`. Pagination is under `category/category_name/page/X`. They include a feed as well, `category/category_name/feed`, in form if an `index.atom` ATOM feed.
## Webserver configuration
A minimal nginx configuration for the virtualhost:
```
# --- Virtual Host ---
upstream {{ domain }} {
server unix:/var/run/php/{{ domain }}.sock;
}
server {
listen 80;
server_name .{{ domain }};
rewrite ^ https://$server_name$request_uri redirect;
access_log /dev/null;
error_log /dev/null;
}
server {
listen 443 ssl http2;
server_name .{{ domain }};
ssl_certificate /etc/letsencrypt/live/{{ domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ domain }}/privkey.pem;
ssl_dhparam dh.pem;
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains;";
root /[path to root]/{{ domain }};
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
}
location ~ ^(?<script_name>.+?\.php)(?<path_info>.*)$ {
try_files $uri $script_name =404;
fastcgi_param SCRIPT_FILENAME $document_root$script_name;
fastcgi_param SCRIPT_NAME $script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param PATH_TRANSLATED $document_root$path_info;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
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;
fastcgi_param HTTP_PROXY "";
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param SSL_PROTOCOL $ssl_protocol if_not_empty;
fastcgi_param SSL_CIPHER $ssl_cipher if_not_empty;
fastcgi_param SSL_SESSION_ID $ssl_session_id if_not_empty;
fastcgi_param SSL_CLIENT_VERIFY $ssl_client_verify if_not_empty;
fastcgi_param REDIRECT_STATUS 200;
fastcgi_index index.php;
fastcgi_connect_timeout 10;
fastcgi_send_timeout 360;
fastcgi_read_timeout 3600;
fastcgi_buffer_size 512k;
fastcgi_buffers 512 512k;
fastcgi_keep_conn on;
fastcgi_intercept_errors on;
fastcgi_split_path_info ^(?<script_name>.+?\.php)(?<path_info>.*)$;
fastcgi_pass {{ domain }};
}
location / {
try_files $uri $uri/ $uri.html $uri/index.html $uri/index.xml $uri/index.atom index.php @rewrites;
}
location @rewrites {
rewrite ^ /magic.php?$args last;
}
location ~* \.(css|js|eot|woff|ttf|woff2)$ {
expires 1d;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
add_header "Vary" "Accept-Encoding";
}
location ~* \.(png|ico|gif|svg|jpg|jpeg|webp|avi|mpg|mpeg|mp4|mp3)$ {
expires 7d;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
add_header "Vary" "Accept-Encoding";
}
}
```