(use-modules (htmlprag) (logging logger) (logging port-log) ((oop goops) #:select (make)) (safsaf) (safsaf handler-wrappers csrf) (safsaf handler-wrappers logging) (safsaf response-helpers) (safsaf router) (safsaf utils) (safsaf params)) ;;; ;;; Paste storage (in-memory) ;;; (define %pastes (make-hash-table)) (define %next-id 0) (define (store-paste! content) "Store CONTENT and return its integer ID." (let ((id %next-id)) (set! %next-id (1+ id)) (hashv-set! %pastes id content) id)) (define (fetch-paste id) "Return the paste content for integer ID, or #f." (hashv-ref %pastes id)) ;;; ;;; Handlers ;;; (define (paste-form-page errors content-val) "Render the paste form, optionally with validation errors and prior input." (html-response `(div (h1 "Paste Bin") (form (@ (method "POST") (action "/pastes")) ,(csrf-token-field) ,@(map (lambda (err) `(p (@ (style "color: #c33")) ,err)) errors) (textarea (@ (name "content") (rows "20") (cols "80")) ,content-val) (br) (button (@ (type "submit")) "Create Paste"))))) (define (index-page request body-port) (paste-form-page '() "")) (define (create-paste request body-port) (let* ((form (parse-form-body request body-port)) (params (parse-form-params `((content ,as-string #:required)) form))) (if (any-invalid-params? params) (paste-form-page (field-errors params 'content) (or (assoc-ref form "content") "")) (let ((id (store-paste! (assq-ref params 'content)))) (redirect-response (string-append "/pastes/" (number->string id))))))) (define (show-paste request body-port) (let* ((params (current-route-params)) (id-str (assoc-ref params 'id)) (id (string->number id-str)) (content (and id (fetch-paste id)))) (if content (html-response `(div (h1 "Paste " ,id-str) (pre ,content) (p (a (@ (href "/")) "New paste")))) (not-found-response "Paste not found")))) ;;; ;;; Logging setup ;;; (define (setup-logging) (let ((lgr (make )) (handler (make #:port (current-error-port)))) (add-handler! lgr handler) (set-default-logger! lgr) (open-log! lgr))) (setup-logging) ;;; ;;; Routes and entry point ;;; (define %port 8081) (define paste-routes (wrap-routes (list (route 'GET '() index-page) (route 'POST '("pastes") create-paste) (route 'GET '("pastes" id) show-paste) (route '* '* (lambda (request body-port) (not-found-response)))) logging-handler-wrapper csrf-handler-wrapper)) (format #t "Paste Bin listening on port ~a~%" %port) (run-safsaf paste-routes #:port %port)