aboutsummaryrefslogtreecommitdiff
path: root/module/calp/server/routes.scm
diff options
context:
space:
mode:
Diffstat (limited to 'module/calp/server/routes.scm')
-rw-r--r--module/calp/server/routes.scm228
1 files changed, 114 insertions, 114 deletions
diff --git a/module/calp/server/routes.scm b/module/calp/server/routes.scm
index 2f3544ee..d05451eb 100644
--- a/module/calp/server/routes.scm
+++ b/module/calp/server/routes.scm
@@ -1,14 +1,13 @@
(define-module (calp server routes)
:use-module (hnh util)
- :use-module ((hnh util path) :select (path-append))
- :use-module (hnh util options)
+ :use-module (hnh util path)
:use-module (hnh util exceptions)
:use-module (srfi srfi-1)
:use-module ((ice-9 rdelim) :select (read-string))
:use-module ((ice-9 ftw) :select (scandir))
- :use-module (ice-9 regex) #| regex here due to bad macros |#
+ :use-module (ice-9 format)
:use-module ((web response) :select (build-response))
:use-module ((web uri) :select (build-relative-ref))
@@ -32,6 +31,7 @@
:autoload (vcomponent util instance) (global-event-object)
+ :use-module (calp util config)
:use-module (calp html view calendar)
:use-module ((calp html view search) :select (search-result-page))
@@ -47,27 +47,50 @@
-(define (directory-table dir)
- `(table
- (thead
- (tr (th "") (th ,(_ "Name"))
- ;; File permissions, should be about as long as three digits
- (th ,(_ "Perm"))))
+;; @var{prefix} directory tree which should be exported
+;; @var{dir} location in exported directory tree
+;; Note that the exported url is currently hard-coded to
+;; start with /static.
+(define (directory-table prefix dir)
+ `(table (@ (class "directory-table"))
+ (thead
+ (tr (th "")
+ (th ,(_ "Name"))
+ ;; File permissions, should be about as long as three digits
+ (th ,(_ "Perm"))
+ ;; File size
+ (th ,(_ "Size"))))
(tbody
+ (tr (td "↩ī¸") (td (@ (colspan 3))
+ (a (@ (href ,(-> (path-split dir)
+ (drop-right 1)
+ (xcons "/static")
+ path-join)))
+ "Return up")))
,@(map (lambda (k)
- (let* ((stat (lstat (path-append dir k))))
+ (let* ((stat (lstat (path-append prefix dir k))))
`(tr (td ,(case (stat:type stat)
[(directory) "📁"]
[(regular) "📰"]
+ [(symlink) "🔗"]
+ [(block-special) "🖴"]
+ [(char-special) "🔌"]
+ ;; [(fifo)]
+ ;; [(socket)]
[else "🙃"]))
- (td (a (@ (href "/" ,dir "/" ,k)) ,k))
- (td ,(number->string (stat:perms stat) 8)))))
- (cdr (or (scandir dir)
- (scm-error
- 'misc-error
- "directory-table"
- (_ "Scandir argument invalid or not directory: ~a")
- (list dir) '())))))))
+ (td (a (@ (href ,(path-append "/static" dir k)))
+ ,k))
+ (td ,(number->string (stat:perms stat) 8))
+ (td (@ (style "text-align:end"))
+ (data (@ (value ,(stat:size stat)))
+ ,(format #f "~:d" (stat:size stat)))))))
+ ;; cddr drops '.' and '..'
+ (cddr (or (scandir (path-append prefix dir))
+ (scm-error
+ 'misc-error
+ "directory-table"
+ (_ "Scandir argument invalid or not directory: ~s")
+ (list dir) '())))))))
@@ -88,6 +111,14 @@
+(define static-dir (make-parameter "static"))
+
+(define-config static-dir "static"
+ description: "Where static files for the web server are located"
+ post: static-dir
+ )
+
+
;; TODO ensure encoding on all fields which take user provided data.
;; Possibly a fallback which strips everything unknown, and treats
@@ -234,70 +265,22 @@
;; accidental overwriting.
- (cond
- [(get-event-by-uid global-event-object (prop event 'UID))
- => (lambda (old-event)
-
- ;; remove old instance of event from runtime
- ((@ (vcomponent util instance methods) remove-event)
- global-event-object old-event)
-
- ;; Add new event to runtime,
- ;; MUST be done after since the two events SHOULD share UID.
- (parameterize ((warnings-are-errors #t))
- (catch 'warning
- (lambda () (add-event global-event-object calendar event))
- (lambda (err fmt args)
- (return (build-response code: 400)
- (format #f "~?~%" fmt args)))))
-
- (set! (prop event 'LAST-MODIFIED)
- (current-datetime))
-
- ;; NOTE Posibly defer save to a later point.
- ;; That would allow better asyncronous preformance.
-
- ;; save-event sets -X-HNH-FILENAME from the UID. This is fine
- ;; since the two events are guaranteed to have the same UID.
- (unless ((@ (vcomponent formats vdir save-delete) save-event) event)
- (return (build-response code: 500)
- (_ "Saving event to disk failed.")))
-
-
- (unless (eq? calendar (parent old-event))
- ;; change to a new calendar
- (format (current-error-port)
- ;; unlinks (removes) a single event, argument is a file name
- (_ "Unlinking old event from ~a~%")
- (prop old-event '-X-HNH-FILENAME))
- ;; NOTE that this may fail, leading to a duplicate event being
- ;; created (since we save beforehand). This is just a minor problem
- ;; which either a better atomic model, or a propper error
- ;; recovery log would solve.
- ((@ (vcomponent formats vdir save-delete) remove-event) old-event))
-
-
- (format (current-error-port)
- (_ "Event updated ~a~%") (prop event 'UID)))]
-
- [else
- (parameterize ((warnings-are-errors #t))
- (catch 'warning
- (lambda () (add-event global-event-object calendar event))
- (lambda (err fmt args)
- (return (build-response code: 400)
- (format #f "~?~%" fmt args)))))
-
- (set! (prop event 'LAST-MODIFIED) (current-datetime))
-
- ;; NOTE Posibly defer save to a later point.
- ;; That would allow better asyncronous preformance.
- (unless ((@ (vcomponent formats vdir save-delete) save-event) event)
- (return (build-response code: 500)
- (_ "Saving event to disk failed.")))
-
- (format (current-error-port)
- (_ "Event inserted ~a~%") (prop event 'UID))])
+ (parameterize ((warnings-are-errors #t))
+ (catch*
+ (lambda () (add-and-save-event global-event-object
+ calendar event))
+ (warning
+ (lambda (err fmt args)
+ (define str (format #f "~?" fmt args))
+ (format (current-error-port) "400 ~a~%" str)
+ (return (build-response code: 400)
+ str)))
+ (#t
+ (lambda (err proc fmt args _)
+ (define str (format #f "~a in ~a: ~?~%" err proc fmt args))
+ (format (current-error-port) "500 ~a~%" str)
+ (return (build-response code: 500)
+ str)))))
(return '((content-type application/xml))
(with-output-to-string
@@ -395,28 +378,27 @@
(define error #f)
(define search-result
- (catch #t
- (lambda ()
- (catch 'max-page
- ;; TODO Get-page only puts a time limiter per page, meaning that
- ;; if a user requests page 1000 the server is stuck trying to
- ;; find that page, which can take up to 1000 * timeslice = 500s = 8min+
- ;; A timeout here, and also an actual multithreaded server should
- ;; solve this.
- (lambda () (get-page paginator page))
- (lambda (err page-number)
- (define location
- (build-relative-ref
- path: r:path ; host: r:host port: r:port
- query: (encode-query-parameters
- `((p . ,page-number)
- (q . ,search-term)))))
- (return (build-response
- code: 307
- headers: `((location . ,location)))))))
- (lambda (err callee fmt arg data)
- (set! error
- (format #f "~?~%" fmt arg)))))
+ ;; TODO Get-page only puts a time limiter per page, meaning that
+ ;; if a user requests page 1000 the server is stuck trying to
+ ;; find that page, which can take up to 1000 * timeslice = 500s = 8min+
+ ;; A timeout here, and also an actual multithreaded server should
+ ;; solve this.
+ (catch* (lambda () (get-page paginator page))
+ (max-page
+ (lambda (err page-number)
+ (define location
+ (build-relative-ref
+ path: r:path ; host: r:host port: r:port
+ query: (encode-query-parameters
+ `((p . ,page-number)
+ (q . ,search-term)))))
+ (return (build-response
+ code: 307
+ headers: `((location . ,location))))))
+ (#t
+ (lambda (err callee fmt arg data)
+ (set! error
+ (format #f "~?~%" fmt arg))))))
(return '((content-type application/xhtml+xml))
(with-output-to-string
@@ -431,6 +413,7 @@
;; is mostly for development, and something like nginx should be used in
;; production it isn't a huge problem.
+
(GET "/static/:*{.*}.:ext" (* ext)
;; Actually parsing /etc/mime.types would be better.
@@ -439,16 +422,33 @@
[(js) "javascript"]
[else ext]))
- (return
- `((content-type ,(string->symbol (string-append "text/" mime))))
- (call-with-input-file (string-append "static/" * "." ext)
- read-string)))
-
- (GET "/static/:*{.*}" (*)
- (return
- '((content-type text/html))
- (sxml->html-string
- (directory-table (path-append "static" *)))))
+ (catch 'system-error
+ (lambda ()
+ (return
+ `((content-type ,(string->symbol (string-append "text/" mime))))
+ (call-with-input-file (path-append (static-dir) (string-append * "." ext))
+ read-string)))
+ (lambda (err proc fmt fmt-args data)
+ (warning (format #f "404|500: ~?" fmt fmt-args))
+ (if (= ENOENT (car data))
+ (return (build-response code: 404)
+ (format #f "~?" fmt fmt-args))
+ (scm-error err proc fmt fmt-args data)))))
+
+ ;; Note that `path' will most likely start with a slash
+ (GET "/static:path{.*}" (path)
+ (catch
+ 'misc-error
+ (lambda () (return
+ '((content-type text/html))
+ (sxml->html-string
+ `(html
+ (head (title "Calp directory listing for " path)
+ ,((@ (calp html components) include-css) "/static/directory-listing.css"))
+ (body ,(directory-table (static-dir) path))))))
+ (lambda (err proc fmt fmt-args data)
+ (return (build-response code: 404)
+ (format #f "~?" fmt fmt-args)))))
;; This is almost the same as /static/, but with the difference that
;; we produce these images during runtime