All checks were successful
/ test (push) Successful in 9s
Safsaf is a Guile web framework, written using Claude Code running Claude Opus 4.6, based off of the Guix Data Service, Nar Herder and Guix Build Coordinator codebases.
107 lines
2.9 KiB
Scheme
107 lines
2.9 KiB
Scheme
(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 <logger>))
|
|
(handler (make <port-log> #: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)
|