(defpackage :webstats (:use :common-lisp :hunchentoot :spinneret)) (defpackage :webstats-js (:use :cl :parenscript)) (in-package :webstats-js) (setf *js-target-version* "1.9") (ps::define-statement-operator for-of ((var iterable) &rest body) `(ps-js::for-of ,(ps::compile-expression var) ,(ps::compile-expression iterable) ,(ps::compile-loop-body (list var) body))) (ps::defprinter ps-js::for-of (var object body-block) "for (const "(ps::ps-print var)" of "(ps::ps-print object)") " (ps::ps-print body-block)) (in-package :webstats) (yesql:import log-queries :from "queries.sql" :as :cl-yesql/sqlite :binding :all-functions) ;; (sqlite:with-open-database (db "test.db") ;; (drop-stats-table db) ;; (create-stats-table db)) ;; (sqlite:with-open-database (db "test.db") ;; (insert db ;; :click nil ;; :page "ho" ;; :referer "ref" ;; :ip "13" ;; :user-agent "sly" ;; :title "try")) (hunchentoot:define-easy-handler (visit :uri "/visit" :default-request-type :both) (title page referer click) (format nil "you are our visit ~d" (insert *sqlite* :click click :page page :referer referer :ip (remote-addr*) :user-agent (user-agent) :title title))) (hunchentoot:define-easy-handler (stat-js :uri "/stats.js") () (setf (hunchentoot:content-type*) "text/javascript") (ps:ps-compile-file "stats.paren")) (hunchentoot:define-easy-handler (metric :uri "/metric.json") () (setf (hunchentoot:content-type*) "application/json") (let ((series (activity-stats *sqlite*))) (cl-json:encode-json-to-string (apply #'mapcar #'list series)))) (hunchentoot:define-easy-handler (graphs :uri "/graphs") () (with-html-string (:doctype) (:html (:head (:title "hu yu ipi") (:meta :charset "utf-8") (:link :rel "stylesheet" :href "/webstats/static/uPlot.min.css") (:script :async t :src "/webstats/static/uPlot.iife.min.js" :type "text/javascript") (:script :async t :src "/stats/stats.js" :type "text/javascript") (:script :async t :src "http://127.0.0.1:8095/skewer")) (:body (:h1 "great graph stats") (:div :id "graph") (:script (:raw (ps:ps (add-event-listener "load" (lambda () (ps:chain (fetch "/stats/metric.json") (then #'response-to-json) (then #'plot))))))))))) (defvar *acceptor*) (defvar *sqlite*) (defun start-server (port) (setf *acceptor* (make-instance 'hunchentoot:easy-acceptor :port port)) (setf *sqlite* (sqlite:connect "test.db")) (hunchentoot:start *acceptor*)) (defun main () (let ((port (parse-integer (or (uiop:getenv "PORT") "4252")))) (start-server port) (format *standard-output* "Hunchentoot server started on port ~d.~&" port) (handler-case (bt:join-thread (find-if (lambda (th) (search "hunchentoot" (bt:thread-name th))) (bt:all-threads))) ;; Catch a user's C-c (#+sbcl sb-sys:interactive-interrupt #+ccl ccl:interrupt-signal-condition #+clisp system::simple-interrupt-condition #+ecl ext:interactive-interrupt #+allegro excl:interrupt-signal () (progn (format *error-output* "Aborting.~&") (hunchentoot:stop *acceptor*) (sqlite:disconnect *sqlite*) (uiop:quit))) (error (c) (format t "Woops, an unknown error occured:~&~a~&" c))))) (defun create-static-assets () (let ((ps:*ps-print-pretty* nil)) (with-open-file (ps:*parenscript-stream* "stats.js" :direction :output :if-exists :supersede) (ps:ps-compile-file "stats.paren"))))