proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=skynet:10m max_size=10g use_temp_path=off;
limit_req_zone $binary_remote_addr zone=stats_by_ip:10m rate=10r/m;
limit_conn_zone $binary_remote_addr zone=uploads_by_ip:10m;
limit_conn_zone $binary_remote_addr zone=downloads_by_ip:10m;
limit_req_status 429;
limit_conn_status 429;

# since we are proxying request to nginx from caddy, access logs will contain caddy's ip address 
# as the request address so we need to use real_ip_header module to use ip address from 
# X-Forwarded-For header as a real ip address of the request
set_real_ip_from  10.0.0.0/8;
set_real_ip_from  127.0.0.1/32;
set_real_ip_from  172.16.0.0/12;
set_real_ip_from  192.168.0.0/16;
real_ip_header    X-Forwarded-For;

upstream siad {
	server sia:9980;
}

server {
	listen 80 default_server;
	listen [::]:80 default_server;

	# parse subdomain (a base32 encoded Skylink) into custom variable
	server_name "~^([a-z0-9]{55})\..*$";
	set $subdomain $1;

	# ddos protection: closing slow connections
	client_body_timeout 5s;
	client_header_timeout 5s;

	# Increase the body buffer size, to ensure the internal POSTs can always
	# parse the full POST contents into memory.
	client_body_buffer_size 128k;
	client_max_body_size 128k;

	location / {
		# The only safe thing to do inside an if in a location block is return
		# or rewrite, since we need to proxy_pass we have to work our way around
		# using a custom error code.
		#
		# See https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/
		error_page 418 = @subdomain;
		recursive_error_pages on;
		if ($subdomain  != "") {
			return 418;
		}

		include /etc/nginx/conf.d/include/cors;

		root /var/www/webportal;
	}

	location /blacklist {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 1m; # cache blacklist for 1 minute
		proxy_set_header User-Agent: Sia-Agent;
		proxy_pass http://siad/skynet/blacklist;
	}

	location /skynet/blacklist {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 1m; # cache blacklist for 1 minute
		proxy_set_header User-Agent: Sia-Agent;
		proxy_pass http://siad/skynet/blacklist;
	}

	location /portals {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 1m; # cache portals for 1 minute
		proxy_set_header User-Agent: Sia-Agent;
		proxy_pass http://siad/skynet/portals;
	}

	location /skynet/portals {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 1m; # cache portals for 1 minute
		proxy_set_header User-Agent: Sia-Agent;
		proxy_pass http://siad/skynet/portals;
	}

	location /stats {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 10m; # cache stats for 10 minutes
		proxy_set_header User-Agent: Sia-Agent;
		proxy_read_timeout 5m; # extend the read timeout
		proxy_pass http://siad/skynet/stats;
	}

	location /skynet/stats {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 10m; # cache stats for 10 minutes
		proxy_set_header User-Agent: Sia-Agent;
		proxy_read_timeout 5m; # extend the read timeout
		proxy_pass http://siad/skynet/stats;
	}

	# This endpoint is a backward compatilibilty workaround for reporting stats from legacy 
	# upload siad node for portals that used double-siad setup. If your portal does not need
	# to support additional siad node, you can remove it from this config.
	location /statsdown {
		include /etc/nginx/conf.d/include/cors;

		proxy_cache skynet;
		proxy_cache_valid any 10m; # cache stats for 10 minutes
		proxy_set_header User-Agent: Sia-Agent;
		proxy_read_timeout 5m; # extend the read timeout
		proxy_pass http://sia-upload:9980/skynet/stats; # serve upload node stats temporarily
	}

	location /health-check {
		include /etc/nginx/conf.d/include/cors;

		proxy_pass http://health-check:3100;
	}

	location /hns {
		include /etc/nginx/conf.d/include/proxy-buffer;

		set $skylink ''; # placeholder for the raw 46 bit skylink
		set $rest ''; # placeholder for the rest of the url that gets appended to skylink (path and args)
		
		# resolve handshake domain by requesting to /hnsres endpoint and assign correct values to $skylink and $rest
		access_by_lua_block {
			local json = require('cjson')
			local hns_domain_name, request_uri_rest = string.match(ngx.var.request_uri, "/hns/([^/?]+)(.*)")
			local hnsres_res = ngx.location.capture("/hnsres/" .. hns_domain_name)

			if hnsres_res.status ~= ngx.HTTP_OK then
				ngx.exit(ngx.HTTP_NOT_FOUND)
			end

			local hnsres_json = json.decode(hnsres_res.body)
			local skylink_prefix, skylink, skylink_rest = string.match(hnsres_json.skylink, "(sia://)([^/?]+)(.*)")

			ngx.var.skylink = skylink
			if request_uri_rest == "" or (request_uri_rest == "/" and skylink_rest ~= "") then
				ngx.var.rest = skylink_rest
			else
				ngx.var.rest = request_uri_rest
			end
		}

		# overwrite the Cache-Control header to only cache for 60s in case the domain gets updated
		more_set_headers 'Cache-Control: public, max-age=60';
		
		# we proxy to another nginx location rather than directly to siad because we don't want to deal with caching here
		proxy_pass http://127.0.0.1/$skylink$rest;

		# in case siad returns location header, we need to replace the skylink with the domain name
		header_filter_by_lua_block {
			if ngx.header.location then
				local hns_domain_name = string.match(ngx.var.request_uri, "/hns/([^/?]+)")
				local location = string.gsub(ngx.header.location, ngx.var.skylink, hns_domain_name)

				ngx.header.location = location
			end
		}
	}

	location /hnsres {
		include /etc/nginx/conf.d/include/cors;

		proxy_pass http://handshake-api:3100;
	}

	location /skynet/skyfile {
		include /etc/nginx/conf.d/include/cors;
		include /etc/nginx/conf.d/include/sia-auth;

		limit_conn uploads_by_ip 10; # ddos protection: max 10 uploads at a time
		client_max_body_size 1000M; # make sure to limit the size of upload to a sane value
		proxy_read_timeout 600;
		proxy_request_buffering off; # stream uploaded files through the proxy as it comes in
		proxy_set_header Expect $http_expect;
		proxy_set_header User-Agent: Sia-Agent;

		# Extract 3 sets of 2 characters from $request_id and assign to $dir1, $dir2, $dir3
		# respectfully. The rest of the $request_id is going to be assigned to $dir4.
		# We use those variables to automatically generate a unique path for the uploaded file.
		# This ensures that not all uploaded files end up in the same directory, which is something
		# that causes performance issues in the renter.
		# Example path result: /af/24/9b/c5ec894920ccc45634dc9a8065
		if ($request_id ~* "(\w{2})(\w{2})(\w{2})(\w+)") {
			set $dir1 $1;
			set $dir2 $2;
			set $dir3 $3;
			set $dir4 $4;
		}

		# proxy this call to siad endpoint (make sure the ip is correct)
		proxy_pass http://siad/skynet/skyfile/$dir1/$dir2/$dir3/$dir4$is_args$args;
	}

	location ~ "^/([a-zA-Z0-9-_]{46}(/.*)?)$" {
		include /etc/nginx/conf.d/include/cors;
		include /etc/nginx/conf.d/include/proxy-buffer;
		include /etc/nginx/conf.d/include/proxy-cache-downloads;

		limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
		add_header Cache-Control "public, max-age=86400"; # allow consumer to cache response

		# we need to explicitly use set directive here because $1 will contain the skylink with
		# decoded whitespaces and set will re-encode it for us before passing it to proxy_pass
		set $skylink $1;

		proxy_read_timeout 600;
		proxy_set_header User-Agent: Sia-Agent;
		# proxy this call to siad /skynet/skylink/ endpoint (make sure the ip is correct)
		proxy_pass http://siad/skynet/skylink/$skylink$is_args$args;
	}

	location @subdomain {
		include /etc/nginx/conf.d/include/cors;
		include /etc/nginx/conf.d/include/proxy-buffer;
		include /etc/nginx/conf.d/include/proxy-cache-downloads;

		limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
		add_header Cache-Control "public, max-age=86400"; # allow consumer to cache response

		proxy_read_timeout 600;
		proxy_set_header User-Agent: Sia-Agent;
		# proxy this call to siad /skynet/skylink/ endpoint (make sure the ip is correct)
		proxy_pass http://siad/skynet/skylink/$subdomain/$request_uri;
	}

	location ~ "^/file/([a-zA-Z0-9-_]{46}(/.*)?)$" {
		include /etc/nginx/conf.d/include/cors;
		include /etc/nginx/conf.d/include/proxy-buffer;
		include /etc/nginx/conf.d/include/proxy-cache-downloads;

		limit_conn downloads_by_ip 100; # ddos protection: max 100 downloads at a time
		add_header Cache-Control "public, max-age=86400"; # allow consumer to cache response

		# we need to explicitly use set directive here because $1 will contain the skylink with
		# decoded whitespaces and set will re-encode it for us before passing it to proxy_pass
		set $skylink $1;

		proxy_read_timeout 600;
		proxy_set_header User-Agent: Sia-Agent;
		# proxy this call to siad /skynet/skylink/ endpoint (make sure the ip is correct)
		# this alias also adds attachment=true url param to force download the file
		proxy_pass http://siad/skynet/skylink/$skylink?attachment=true&$args;
	}
}