basic user profile pages
This commit is contained in:
parent
4a582e8079
commit
33664eff2c
7 changed files with 105 additions and 11 deletions
|
@ -57,14 +57,14 @@ pub fn create_user(credentials: &Credentials, conn: &PgConnection) -> QueryResul
|
||||||
.get_result::<User>(conn)
|
.get_result::<User>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_user(username: &str, db_conn: &PgConnection) -> QueryResult<User> {
|
pub fn find_user_by_name(username: &str, db_conn: &PgConnection) -> QueryResult<User> {
|
||||||
users::table
|
users::table
|
||||||
.filter(users::username.eq(username))
|
.filter(users::username.eq(username))
|
||||||
.first::<User>(db_conn)
|
.first::<User>(db_conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option<User> {
|
pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option<User> {
|
||||||
find_user(credentials.username, db_conn)
|
find_user_by_name(credentials.username, db_conn)
|
||||||
.optional()
|
.optional()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_then(|user| {
|
.and_then(|user| {
|
||||||
|
|
|
@ -119,11 +119,11 @@ pub fn api() -> Router {
|
||||||
.route("/register", post(routes::users::register))
|
.route("/register", post(routes::users::register))
|
||||||
.route("/login", post(routes::users::login))
|
.route("/login", post(routes::users::login))
|
||||||
.route("/users/me", get(routes::users::current_user))
|
.route("/users/me", get(routes::users::current_user))
|
||||||
|
.route("/users/:user/bots", get(routes::bots::get_user_bots))
|
||||||
.route(
|
.route(
|
||||||
"/bots",
|
"/bots",
|
||||||
get(routes::bots::list_bots).post(routes::bots::create_bot),
|
get(routes::bots::list_bots).post(routes::bots::create_bot),
|
||||||
)
|
)
|
||||||
.route("/bots/my_bots", get(routes::bots::get_my_bots))
|
|
||||||
.route("/bots/:bot_id", get(routes::bots::get_bot))
|
.route("/bots/:bot_id", get(routes::bots::get_bot))
|
||||||
.route(
|
.route(
|
||||||
"/bots/:bot_id/upload",
|
"/bots/:bot_id/upload",
|
||||||
|
|
|
@ -12,6 +12,7 @@ use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use thiserror;
|
use thiserror;
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
use crate::db::bots::{self, BotVersion};
|
use crate::db::bots::{self, BotVersion};
|
||||||
use crate::db::ratings::{self, RankedBot};
|
use crate::db::ratings::{self, RankedBot};
|
||||||
use crate::db::users::User;
|
use crate::db::users::User;
|
||||||
|
@ -158,11 +159,13 @@ pub async fn get_bot(
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_my_bots(
|
pub async fn get_user_bots(
|
||||||
conn: DatabaseConnection,
|
conn: DatabaseConnection,
|
||||||
user: User,
|
Path(user_name): Path<String>,
|
||||||
) -> Result<Json<Vec<Bot>>, StatusCode> {
|
) -> Result<Json<Vec<Bot>>, StatusCode> {
|
||||||
bots::find_bots_by_owner(user.id, &conn)
|
let user =
|
||||||
|
db::users::find_user_by_name(&user_name, &conn).map_err(|_| StatusCode::NOT_FOUND)?;
|
||||||
|
db::bots::find_bots_by_owner(user.id, &conn)
|
||||||
.map(Json)
|
.map(Json)
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl RegistrationParams {
|
||||||
errors.push("password must be at least 8 characters".to_string());
|
errors.push("password must be at least 8 characters".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
if users::find_user(&self.username, &conn).is_ok() {
|
if users::find_user_by_name(&self.username, &conn).is_ok() {
|
||||||
errors.push("username is already taken".to_string());
|
errors.push("username is already taken".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,8 @@
|
||||||
<td class="leaderboard-bot">{entry["bot"]["name"]}</td>
|
<td class="leaderboard-bot">{entry["bot"]["name"]}</td>
|
||||||
<td class="leaderboard-author">
|
<td class="leaderboard-author">
|
||||||
{#if entry["author"]}
|
{#if entry["author"]}
|
||||||
{entry["author"]["username"]}
|
<!-- TODO: remove duplication -->
|
||||||
|
<a href="/users/{entry["author"]["username"]}">{entry["author"]["username"]}</a>
|
||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -69,4 +70,9 @@
|
||||||
.leaderboard-rank {
|
.leaderboard-rank {
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.leaderboard-author a{
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -36,9 +36,9 @@
|
||||||
|
|
||||||
<div class="user-controls">
|
<div class="user-controls">
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
<div class="current-user-name">
|
<a class="current-user-name" href="/users/{$currentUser["username"]}">
|
||||||
{$currentUser["username"]}
|
{$currentUser["username"]}
|
||||||
</div>
|
</a>
|
||||||
<div class="sign-out" on:click={signOut}>Sign out</div>
|
<div class="sign-out" on:click={signOut}>Sign out</div>
|
||||||
{:else}
|
{:else}
|
||||||
<a class="account-href" href="login">Sign in</a>
|
<a class="account-href" href="login">Sign in</a>
|
||||||
|
@ -61,6 +61,7 @@
|
||||||
|
|
||||||
.current-user-name {
|
.current-user-name {
|
||||||
@include navbar-item;
|
@include navbar-item;
|
||||||
|
text-decoration: none;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
84
web/pw-server/src/routes/users/[user_name].svelte
Normal file
84
web/pw-server/src/routes/users/[user_name].svelte
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
<script lang="ts" context="module">
|
||||||
|
function fetchJson(url: string): Promise<Response> {
|
||||||
|
return fetch(url, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function load({ params, fetch }) {
|
||||||
|
const userName = params["user_name"];
|
||||||
|
const userBotsResponse = await fetch(`/api/users/${userName}/bots`);
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
userName,
|
||||||
|
bots: await userBotsResponse.json(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// status: matchDataResponse.status,
|
||||||
|
// error: new Error("failed to load match"),
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export let userName: string;
|
||||||
|
export let bots: object[];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1 class="user-name">{userName}</h1>
|
||||||
|
</div>
|
||||||
|
<h2>Bots</h2>
|
||||||
|
<ul class="bot-list">
|
||||||
|
{#each bots as bot}
|
||||||
|
<li class="bot">
|
||||||
|
<span class="bot-name">{bot['name']}</span>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.container {
|
||||||
|
min-width: 600px;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 50px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
margin-bottom: .5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$border-color: #d0d7de;
|
||||||
|
|
||||||
|
.bot {
|
||||||
|
display: block;
|
||||||
|
padding: 24px 0;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-name {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot:first-child {
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in a new issue