diff --git a/DB/users.sql b/DB/users.sql index ef78b16..22c1342 100644 --- a/DB/users.sql +++ b/DB/users.sql @@ -1,15 +1,19 @@ CREATE TABLE users ( - id uuid not null references auth.users on delete cascade, - avatar_url text, - username text not null, - spotify_id text not null, - spotify_visibility boolean not null default false, + id uuid not null references auth.users on delete cascade, + avatar_url text, + username text not null, + spotify_id text not null, + spotify_visibility boolean not null default false, + created_at timestamp with time zone default now(), + daily_streak integer not null default 0, + daily_streak_updated_at timestamp with time zone not null default now(), primary key (id) ); ALTER TABLE users ADD CONSTRAINT unique_username UNIQUE (username), - ADD CONSTRAINT valid_username check (username <> '' AND length(trim(username)) >= 4 AND username ~ '^[a-zA-Z0-9_]+$'); + ADD CONSTRAINT valid_username check (username <> '' AND length(trim(username)) >= 4 AND + username ~ '^[a-zA-Z0-9_]+$'); -CREATE INDEX idx_username ON users(username); \ No newline at end of file +CREATE INDEX idx_username ON users (username); \ No newline at end of file diff --git a/server/api/v1/user/[uid].get.ts b/server/api/v1/user/[uid].get.ts index f42c743..84cd857 100644 --- a/server/api/v1/user/[uid].get.ts +++ b/server/api/v1/user/[uid].get.ts @@ -1,5 +1,41 @@ -export default defineEventHandler((event) => { +import {isValidUUID} from "~/server/utils/data-validation"; +import {serverSupabaseServiceRole, serverSupabaseUser} from "#supabase/server"; +import type {GetUserResponse} from "~/types/api/users"; + +export default defineEventHandler(async (event) => { const userId = getRouterParam(event, 'uid') - return; + // Require user to be authenticated + const user = await serverSupabaseUser(event); + if (!user?.id) { + setResponseStatus(event, 401); + return {error: 'unauthenticated'}; + } + + if (!userId || !isValidUUID(userId)) { + setResponseStatus(event, 400); + return {error: 'Invalid user ID'}; + } + + // Send request + const client = serverSupabaseServiceRole(event); + const {data, error}:{ data: GetUserResponse|null, error: any} = await client.from('users').select('*').eq('id', userId).maybeSingle(); + + if (!data) { + setResponseStatus(event, 404); + return {error: 'User not found'}; + } + + // Handle errors + if (error) { + setResponseStatus(event, 500); + return {error: error.message}; + } + + // Hide spotify id if not visible + if (!data.spotify_visibility) { + delete data.spotify_id; + } + + return data; }) \ No newline at end of file diff --git a/server/api/v1/user/friends/index.get.ts b/server/api/v1/user/friends/index.get.ts index 9942fd0..3e884ed 100644 --- a/server/api/v1/user/friends/index.get.ts +++ b/server/api/v1/user/friends/index.get.ts @@ -27,5 +27,15 @@ export default defineEventHandler(async (event) => { return {error: error.message}; } + if(data === null) return []; + + // Hide spotify id if not visible + // Yes this works even when the IDE marks it as an error + data!.forEach(friend => { + if (!friend.friend_spotify_visibility) { + delete friend.friend_spotify_id; + } + }); + return data; }); \ No newline at end of file diff --git a/server/utils/data-validation.ts b/server/utils/data-validation.ts index d8d2876..0c830e6 100644 --- a/server/utils/data-validation.ts +++ b/server/utils/data-validation.ts @@ -9,5 +9,15 @@ export const spotifyIDRegex = /^[a-zA-Z0-9]+$/; // base62 * @returns Whether the string is a valid Spotify ID */ export function isValidSpotifyID(spotifyID: string): boolean { - return spotifyID.match(spotifyIDRegex) !== null; // base62 + return spotifyID.match(spotifyIDRegex) !== null; // base62 +} + +/** + * Check if a string is a valid UUID + * @param uuid - The string to check + * @returns Whether the string is a valid UUID + */ +export function isValidUUID(uuid: string): boolean { + if(uuid.length !== 36) return false; + return uuid.match(/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/) !== null; } \ No newline at end of file diff --git a/types/api/user.friends.ts b/types/api/user.friends.ts index 5add186..06c54da 100644 --- a/types/api/user.friends.ts +++ b/types/api/user.friends.ts @@ -23,7 +23,7 @@ export interface GetFriendsResponse { friend_id: string, friend_username: string, friend_avatar: url.URL | null, - friend_spotify_id: string, + friend_spotify_id?: string, friend_spotify_visibility: boolean, created_at: string updated_at: string diff --git a/types/api/users.ts b/types/api/users.ts new file mode 100644 index 0000000..44f7eda --- /dev/null +++ b/types/api/users.ts @@ -0,0 +1,11 @@ +import type url from "node:url"; + +export interface GetUserResponse { + id: string, + avatar_url?: url.URL | null, + username: string, + spotify_id?: string, + spotify_visibility: boolean, + daily_streak: number, + daily_streak_updated_at: string +} \ No newline at end of file