summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdb/0-create.psql14
-rw-r--r--src/site.clj119
-rwxr-xr-xstatic/index.html2
-rwxr-xr-xstatic/pichat.css3
-rwxr-xr-xstatic/pichat.js84
-rwxr-xr-xstatic/register.html30
6 files changed, 149 insertions, 103 deletions
diff --git a/db/0-create.psql b/db/0-create.psql
index 39b4efd..d34ccdd 100755
--- a/db/0-create.psql
+++ b/db/0-create.psql
@@ -9,7 +9,7 @@ CREATE TABLE users (
CREATE TABLE rooms (
room_id SERIAL PRIMARY KEY,
name text UNIQUE NOT NULL,
- created_on timestamp NOT NULL
+ created_on timestamp NOT NULL DEFAULT now()
);
CREATE TABLE messages (
@@ -17,12 +17,14 @@ CREATE TABLE messages (
user_id integer NOT NULL REFERENCES users,
room_id integer NOT NULL REFERENCES rooms,
content text NOT NULL,
- created_on timestamp NOT NULL
+ created_on timestamp NOT NULL DEFAULT now()
);
CREATE TABLE user_session (
- session_id bigint PRIMARY KEY,
+ session_id bigint NOT NULL,
user_id integer NOT NULL REFERENCES users,
- id_address text NOT NULL,
- ttl timestamp NOT NULL
-); \ No newline at end of file
+ ttl timestamp NOT NULL,
+ PRIMARY KEY (session_id, user_id)
+);
+
+INSERT INTO rooms (room_id, name) VALUES (1, 'dumpfm'); \ No newline at end of file
diff --git a/src/site.clj b/src/site.clj
index 417130e..2f4f5d8 100644
--- a/src/site.clj
+++ b/src/site.clj
@@ -1,8 +1,10 @@
; site.clj
(ns pichat
- (:import java.lang.System
- org.apache.commons.codec.digest.DigestUtils)
+ (:import java.lang.System
+ clojure.lang.PersistentQueue
+ org.apache.commons.codec.digest.DigestUtils
+ javax.servlet.http.Cookie)
(:use compojure
clojure.contrib.json.write
clojure.contrib.sql))
@@ -13,10 +15,10 @@
(def db {:classname "org.postgresql.Driver"
:subprotocol "postgresql"
:subname (str "//" db-host ":" db-port "/" db-name)
- :user "postgres"
+ :user "postgres"
:password "root"}))
-(defstruct user-struct :nick :last-seen)
+(defstruct user-struct :user-id :nick :last-seen)
(defstruct message-struct :nick :content :timestamp)
(def users (ref {}))
@@ -37,7 +39,7 @@
(dosync
(let [now (System/currentTimeMillis)
alive? (fn [[n u]] (> (u :last-seen) (- now user-timeout-ms)))]
- (ref-set users
+ (ref-set users
(into {} (filter alive? @users)))))
(. Thread (sleep flusher-sleep-ms))
x)
@@ -67,22 +69,31 @@
(defn do-select [query]
(with-connection db
- (with-query-results rs [query]
+ (with-query-results rs query
(doall rs))))
-(defn retrieve-nick [nick]
- (let [query (str "SELECT * FROM users WHERE nick = '" nick "'")]
- (first (do-select query))))
+(defn fetch-messages [room_id]
+ (let [query (str "SELECT m.content, m.created_on, u.nick FROM messages m, users u "
+ "WHERE room_id = ? AND m.user_id = u.user_id")
+ res (do-select [query room_id])]
+ (map (fn [r] (struct message-struct (r :nick) (r :content) (.getTime (r :created_on))))
+ res)))
+
+(defn fetch-nick [nick]
+ (let [query "SELECT * FROM users WHERE nick = ?"]
+ (first (do-select [query nick]))))
(defn authorize-nick-hash [nick hash]
- (let [db-user (retrieve-nick nick)]
- (and db-user (= (db-user :hash) hash))))
+ (let [db-user (fetch-nick nick)]
+ (if (and db-user (= (db-user :hash) hash))
+ (db-user :user_id)
+ false)))
(defn register [session params]
(let [nick (params :nick)
email (params :email)
hash (params :hash)]
- (if (retrieve-nick nick)
+ (if (fetch-nick nick)
(resp-error "NICK_TAKEN")
(with-connection db
(insert-values :users
@@ -90,52 +101,71 @@
[nick hash email])
(resp-success "OK")))))
-(defn renamed-user-struct [old-nick nick]
- (let [old-struct (@users old-nick)]
- (if old-struct
- (assoc old-struct :nick nick)
- (struct user-struct nick (System/currentTimeMillis)))))
+(defn init [session]
+ (prn session)
+ (let [now (System/currentTimeMillis)
+ user-id (session :user-id)
+ nick (session :nick)
+ resp (updates)]
+ (dosync
+ (if (and user-id nick)
+ (let [user-struct (struct user-struct user-id nick now)]
+ (alter users assoc nick user-struct)
+ (resp-success (merge resp {"nick" nick})))
+ [(session-assoc :last-seen now)
+ (resp-success resp)]))))
(defn login [session params]
(let [old-nick (session :nick)
nick (params :nick)
hash (params :hash)
- ts (params :ts)]
- (if (authorize-nick-hash nick hash)
+ user-id (authorize-nick-hash nick hash)]
+ (if user-id
(dosync
- (set-session {:nick nick :logged-in true})
- (let [user-struct (renamed-user-struct old-nick nick)]
+ (set-session {:user-id user-id :nick nick})
+ (let [user-struct (struct user-struct user-id nick (System/currentTimeMillis))]
(alter users dissoc old-nick)
(alter users assoc nick user-struct)
- [(session-assoc :nick nick :logged-in true)
+ [(session-assoc :user-id user-id :nick nick)
(resp-success "OK")]))
(resp-error "BAD_LOGIN"))))
-(defn init [session]
- (let [new-nick (make-random-nick)]
+(defn refresh [session]
+ (prn session)
+ (let [nick (session :nick)
+ now (System/currentTimeMillis)]
(dosync
- (alter users assoc new-nick
- (struct user-struct new-nick (System/currentTimeMillis)))
- [(session-assoc :nick new-nick)
- (resp-success (assoc (updates) :nick new-nick :loggedin false))])))
+ (if (contains? (ensure users) nick)
+ (let [last-seen (get-in @users [nick :last-seen])]
+ (alter users assoc-in [nick :last-seen] now)
+ (resp-success (updates last-seen)))
+ (let [last-seen (max (or (session :last-seen) 0) (- now (* 60 20)))]
+ [(session-assoc :last-seen now)
+ (updates last-seen)])))))
-(defn refresh [nick]
+(defn msg-transaction [nick msg]
(dosync
- (if (contains? @users nick)
- (let [last-seen (get-in @users [nick :last-seen])]
- (alter users assoc-in [nick :last-seen] (System/currentTimeMillis))
- (resp-success (updates last-seen)))
- (resp-error "UNKNOWN_USER"))))
+ (if (contains? (ensure users) nick)
+ (do (alter messages (swap cons) msg)
+ true)
+ false)))
+
+(defn msg-db [user-id room-id msg]
+ (with-connection db
+ (insert-values :messages
+ [:user_id :room_id :content]
+ [user-id room-id (msg :content)])))
(defn msg [session params]
- (dosync
- (let [nick (session :nick)
+ (let [user-id (session :user-id)
+ nick (session :nick)
content (params :content)
msg (struct message-struct nick content (System/currentTimeMillis))]
- (if (contains? @users nick)
- (do (alter messages (swap cons) msg)
- (resp-success "OK"))
- (resp-error "UNKNOWN_USER")))))
+ (if (msg-transaction nick msg)
+ (do
+ (msg-db user-id 1 msg)
+ (resp-success "OK"))
+ (resp-error "MUST_LOGIN"))))
(defroutes pichat
(GET "/" (serve-file "static" "index.html"))
@@ -144,9 +174,9 @@
(GET "/favicon.ico" (serve-file "static" "favicon.ico"))
(GET "/register" (serve-file "static" "register.html"))
(GET "/submit-registration" (register session params))
- (GET "/init" (init (session :nick)))
(GET "/login" (login session params))
- (GET "/refresh" (refresh (session :nick)))
+ (GET "/init" (init session))
+ (GET "/refresh" (refresh session))
(GET "/msg" (msg session params))
(ANY "*" [404 "Page not found"]))
@@ -154,6 +184,11 @@
(with-mimetypes)
(with-session {:type :memory, :expires (* 60 60)}))
+
+; Load messages from database
+(dosync
+ (ref-set messages (fetch-messages 1)))
+
(run-server {:port 8080}
"/*" (servlet pichat))
diff --git a/static/index.html b/static/index.html
index ae4a48f..8087996 100755
--- a/static/index.html
+++ b/static/index.html
@@ -25,7 +25,7 @@
</head>
<body>
- <div id="loginbar" style="display: none">
+ <div id="loginbar" style="display: none;">
<span>Username:</span><input id="nickInput" type="input" />
<br />
<span>Password:</span><input id="passwordInput" type="password" />
diff --git a/static/pichat.css b/static/pichat.css
index 28e7431..584189e 100755
--- a/static/pichat.css
+++ b/static/pichat.css
@@ -174,12 +174,13 @@ height: expression(this.width > 400 ? 400: true);max-width:400px;}
}
#loginbar {
+ background: white;
padding: 5px;
position: absolute;
bottom: 40px;
left: 40px;
line-height: 8px;
- z-index: 2;
+ z-index: 5;
border: 1px solid #15fff3;
}
diff --git a/static/pichat.js b/static/pichat.js
index d665e2b..56dbd36 100755
--- a/static/pichat.js
+++ b/static/pichat.js
@@ -1,7 +1,6 @@
// pichat.js
var Nick = null;
-var LoggedIn = false;
function handleMsgError(resp) {
var respText = resp.responseText ? resp.responseText.trim() : false;
@@ -54,6 +53,9 @@ function setNick(nick) {
Nick = nick;
$('#nickspan').text(nick);
$('#welcomebar').show();
+ $('#msgInput, #msgSubmit').removeAttr('disabled');
+ $('#msgInput').keyup(ifEnter(submitMessage));
+ $('#msgSubmit').click(submitMessage);
}
function submitMessage() {
@@ -100,7 +102,6 @@ function login() {
var onSuccess = function(json) {
$('#loginbar').hide();
- LoggedIn = true;
setNick(nick);
};
@@ -128,25 +129,37 @@ function scrollToBottom(div) {
div.scrollTop = div.scrollHeight;
}
-function refresh() {
- var onSuccess = function(json) {
- if (json.messages.length > 0) {
- var shouldScroll = isScrolledToBottom($('#messageList')[0]);
-
- // Ignore our own messages
+function updateUI(json, initialUpdate) {
+ if (json.messages.length > 0) {
+ if (initialUpdate) {
+ var messages = json.messages;
+ } else {
+ // Our own messages have already been displayed.
var filterFunc = function(m) { return m.nick != Nick };
- var msgStr = $.map($.grep(json.messages, filterFunc),
- buildMessageDiv).join('');
- $('#messageList').append(msgStr);
-
- if (shouldScroll) {
- scrollToBottom($('#messageList')[0]);
- }
+ var messages = $.grep(json.messages, filterFunc);
}
- $("#userList").html($.map(json.users, buildUserDiv).join(''));
- };
+
+ var msgStr = $.map(messages,
+ buildMessageDiv).join('');
+ var wasScrolledToBottom = isScrolledToBottom($('#messageList')[0]);
+ $('#messageList').append(msgStr);
+
+ if (initialUpdate || wasScrolledToBottom) {
+ // Delay scrolling by .5 seconds so images can start loading.
+ setTimeout(scrollToBottom, 500, $('#messageList')[0]);
+ }
+ }
+ $("#userList").html($.map(json.users, buildUserDiv).join(''));
+}
- var onError = function(resp, textStatus, errorThrown) {};
+function refresh() {
+ var onSuccess = function(json) {
+ updateUI(json, false);
+ setTimeout(refresh, 1000);
+ };
+ var onError = function(resp, textStatus, errorThrown) {
+ setTimeout(refresh, 1000);
+ };
$.ajax({
type: 'GET',
@@ -159,34 +172,22 @@ function refresh() {
});
}
-function init() {
+function init() {
var onSuccess = function(json) {
- $('#loadingbox').hide();
- Nick = json.nick;
-
- setNick(Nick);
- if (json.loggedin) {
- LoggedIn = true;
+ if (json.nick) {
+ setNick(json.nick);
} else {
$('#loginbar').show();
- $('#loginSubmit').click(login);
$('#passwordInput').keyup(ifEnter(login));
+ $('#loginSubmit').click(login);
}
-
- var msgStr = $.map(json.messages, buildMessageDiv).join('');
- $('#messageList').append(msgStr);
- $("#userList").html($.map(json.users, buildUserDiv).join(''));
- $('#nickInput, #nickSubmit, #msgInput, #msgSubmit').removeAttr('disabled');
-
- // Delay scrolling by .5 seconds so images can start loading.
- setTimeout(scrollToBottom, 500, $('#messageList')[0]);
- setInterval(refresh, 1000);
+ updateUI(json, true);
+ setTimeout(refresh, 1000);
};
-
var onError = function(resp, textStatus, errorThrown) {
- alert("Error connecting to chat server!");
- };
-
+ alert("Error initializing!");
+ };
+
$.ajax({
type: 'GET',
timeout: 5000,
@@ -194,9 +195,8 @@ function init() {
cache: false,
dataType: 'json',
success: onSuccess,
- error: onError
+ error: onError
});
- $('#msgInput').keyup(ifEnter(submitMessage));
- $('#msgSubmit').click(submitMessage);
+
}
diff --git a/static/register.html b/static/register.html
index e5ae761..acc13da 100755
--- a/static/register.html
+++ b/static/register.html
@@ -11,16 +11,24 @@
</script>
</head>
<body>
- <h1>Register</h1>
- <span>Nickname:</span>
- <input type="text" id="nickInput" />
- <br />
- <span>Email:</span>
- <input type="text" id="emailInput" />
- <br />
- <span>Password:</span>
- <input type="password" id="passwordInput" />
- <br />
- <input type="submit" id="submit" value="register" />
+ <div id="registerbox">
+ <h1>Register</h1>
+ <span>Nickname:</span>
+ <input type="text" id="nickInput" />
+ <br />
+ <span>Email:</span>
+ <input type="text" id="emailInput" />
+ <br />
+ <span>Password:</span>
+ <input type="password" id="passwordInput" />
+ <br />
+ <input type="submit" id="submit" value="register" />
+ </div>
+
+ <div id="confirmationbox" style="display: none">
+ <h1>You've registered!</h1>
+ <span>Please wait as you're redirected to the chat.</span>
+ </div>
+
</body>
</html>