Browse Source

Users lists

A users list in both HTML and JSON formats

Can be searched with GET requests
pull/23/head
Steven vanZyl 6 months ago
parent
commit
870ec9dfaa

+ 6
- 3
Cargo.toml View File

@@ -7,11 +7,14 @@ edition = "2018"
7 7
 [dependencies]
8 8
 askama = { version = "^0.8.0", features = ["with-rocket"] }
9 9
 rocket = "^0.4.0"
10
-hyper = "^0.12.25"
11
-futures = "^0.1.25"
12 10
 diesel = { version = "^1.4.2", features = ["sqlite"] }
13 11
 
12
+# By using * we match the Rocket versions
13
+openssl = "*"
14
+serde = "*"
15
+serde_derive = "*"
16
+
14 17
 [dependencies.rocket_contrib]
15 18
 version = "^0.4.0"
16 19
 default-features = false
17
-features = ["diesel_sqlite_pool"]
20
+features = ["diesel_sqlite_pool", "json"]

+ 1
- 1
LICENSE View File

@@ -1,6 +1,6 @@
1 1
 MIT License
2 2
 
3
-Copyright (c) <year> <copyright holders>
3
+Copyright (c) 2019 Rensselaer Center for Open Source
4 4
 
5 5
 Permission is hereby granted, free of charge, to any person obtaining a copy
6 6
 of this software and associated documentation files (the "Software"), to deal

+ 4
- 4
migrations/2019-03-25-190545_create_users/up.sql View File

@@ -1,18 +1,18 @@
1 1
 -- Your SQL goes here
2 2
 CREATE TABLE users (
3
-    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
3
+    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
4 4
     -- User's real name
5 5
     real_name TEXT NOT NULL,
6 6
     -- User's online chat handle
7
-    handle TEXT NOT NULL,
7
+    handle TEXT NOT NULL UNIQUE,
8 8
     -- User's email address
9
-    email TEXT NOT NULL,
9
+    email TEXT NOT NULL UNIQUE,
10 10
     -- The hash of the user's password
11 11
     password_hash TEXT NOT NULL,
12 12
     -- Is the user active?
13 13
     active BOOLEAN NOT NULL DEFAULT 1,
14 14
     -- SQLite stores dates as UNIX time
15
-    joined_on INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP,
15
+    joined_on DATE NOT NULL DEFAULT (datetime('now','localtime')),
16 16
     -- Priveledge tier
17 17
     -- 0 normal member
18 18
     -- 1 mentor

+ 2
- 2
migrations/2019-03-25-190605_create_meetings/up.sql View File

@@ -2,9 +2,9 @@
2 2
 CREATE TABLE meetings (
3 3
     id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
4 4
     -- The datetime in UNIX time
5
-    datetime INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP,
5
+    happened_on DATE NOT NULL DEFAULT (datetime('now','localtime')),
6 6
     -- The attendance code of the meeting
7
-    code TEXT NOT NULL,
7
+    code TEXT NOT NULL UNIQUE,
8 8
     -- The ID of the group the meeting was for
9 9
     group_id INTEGER NOT NULL DEFAULT 0,
10 10
     -- The ID of the user who hosted the meeting

+ 49
- 8
src/handlers.rs View File

@@ -1,16 +1,18 @@
1
+use diesel::insert_into;
2
+use diesel::prelude::*;
3
+use rocket::http::{Cookie, Cookies, RawStr};
1 4
 use rocket::request::Form;
2 5
 use rocket::response::Redirect;
3
-use diesel::insert_into;
4
-use diesel::RunQueryDsl;
6
+use rocket_contrib::json::Json;
5 7
 
6
-use crate::templates::*;
7 8
 use crate::models::*;
9
+use crate::templates::*;
8 10
 use crate::ObservDbConn;
9 11
 
10 12
 #[get("/")]
11 13
 pub fn index() -> Index {
12 14
     Index {
13
-        version: env!("CARGO_PKG_VERSION")
15
+        version: env!("CARGO_PKG_VERSION"),
14 16
     }
15 17
 }
16 18
 
@@ -27,14 +29,53 @@ pub fn signup() -> SignUp {
27 29
     SignUp
28 30
 }
29 31
 
30
-#[post("/signup", data="<user>")]
31
-pub fn signup_post(conn: ObservDbConn, user: Form<NewUser>) -> Redirect {
32
+#[post("/signup", data = "<user>")]
33
+pub fn signup_post(conn: ObservDbConn, mut cookies: Cookies, user: Form<NewUser>) -> Redirect {
32 34
     use crate::schema::users::dsl::*;
33 35
 
36
+    let mut user = user.into_inner();
37
+    user.password_hash = String::from("Password Hashing Not Implemented");
38
+
34 39
     insert_into(users)
35
-        .values(&user.into_inner())
40
+        .values(&user)
36 41
         .execute(&conn.0)
37 42
         .expect("Failed to add user to database");
38 43
 
44
+    let uid: i32 = users
45
+        .filter(&email.eq(user.email))
46
+        .first::<User>(&conn.0)
47
+        .expect("Failed to get user from database")
48
+        .id;
49
+
50
+    cookies.add_private(Cookie::new("user_id", format!("{}", uid)));
51
+
39 52
     Redirect::to("/")
40
-}
53
+}
54
+
55
+#[get("/users?<s>")]
56
+pub fn users(conn: ObservDbConn, s: Option<&RawStr>) -> Users {
57
+    Users {
58
+        users: filter_users(&conn.0, s),
59
+    }
60
+}
61
+
62
+#[get("/users.json?<s>")]
63
+pub fn users_json(conn: ObservDbConn, s: Option<&RawStr>) -> Json<Vec<User>> {
64
+    Json(filter_users(&conn.0, s))
65
+}
66
+
67
+fn filter_users(conn: &SqliteConnection, term: Option<&RawStr>) -> Vec<User> {
68
+    use crate::schema::users::dsl::*;
69
+
70
+    if let Some(term) = term {
71
+        let sterm = format!("%{}%", term);
72
+        let filter = real_name
73
+            .like(&sterm)
74
+            .or(email.like(&sterm))
75
+            .or(handle.like(&sterm));
76
+        users.filter(filter).load(conn)
77
+    } else {
78
+        users.load(conn)
79
+    }
80
+    .expect("Failed to get users")
81
+}

+ 16
- 6
src/main.rs View File

@@ -2,16 +2,22 @@
2 2
 #![feature(proc_macro_hygiene, decl_macro)]
3 3
 
4 4
 // Ensure all the macros are imported
5
-#[macro_use] extern crate rocket;
6
-#[macro_use] extern crate rocket_contrib;
7
-#[macro_use] extern crate diesel;
8
-#[macro_use] extern crate askama;
5
+#[macro_use]
6
+extern crate rocket;
7
+#[macro_use]
8
+extern crate rocket_contrib;
9
+#[macro_use]
10
+extern crate diesel;
11
+#[macro_use]
12
+extern crate askama;
13
+#[macro_use]
14
+extern crate serde_derive;
9 15
 
10 16
 // Module files
17
+mod handlers;
11 18
 mod models;
12 19
 mod schema;
13 20
 mod templates;
14
-mod handlers;
15 21
 
16 22
 use handlers::*;
17 23
 
@@ -22,5 +28,9 @@ pub struct ObservDbConn(diesel::SqliteConnection);
22 28
 fn main() {
23 29
     rocket::ignite()
24 30
         .attach(ObservDbConn::fairing())
25
-        .mount("/", routes![index, signup, signup_post, staticfile]).launch();
31
+        .mount(
32
+            "/",
33
+            routes![index, signup, signup_post, staticfile, users, users_json],
34
+        )
35
+        .launch();
26 36
 }

+ 4
- 4
src/models.rs View File

@@ -2,16 +2,17 @@
2 2
 
3 3
 use super::schema::*;
4 4
 
5
-#[derive(Queryable, Template)]
5
+#[derive(Queryable, Serialize, Template)]
6 6
 #[template(path = "user.html")]
7 7
 pub struct User {
8 8
     pub id: i32,
9 9
     pub real_name: String,
10 10
     pub handle: String,
11 11
     pub email: String,
12
+    #[serde(skip)]
12 13
     pub password_hash: String,
13 14
     pub active: bool,
14
-    pub joined_on: i32,
15
+    pub joined_on: String,
15 16
     pub tier: i32,
16 17
 }
17 18
 
@@ -27,7 +28,7 @@ pub struct NewUser {
27 28
 #[derive(Queryable)]
28 29
 pub struct Meeting {
29 30
     pub id: i32,
30
-    pub datetime: i32,
31
+    pub happened_on: String,
31 32
     pub code: String,
32 33
     pub group_id: i32,
33 34
     pub hosted_by: i32,
@@ -36,7 +37,6 @@ pub struct Meeting {
36 37
 #[derive(Default, Insertable)]
37 38
 #[table_name = "meetings"]
38 39
 pub struct NewMeeting {
39
-    pub datetime: i32,
40 40
     pub code: String,
41 41
     pub group_id: i32,
42 42
 }

+ 2
- 2
src/schema.rs View File

@@ -10,7 +10,7 @@ table! {
10 10
 table! {
11 11
     meetings (id) {
12 12
         id -> Integer,
13
-        datetime -> Integer,
13
+        happened_on -> Date,
14 14
         code -> Text,
15 15
         group_id -> Integer,
16 16
         hosted_by -> Integer,
@@ -35,7 +35,7 @@ table! {
35 35
         email -> Text,
36 36
         password_hash -> Text,
37 37
         active -> Bool,
38
-        joined_on -> Integer,
38
+        joined_on -> Date,
39 39
         tier -> Integer,
40 40
     }
41 41
 }

+ 10
- 2
src/templates.rs View File

@@ -1,9 +1,17 @@
1
+use crate::models;
2
+
1 3
 #[derive(Template)]
2 4
 #[template(path = "index.html")]
3 5
 pub struct Index {
4
-    pub version: &'static str
6
+    pub version: &'static str,
5 7
 }
6 8
 
7 9
 #[derive(Template)]
8 10
 #[template(path = "signup.html")]
9
-pub struct SignUp;
11
+pub struct SignUp;
12
+
13
+#[derive(Template)]
14
+#[template(path = "users.html")]
15
+pub struct Users {
16
+    pub users: Vec<models::User>,
17
+}

+ 4
- 2
templates/base.html View File

@@ -2,14 +2,16 @@
2 2
 <html lang="en">
3 3
 
4 4
 <head>
5
-    <title>{% block title %}{{ title }} - My Site{% endblock %}</title>
5
+    <title>{% block title %}{{ title }} - RCOS{% endblock %}</title>
6 6
     <link rel="stylesheet" href="/static/base.css">
7 7
     {% block head %}{% endblock %}
8 8
 </head>
9 9
 
10 10
 <body>
11 11
     <div>
12
-        <h1>RCOS</h1>
12
+        <h1><a href="/">RCOS</a></h1>
13
+        <a href="/signup">Sign Up</a>
14
+        <a href="/login">Log In</a>
13 15
     </div>
14 16
     <div id="content">
15 17
         {% block content %}{% endblock %}

+ 12
- 0
templates/login.html View File

@@ -0,0 +1,12 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}Log In{% endblock %}
4
+
5
+{% block head %}
6
+<style>
7
+</style>
8
+{% endblock %}
9
+
10
+{% block content %}
11
+<h1>Log In</h1>
12
+{% endblock %}

+ 30
- 0
templates/users.html View File

@@ -0,0 +1,30 @@
1
+{% extends "base.html" %}
2
+
3
+{% block title %}Users{% endblock %}
4
+
5
+{% block head %}
6
+<style>
7
+</style>
8
+{% endblock %}
9
+
10
+{% block content %}
11
+<h1>Users</h1>
12
+<form method="GET" action="/users">
13
+    <input type="text" name="s" placeholder="Search">
14
+    <button type="submit">Search</button>
15
+</form>
16
+<table>
17
+    <thead>
18
+        <th>Name</th>
19
+        <th>Handle</th>
20
+        <th>Email</th>
21
+    </thead>
22
+    {% for user in users %}
23
+    <tr>
24
+        <td>{{ user.real_name }}</td>
25
+        <td>{{ user.handle }}</td>
26
+        <td>{{ user.email }}</td>
27
+    </tr>
28
+    {% endfor %}
29
+</table>
30
+{% endblock %}

Loading…
Cancel
Save