From 499a0ea289bdd35299b21264632099fcda9815b2 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 29 Jan 2025 11:34:04 +0100 Subject: [PATCH 01/56] change providerEndpouint to providername --- api/src/db/schema.ts | 23 ++++++++++---- api/src/routes/auth.ts | 61 +++++++++++++++++++++++--------------- api/src/types/responses.ts | 26 ++++++++-------- 3 files changed, 67 insertions(+), 43 deletions(-) diff --git a/api/src/db/schema.ts b/api/src/db/schema.ts index 81bfd596c..86d1f5a30 100644 --- a/api/src/db/schema.ts +++ b/api/src/db/schema.ts @@ -4,9 +4,10 @@ import { boolean, text, serial, pgTable as table, timestamp, integer, pgView } f export const users = table('users', { id: serial().primaryKey(), name: text().notNull(), + displayName: text('display_name').notNull().unique(), webId: text('web_id').notNull().unique(), email: text('email').notNull().unique(), - providerEndpoint: text('provider_endpoint').notNull(), + providerName: text('provider_name').notNull() // This is done so there is no import here. It crashes the drizzle:push command }) export const usersRelations = relations(users, ({one}) => ({ @@ -18,23 +19,33 @@ export const posts = table('posts', { content: text().notNull(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), isPublic: boolean('is_public').notNull(), - authorId: integer('author_id').notNull().references(() => users.id), + authorId: integer('author_id').notNull() }) +export const postsRelations = relations(posts, ({ one }) => ({ + author: one(users, { + fields: [posts.authorId], + references: [users.id] + }) +})) + +// Views export const postsView = pgView('posts_view', { id: serial().primaryKey(), content: text().notNull(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), isPublic: boolean('is_public').notNull(), - authorId: integer('author_id').notNull().references(() => users.id), - authorName: text("author_name").notNull(), + authorId: integer('author_id') + .notNull() + .references(() => users.id), + authorName: text('author_name').notNull(), authorWebId: text('author_web_id').notNull().unique(), - authorProviderEndpoint: text('author_provider_endpoint').notNull(), + authorProviderEndpoint: text('author_provider_endpoint').notNull() }).as(sql`SELECT posts.*, users.name as author_name, users.web_id as author_web_id, - users.provider_endpoint as author_provider_endpoint + users.provider_name as author_provider_name FROM posts INNER JOIN users on posts.author_id = users.id WHERE posts.is_public = true`) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index 3732d7c30..10bb93239 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -1,14 +1,22 @@ -import { users } from "../db/schema" -import ActivityPod from "../services/ActivityPod" -import { type PodProviderSignInResponse, type SelectUsers, viablePodProviders, signinResponse, signUpBody, signinBody } from "../types" -import { eq } from "drizzle-orm" -import Elysia, { t } from "elysia" -import { db } from ".." -import setupPlugin from "./setup" -import { getTokenObject } from "../services/jwt" -import User from "../decorater/User" +import { users } from '../db/schema' +import ActivityPod from '../services/ActivityPod' +import { + type PodProviderSignInResponse, + type SelectUsers, + signinResponse, + signUpBody, + signinBody, + viablePodProviders +} from '../types' +import { eq } from 'drizzle-orm' +import Elysia, { t } from 'elysia' +import { db } from '..' +import setupPlugin from './setup' +import { getTokenObject } from '../services/jwt' +import User from '../decorater/User' +import { HTTPError } from 'ky' -const authPlugin = new Elysia({name: 'auth'}) +const authPlugin = new Elysia({ name: 'auth' }) .use(setupPlugin) .post( '/signin', @@ -17,13 +25,13 @@ const authPlugin = new Elysia({name: 'auth'}) if (auth && (await jwt.verify(auth))) { return error(204, "You're already logged in") } - const { username, password, providerEndpoint } = body + const { username, password, providerName } = body let providerResponse: PodProviderSignInResponse // try to signIn to the endpoint try { - providerResponse = await ActivityPod.signIn(providerEndpoint, username, password) + providerResponse = await ActivityPod.signIn(viablePodProviders[providerName], username, password) } catch (e) { console.error('Error while logging in to endpoint: ', e) return error(400, "Endpoint didn't respond with a 200 status code") @@ -43,9 +51,10 @@ const authPlugin = new Elysia({name: 'auth'}) .insert(users) .values({ name: username as string, + displayName: username as string, email: username as string, webId: providerResponse.webId, - providerEndpoint: providerEndpoint + providerName }) .returning() } @@ -91,11 +100,11 @@ const authPlugin = new Elysia({name: 'auth'}) if (auth && (await jwt.verify(auth))) { return "You're already logged in" } - const { username, password, email, providerEndpoint } = body + const { username, password, email, providerName } = body // try to sign up the user with the current provider try { - const providerResponse = await ActivityPod.signup(providerEndpoint, username, password, email) + const providerResponse = await ActivityPod.signup(viablePodProviders[providerName], username, password, email) let userResponse: SelectUsers[] = [] if (providerResponse.token === undefined) { @@ -106,12 +115,16 @@ const authPlugin = new Elysia({name: 'auth'}) const user = await db.select().from(users).where(eq(users.webId, providerResponse.webId)) if (user.length === 0) { // the user is not in the database yet, so we need to create a new user - userResponse = await db.insert(users).values({ - name: username as string, - email, - webId: providerResponse.webId, - providerEndpoint: providerEndpoint - }).returning() + userResponse = await db + .insert(users) + .values({ + name: username as string, + displayName: username as string, + email, + webId: providerResponse.webId, + providerName + }) + .returning() } } catch (e) { console.error('Error while checking if user is in the database: ', e) @@ -124,8 +137,8 @@ const authPlugin = new Elysia({name: 'auth'}) user: userResponse[0] } } - } catch (e: any) { - if (e.name === 'HTTPError') { + } catch (e: unknown) { + if (e instanceof HTTPError) { const errorJson = await e.response.json() console.error('Error while signing up the user', errorJson) return error(errorJson.code, errorJson.message) @@ -140,7 +153,7 @@ const authPlugin = new Elysia({name: 'auth'}) response: { 200: signinResponse, 400: t.String(), - 500: t.String(), + 500: t.String() } } ) diff --git a/api/src/types/responses.ts b/api/src/types/responses.ts index a8a0b5f5d..7f521c3a3 100644 --- a/api/src/types/responses.ts +++ b/api/src/types/responses.ts @@ -2,16 +2,16 @@ import { t, type Static } from 'elysia' import { _selectUsers } from './db' import { viablePodProviders } from './enums' import { createSchemaFactory } from 'drizzle-typebox' -import { posts, postsView, users } from '../db/schema' +import { postsView, users } from '../db/schema' -const {createSelectSchema} = createSchemaFactory({typeboxInstance: t}) +const { createSelectSchema } = createSchemaFactory({ typeboxInstance: t }) // Auth // SignIn export const signinBody = t.Object({ username: t.String(), password: t.String(), - providerEndpoint: viablePodProviders + providerName: viablePodProviders }) export type SignInBody = Static @@ -25,7 +25,7 @@ export const signUpBody = t.Object({ username: t.String(), password: t.String(), email: t.String(), - providerEndpoint: viablePodProviders + providerName: viablePodProviders }) export type SignUpBody = Static @@ -35,14 +35,14 @@ export const selectUser = createSelectSchema(users) // Posts export const selectPost = createSelectSchema(postsView) export type SelectPost = { - id: number; - content: string; - isPublic: boolean; - createdAt: string; - authorId: number; + id: number + content: string + isPublic: boolean + createdAt: string + authorId: number author: { - id: number; - name: string; - webId: string; - }; + id: number + name: string + webId: string + } } From fada10b5ee85bb8db96df3ae7eb0c23cee0dbebb Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 29 Jan 2025 11:46:14 +0100 Subject: [PATCH 02/56] change enum to contain names not endpoints --- api/src/routes/auth.ts | 6 +++--- api/src/types/enums.ts | 14 +++++++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/api/src/routes/auth.ts b/api/src/routes/auth.ts index 10bb93239..4222f5522 100644 --- a/api/src/routes/auth.ts +++ b/api/src/routes/auth.ts @@ -6,7 +6,7 @@ import { signinResponse, signUpBody, signinBody, - viablePodProviders + podProviderEndpoint } from '../types' import { eq } from 'drizzle-orm' import Elysia, { t } from 'elysia' @@ -31,7 +31,7 @@ const authPlugin = new Elysia({ name: 'auth' }) // try to signIn to the endpoint try { - providerResponse = await ActivityPod.signIn(viablePodProviders[providerName], username, password) + providerResponse = await ActivityPod.signIn(podProviderEndpoint[providerName], username, password) } catch (e) { console.error('Error while logging in to endpoint: ', e) return error(400, "Endpoint didn't respond with a 200 status code") @@ -104,7 +104,7 @@ const authPlugin = new Elysia({ name: 'auth' }) // try to sign up the user with the current provider try { - const providerResponse = await ActivityPod.signup(viablePodProviders[providerName], username, password, email) + const providerResponse = await ActivityPod.signup(podProviderEndpoint[providerName], username, password, email) let userResponse: SelectUsers[] = [] if (providerResponse.token === undefined) { diff --git a/api/src/types/enums.ts b/api/src/types/enums.ts index 0e6fc1bb2..7446dbb89 100644 --- a/api/src/types/enums.ts +++ b/api/src/types/enums.ts @@ -1,5 +1,13 @@ import { t } from 'elysia' -export const viablePodProviders = t.Enum({ - 'http://localhost:3000': 'http://localhost:3000' -}) +export const vibaleProviderNames = ['memory.'] + +export enum ViablePodProvider { + 'memory.' = 'memory.' +} + +export const podProviderEndpoint: { [key in ViablePodProvider]: string } = { + 'memory.': 'http://localhost:3000' +} + +export const viablePodProviders = t.Enum(ViablePodProvider) From 117f692e828280de1f0782c3d4396048dbfc1f04 Mon Sep 17 00:00:00 2001 From: Clara Date: Wed, 29 Jan 2025 11:46:56 +0100 Subject: [PATCH 03/56] change site to provider providerName instead of endpoint --- frontend/src/components/SignInForm.vue | 4 ++-- frontend/src/components/SignupForm.vue | 4 ++-- frontend/src/stores/authStore.ts | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/SignInForm.vue b/frontend/src/components/SignInForm.vue index 4a355dc59..284e6c9ec 100644 --- a/frontend/src/components/SignInForm.vue +++ b/frontend/src/components/SignInForm.vue @@ -1,14 +1,14 @@ From e9aa891e9f4b259536a0f2663c29dd33df5ff755 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 11:51:06 +0100 Subject: [PATCH 46/56] feat: make apirequest work with new responses --- frontend/src/controller/api.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/frontend/src/controller/api.ts b/frontend/src/controller/api.ts index 5b693ac60..6e9372855 100644 --- a/frontend/src/controller/api.ts +++ b/frontend/src/controller/api.ts @@ -1,7 +1,6 @@ import type { CreatePost, FollowersFollowedResponse, - FollowUnfollowResponse, SelectPost, SelectQueryObject, SignInBody, @@ -176,11 +175,11 @@ export class ApiClient { * @param {string} userId - id of the user to follow * @returns {Promise} - response of the request */ - async followUser(userId: string): Promise> { + async followUser(userId: string): Promise> { try { - const response = await this.authRequest.post(`${this.baseUrl}/users/${userId}/follow`) + const response = await this.authRequest.post(`${this.baseUrl}/user/${userId}/follow`) return { - data: await response.text(), + data: await response.json(), status: response.status } } catch (e) { @@ -193,11 +192,11 @@ export class ApiClient { * @param {string} userId - id of the user to unfollow * @returns {Promise>} */ - async unfollowUser(userId: string): Promise> { + async unfollowUser(userId: string): Promise> { try { - const response = await this.authRequest.post(`${this.baseUrl}/users/${userId}/unfollow`) + const response = await this.authRequest.post(`${this.baseUrl}/user/${userId}/unfollow`) return { - data: await response.text(), + data: await response.json(), status: response.status } } catch (e) { @@ -211,7 +210,7 @@ export class ApiClient { */ async fetchFollowing(): Promise> { try { - const response = await this.authRequest.get(`${this.baseUrl}/users/following`) + const response = await this.authRequest.get(`${this.baseUrl}/user/following`) return { data: await response.json(), status: response.status @@ -227,7 +226,7 @@ export class ApiClient { */ async fetchFollowers(): Promise> { try { - const response = await this.authRequest.get(`${this.baseUrl}/users/followers`) + const response = await this.authRequest.get(`${this.baseUrl}/user/followers`) return { data: await response.json(), status: response.status From 8aa978a63f694d91b083f2c0c9b63f7faae6c300 Mon Sep 17 00:00:00 2001 From: Clara Date: Fri, 31 Jan 2025 11:54:19 +0100 Subject: [PATCH 47/56] feat: make it possible to follow user --- frontend/src/components/PostList.vue | 6 ++++- frontend/src/stores/userStore.ts | 39 +++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/PostList.vue b/frontend/src/components/PostList.vue index bb5cec914..829434ee6 100644 --- a/frontend/src/components/PostList.vue +++ b/frontend/src/components/PostList.vue @@ -2,8 +2,10 @@ import { usePostsStore } from '@/stores/postsStore' import { DateTime } from 'luxon' import MemoryButton from './MemoryButton.vue' +import { useUserStore } from '@/stores/userStore' const postsStore = usePostsStore() +const userStore = useUserStore()