migrated to API internally
This commit is contained in:
+19
-23
@@ -7,9 +7,9 @@
|
||||
:only [replace blank? lower-case split-lines]])
|
||||
(:require [NoteHub.storage :as storage]))
|
||||
|
||||
(def api-version "1.0")
|
||||
(def version "1.0")
|
||||
|
||||
(def domain "http://notehub.org/")
|
||||
(def domain (get-setting :domain))
|
||||
|
||||
; Concatenates all fields to a string
|
||||
(defn build-key
|
||||
@@ -28,10 +28,10 @@
|
||||
([success message & params]
|
||||
(assoc (create-response success) :message (apply format message params))))
|
||||
|
||||
(defn- getURL [noteID & description]
|
||||
(defn- getPath [noteID & description]
|
||||
(if description
|
||||
(str domain (storage/get-short-url noteID))
|
||||
(str domain (sreplace noteID #" " "/"))))
|
||||
(str "/" (storage/get-short-url noteID))
|
||||
(str "/" (sreplace noteID #" " "/"))))
|
||||
|
||||
(let [md5Instance (java.security.MessageDigest/getInstance "MD5")]
|
||||
(defn get-signature
|
||||
@@ -43,13 +43,13 @@
|
||||
(.toString (new java.math.BigInteger 1 (.digest md5Instance)) 16)))))
|
||||
|
||||
(defn get-note [noteID]
|
||||
{:note (storage/get-note noteID)
|
||||
:longURL (getURL noteID)
|
||||
:shortURL (getURL noteID :short)
|
||||
:statistics (storage/get-note-statistics noteID)
|
||||
:status (if (storage/note-exists? noteID)
|
||||
(create-response true)
|
||||
(create-response false "noteID '%s' unknown" noteID))})
|
||||
(if (storage/note-exists? noteID)
|
||||
{:note (storage/get-note noteID)
|
||||
:longPath (getPath noteID)
|
||||
:shortPath (getPath noteID :short)
|
||||
:statistics (storage/get-note-statistics noteID)
|
||||
:status (create-response true)}
|
||||
(create-response false "noteID '%s' unknown" noteID)))
|
||||
|
||||
(defn post-note
|
||||
([note pid signature] (post-note note pid signature nil))
|
||||
@@ -76,12 +76,10 @@
|
||||
(do
|
||||
(storage/add-note noteID note password)
|
||||
(storage/create-short-url noteID)
|
||||
{
|
||||
:noteID noteID
|
||||
:longURL (getURL noteID)
|
||||
:shortURL (getURL noteID :short)
|
||||
:status (create-response true)
|
||||
}))
|
||||
{:noteID noteID
|
||||
:longPath (getPath noteID)
|
||||
:shortPath (getPath noteID :short)
|
||||
:status (create-response true)}))
|
||||
{:status (create-response false (first errors))}))))
|
||||
|
||||
|
||||
@@ -97,9 +95,7 @@
|
||||
(if (empty? errors)
|
||||
(do
|
||||
(storage/edit-note noteID note)
|
||||
{
|
||||
:longURL (getURL noteID)
|
||||
:shortURL (getURL noteID :short)
|
||||
:status (create-response true)
|
||||
})
|
||||
{:longPath (getPath noteID)
|
||||
:shortPath (getPath noteID :short)
|
||||
:status (create-response true)})
|
||||
{:status (create-response false (first errors))})))
|
||||
|
||||
@@ -6,6 +6,5 @@
|
||||
(defn -main [& m]
|
||||
(let [mode (keyword (or (first m) :prod))
|
||||
port (Integer. (get (System/getenv) "PORT" "8080"))]
|
||||
(server/start port {:mode mode
|
||||
:ns 'NoteHub})))
|
||||
(server/start port {:mode mode :ns 'NoteHub})))
|
||||
|
||||
|
||||
+4
-14
@@ -40,28 +40,18 @@
|
||||
(defn get-psk [pid]
|
||||
(redis/hget db publisher pid))
|
||||
|
||||
(defn create-session
|
||||
[]
|
||||
(defn create-session []
|
||||
(let [token (encrypt (str (rand-int Integer/MAX_VALUE)))]
|
||||
(do (redis/sadd db sessions token)
|
||||
token)))
|
||||
|
||||
(defn invalidate-session
|
||||
[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
|
||||
@@ -97,8 +87,8 @@
|
||||
"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) })
|
||||
:published (redis/hget db published noteID)
|
||||
:edited (redis/hget db edited noteID) })
|
||||
|
||||
(defn note-exists?
|
||||
[noteID]
|
||||
|
||||
+50
-75
@@ -4,12 +4,12 @@
|
||||
[NoteHub.storage :as storage]
|
||||
[cheshire.core :refer :all])
|
||||
(:use
|
||||
[NoteHub.storage] ; TODO: delete this
|
||||
[NoteHub.settings]
|
||||
[NoteHub.views.common]
|
||||
[clojure.string :rename {replace sreplace}
|
||||
:only [escape split replace blank? split-lines lower-case]]
|
||||
[clojure.core.incubator :only [-?>]]
|
||||
[noir.util.crypt :only [encrypt]]
|
||||
[hiccup.form]
|
||||
[hiccup.core]
|
||||
[hiccup.element]
|
||||
@@ -17,21 +17,8 @@
|
||||
[noir.core :only [defpage defpartial]]
|
||||
[noir.statuses]))
|
||||
|
||||
(defn get-hash
|
||||
"A simple hash-function, which computes a hash from the text field
|
||||
content and given session number. It is intended to be used as a spam
|
||||
protection / captcha alternative. (Probably doesn't work for UTF-16)"
|
||||
[s]
|
||||
(let [short-mod #(mod % 32767)
|
||||
char-codes (map #(.codePointAt % 0) (remove #(contains? #{"\n" "\r"} %) (map str s)))
|
||||
zip-with-index (map list char-codes (range))]
|
||||
(reduce
|
||||
#(short-mod (+ %
|
||||
(short-mod (* (first %2)
|
||||
((if (odd? %)
|
||||
bit-xor
|
||||
bit-and) 16381 (second %2))))))
|
||||
0 zip-with-index)))
|
||||
(when-not (storage/valid-publisher? api/domain)
|
||||
(storage/register-publisher api/domain))
|
||||
|
||||
; Sets a custom message for each needed HTTP status.
|
||||
; The message to be assigned is extracted with a dynamically generated key
|
||||
@@ -70,15 +57,19 @@
|
||||
[:div.central-element.helvetica {:style "margin-bottom: 3em"}
|
||||
(form-to {:autocomplete :off} [:post form-url]
|
||||
(hidden-field :action command)
|
||||
(hidden-field :version api/version)
|
||||
(hidden-field :password)
|
||||
fields
|
||||
(text-area {:class :max-width} :draft content)
|
||||
(text-area {:class :max-width} :note content)
|
||||
[:fieldset#input-elems {:class css-class}
|
||||
(text-field {:class "ui-elem" :placeholder (get-message passwd-msg)}
|
||||
:plain-password)
|
||||
(submit-button {:class "button ui-elem"
|
||||
:id :publish-button} (get-message command))])])))
|
||||
|
||||
(defn generate-session []
|
||||
(encrypt (str (rand-int Integer/MAX_VALUE))))
|
||||
|
||||
; Routes
|
||||
; ======
|
||||
|
||||
@@ -98,24 +89,10 @@
|
||||
; Displays the note
|
||||
(defpage "/:year/:month/:day/:title" {:keys [year month day title theme header-font text-font] :as params}
|
||||
(wrap
|
||||
(create-short-url params)
|
||||
(storage/create-short-url params)
|
||||
(select-keys params [:title :theme :header-font :text-font])
|
||||
(:note (api/get-note (api/build-key [year month day] title)))))
|
||||
|
||||
; Update Note Page
|
||||
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
|
||||
(let [noteID (api/build-key [year month day] title)]
|
||||
(input-form "/update-note" :update
|
||||
(html (hidden-field :key noteID))
|
||||
(:note (api/get-note noteID)) :enter-passwd)))
|
||||
|
||||
; New Note Page
|
||||
(defpage "/new" {}
|
||||
(input-form "/post-note" :publish
|
||||
(html (hidden-field :session-key (create-session))
|
||||
(hidden-field {:id :session-value} :session-value))
|
||||
(get-message :loading) :set-passwd))
|
||||
|
||||
; Provides Markdown of the specified note
|
||||
(defpage "/:year/:month/:day/:title/export" {:keys [year month day title]}
|
||||
(when-let [md-text (:note (api/get-note (api/build-key [year month day] title)))]
|
||||
@@ -137,53 +114,55 @@
|
||||
[:td (get-message :article-views)]
|
||||
[:td (:views stats)]]])))
|
||||
|
||||
; Updates a note
|
||||
(defpage [:post "/update-note"] {:keys [key draft password]}
|
||||
(if (update-note key draft password)
|
||||
(redirect (apply url (split key #" ")))
|
||||
(response 403)))
|
||||
|
||||
; New Note Posting — the most "complex" function in the entire app ;)
|
||||
(defpage [:post "/post-note"] {:keys [draft password session-key session-value]}
|
||||
; first we collect all info needed to evaluate the validity of the note creation request
|
||||
(let [valid-session (invalidate-session session-key) ; was the note posted from a newly generated form?
|
||||
valid-draft (not (blank? draft)) ; has the note a meaningful content?
|
||||
; is the hash code correct?
|
||||
valid-hash (try
|
||||
(= (Short/parseShort session-value)
|
||||
(get-hash (str draft session-key)))
|
||||
(catch Exception e nil))]
|
||||
; check whether the new note can be added
|
||||
(if (and valid-session valid-draft valid-hash)
|
||||
; if yes, we compute the current date, extract a title string from the text,
|
||||
; which will be a part of the url and look whether this title is free today;
|
||||
; if not, append "-n", where "n" is the next free number
|
||||
(let [[year month day] (api/get-date)
|
||||
untrimmed-line (filter #(or (= \- %) (Character/isLetterOrDigit %))
|
||||
(-> draft split-lines first (sreplace " " "-") lower-case))
|
||||
trim (fn [s] (apply str (drop-while #(= \- %) s)))
|
||||
title-uncut (-> untrimmed-line trim reverse trim reverse)
|
||||
max-length (get-setting :max-title-length #(Integer/parseInt %) 80)
|
||||
; TODO: replace to ccs/take when it gets fixed
|
||||
proposed-title (apply str (take max-length title-uncut))
|
||||
date [year month day]
|
||||
title (first (drop-while #(note-exists? (api/build-key date %))
|
||||
(cons proposed-title
|
||||
(map #(str proposed-title "-" (+ 2 %)) (range)))))]
|
||||
(do
|
||||
(add-note (api/build-key date title) draft password)
|
||||
(redirect (url year month day title))))
|
||||
(response 400))))
|
||||
|
||||
; Resolving of a short url
|
||||
(defpage "/:short-url" {:keys [short-url]}
|
||||
(when-let [params (resolve-url short-url)]
|
||||
(when-let [params (storage/resolve-url short-url)]
|
||||
(let [{:keys [year month day title]} params
|
||||
rest-params (dissoc params :year :month :day :title)
|
||||
core-url (url year month day title)
|
||||
long-url (if (empty? rest-params) core-url (util/url core-url rest-params))]
|
||||
(redirect long-url))))
|
||||
|
||||
; New Note Page
|
||||
(defpage "/new" {}
|
||||
(input-form "/post-note" :publish
|
||||
(html (hidden-field :session (storage/create-session))
|
||||
(hidden-field {:id :signature} :signature))
|
||||
(get-message :loading) :set-passwd))
|
||||
|
||||
; Update Note Page
|
||||
(defpage "/:year/:month/:day/:title/edit" {:keys [year month day title]}
|
||||
(let [noteID (api/build-key [year month day] title)]
|
||||
(input-form "/update-note" :update
|
||||
(html (hidden-field :noteID noteID))
|
||||
(:note (api/get-note noteID)) :enter-passwd)))
|
||||
|
||||
; Creates New Note from Web
|
||||
(defpage [:post "/post-note"] {:keys [session note signature password version]}
|
||||
(if (= signature (api/get-signature session note))
|
||||
(let [pid api/domain
|
||||
psk (storage/get-psk pid)]
|
||||
(if (storage/valid-publisher? pid)
|
||||
(let [resp (api/post-note note pid (api/get-signature (str pid psk note)) password)]
|
||||
(if (get-in resp [:status :success])
|
||||
(redirect (:longPath resp))
|
||||
(response 400)))
|
||||
(response 500)))
|
||||
(response 400)))
|
||||
|
||||
; Updates a note
|
||||
(defpage [:post "/update-note"] {:keys [noteID note password version]}
|
||||
(let [pid api/domain
|
||||
psk (storage/get-psk pid)]
|
||||
(if (storage/valid-publisher? pid)
|
||||
(let [resp (api/update-note noteID note pid
|
||||
(api/get-signature (str pid psk noteID note password))
|
||||
password)]
|
||||
(if (get-in resp [:status :success])
|
||||
(redirect (:longPath resp))
|
||||
(response 403)))
|
||||
(response 500))))
|
||||
|
||||
; Here lives the API
|
||||
|
||||
(defpage "/api" args
|
||||
@@ -199,7 +178,3 @@
|
||||
(defpage [:put "/api/note"] {:keys [version noteID note pid signature password]}
|
||||
(generate-string (api/update-note noteID note pid signature password)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user