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.
This commit is contained in:
commit
5b0e6397dc
53 changed files with 7427 additions and 0 deletions
107
examples/paste-bin/paste-bin.scm
Normal file
107
examples/paste-bin/paste-bin.scm
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
(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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue