diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/.eslintignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.eslintrc.cjs b/.eslintrc.cjs index fba386194..3ccf435f0 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,7 +10,7 @@ module.exports = { }, parserOptions: { sourceType: 'module', - ecmaVersion: 2019 + ecmaVersion: 2020 }, env: { browser: true, diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 717f8cb15..535f55fbc 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -2,28 +2,44 @@ # ... GITHUB ACIONS CI/CD WORKFLOW ... # .................................... name: Docker Image CI -# ... when to trigger this project +# [ℹ] NOTE:when to trigger this project on: push: branches: [main, dev] pull_request: branches: [main, dev] -# ... declaring ENV variables to be used in the project-CI/CD +# [ℹ] NOTE: declaring ENV variables to be +# [ℹ] NOTE: used in the project-CI/CD env: + # [ℹ] other + PORT: ${{secrets.PORT}} + # [ℹ] hasura VITE_HASURA_DB_URL: ${{secrets.VITE_HASURA_DB_URL}} VITE_HASURA_DB_TOKEN: ${{secrets.VITE_HASURA_DB_TOKEN}} + # [ℹ] firebase VITE_FIREBASE_DB_API_KEY: ${{secrets.VITE_FIREBASE_DB_API_KEY }} VITE_FIREBASE_DB_AUTH_DOMAIN: ${{secrets.VITE_FIREBASE_DB_AUTH_DOMAIN}} VITE_FIREBASE_DB_PROJECT_ID: ${{secrets.VITE_FIREBASE_DB_PROJECT_ID}} VITE_FIREBASE_DB_DATABASE_URL: ${{secrets.VITE_FIREBASE_DB_DATABASE_URL}} - VITE_REDIS_CONNECTION_URL: ${{secrets.VITE_REDIS_CONNECTION_URL}} -# ... JOBS TO DO WITH GITHUB WORFLOWS; + VITE_FIREBASE_DB_STORAGE_BUCKET: ${{secrets.VITE_FIREBASE_DB_STORAGE_BUCKET}} + VITE_FIREBASE_DB_AUTH_DOMAIN_MAIN: ${{secrets.VITE_FIREBASE_DB_AUTH_DOMAIN_MAIN}} + VITE_FIREBASE_DB_PROJECT_ID_MAIN: ${{secrets.VITE_FIREBASE_DB_PROJECT_ID_MAIN}} + VITE_FIREBASE_DB_DATABASE_URL_MAIN: ${{secrets.VITE_FIREBASE_DB_DATABASE_URL_MAIN}} + # [ℹ] redis + VITE_REDIS_HOST: ${{secrets.VITE_REDIS_HOST}} + VITE_REDIS_PORT: ${{secrets.VITE_REDIS_PORT}} + VITE_REDIS_PASS: ${{secrets.VITE_REDIS_PASS}} + VITE_REDIS_CACHE_DB: ${{secrets.VITE_REDIS_CACHE_DB}} + VITE_REDIS_BULL_DB: ${{secrets.VITE_REDIS_BULL_DB}} + # [ℹ] discord + VITE_DISCORD_OAUTH_URL: ${{secrets.VITE_DISCORD_OAUTH_URL}} +# [ℹ] NOTE: JOBS TO DO WITH GITHUB WORFLOWS; jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - # ... instantiate a `.env` file for the JOB to access; + # [ℹ] instantiate a `.env` file for the JOB to access; - name: create env file run: | touch .env @@ -33,8 +49,16 @@ jobs: echo VITE_FIREBASE_DB_AUTH_DOMAIN="${{secrets.VITE_FIREBASE_DB_AUTH_DOMAIN}}" >> .env echo VITE_FIREBASE_DB_PROJECT_ID="${{secrets.VITE_FIREBASE_DB_PROJECT_ID}}" >> .env echo VITE_FIREBASE_DB_DATABASE_URL="${{secrets.VITE_FIREBASE_DB_DATABASE_URL}}" >> .env - echo VITE_REDIS_CONNECTION_URL="${{secrets.VITE_REDIS_CONNECTION_URL}}" >> .env - # ... build the DOCKER IMAGE CONTAINER; + echo VITE_FIREBASE_DB_STORAGE_BUCKET="${{secrets.VITE_FIREBASE_DB_STORAGE_BUCKET}}" >> .env + echo VITE_FIREBASE_DB_AUTH_DOMAIN_MAIN="${{secrets.VITE_FIREBASE_DB_AUTH_DOMAIN_MAIN}}" >> .env + echo VITE_FIREBASE_DB_PROJECT_ID_MAIN="${{secrets.VITE_FIREBASE_DB_PROJECT_ID_MAIN}}" >> .env + echo VITE_FIREBASE_DB_DATABASE_URL_MAIN="${{secrets.VITE_FIREBASE_DB_DATABASE_URL_MAIN}}" >> .env + echo VITE_REDIS_HOST="${{secrets.VITE_REDIS_HOST}}" >> .env + echo VITE_REDIS_PORT="${{secrets.VITE_REDIS_PORT}}" >> .env + echo VITE_REDIS_PASS="${{secrets.VITE_REDIS_PASS}}" >> .env + echo VITE_REDIS_CACHE_DB="${{secrets.VITE_REDIS_CACHE_DB}}" >> .env + echo VITE_REDIS_BULL_DB="${{secrets.VITE_REDIS_BULL_DB}}" >> .env + # [ℹ] build the DOCKER IMAGE CONTAINER; - name: Build the Docker image run: | docker build . --file Dockerfile --tag my-image-name:$(date +%s) diff --git a/.gitignore b/.gitignore index 859f2f131..f8034d458 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,7 @@ certs/* .env WEBSITE-DEV-README.md -datalog/ \ No newline at end of file +datalog/ + +# [PERSONAL] +.vscode/ \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..38972655f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,13 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example + +# Ignore files for PNPM, NPM and YARN +pnpm-lock.yaml +package-lock.json +yarn.lock diff --git a/.prettierrc b/.prettierrc index ff2677efd..5bf51dcac 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,5 +2,13 @@ "useTabs": true, "singleQuote": true, "trailingComma": "none", - "printWidth": 100 -} + "singleAttributePerLine": true, + "bracketSameLine": false, + "printWidth": 120, + "plugins": ["prettier-plugin-svelte"], + "pluginSearchDirs": ["."], + "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }], + "svelteSortOrder" : "scripts-options-markup-styles", + "svelteStrictMode": false, + "svelteAllowShorthand": true +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f58ae0e4..ed52430d8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,9 +2,9 @@ // DOC: https://stackoverflow.com/questions/64365300/how-can-i-remove-unused-imports-declarations-from-the-entire-project-of-react-ty // DOC: https://stackoverflow.com/questions/46722701/is-there-a-way-to-remove-unused-imports-and-declarations-from-angular-2 "editor.codeActionsOnSave": { - "source.fixAll": true, - "source.organizeImports": true, - "source.sortMembers": true + // "source.fixAll": true, + // "source.organizeImports": true, + // "source.sortMembers": true }, // NOTE: custom highlighter for data "todohighlight.keywords": [ @@ -38,5 +38,42 @@ "backgroundColor": "red", "overviewRulerColor": "grey" } - ] + ], + // DOC: https://discord.com/channels/457912077277855764/1036919129782878218 + // DOC: https://discord.com/channels/457912077277855764/1036369208721813554 + "svelte.plugin.svelte.compilerWarnings": { + "a11y-click-events-have-key-events": "ignore", + "a11y-aria-attributes": "ignore", + "a11y-incorrect-aria-attribute-type": "ignore", + "a11y-unknown-aria-attribute": "ignore", + "a11y-hidden": "ignore", + "a11y-misplaced-role": "ignore", + "a11y-unknown-role": "ignore", + "a11y-no-abstract-role": "ignore", + "a11y-no-redundant-roles": "ignore", + "a11y-role-has-required-aria-props": "ignore", + "a11y-accesskey": "ignore", + "a11y-autofocus": "ignore", + "a11y-misplaced-scope": "ignore", + "a11y-positive-tabindex": "ignore", + "a11y-invalid-attribute": "ignore", + "a11y-missing-attribute": "ignore", + "a11y-img-redundant-alt": "ignore", + "a11y-label-has-associated-control": "ignore", + "a11y-media-has-caption": "ignore", + "a11y-distracting-elements": "ignore", + "a11y-structure": "ignore", + "a11y-mouse-events-have-key-events": "ignore", + "a11y-missing-content": "ignore", + }, + // DOC: https://stackoverflow.com/a/49777201/8421215 + "editor.folding": true, + "editor.showFoldingControls": "always", + "editor.foldingStrategy": "indentation", + "editor.foldingImportsByDefault": true, + // DOC: https://marketplace.visualstudio.com/items?itemName=maptz.regionfolder + "maptz.regionfolder": { + "collapseAllRegions": true, + "collapseDefaultRegionsOnOpen": true + } } \ No newline at end of file diff --git a/CONTRIBUTING/EXAMPLE_WIDGET.svelte b/CONTRIBUTING/TEMPLATE-WIDGET.svelte similarity index 66% rename from CONTRIBUTING/EXAMPLE_WIDGET.svelte rename to CONTRIBUTING/TEMPLATE-WIDGET.svelte index aa7292b89..accbe0e90 100644 --- a/CONTRIBUTING/EXAMPLE_WIDGET.svelte +++ b/CONTRIBUTING/TEMPLATE-WIDGET.svelte @@ -4,6 +4,14 @@ COMPONENT JS (w/ TS) - - - - - - - + + + + %sveltekit.head% - -
%sveltekit.body%
+ +
+ %sveltekit.body% +
- \ No newline at end of file + diff --git a/src/global.d.ts b/src/global.d.ts deleted file mode 100644 index 6dd6bf078..000000000 --- a/src/global.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -/// - -interface Web3_Providers -extends import('ethers').providers.ExternalProvider { - isCoinbaseWallet: boolean; - isCoinbaseBrowser: boolean; -} - -interface Window { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - gtag: unknown; - ethereum: unknown; -} \ No newline at end of file diff --git a/src/hooks.server.ts b/src/hooks.server.ts index dba21d8d4..ba3db7e62 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,68 +1,93 @@ import { v4 as uuid } from '@lukeed/uuid'; import cookie from 'cookie'; -import type { Handle } from '@sveltejs/kit'; -// https://dev.to/krowemoh/sveltekit-hooks-2bii -// https://dev.to/kudadam/sveltekit-hooks-everything-you-need-to-know-3l39 -// https://rodneylab.com/sveltekit-session-cookies/ -// https://stackoverflow.com/questions/71105799/sveltekit-pass-data-from-server-to-browser -// https://github.com/sveltejs/kit/pull/3993 -// https://stackoverflow.com/questions/69066169/how-to-implement-cookie-authentication-sveltekit-mongodb -// https://blog.logrocket.com/authentication-svelte-using-cookies/ +import { dlog } from '$lib/utils/debug'; +import { platfrom_lang_ssr } from '$lib/utils/platform-functions'; +import type { + Handle, + RequestEvent +} from '@sveltejs/kit'; -export const handle: Handle = async ({ event, resolve }) => { +/** + * @description + * DOC: REF: [3] + * @param param0 + * @returns + */ +export const handle: Handle = async ({ + event, + resolve +}) => { + // https://github.com/sveltejs/kit/issues/4873 + // const clientAddress = !prerendering ? await event.clientAddress : ''; // incorrect-IP + // const clientAddressv2 = !prerendering ? event : '' // no-working - // https://github.com/sveltejs/kit/issues/4873 - // const clientAddress = !prerendering ? await event.clientAddress : ''; // incorrect-IP - // const clientAddressv2 = !prerendering ? event : '' // no-working - - // ----------------- - // [ℹ] before endpoint call + // ----------------- + // [ℹ] before endpoint call + // ----------------- - // [ℹ] getting cookies from request headers - all requests have cookies on them - const cookies = cookie.parse(event.request.headers.get('cookie') || ''); + // [ℹ] getting cookies from request headers + // [ℹ] all requests have cookies on them + const cookies = cookie.parse( + event.request.headers.get('cookie') || '' + ); - // [ℹ] assign "locals" context from "cookie" - // [ℹ] or, load defaults + // [ℹ] assign "locals" context from "cookie" + // [ℹ] or, load defaults event.locals.user = cookies.betarenaCOOKIE || { - userid: uuid(), - // originIP: event.request.headers['x-forwarded-for'] || - // event.request.socket.remoteAddress || - // null - // originIP: clientAddress, - // geoPos: !prerendering ? (await getUserLocationFromIP(clientAddress)) : '', - lang: 'en', - theme: 'Light', - }; + userid: uuid(), + // originIP: event.request.headers['x-forwarded-for'] || + // event.request.socket.remoteAddress || + // null + // originIP: clientAddress, + // geoPos: !prerendering ? (await getUserLocationFromIP(clientAddress)) : '', + lang: 'en', + theme: 'Light' + }; + dlog(event?.locals?.user); - // [🐛] debug - // console.log('event.locals.user', event.locals.user); + // [ℹ] assign "locals" context from "cookie" + // [ℹ] or, load defaults + event.locals.betarenaUser = cookies.betarenaCookieLoggedIn || null; + dlog(event?.locals?.betarenaUser); - // TODO https://github.com/sveltejs/kit/issues/1046 + // TODO: https://github.com/sveltejs/kit/issues/1046 // if (event.url.searchParams.has('_method')) { // event.method = event.url.searchParams.get('_method').toUpperCase(); // } - // ----------------- - // [ℹ] endpoint call + // ----------------- + // [ℹ] endpoint call + // ----------------- - const response = await resolve(event); + // [ℹ] past use with cookies-template + // const response = await resolve(event); + // [ℹ] new with response of + // DOC: https://github.com/sveltejs/kit/issues/3091 + const response = await resolve(event, { + transformPageChunk: ({ html }) => + html.replace('%lang%', get_lang(event)) + }); - // ----------------- - // [ℹ] after endpoint call + // ----------------- + // [ℹ] after endpoint call + // ----------------- - // [ℹ] if this is the first time the user has visited this app, + // [ℹ] if this is the first time the user has visited this app, if (!cookies.betarenaCOOKIE) { // [ℹ] set a cookie so that we recognise them when they return - response.headers.set('Set-Cookie', cookie.serialize( - 'betarenaCOOKIE', // 'name' - JSON.stringify(event.locals.user), // 'value' - { - path: '/', - httpOnly: true, - maxAge: 60 * 60 * 24 * 7 // 1 week - } - )); + response.headers.set( + 'Set-Cookie', + cookie.serialize( + 'betarenaCOOKIE', // 'name' + JSON.stringify(event.locals.user), // 'value' + { + path: '/', + httpOnly: true, + maxAge: 60 * 60 * 24 * 7 // 1 week + } + ) + ); } return response; @@ -70,21 +95,44 @@ export const handle: Handle = async ({ event, resolve }) => { /** @type {import('@sveltejs/kit').GetSession} */ export function getSession(event) { + return event?.locals?.user + ? { + user: { + // [ℹ] only include properties needed client-side — + // [ℹ] exclude anything else attached to the user + // [ℹ] like access tokens etc + userid: event.locals.user.userid, + originIP: event.locals.user.originIP, + geoPos: event.locals.user.geoPos, + lang: event.locals.user.lang, + theme: event.locals.user.theme + } + } + : {}; +} + +/** @type {import('@sveltejs/kit').GetSession} */ +export function getSignedInUser(event) { + return event?.locals?.betarenaUser + ? true + : false + ; +} - return event?.locals?.user - ? - { - user: { - // [ℹ] only include properties needed client-side — - // [ℹ] exclude anything else attached to the user - // [ℹ] like access tokens etc - userid: event.locals.user.userid, - originIP: event.locals.user.originIP, - geoPos: event.locals.user.geoPos, - lang: event.locals.user.lang, - theme: event.locals.user.theme - } - } - : - {}; -} \ No newline at end of file +/** + * @description obtains the current platform translation + * as a hook.server.ts method/function + * @param {RequestEvent>>} event + * @returns {string} language + */ +function get_lang( + event: RequestEvent>> +): string { + const lang = platfrom_lang_ssr( + event?.route.id, + event?.error, // FIXME: event.error does not exist in a hook + event?.params?.lang + ); + dlog(`HOOKS | get_lang: ${lang}`, true); + return lang; +} diff --git a/src/lib/api/utils.ts b/src/lib/api/utils.ts index 051ca6eea..0aea94c7c 100644 --- a/src/lib/api/utils.ts +++ b/src/lib/api/utils.ts @@ -1,5 +1,5 @@ -import { dev } from "$app/environment"; -import { logErrorGroup } from "$lib/utils/debug"; +import { dev } from '$app/environment'; +import { logErrorGroup } from '$lib/utils/debug'; /** * EXPORT @@ -11,21 +11,29 @@ import { logErrorGroup } from "$lib/utils/debug"; * @param endpoint * @returns */ - export async function get(endpoint: string): Promise { - // curcanavigate CORS issues - // endpoint = 'https://cors-anywhere.herokuapp.com/' + endpoint // comment this out before subemission, - // ... - return await fetch(endpoint, { - method: 'GET' - }).then((response) => { - // ... verify if the response is error-free - if (!response.ok) { - if (dev) logErrorGroup ("utils [DEV]", `response: ${response}`) - throw new Error('Network response was not ok'); - } - // ... return the data - return response.json(); - }); +export async function get( + endpoint: string +): Promise { + // curcanavigate CORS issues + // endpoint = 'https://cors-anywhere.herokuapp.com/' + endpoint // comment this out before subemission, + // ... + return await fetch(endpoint, { + method: 'GET' + }).then((response) => { + // ... verify if the response is error-free + if (!response.ok) { + if (dev) + logErrorGroup( + 'utils [DEV]', + `response: ${response}` + ); + throw new Error( + 'Network response was not ok' + ); + } + // ... return the data + return response.json(); + }); } /** @@ -33,32 +41,41 @@ import { logErrorGroup } from "$lib/utils/debug"; * Used as a PROXY on the website * to handle requests correctly and * in-line with the CORS requirements. - * + * * A POST request is used to appropietly * pass data from the client-to-the-backend. - * - * @param {*} path - * @param {*} data - * @returns -*/ -export async function post(path, data): Promise { - // ... - return await fetch(path, { - method: 'POST', - credentials: 'include', - body: JSON.stringify(data), - mode: 'cors', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - }).then((response) => { - // ... verify if the response is error-free - if (!response.ok) { - if (dev) logErrorGroup ("utils [DEV]", `response: ${response}`) - throw new Error('Network response was not ok'); - } - // ... return the data - return response.json() - }) + * + * @param {*} path + * @param {*} data + * @returns + */ +export async function post( + path, + data +): Promise { + // ... + return await fetch(path, { + method: 'POST', + credentials: 'include', + body: JSON.stringify(data), + mode: 'cors', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + } + }).then((response) => { + // ... verify if the response is error-free + if (!response.ok) { + if (dev) + logErrorGroup( + 'utils [DEV]', + `response: ${response}` + ); + throw new Error( + 'Network response was not ok' + ); + } + // ... return the data + return response.json(); + }); } diff --git a/src/lib/components/_Email_subscribe.svelte b/src/lib/components/_Email_subscribe.svelte index c7cb84c04..f4f26dea7 100644 --- a/src/lib/components/_Email_subscribe.svelte +++ b/src/lib/components/_Email_subscribe.svelte @@ -2,13 +2,12 @@ COMPONENT JS - BASIC [TypeScript Written] =================== --> - {#if $sessionStore.newsletterPopUpShow} -
$sessionStore.newsletterPopUpShow = false} - in:fade /> - -
- - close-svg $sessionStore.newsletterPopUpShow = false} /> - - {#if server_side_language == 'en'} - -