diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 3ccf435f..96606c7d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -17,4 +17,4 @@ module.exports = { es2017: true, node: true } -}; +} diff --git a/.prettierrc b/.prettierrc index f6ad47c2..201abb50 100644 --- a/.prettierrc +++ b/.prettierrc @@ -7,5 +7,6 @@ "pluginSearchDirs": ["."], "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], "semi": false, - "bracketSameLine": true + "bracketSameLine": true, + "arrow-parens": "always" } diff --git a/package.json b/package.json index d21698b2..e36fb66d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mage-website", - "version": "0.0.2", + "version": "0.0.1", "license": "GPL-3.0", "private": true, "type": "module", @@ -39,6 +39,7 @@ "svelte": "^3.55.1", "svelte-check": "^3.0.2", "svelte-preprocess": "^5.0.1", + "svelte-tags-input": "^4.0.0", "svgo": "^3.0.2", "tailwind-clip-path": "^1.0.0", "tailwind-scrollbar": "^2.1.0", diff --git a/src/app.d.ts b/src/app.d.ts index ce033eca..3590a87e 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -3,16 +3,19 @@ // and what to do when importing types declare namespace App { - interface Locals { - user: { - token?: string - userId?: string - user?: object - } - } - // interface PageData {} - // interface Error {} - // interface Platform {} + interface Locals { + user: { + token?: string + userId?: string + user?: object + } + isBanned?: boolean + } + // interface PageData {} + // interface Error {} + // interface Platform {} } -declare const __VERSION__ : string \ No newline at end of file +declare const __VERSION__: string + +declare module 'svelte-tags-input' \ No newline at end of file diff --git a/src/app.html b/src/app.html index aca596b8..882beccd 100644 --- a/src/app.html +++ b/src/app.html @@ -2,15 +2,15 @@ - - - - %sveltekit.head% + + + + %sveltekit.head% -
%sveltekit.body%
+ class="scrollbar-thin scrollbar-thumb-[#aeaeae] scrollbar-track-[#25252A] overflow-y-scroll scrollbar-thumb-rounded"> +
%sveltekit.body%
\ No newline at end of file diff --git a/src/hooks.server.ts b/src/hooks.server.ts index b0a8c16d..a608bf9f 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,215 +1,158 @@ -import { redirect } from '@sveltejs/kit' -import { get } from 'svelte/store' -import { getUserDetails, userRole, currentUser } from '$lib/stores/authStore' -import { getUserRole, getRoles } from '$lib/stores/adminStore' +import { redirect, type HandleFetch } from '@sveltejs/kit' +import { get as getWritableVal } from 'svelte/store' +import { + isMaintenanceModeEnabled, + isFeatureVideoResponsesEnabled, + isFeatureGroupChatEnabled, + isFeatureMintPageEnabled, + isFeaturePremiumPageEnabled +} from '$lib/stores/remoteConfigStore' import { Authenticate } from '$lib/authentication/authentication' -import type { Handle, HandleFetch } from '@sveltejs/kit' +import type { Handle } from '@sveltejs/kit' import { env } from '$env/dynamic/public' +import { get } from '$lib/api' +import { current_user, user_role } from '$lib/stores/authStore' export const handle: Handle = async ({ event, resolve }) => { - const pathname = event.url.pathname - const userId = event.url.searchParams.get('userId') || event.cookies.get('userId') || '' - let token = event.url.searchParams.get('token') || event.cookies.get('token') || '' - let user = get(currentUser), - role = get(userRole) - - if (token && userId) { - if (!user) { - const response = await getUserDetails(token, userId) - if (response) { - if (response.freshJwt) { - token = response.freshJwt - } - user = response - currentUser.set(user) - } - } - - if (!role) { - try { - const headers: any = { - userId: userId - } - if (env.PUBLIC_CROSS_ORIGIN === 'false') { - headers['authorization'] = token - } else { - headers['x-api-key'] = env.PUBLIC_API_KEY - } - - const all_roles = await getRoles(true, headers) - if (Array.isArray(all_roles)) { - const get_role = await getUserRole(true, headers) - if (get_role && get_role.role) { - role = all_roles.find((item) => { - return item._id == get_role.role - })?.name - - userRole.set(role) - } - } - } catch (e) { - console.log('something wrong', e) - } - } - - if (pathname === '/') { - event.cookies.set('token', token, { - path: '/', - maxAge: 60 * 60 * 24 * 30 - }) - event.cookies.set('userId', userId, { - path: '/', - maxAge: 60 * 60 * 24 * 30 - }) - } - - event.locals.user = { - userId, - token, - user - } - } - - if ( - Authenticate({ pathname, user_role: role || 'user' }) || - pathname === '/browse' || - pathname === '/' - ) { - return await resolve(event) - } - throw redirect(302, '/browse') + const pathname = event.url.pathname + const userId = event.url.searchParams.get('userId') || event.cookies.get('userId') || '' + let token = event.url.searchParams.get('token') || event.cookies.get('token') || '' + + let user: any = event.locals.user?.user || '', + isBanned = false + const role = getWritableVal(user_role) + + const remoteConfigs = await get('remote-configs', { userId, token }) + if (remoteConfigs && remoteConfigs.length) { + remoteConfigs.map((config: { flagKey: string; flagValue: boolean }) => { + if (config.flagKey === 'maintenance-mode') isMaintenanceModeEnabled.set(config.flagValue) + if (config.flagKey === 'feature-video-responses') + isFeatureVideoResponsesEnabled.set(config.flagValue) + if (config.flagKey === 'feature-group-chat') isFeatureGroupChatEnabled.set(config.flagValue) + if (config.flagKey === 'feature-mint-page') isFeatureMintPageEnabled.set(config.flagValue) + if (config.flagKey === 'feature-premium-page') + isFeaturePremiumPageEnabled.set(config.flagValue) + }) + } + + const maintenance_mode = getWritableVal(isMaintenanceModeEnabled) || false + + if (token && userId) { + if (!user) { + const response = await get('auth/me', { userId, token }) + if (response) { + if (response.freshJwt) { + token = response.freshJwt + } + user = response.user + current_user.set(user) + } + } + + if (!role) { + try { + const headers: any = { + userId: userId + } + if (env.PUBLIC_CROSS_ORIGIN === 'false') { + headers['authorization'] = token + } else { + headers['x-api-key'] = env.PUBLIC_API_KEY + } + + const allRoles = await get('roles', headers) + if (Array.isArray(allRoles)) { + const userRole = await get('roles/role-mapping', headers) + if (userRole && userRole.role) { + const usersRoleName = allRoles.find((item) => { + return item._id == userRole.role + })?.name + + user_role.set(usersRoleName) + } + } + } catch (err) { + console.log('something went wrong', err) + } + } + + if (pathname === '/') { + event.cookies.set('token', token, { + path: '/', + maxAge: 60 * 60 * 24 * 30 + }) + event.cookies.set('userId', userId, { + path: '/', + maxAge: 60 * 60 * 24 * 30 + }) + } + + event.locals = { + user: { + userId, + token, + user + } + } + } + + if (user && user.isBanned) { + isBanned = true + const cookieItem = ['token', 'userId'] + cookieItem.forEach((item) => { + event.cookies.set(item, '', { + path: '/', + expires: new Date(0) + }) + }) + user_role.set('user') + event.locals['isBanned'] = isBanned + } + + if ( + Authenticate({ pathname, user_role: role || 'user' }) || + pathname === '/browse' || + pathname === '/' + ) { + if (maintenance_mode && !['/contact', '/legal', '/maintenance'].includes(pathname) && !user) { + if (pathname === '/maintenance') { + return await resolve(event) + } else { + throw redirect(302, '/maintenance') + } + } else { + return await resolve(event) + } + } + throw redirect(302, '/browse') } -export function handleError({ error }: { error: any }) { - console.log('error', error) - return { - message: 'Whoops something wrong!' - } +export const handleError = ({ error }: { error: any }) => { + console.log('error', error) + // example integration with https://sentry.io/ + // Sentry.captureException(error, { event, errorId }); + return { + message: 'Whoops something went wrong!' + } } -//TODO: fix global handleFetch -// export const handleFetch: HandleFetch = async ({ request, fetch }) => { -// let headers: any = {} -// if (request.url.startsWith(env.PUBLIC_API_URL)) { -// if (env.PUBLIC_CROSS_ORIGIN === 'false') { -// headers = { -// authorization: request.locals.user.token, -// userId: request.locals.user.userId -// } -// } else { -// headers = { -// 'x-api-key': env.PUBLIC_API_KEY, -// userId: request.locals.user.userId -// } -// } -// } -// return fetch(request, headers) -// } - -// const isAdminPage = /^\/admin\/(.*)/.test(route.id) -// const isProfilePage = /^\/profile\/(.*)/.test(route.id) -// const isPremiumPage = /^\/premium\/(.*)/.test(route.id) -// await getRemoteConfigs() -// if (isMaintenanceModeEnabled) { -// if (locals.user.user.isAdmin) { -// throw redirect(302, '/maintenance') -// } -// } else { -// if (!locals.user) { -// throw redirect(302, '/browse') -// } else { -// if (locals.user.user.isBanned) { -// throw redirect(308, '/banned') -// } - -// if (isAdminPage && !locals.user.user.isAdmin) { -// throw redirect(302, '/browse') -// } -// } -// } - -// import { remoteConfigStore } from '$lib/stores/remoteConfigStore' -// import type { Handle, HandleServerError, RequestEvent } from '@sveltejs/kit' -// import type { MaybePromise } from '@sveltejs/kit/types/private' -// import { authStore } from '$lib/stores/authStore' - -// export const handle = (async ({ event, resolve }: { event: RequestEvent, resolve: MaybePromise }) => { -// const isAdminPage = /^\/admin\/(.*)/.test(event.url.pathname) -// const isHomePage = /^\/home\/(.*)/.test(event.url.pathname) -// const isProfilePage = /^\/profile\/(.*)/.test(event.url.pathname) -// const isPremiumPage = /^\/premium\/(.*)/.test(event.url.pathname) -// const isChannelPage = /^\/channel\/(.*)/.test(event.url.pathname) -// await remoteConfigStore.getRemoteConfigs() -// const isMaintenanceModeEnabled = remoteConfigStore.isMaintenanceModeEnabled - -// const jwtToken = event.url.searchParams.get('token') -// const userId = event.url.searchParams.get('userId') - -// if (jwtToken) authStore.setJWT({ jwt: jwtToken }) -// if (userId) authStore.setUserId({ userId }) -// const user = await authStore.me() -// if (user) { -// if (user.status == 401 || user.status == 500) { -// await authStore.logout() -// } else if (user.isBanned) { -// // this.snackBar.open( -// // 'Your account is banned. Please contact support if you have any questions', -// // null, -// // { duration: 10000 } -// // ) -// await authStore.logout() -// } else { -// // try { -// // const onConnectionSuccess = async () => { -// // await this.socket.emitUserConnection(user._id, true) -// // this.socket.listenToUserConnection(user._id).subscribe(async (data) => { -// // authStore.setUser(data.user) -// // }) -// // } -// // if (this.socket.apiSocket.readyState == WebSocket.OPEN) await onConnectionSuccess() - -// // } catch (e) { -// // console.log(e) -// // } -// return { -// status: 302, -// redirect: '/home' -// } -// } -// } else { -// return { -// status: 302, -// redirect: '/login' -// } -// } - -// // if ( -// // !event.locals.session.user && -// // (isAdminPage || isHomePage || isProfilePage || isPremiumPage || isChannelPage) -// // ) { -// // return { -// // status: 302, -// // redirect: '/login' -// // } -// // } else { -// // if (isMaintenanceModeEnabled) { -// // if (!event.locals.session.user.isAdmin) { -// // return { -// // status: 302, -// // redirect: '/maintenance' -// // } -// // } else return { props: {} } -// // } else return { props: {} } -// // } -// }) - -// export const handleError = (({ error, event }) => { -// const errorId = crypto.randomUUID(); -// // example integration with https://sentry.io/ -// // Sentry.captureException(error, { event, errorId }); - -// return { -// message: 'Whoops!', -// errorId -// }; -// }) satisfies HandleServerError; +export const handleFetch = (async ({ + event, + request, + fetch +}: { + event: any + request: any + fetch: any +}) => { + if (request.url.startsWith(env.PUBLIC_API_URL)) { + request.headers['userId'] = event.locals.user.userId + if (env.PUBLIC_CROSS_ORIGIN === 'false') { + request.headers['authorization'] = event.locals.user.token + } else { + request.headers['x-api-key'] = env.PUBLIC_X_API_KEY + } + } + + return fetch(request) +}) satisfies HandleFetch diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 00000000..bf3dc84d --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,61 @@ +import { error } from '@sveltejs/kit' +import { env } from '$env/dynamic/public' + +const base = env.PUBLIC_API_URL + +async function send({ + method, + path, + data, + headers +}: { + method: string + path: string + data?: any + headers?: any +}) { + const opts: any = { method, headers: {} } + + if (data) { + opts.headers['Content-Type'] = 'application/json' + opts.body = JSON.stringify(data) + } + + // if headers sent manually + if (headers && headers.userId) { + opts.headers['userId'] = headers.userId + } + + if (env.PUBLIC_CROSS_ORIGIN === 'false') { + if (headers && headers.token) { + opts.headers['authorization'] = headers.token + } + } else { + opts.headers['x-api-key'] = env.PUBLIC_X_API_KEY + } + + const res = await fetch(`${base}/${path}`, opts) + if (res.ok || res.status === 422) { + const text = await res.text() + if (path === 'wsinit/wsid') return text + return text ? JSON.parse(text) : {} + } + + return { error: res.status } +} + +export function get(path: string, headers?: any) { + return send({ method: 'GET', path, headers }) +} + +export function del(path: string) { + return send({ method: 'DELETE', path }) +} + +export function post(path: string, data: any) { + return send({ method: 'POST', path, data }) +} + +export function put(path: string, data: any = {}) { + return send({ method: 'PUT', path, data }) +} diff --git a/src/lib/assets/icons/IconInfo.svelte b/src/lib/assets/icons/IconInfo.svelte new file mode 100644 index 00000000..7547a9b6 --- /dev/null +++ b/src/lib/assets/icons/IconInfo.svelte @@ -0,0 +1,10 @@ + diff --git a/src/lib/assets/icons/IconMageLogo.svg b/src/lib/assets/icons/IconMageLogo.svg new file mode 100644 index 00000000..6c6a0ec2 --- /dev/null +++ b/src/lib/assets/icons/IconMageLogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/lib/assets/icons/IconMageLogoDark.svg b/src/lib/assets/icons/IconMageLogoDark.svg new file mode 100644 index 00000000..eb10cdad --- /dev/null +++ b/src/lib/assets/icons/IconMageLogoDark.svg @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/src/lib/assets/icons/IconPhoto.svelte b/src/lib/assets/icons/IconPhoto.svelte new file mode 100644 index 00000000..6e809e5e --- /dev/null +++ b/src/lib/assets/icons/IconPhoto.svelte @@ -0,0 +1,6 @@ + + + diff --git a/src/lib/assets/icons/browse/IconBrowseItemPlay.svelte b/src/lib/assets/icons/browse/IconBrowseItemPlay.svelte new file mode 100644 index 00000000..356e072c --- /dev/null +++ b/src/lib/assets/icons/browse/IconBrowseItemPlay.svelte @@ -0,0 +1,8 @@ + diff --git a/src/lib/assets/icons/browse/IconBrowseItemUser.svelte b/src/lib/assets/icons/browse/IconBrowseItemUser.svelte new file mode 100644 index 00000000..ee6029d8 --- /dev/null +++ b/src/lib/assets/icons/browse/IconBrowseItemUser.svelte @@ -0,0 +1,10 @@ + diff --git a/src/lib/assets/icons/chat/IconChatAttachment.svelte b/src/lib/assets/icons/chat/IconChatAttachment.svelte new file mode 100644 index 00000000..3a2d7cbd --- /dev/null +++ b/src/lib/assets/icons/chat/IconChatAttachment.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/assets/icons/chat/IconChatCode.svelte b/src/lib/assets/icons/chat/IconChatCode.svelte new file mode 100644 index 00000000..3adb2ff0 --- /dev/null +++ b/src/lib/assets/icons/chat/IconChatCode.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/assets/icons/chat/IconChatEmoji.svelte b/src/lib/assets/icons/chat/IconChatEmoji.svelte new file mode 100644 index 00000000..e84dba84 --- /dev/null +++ b/src/lib/assets/icons/chat/IconChatEmoji.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/assets/icons/chat/IconChatGif.svelte b/src/lib/assets/icons/chat/IconChatGif.svelte new file mode 100644 index 00000000..6c6c50df --- /dev/null +++ b/src/lib/assets/icons/chat/IconChatGif.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/lib/assets/icons/chat/IconChatSendMessage.svelte b/src/lib/assets/icons/chat/IconChatSendMessage.svelte new file mode 100644 index 00000000..5ea35816 --- /dev/null +++ b/src/lib/assets/icons/chat/IconChatSendMessage.svelte @@ -0,0 +1,8 @@ + diff --git a/src/lib/assets/icons/drawer/IconDrawerCommunity.svelte b/src/lib/assets/icons/drawer/IconDrawerCommunity.svelte deleted file mode 100644 index fce2ce79..00000000 --- a/src/lib/assets/icons/drawer/IconDrawerCommunity.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/src/lib/assets/icons/social/IconSocialDexlab.svelte b/src/lib/assets/icons/social/IconSocialDexlab.svelte deleted file mode 100644 index 0243d7ab..00000000 --- a/src/lib/assets/icons/social/IconSocialDexlab.svelte +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/lib/assets/icons/social/IconDexlab.svg b/src/lib/assets/icons/social/IconSocialDexlab.svg similarity index 100% rename from src/lib/assets/icons/social/IconDexlab.svg rename to src/lib/assets/icons/social/IconSocialDexlab.svg diff --git a/src/lib/assets/icons/social/IconSocialDiscord.svelte b/src/lib/assets/icons/social/IconSocialDiscord.svelte deleted file mode 100644 index 29cc465b..00000000 --- a/src/lib/assets/icons/social/IconSocialDiscord.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/lib/assets/icons/social/IconDiscord.svg b/src/lib/assets/icons/social/IconSocialDiscord.svg similarity index 100% rename from src/lib/assets/icons/social/IconDiscord.svg rename to src/lib/assets/icons/social/IconSocialDiscord.svg diff --git a/src/lib/assets/icons/social/IconSocialMagicEden.svelte b/src/lib/assets/icons/social/IconSocialMagicEden.svelte deleted file mode 100644 index 03ef9146..00000000 --- a/src/lib/assets/icons/social/IconSocialMagicEden.svelte +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/lib/assets/icons/social/IconMagicEden.svg b/src/lib/assets/icons/social/IconSocialMagicEden.svg similarity index 100% rename from src/lib/assets/icons/social/IconMagicEden.svg rename to src/lib/assets/icons/social/IconSocialMagicEden.svg diff --git a/src/lib/components/Browse/AddCategoryDrawer.svelte b/src/lib/components/Browse/AddCategoryDrawer.svelte new file mode 100644 index 00000000..10b6f91f --- /dev/null +++ b/src/lib/components/Browse/AddCategoryDrawer.svelte @@ -0,0 +1,183 @@ + + +
+

Select category

+
+
+
+
+ {#if categoryIcons.length} + {#each categoryIcons as icon} + removeCategory(icon)} /> + {/each} + {/if} +
+ + searchCategory()} + name="" + class="grow md:ml-4 md:mr-12 focus:outline-0 max-w-[8rem] bg-base-100 md:max-w-xs" + placeholder={categoryIcons.length ? '' : 'Categories'} + autocomplete="off" /> +
+ ({maxCategoryLabel}) +
+ +
+ {#each tabs as tab} + + setActiveTab(tab)} + >{tab} + {/each} +
+ +
+ {#if renderingAssets.length} + {#each renderingAssets as [name, image_url]} + + + {/each} + {:else if searchQuery != ''} +
+ + +

No results for the search query

+
+ {:else} +
+ +
+ {/if} +
+
+ +
+ + +
+
+ + diff --git a/src/lib/components/Browse/Carousel.svelte b/src/lib/components/Browse/Carousel.svelte deleted file mode 100644 index 3160f5d2..00000000 --- a/src/lib/components/Browse/Carousel.svelte +++ /dev/null @@ -1,138 +0,0 @@ - - - -
- - -
- -
- -