Skip to content
This repository has been archived by the owner on Apr 13, 2024. It is now read-only.

Commit

Permalink
Simple profile pages
Browse files Browse the repository at this point in the history
  • Loading branch information
hazelthatsme committed Dec 8, 2023
1 parent f7121c9 commit f58beb8
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 9 deletions.
12 changes: 12 additions & 0 deletions constants/types/User.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ const user: string = /* surrealql */ `
FOR update WHERE id = $auth.id
FOR delete NONE;
DEFINE TABLE pubuser AS SELECT username, avatar, profile FROM user;
DEFINE FIELD username ON user TYPE string ASSERT 3 < string::len($value) < 16;
DEFINE FIELD email ON user TYPE string ASSERT string::is::email($value);
DEFINE FIELD password ON user TYPE string;
DEFINE FIELD avatar ON user TYPE option<string> ASSERT string::is::url($value);
DEFINE FIELD profile ON user TYPE object;
DEFINE FIELD profile.displayname ON user TYPE option<string> ASSERT 1 < string::len($value) < 255;
DEFINE INDEX username ON TABLE user COLUMNS username UNIQUE;
`;
Expand All @@ -21,8 +27,14 @@ export const User = z.object({
username: z.string(),
email: z.string().email(),
password: z.string(),
avatar: z.string().url(),
profile: z.object({
displayname: z.string().optional()

Check failure on line 32 in constants/types/User.types.ts

View workflow job for this annotation

GitHub Actions / Lint

Insert `,`
})

Check failure on line 33 in constants/types/User.types.ts

View workflow job for this annotation

GitHub Actions / Lint

Insert `,`
});
export const PublicUser = User.pick({ username: true, avatar: true, profile: true })

Check failure on line 35 in constants/types/User.types.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `·username:·true,·avatar:·true,·profile:·true·})` with `⏎····username:·true,⏎····avatar:·true,⏎····profile:·true,⏎});`

export type User = z.infer<typeof User>;
export type PublicUser = z.infer<typeof PublicUser>;

export default user;
28 changes: 21 additions & 7 deletions layouts/default.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,8 @@
const auth = useAuthStore()
const { $surreal } = useNuxtApp()
async function setup() {
if (process.client)
auth.authenticate(localStorage.getItem('lflsess') ?? '')
}
if (process.client) setup()
</script>

<template>
Expand All @@ -19,8 +14,10 @@
<Logo />
</NavLink>
<div class="account" v-if="auth.user">
{{ auth.user.username }}
<span class="name" >{{ auth.user.profile.displayname ?? auth.user.username }}</span>
<img :src="auth.user.avatar" :alt="auth.user.username" v-if="auth.user">
</div>
<a href="/auth" v-else>Log in</a>
</div>
</div>
<div class="content">
Expand Down Expand Up @@ -58,4 +55,21 @@
font-size: 3rem;
margin-right: auto;
}

.account {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
max-height: 3rem;

gap: 1em;

img {
width: 3rem;
height: 3rem;
object-fit: cover;
border-radius: 50%;
}
}
</style>
1 change: 1 addition & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default defineNuxtConfig({

routeRules: {
'/product/[slug]': { ssr: true },
'/profile/[slug]': { ssr: true },
},

app: {
Expand Down
1 change: 1 addition & 0 deletions pages/auth/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
email: "",
password: ""
})
const loginInfo = ref({
identifier: "",
password: ""
Expand Down
2 changes: 1 addition & 1 deletion pages/product/[slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
twitterTitle: product.title,
twitterDescription: product.description,
twitterImage: product.thumbnail,
twitterCard: 'summary'
twitterCard: 'summary_large_image'
})
</script>

Expand Down
45 changes: 45 additions & 0 deletions pages/profile/[slug].vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { PublicUser } from '~/constants/types/User.types'
const route = useRoute();
const { data: result } = await useFetch(`/api/v1/profile?username=${route.params.slug}`);
if (!result.value) throw createError({ statusCode: 404, statusMessage: 'User not found.' });
const user = PublicUser.parse(result.value);
useSeoMeta({
// Standard
title: user.profile.displayname ?? user.username,
description: '',
// OpenGraph
ogTitle: user.profile.displayname ?? user.username,
ogDescription: '',
ogImage: user.avatar ?? 'https://leafal.io/default.svg',
ogUrl: `https://www.leafal.io${route.fullPath}`,
// Twitter
twitterTitle: user.profile.displayname ?? user.username,
twitterDescription: '',
twitterImage: user.avatar ?? 'https://leafal.io/default.svg',
twitterCard: 'summary'
})
</script>

<template>
<main>
<div class="user">
<h1>{{ user.profile.displayname ?? user.username }}</h1>
</div>
</main>
</template>

<style scoped lang="scss">
.user {
margin: 2rem auto;
position: relative;
z-index: 5;
max-width: 90vw;
}
</style>
24 changes: 24 additions & 0 deletions server/api/v1/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { PublicUser } from '~/constants/types/User.types';
import { connectDatabase } from '~/server/functions/database';

export default defineEventHandler(async (event) => {
try {
const username = getQuery(event)['username'];
if (!username) throw { message: 'No username provided.' };

const database = await connectDatabase();
const user = await database.query<[PublicUser[]]>(
'SELECT * FROM pubuser WHERE username = $username',
{ username }
);

if (!user) throw { error: 'User not found', status: 404 };

setResponseStatus(event, 200);
return user[0][0];
} catch (e: unknown) {
const error = e as { error: unknown; status: number };
setResponseStatus(event, error.status);
return e;
}
});
2 changes: 1 addition & 1 deletion stores/Auth.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type AuthState = {
}

export const useAuthStore = defineStore('auth', {
state: () => ({ token: process.client ? localStorage.getItem('lflsess'): undefined, user: undefined } as AuthState),
state: () => ({ token: undefined, user: undefined } as AuthState),
actions: {
async fetch() {
const results = await useSurreal().query<[User[]]>("SELECT * FROM user WHERE id = $auth.id")
Expand Down

0 comments on commit f58beb8

Please sign in to comment.