#lang racket/base (require net/url net/url-connect racket/file racket/path racket/port "config.rkt") (provide vendor-files vendor-files-ready? download-vendor-files!) (define vendor-files (list (cons "font-awesome/css/font-awesome.min.css" "https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css") (cons "font-awesome/fonts/fontawesome-webfont.woff2" "https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/fonts/fontawesome-webfont.woff2") (cons "easymde.min.js" "https://cdn.jsdelivr.net/npm/easymde@2.21.0/dist/easymde.min.js") (cons "easymde.min.css" "https://cdn.jsdelivr.net/npm/easymde@2.21.0/dist/easymde.min.css") (cons "purify.min.js" "https://cdn.jsdelivr.net/npm/dompurify@3.4.13/dist/purify.min.js") (cons "highlight.min.js" "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/highlight.min.js") (cons "highlight-scheme.min.js" "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/languages/scheme.min.js") (cons "highlight-github.min.css" "https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.12.0/build/styles/github.min.css") (cons "diff2html-ui-base.min.js" "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/js/diff2html-ui-base.min.js") (cons "diff2html.min.css" "https://cdn.jsdelivr.net/npm/diff2html@3.4.56/bundles/css/diff2html.min.css"))) (define (vendor-file-ready? config name) (define path (build-path (vendor-directory config) name)) (and (file-exists? path) (> (file-size path) 0))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Check whether all required browser libraries are installed. ; pre : config is a wiki-config value. ; post : Vendor files have only been inspected. ; result : #t when every required vendor file exists and is non-empty, ; otherwise #f. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (vendor-files-ready? config) (for/and ([entry (in-list vendor-files)]) (vendor-file-ready? config (car entry)))) (define (download-content source) (parameterize ((current-https-protocol 'secure)) (define-values (in headers) (get-pure-port/headers (string->url source) '() #:redirections 5 #:status? #t)) (dynamic-wind void (λ () (define status-match (regexp-match #px"^HTTP/[^ ]+ ([0-9][0-9][0-9])" headers)) (unless (and status-match (= (string->number (list-ref status-match 1)) 200)) (error 'download-vendor-files! "download failed for ~a: ~a" source (or (and status-match (list-ref status-match 1)) "invalid HTTP status"))) (port->bytes in)) (λ () (close-input-port in))))) (define (download-file! config name source) (define directory (vendor-directory config)) (define target (build-path directory name)) (define temporary-target (build-path directory (string-append name ".download"))) (define content (download-content source)) (make-directory* (path-only target)) (call-with-output-file temporary-target (λ (out) (write-bytes content out)) #:exists 'truncate/replace) (rename-file-or-directory temporary-target target #t) (void)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; goal : Download all browser libraries required by the wiki frontend. ; pre : config identifies a writable data directory and outbound HTTPS is ; available for the configured vendor URLs. ; post : Every required vendor file is stored below data/static/vendor. ; result : void. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (download-vendor-files! config) (make-directory* (vendor-directory config)) (for ([entry (in-list vendor-files)]) (define name (car entry)) (define source (cdr entry)) (unless (vendor-file-ready? config name) (download-file! config name source))) (unless (vendor-files-ready? config) (error 'download-vendor-files! "frontend vendor setup is incomplete")) (void))