spiffy-server added by dieggsy on Fri Jul 12 17:12:20 2019
#!/usr/bin/chicken-csi -s
(import spiffy
cgi-handler
uri-common
intarweb
(only chicken.format format)
(only chicken.string string-split string-intersperse ->string)
(only chicken.file file-exists? directory)
(only chicken.pathname
make-pathname pathname-extension
pathname-file pathname-directory)
(only chicken.tcp tcp-listen tcp-accept)
(only chicken.io read-line)
(only chicken.sort sort)
(only srfi-13 string-join string-null?)
(only srfi-18 thread-start!)
(only openssl
ssl-listen* ssl-accept
ssl-load-certificate-chain! ssl-load-private-key!)
(only matchable match)
(only nrepl nrepl nrepl-loop)
(only inotify init! clean-up! add-watch! next-events!))
;; TODO: favicon
(spiffy-group "http")
(spiffy-user "http")
(root-path "/srv/http/dieggsy-domains")
(access-log "/var/log/spiffy/access.log")
(error-log "/var/log/spiffy/error.log")
(debug-log "/var/log/spiffy/debug.log")
(define cgit (cgi-handler* "/usr/lib/cgit/cgit.cgi"))
(define ssl-listener
(ssl-listen* port: 443
certificate: "/etc/letsencrypt/live/dieggsy.com/fullchain.pem"
private-key: "/etc/letsencrypt/live/dieggsy.com/privkey.pem"))
(define http-listener (tcp-listen 80))
;; (ssl-load-certificate-chain! ssl-listener "/etc/letsencrypt/live/dieggsy.com/fullchain.pem")
;; (ssl-load-private-key! ssl-listener "/etc/letsencrypt/live/dieggsy.com/privkey.pem")
(switch-user/group (spiffy-user) (spiffy-group))
;; Don't serve org files, these are internal to generate html
(file-extension-handlers
`(("org" . ,(lambda (p) (handle-not-found p)))))
(handle-not-found
(lambda (path)
(send-status 'not-found
"There seems to be nothing here!
")))
;; TODO: Modularize this per website?
(define (make-generic-handler root fn)
(lambda (continue)
(let* ((req (current-request))
(uri (request-uri req))
(path (uri-path uri))
(scheme (uri-scheme uri)))
(if (eqv? scheme 'http)
(match path
;; Handle let's encrypt certs
(('/ ".well-known" "acme-challenge" . rest)
(parameterize ((root-path root))
(continue)))
;; Redirect to https
(else
(let ((new-u (update-uri uri scheme: 'https)))
(with-headers `((location ,new-u))
(lambda () (send-status 'moved-permanently))))))
;; HTTPS
(match path
(('/ (and subdir (or "css" "image" "js")) filename)
(continue))
(else (fn continue root uri)))))))
(define (blog-fn continue root uri)
(match (uri-path uri)
(('/ (and subdir (or "prime" "fibonacci")) nth)
(parameterize ((root-path root))
(send-static-file (make-pathname "fun" subdir "html"))))
(else
(parameterize ((root-path root))
(continue)))))
(define (paste-fn continue root uri)
(match (uri-path uri)
;; TODO
(('/ (or "" "pastes.html"))
(parameterize ((root-path root))
(continue)))
(('/ title . raw)
(let* ((raw? (and (not (null? raw)) (car raw)))
(subdir (if raw? "raw" "html"))
(ext (if raw? "txt" "html"))
(fname (make-pathname #f title ext))
(new-path `(/ ,subdir ,fname))
(new-uri (update-uri uri path: new-path)))
(parameterize ((root-path root)
(current-request (update-request (current-request)
uri: new-uri)))
(continue))))))
(define (wiki-fn continue root uri)
(parameterize ((root-path root))
(continue)))
(define handle-blog (make-generic-handler "/srv/http/dieggsy-domains/blog" blog-fn))
(define handle-paste (make-generic-handler "/srv/http/dieggsy-domains/paste" paste-fn))
(define handle-wiki (make-generic-handler "/srv/http/dieggsy-domains/wiki" wiki-fn))
(define (handle-cgit continue)
(parameterize
((root-path "/srv/git")
(handle-directory cgit)
(handle-not-found
(lambda (p)
(let* ((uri (request-uri (current-request)))
(path (string-intersperse (cdr (uri-path uri)) "/")))
(parameterize ((current-pathinfo (string-split path "/")))
(cgit path))))))
(continue)))
(vhost-map `(("dieggsy.com" . ,(lambda (c) (handle-blog c)))
("paste.dieggsy.com" . ,(lambda (c) (handle-paste c)))
("wiki.dieggsy.com" . ,(lambda (c) (handle-wiki c)))
("code.dieggsy.com" . ,(lambda (c) (handle-cgit c)))
("www.dieggsy.com" .
,(lambda (c)
(let* ((old-u (request-uri (current-request)))
(new-u (update-uri old-u host: "dieggsy.com")))
(with-headers `((location ,new-u))
(lambda () (send-status 'moved-permanently))))))))
;; Watch for pastes - if a new one is created, re-generate the listing in
;; pastes.html (for paste.dieggsy.com homepage)
(init!)
(on-exit clean-up!)
(add-watch! (make-pathname (root-path) "paste/raw") '(move create delete))
(define (watch-paste)
(next-events!)
(call-with-output-file (make-pathname (root-path) "paste/pastes" "html")
(lambda (p)
(display
(string-join
(map
(lambda (f)
(let ((f (pathname-file f)))
(format "~a
" f f)))
(sort (directory (make-pathname (root-path) "paste/raw")) string>?))
"\n")
p)))
(watch-paste))
;; Generate pastes homepage
(thread-start! watch-paste)
;; Start on HTTP
(thread-start! (lambda ()
(parameterize ((server-port 80))
(accept-loop http-listener tcp-accept))))
;; Start on SSL
(parameterize ((server-port 443))
(accept-loop ssl-listener ssl-accept))