(ns NoteHub.storage (:use [NoteHub.settings] [clojure.string :only (blank?)] [noir.util.crypt :only [encrypt]] [noir.options :only [dev-mode?]]) (:require [clj-redis.client :as redis])) ; Initialize the data base (def db (redis/init (when-not (dev-mode?) {:url (get-setting :db-url)}))) (defn get-current-date [] (str (java.util.Date.))) ; DB hierarchy levels (def note "note") (def published "published") (def edited "edited") (def views "views") (def password "password") (def sessions "sessions") (def short-url "short-url") (def publisher "publisher") (defn valid-publisher? [pid] (redis/hexists db publisher pid)) (defn register-publisher [pid] "Returns nil if given PID exists or a PSK otherwise" (when (not (valid-publisher? pid)) (let [psk (encrypt (str (rand-int Integer/MAX_VALUE) pid)) _ (redis/hset db publisher pid psk)] psk))) (defn revoke-publisher [pid] (redis/hdel db publisher pid)) (defn get-psk [pid] (redis/hget db publisher pid)) (defn create-session [] (let [token (encrypt (str (rand-int Integer/MAX_VALUE)))] (do (redis/sadd db sessions token) token))) (defn invalidate-session [token] ; Jedis is buggy & returns an NPE for token == nil (when token (let [was-valid (redis/sismember db sessions token)] (do (redis/srem db sessions token) was-valid)))) ; TODO: deprecated (defn update-note [noteID text passwd] (let [stored-password (redis/hget db password noteID)] (when (and stored-password (= passwd stored-password)) (redis/hset db edited noteID (get-current-date)) (redis/hset db note noteID text)))) (defn edit-note [noteID text] (do (redis/hset db edited noteID (get-current-date)) (redis/hset db note noteID text))) (defn add-note ([noteID text] (add-note noteID text nil)) ([noteID text passwd] (do (redis/hset db note noteID text) (redis/hset db published noteID (get-current-date)) (when (not (blank? passwd)) (redis/hset db password noteID passwd))))) (defn valid-password? [noteID passwd] (let [stored (redis/hget db password noteID)] (and stored (= stored passwd)))) (defn get-note [noteID] (let [text (redis/hget db note noteID)] (when text (do (redis/hincrby db views noteID 1) text)))) (defn get-note-views [noteID] (redis/hget db views noteID)) (defn get-note-statistics "Return views, publishing and editing timestamp" [noteID] { :views (redis/hget db views noteID) :published (redis/hget db published noteID) :edited (redis/hget db edited noteID) }) (defn note-exists? [noteID] (redis/hexists db note noteID)) (defn delete-note [noteID] (doseq [kw [password views note]] ; TODO: delete short url by looking for the title (redis/hdel db kw noteID))) (defn short-url-exists? "Checks whether the provided short url is taken (for testing only)" [url] (redis/hexists db short-url url)) (defn get-short-url [noteID] (redis/hget db short-url noteID)) (defn resolve-url "Resolves short url by providing all metadata of the request" [url] (let [value (redis/hget db short-url url)] (when value (read-string value)))) (defn delete-short-url "Deletes a short url (for testing only)" [noteID] (let [value (redis/hget db short-url noteID)] (do (redis/hdel db short-url noteID) (redis/hdel db short-url value)))) (defn create-short-url "Creates a short url for the given request metadata or noteID or extracts one if it was already created" [arg] (let [key (if (map? arg) (str (into (sorted-map) arg)) arg)] (if (short-url-exists? key) (redis/hget db short-url key) (let [hash-stream (partition 5 (repeatedly #(rand-int 36))) hash-to-string (fn [hash] (apply str ; map first 10 numbers to digits ; and the rest to chars (map #(char (+ (if (< 9 %) 87 48) %)) hash))) url (first (remove short-url-exists? (map hash-to-string hash-stream)))] (do ; we create two mappings: key params -> short url and back, ; s.t. we can later easily check whether a short url already exists (redis/hset db short-url url key) (redis/hset db short-url key url) url)))))