(in-package #:cl-user) (defpackage #:ritherdon-archive.web (:use #:cl #:caveman2 #:ritherdon-archive.config #:ritherdon-archive.view #:ritherdon-archive.db #:datafly #:sxql #:cl-pass #:app-constants #:status-codes ; (HTTP Status Codes) #:hermetic #:auth #:utils #:validation #:user #:nera-db) (:export #:*web*)) (in-package #:ritherdon-archive.web) ;; for @route annotation (syntax:use-syntax :annot) ;; ;; Application (defclass () ()) (defvar *web* (make-instance ')) (clear-routing-rules *web*) ;; ;; Routing rules (defroute "/" () (if (not (uiop:file-exists-p (ritherdon-archive.config:database-name))) (redirect "/setup") (render #P"index.html" (append (if (hermetic:logged-in-p) (auth:auth-user-data)) `(:alert ,(utils:get-and-reset-alert) :nav-menu ,(nera:nav-menu-slugs) :system-data ,(nera:system-data) :content ,(storage:open-text-file "" "pages" (site-settings::home-page-of (nera:get-site-settings)))))))) (defroute "/setup" () ;; If there is no database, there is no user, hence no more checks. (cond ((not (uiop:file-exists-p (ritherdon-archive.config:database-name))) (render #P"initial-setup.html" `(:token ,(auth:csrf-token)))) (t `(,+service-unavailable+ (:location "/"))))) (defroute ("/run-setup" :method :POST) () (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) ((uiop:file-exists-p (ritherdon-archive.config:database-name)) `(,+service-unavailable+ (:location "/"))) (t (nera-db:init-db (lack.request:request-body-parameters ningle:*request*)) (storage:init-storage) ;; Redirect to /setup-complete? (utils:set-alert "All set-up. Log-in to continue.") (redirect "/login"))))) (defroute ("/sign-up" :method :GET) () (if (= +false+ (site-settings::enable-sign-up-p (nera:get-site-settings))) (progn (utils:set-alert "This feature has been disabled.") (redirect "/")) (hermetic:auth (:logged-in) ;; Hermetic Authorised (Logged in users can't sign-up). (progn (utils:set-alert "You need to be signed out to create a new account.") (redirect "/dashboard")) ;; Hermetic Not Authorised (let ((alert (utils:get-and-reset-alert))) (render "sign-up.html" `(:token ,(auth:csrf-token) :system-data ,(nera:system-data) :alert ,alert)))))) (defroute ("/sign-up" :method :POST) () (destructuring-bind (&key username display-name password password-check authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) ((not (null (nera:get-user username))) (utils:set-alert "Username already taken.") (redirect "/sign-up")) ((not (string= password password-check)) (utils:set-alert "Passwords don't match.") (redirect "/sign-up")) ((find t (mapcar #'utils:string-is-nil-or-empty? `(,username ,display-name ,password))) (utils:set-alert "Incomplete form. Please fill out every section.") (redirect "/sign-up")) (t (hermetic:auth (:logged-in) ;; Authorised (Logged in user can't sign-up). (progn (utils:set-alert "Logged in users are not allowed to create account.") (redirect "/dashboard")) ;; Not Authorised (progn (nera-db:create-user username display-name password +false+) (utils:set-alert "Account created.") (redirect "/login"))))))) (defroute ("/login" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (redirect "/dashboard") ;; Not Authorised (let ((alert (utils:get-and-reset-alert))) (render "user/log-in.html" `(:token ,(auth:csrf-token) :system-data ,(nera:system-data) :alert ,alert))))) (defroute ("/login" :method :POST) () (destructuring-bind (&key username password authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (let ((params (list :|username| username :|password| password))) (hermetic:login params ;; Successful log-in attempt. (progn (setf ;; Set session Id. to the logged in user. (gethash :id ningle:*session*) (nera:get-user-id username) ;; Set the users password (for session) (gethash :password ningle:*session*) password) (redirect "/dashboard")) ;; Failed log-in attempt. (progn (utils:set-alert "Incorrect details provided.") (redirect "/login")) ;; No user found. (progn (utils:set-alert "Can't find that user.") (redirect "/login"))))))) (defroute ("/logout" :method :POST) () (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:logged-in) (hermetic:logout ;; Successful log-out. (progn (auth:flash-gethash :id ningle:*session*) (redirect "/")) ;; Failed log-out (progn (utils:set-alert "Error: Unable to log out.") (redirect "/"))))))) (defroute ("/dashboard" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render #P"user/dashboard.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/site-settings" :method :GET) () (hermetic:auth (:administrator) ;; Authorised (render #P"user/site-settings.html" (append (auth:auth-user-data) `(:alert ,(utils:get-and-reset-alert) :pages ,(nera:get-all-pages) :system-data ,(nera:system-data) :site-wide-snippet ,(storage:open-text-file-with-raw-path "static/js/site-wide-snippet.js") ;; Should this be replaced by `SYSTEM-DATA'? :settings ,(nera:get-site-settings)))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))) (defroute ("/site-settings/update-sign-up" :method :POST) () (destructuring-bind (&key enable-sign-up authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (progn (nera:update-enable-sign-on-settings enable-sign-up) (utils:set-alert "Enable Sign-Up setting saved.") (redirect "/site-settings")) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-nav-menu" :method :POST) (&key _parsed) (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (progn (format t "~a" (cdr (assoc "page" _parsed :test #'string=))) (nera:update-nav-menu _parsed) (utils:set-alert "Nav. menu updated.") (redirect "/site-settings")) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-home-page" :method :POST) () (destructuring-bind (&key set-home-page authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (cond ((or (string= set-home-page "Select Page") (utils:string-is-nil-or-empty? set-home-page)) (utils:set-alert "No value provided. Home page not changed.") (redirect "/site-settings")) (t (nera:set-home-page set-home-page) (utils:set-alert "Home page set.") (redirect "/site-settings"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-site-name" :method :POST) () (destructuring-bind (&key site-name authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? site-name) (utils:set-alert "No value provided. Site name not changed.") (redirect "/site-settings")) (t (nera:update-site-name site-name) (utils:set-alert "Site name updated.") (redirect "/site-settings"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-code-snippet" :method :POST) () (destructuring-bind (&key code-snippet authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (progn (storage:store-text-with-raw-path "static/js/site-wide-snippet.js" code-snippet) (utils:set-alert "Site-Wide snippet updated.") (redirect "/site-settings")) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-favicon" :method :POST) () (destructuring-bind (&key favicon-file authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? (cadr favicon-file)) (utils:set-alert "No file provided. Favicon not uploaded.") (redirect "/site-settings")) (t (storage:store-with-raw-path "static/images/favicon.png" favicon-file) (utils:set-alert "Favicon saved.") (utils:run-bash-command (format nil "convert ~a -resize 192x192\\> ~a" (merge-pathnames #P"static/images/favicon.png" ritherdon-archive.config:*application-root*) (merge-pathnames #P"static/images/favicon.png" ritherdon-archive.config:*application-root*))) (redirect "/site-settings"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-enable-site-logo" :method :POST) () (destructuring-bind (&key enable-site-logo authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (progn (nera:update-enable-site-logo-setting enable-site-logo) (utils:set-alert "Enable Site Logo setting saved.") (redirect "/site-settings")) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/site-settings/update-site-logo" :method :POST) () (destructuring-bind (&key site-logo authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? (cadr site-logo)) (utils:set-alert "No file provided. Site logo not uploaded.") (redirect "/site-settings")) (t (storage:store-with-raw-path "static/images/site-logo.png" site-logo) (utils:set-alert "Site logo saved.") (utils:run-bash-command (format nil "convert ~a -resize 500x500\\> ~a" (merge-pathnames #P"static/images/site-logo.png" ritherdon-archive.config:*application-root*) (merge-pathnames #P"static/images/site-logo.png" ritherdon-archive.config:*application-root*))) (redirect "/site-settings"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/users" :method :GET) () (hermetic:auth (:administrator) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render #P"user/index.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data) :users ,(nera:get-all-users))))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))) (defroute ("/user/admin/create" :method :POST) () (destructuring-bind (&key username display-name password password-check authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) ((not (string= password password-check)) (utils:set-alert "Passwords don't match.") (redirect "/users")) ((find t (mapcar #'utils:string-is-nil-or-empty? `(,username ,display-name ,password))) (utils:set-alert "Incomplete form. Please fill out every section.") (redirect "/users")) ((not (null (nera:get-user username))) (utils:set-alert "Username already taken.") (redirect "/users")) (t (hermetic:auth (:administrator) ;; Authorised (progn (nera-db:create-user username display-name password +false+) (utils:set-alert "Account created.") (redirect "/users")) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view that page.") (redirect "/"))))))) (defroute ("/user/edit" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render #P"user/edit.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/user/edit" :method :POST) () (destructuring-bind (&key display-name new-password password-check authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) ((not (string= new-password password-check)) (utils:set-alert "Passwords don't match.") (redirect "/user/edit")) (t (hermetic:auth (:logged-in) ;; Authorised (progn (nera-db:update-user (user::username-of (auth:get-current-user)) display-name new-password) (utils:set-alert "User details updated.") (redirect "/dashboard")) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/user/admin/edit-password" :method :POST) () (destructuring-bind (&key username password authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? username) (utils:set-alert "Username not provided. No change made.") (redirect "/users")) ((utils:string-is-nil-or-empty? password) (utils:set-alert "Password not provided. No change made.") (redirect "/users")) ((null (nera:get-user username)) (utils:set-alert "Unable to find user. Unable to delete account") (redirect "/users")) (t (nera:update-user (user::username-of (nera:get-user username)) :new-password password) (utils:set-alert "Password changed.") (redirect "/users"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view that page.") (redirect "/"))))))) (defroute ("/user/admin/delete" :method :POST) () (destructuring-bind (&key username authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? username) (utils:set-alert "Username not provided. Unable to delete account.") (redirect "/users")) ((null (nera:get-user username)) (utils:set-alert "Unable to find user. Unable to delete account") (redirect "/users")) (t (nera:delete-user (user::username-of (nera:get-user username))) (utils:set-alert "Account deleted.") (redirect "/users"))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/user/delete" :method :POST) () (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:logged-in) ;; Authorised (progn (nera-db:delete-user (user::username-of (auth:get-current-user))) (hermetic:logout ;; Successful log-out -- after account deleted ;; (session data cleared). (progn (auth:flash-gethash :id ningle:*session*) (redirect "/")) ;; Failed log-out -- after account deleted ;; (session data persits). (progn (utils:set-alert "Error: Unable to delete session data.") (redirect "/")))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/create/page" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render "/user/create-page.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/create/page" :method :POST) () (destructuring-bind (&key title page-content authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (format t "~a" page-content) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? title) (render "/user/create-page.html" (append (auth:auth-user-data) `(:alert "Title not provided. Unable to save page." :title ,title :data ,page-content)))) ((storage:file-exists-p "" "pages" (utils:slugify title)) (render "/user/create-page.html" (append (auth:auth-user-data) `(:alert "Page with that title already exists. Unable to save page." :title ,title :data ,page-content)))) (t (storage:store-text "" ; `USERNAME' blank because it's not used/needed. "pages" (utils:slugify title) page-content) (nera:create-page title (utils:slugify title) +false+ +true+) (utils:set-alert "Page created.") (redirect "/dashboard"))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/user/pages" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render "/user/pages.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data) :pages ,(nera:get-all-pages))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/pages" :method :GET) () (let ((alert (utils:get-and-reset-alert))) (render #P"pages.html" (append (if (hermetic:logged-in-p) (auth:auth-user-data)) `(:alert ,alert :system-data ,(nera:system-data) :pages ,(nera:get-all-pages)))))) (defroute ("/view/page/:slug" :method :GET) (&key slug) (let ((alert (utils:get-and-reset-alert))) (if (storage:file-exists-p "" "pages" slug) (render #P"page.html" (append (if (hermetic:logged-in-p) (auth:auth-user-data)) `(:alert ,alert :db-data ,(nera:get-page slug) :system-data ,(nera:system-data) :data ,(storage:open-text-file "" "pages" slug)))) (on-exception *web* 404)))) (defroute ("/edit/page/:slug" :method :GET) (&key slug) (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render "/user/edit-page.html" (append (auth:auth-user-data) `(:alert ,alert :system-data ,(nera:system-data) :db-data ,(nera:get-page slug) :data ,(storage:open-text-file "" "pages" slug))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/edit/page" :method :POST) () (destructuring-bind (&key slug page-content authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (format t "~a" page-content) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? slug) (utils:set-alert "Page cannot be found.") (redirect "/user/pages")) (t (storage:store-text "" ; `USERNAME' blank because it's not used/needed. "pages" slug page-content) (utils:set-alert "Page updated.") (redirect "/user/pages"))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/rename/page" :method :POST) () (destructuring-bind (&key id slug new-title authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? slug) (utils:set-alert "Cannot find file. Unable to save changes.") (redirect "/user/pages")) ((utils:string-is-nil-or-empty? new-title) (utils:set-alert "No title provided. Unable to save changes.") (redirect (format nil "/edit/page/~a" slug))) ((utils:string-is-nil-or-empty? id) (utils:set-alert "No Id. found. Cannot find page in database.") (redirect "/user/pages")) (t (storage:rename-content-file "" ; `USERNAME' blank because it's not used/needed. "pages" slug (utils:slugify new-title)) (nera:update-page id new-title (utils:slugify new-title)) (utils:set-alert "File name changed.") (redirect (format nil "/edit/page/~a" (utils:slugify new-title))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/page/set-nav-menu" :method :POST) () (destructuring-bind (&key slug show-in-nav authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? slug) (utils:set-alert "Cannot find page in database.") (redirect "/user/pages")) (t (nera:update-nav-menu slug (if (null show-in-nav) +false+ +true+)) (utils:set-alert "Updated name changed.") (redirect (format nil "/edit/page/~a" slug)))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login"))))))) (defroute ("/page/delete" :method :POST) () (destructuring-bind (&key slug authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:administrator) ;; Authorised (cond ((utils:string-is-nil-or-empty? slug) (utils:set-alert "Cannot find page in database. Nothing deleted.") (redirect "/user/pages")) ((null (storage:file-exists-p "" "pages" slug)) (utils:set-alert "Unable to find page. Nothing deleted.") (redirect "/user/pages")) ((eql (pages::can-delete-p (nera:get-page slug)) +false+) (utils:set-alert "File not allowed to be deleted.") (redirect "/user/pages")) (t (storage:remove-file "" ; `USERNAME' blank because it's not used/needed. "pages" slug) (nera:delete-page :slug slug) (utils:set-alert "Page deleted.") (redirect "/user/pages"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to delete page.") (redirect "/login"))))))) (defroute ("/storage/manage" :method :GET) () (hermetic:auth (:logged-in) ;; Authorised (let ((alert (utils:get-and-reset-alert))) (render #P"/user/storage.html" (append (auth:auth-user-data) `(:alert ,alert :files ,(nera:get-all-storage-files) :system-data ,(nera:system-data))))) ;; Not Authorised (progn (utils:set-alert "You are not logged in.") (redirect "/login")))) (defroute ("/storage/upload" :method :POST) () (destructuring-bind (&key file-name storage-file authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? (cadr storage-file)) (utils:set-alert "No file provided.") (redirect "/storage/manage")) ((not (null (nera:get-storage-file :filename file-name))) (utils:set-alert "File with that name already exists. File not saved.") (redirect "/storage/manage")) (t (storage:store-file "" "media" file-name storage-file) (nera:add-storage-file file-name (utils:slugify file-name) (caddr storage-file)) (utils:set-alert "File uploaded.") (redirect "/storage/manage"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) (defroute ("/storage/multi-upload" :method :POST) () (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (let ((files (utils:separate-files-in-web-request (lack.request:request-body-parameters ningle:*request*) "STORAGE-FILES"))) (format t "[INFO] Files: ~a" (length files)) (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? (caddr (car files))) (utils:set-alert "No files provided.") (redirect "/storage/manage")) (t (loop :for item :in files :do (storage:store-file "" "media" (utils:slugify (caddr item)) (cdr item)) (nera:add-storage-file (caddr item) (utils:slugify (caddr item)) (cadddr item))) (utils:set-alert "Multi-File upload complete.") (redirect "/storage/manage"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login"))))))) (defroute ("/storage/rename/:slug" :method :POST) (&key slug) (destructuring-bind (&key new-file-name authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (if (not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied")) (hermetic:auth (:logged-in) ;; Authorised (cond ((utils:string-is-nil-or-empty? new-file-name) (utils:set-alert "No file name provided. Nothing updated.") (redirect "/storage/manage")) ((not (null (nera:get-storage-file :filename new-file-name))) (utils:set-alert "File with that name already exists. File not saved.") (redirect "/storage/manage")) (t (storage:rename-content-file "" "media" (files::name-of (nera:get-storage-file :slug slug)) new-file-name) (nera:rename-storage-file (files::name-of (nera:get-storage-file :slug slug)) new-file-name) (utils:set-alert "File uploaded.") (redirect "/storage/manage"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to view this page.") (redirect "/login")))))) ;; TODO: FINISH WORKING ON /STORAGE/DELETE DEFROUTE -- NEED TO TEST IT. (defroute ("/storage/delete/:slug" :method :POST) () (destructuring-bind (&key authenticity-token &allow-other-keys) (utils:request-params (lack.request:request-body-parameters ningle:*request*)) (cond ((not (string= authenticity-token (auth:csrf-token))) `(,+forbidden+ (:content-type "text/plain") ("Denied"))) (t (hermetic:auth (:administrator) ;; Authorised (cond ((and (null (storage:file-exists-p "" "media" :slug slug)) (null (nera:get-storage-file :slug slug))) (utils:set-alert "Unable to find file. Nothing deleted.") (redirect "/storage/manage")) ((and (storage:file-exists-p "" "media" :slug slug) (null (nera:get-storage-file :slug slug))) (utils:set-alert "Unable to entry in database. Delete file from system.") (storage:remove-file "" "media" slug) (redirect "/storage/manage")) ((and (null (storage:file-exists-p "" "media" :slug slug)) ((nera:get-storage-file :slug slug))) (utils:set-alert "Unable to find file. Deleted entry from database.") (nera:delete-storage-file :slug slug) (redirect "/storage/manage")) (t (storage:remove-file "" ; `USERNAME' blank because it's not used/needed. "media" slug) (nera:delete-storage-file :slug slug) (utils:set-alert "File deleted.") (redirect "/storage/manage"))) ;; Not Authorised (progn (utils:set-alert "You are not authorised to delete page.") (redirect "/login"))))))) ;; ;; Error pages (defmethod on-exception ((app ) (code (eql 404))) (declare (ignore app)) (merge-pathnames #P"_errors/404.html" *template-directory*))