├── .eslintrc ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.org ├── app ├── .env ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── jsconfig.json ├── package.json ├── src │ ├── app.d.ts │ ├── app.html │ ├── components │ │ ├── Auth.svelte │ │ ├── Nav.svelte │ │ ├── PassReset.svelte │ │ ├── Presentation.svelte │ │ ├── SideBarLayout.svelte │ │ ├── layout │ │ │ ├── Box.svelte │ │ │ ├── Bracket.svelte │ │ │ ├── Cluster.svelte │ │ │ ├── Cover.svelte │ │ │ ├── Frame.svelte │ │ │ ├── Grid.svelte │ │ │ ├── Imposter.svelte │ │ │ ├── Reel.svelte │ │ │ ├── Sidebar.svelte │ │ │ ├── Stack.svelte │ │ │ ├── Switcher.svelte │ │ │ └── index.js │ │ └── lib │ │ │ └── helpers.js │ ├── lib │ │ ├── notifications.js │ │ └── stores.js │ └── routes │ │ ├── +page.svelte │ │ ├── __layout.svelte │ │ ├── _error.svelte │ │ ├── auth │ │ ├── +page.svelte │ │ ├── login │ │ │ └── +page.svelte │ │ ├── pass-reset-new │ │ │ └── +page.svelte │ │ ├── pass-reset │ │ │ └── +page.svelte │ │ ├── register │ │ │ └── +page.svelte │ │ ├── social │ │ │ └── +page.svelte │ │ ├── unapproved-user │ │ │ └── +page.svelte │ │ ├── unvalidated-user │ │ │ └── +page.svelte │ │ └── validate │ │ │ └── +page.svelte │ │ ├── box │ │ └── +page.svelte │ │ ├── bracket │ │ └── +page.svelte │ │ ├── cluster │ │ └── +page.svelte │ │ ├── cover │ │ └── +page.svelte │ │ ├── frame │ │ └── +page.svelte │ │ ├── imposter │ │ └── +page.svelte │ │ ├── index.svelte │ │ ├── reel │ │ └── +page.svelte │ │ ├── sidebar │ │ └── +page.svelte │ │ ├── stack.svelte │ │ ├── stack │ │ └── +page.svelte │ │ └── switcher │ │ └── +page.svelte ├── static │ ├── favicon.png │ ├── fonts │ │ ├── Bevan-Regular.ttf │ │ ├── JosefinSlab-Bold.ttf │ │ ├── JosefinSlab-BoldItalic.ttf │ │ ├── JosefinSlab-Light.ttf │ │ ├── JosefinSlab-LightItalic.ttf │ │ ├── JosefinSlab-Regular.ttf │ │ ├── JosefinSlab-RegularItalic.ttf │ │ ├── JosefinSlab-SemiBold.ttf │ │ ├── JosefinSlab-SemiBoldItalic.ttf │ │ ├── JosefinSlab-Thin.ttf │ │ ├── JosefinSlab-ThinItalic.ttf │ │ └── OFL.txt │ ├── global.css │ ├── great-success.png │ ├── layouts-global.css │ ├── logo-192.png │ ├── logo-512.png │ └── manifest.json ├── svelte.config.js └── vite.config.js ├── cli ├── .env ├── .gitignore ├── email_worker.js ├── package.json └── sendmail.js ├── common ├── .gitignore ├── funcs.js ├── package.json └── postgrest.js ├── db_init.sh ├── envs └── local.tpl ├── lambda ├── .env ├── .gitignore ├── .nvmrc ├── README.org ├── index.js ├── pack-update-test.sh ├── pack.sh ├── package.json ├── postgrest-download.sh ├── postgrest.js ├── rds-create.sh ├── trust-policy.tpl.json └── update.sh ├── lint.sh ├── npm-init.sh ├── pg_schema_dump.sh ├── postgrest.conf ├── psql-admin.sh ├── psql.sh ├── server ├── .env ├── .gitignore ├── index.js └── package.json ├── setenv.sh ├── sql ├── fixtures.sql ├── pgjwt-nords.sql ├── pgjwt.sql └── schema │ ├── 00 │ ├── EXTENSION.pgcrypto.COMMENT.sql │ ├── EXTENSION.pgjwt.COMMENT.sql │ ├── FUNCTION.role.email.cv.newrole.cv.ACL.sql │ ├── FUNCTION.user_delete.email.cv.ACL.sql │ ├── FUNCTION.user_insert.email.cv.ACL.sql │ ├── FUNCTION.validate.email.cv.ACL.sql │ ├── FUNCTION.verify_token.token.text,.algorithm.text.ACL.sql │ ├── basic_auth.SCHEMA.sql │ ├── check_role_exists.FUNCTION.sql │ ├── encrypt_pass.FUNCTION.sql │ ├── invite.cv.FUNCTION.sql │ ├── jwt_test.FUNCTION.sql │ ├── jwt_token.TYPE.sql │ ├── login.text,.text.FUNCTION.sql │ ├── order.txt │ ├── pass_reset.text.FUNCTION.sql │ ├── pass_reset_new.text,.text.FUNCTION.sql │ ├── pgcrypto.EXTENSION.sql │ ├── pgjwt.EXTENSION.sql │ ├── refresh.FUNCTION.sql │ ├── register.text,.text,.text.FUNCTION.sql │ ├── register.text,.text.FUNCTION.sql │ ├── role.cv.cv.FUNCTION.sql │ ├── set_user_role.text,.text.FUNCTION.sql │ ├── settings.TABLE.sql │ ├── settings.table_name_pk.CONSTRAINT.sql │ ├── unvalidate.cv.FUNCTION.sql │ ├── user_approved.text,.boolean.FUNCTION.sql │ ├── user_delete.cv.FUNCTION.sql │ ├── user_insert.cv.FUNCTION.sql │ ├── user_role.text,.text.FUNCTION.sql │ ├── user_validated.text,.boolean.FUNCTION.sql │ ├── user_validated.text,.text.FUNCTION.sql │ ├── users.TABLE.sql │ ├── users.VIEW.sql │ ├── users.encrypt_pass.TRIGGER.sql │ ├── users.ensure_user_role_exists.TRIGGER.sql │ ├── users.users_notify.TRIGGER.sql │ ├── users.users_pass_reset_notify_trig.TRIGGER.sql │ ├── users.users_pkey.CONSTRAINT.sql │ ├── users_notify_trigger.FUNCTION.sql │ ├── users_pass_reset_notify.FUNCTION.sql │ ├── validate.cv.FUNCTION.sql │ ├── validate_token.cv.FUNCTION.sql │ ├── validation_reset.FUNCTION.sql │ └── verify_token.text,.text.FUNCTION.sql ├── systemd.sample └── tmux.sh /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2019, 10 | "sourceType": "module" 11 | }, 12 | "plugins": ["svelte3"], 13 | "extends": ["eslint:recommended"], 14 | "overrides": [ 15 | { 16 | "files": ["**/*.svelte"], 17 | "processor": "svelte3/svelte3" 18 | } 19 | ], 20 | "rules": {} 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/* 3 | yarn-error.log 4 | /cypress/screenshots/ 5 | \#* 6 | *~ 7 | .env 8 | .bash_logout 9 | .bashrc 10 | .cloud-locale-test.skip 11 | .config/configstore/update-notifier-npm.json 12 | .npm/anonymous-cli-metrics.json 13 | .nvm 14 | .profile 15 | .psql_history 16 | .degit/ 17 | .emacs 18 | .emacs.d/ 19 | .gitconfig 20 | .npm/ 21 | .ssh/ 22 | .bash_history 23 | .idea/ 24 | engine/ 25 | .env.sh 26 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v14.16.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "semi": false, 4 | "singleQuote": true, 5 | "trailingComma": "es5", 6 | "plugins": ["prettier-plugin-svelte"], 7 | "svelteStrictMode": false, 8 | "svelteBracketNewLine": true, 9 | "svelteAllowShorthand": true, 10 | "htmlWhitespaceSensitivity": "ignore", 11 | "jsxBracketSameLine": true, 12 | "endOfLine": "lf" 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Guy Romm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * what & why? 2 | - [[https://svelte.dev/][svelte]] is the hot new FE dev stuff. do [[https://svelte.dev/tutorial/basics][the tutorial]] to get hooked. [[https://kit.svelte.dev/][SvelteKit]] - its equivalent of SSR 3 | framework, not unlike next.js 4 | - [[http://postgrest.org/][postgrest]] is a really nice backend to quickly prototype apps by 5 | definining data & permissions, without having to write backend. 6 | - [[https://every-layout.dev/][every layout]] is a fresh approach to plain css, with reusable components that make sense. borrowed from [[https://github.com/SilvanCodes/svelte-layout-components][SilvanCodes/svelte-layout-components]]. 7 | - some social login love thrown in for good measure, uses [[https://github.com/beyonk-adventures/svelte-social-auth][beyonk-adventures/svelte-social-auth]] 8 | - tmux is used to run multiple processes and an editor, because nothing beats tmux and emacs (use [[http://web-mode.org/][web-mode]] to edit svelte!) 9 | * prequisites for dev env 10 | 1. [[https://github.com/PostgREST/postgrest/releases/latest][postgrest]] - install according to [[http://postgrest.org/en/v6.0/tutorials/tut0.html][instructions]]. 11 | 1. [[https://github.com/michelp/pgjwt][pgjwt]] - postgresql jwt extension for postgrest auth 12 | 2. [[https://github.com/nvm-sh/nvm][nvm]] - to easily swap node/npm versions. tested with node v13.11.0. 13 | * cloning 14 | #+BEGIN_SRC bash 15 | npx degit https://github.com/guyromm/svelte-postgrest-template svelte-postgrest-app 16 | #+END_SRC 17 | * package.json dependencies 18 | #+BEGIN_SRC bash 19 | nvm use ; ./npm-init.sh 20 | #+END_SRC 21 | 22 | * .env 23 | use envs/local.tpl to create an envs/local .env shell file, and then 24 | expand/eval it using ./setenv.sh 25 | #+BEGIN_SRC bash 26 | function freeport() { 27 | FROM=$1 28 | TO=$2 29 | HOWMANY=$3 30 | comm -23 \ 31 | <(seq "$FROM" "$TO" | sort) \ 32 | <(ss -Htan | awk '{print $4}' | cut -d':' -f2 | sort -u) \ 33 | | shuf | head -n "$HOWMANY" 34 | } 35 | export APPNAME=$(basename $(pwd)) 36 | export DBNAME=$APPNAME 37 | export APPPORT=$(freeport 3000 4000 1) 38 | export POSTGRESTPORT=$[APPPORT+1] 39 | export SERVERPORT=$[APPPORT+2] 40 | export JWTSECRET=$(head /dev/urandom | tr -dc A-F0-9 | head -c 64 ; echo '') 41 | 42 | cp envs/local.tpl envs/local 43 | sed -i -E "s/APPPORTREPLACE/$APPPORT/g" envs/local 44 | sed -i -E "s/SERVERPORTREPLACE/$SERVERPORT/g" envs/local 45 | sed -i -E "s/POSTGRESTPORTREPLACE/$POSTGRESTPORT/g" envs/local 46 | sed -i -E "s/DBNAMEREPLACE/$DBNAME/g" envs/local 47 | sed -i -E "s/JWTSECRETREPLACE/$JWTSECRET/g" envs/local 48 | ./setenv.sh local 49 | #+END_SRC 50 | 51 | * deploying on aws 52 | ** create a separate env 53 | #+BEGIN_SRC bash 54 | test -f envs/aws || cp envs/local envs/aws 55 | sed -i -E "s/^RDS=''$/RDS=1/g" envs/aws 56 | echo POSTGREST_PATH_AS_ARG=1 >> envs/aws 57 | echo 'VITE_POSTGREST_PATH_AS_ARG=$POSTGREST_PATH_AS_ARG' >> envs/aws 58 | RDS_PASSWORD=$(tr -dc A-Za-z0-9 > envs/aws 61 | egrep '^RDS_PASSWORD=(.+)$' envs/aws || echo "RDS_PASSWORD=$RDS_PASSWORD" >> envs/aws 62 | ./setenv.sh aws 63 | lambda/postgrest-download.sh 64 | #+END_SRC 65 | 66 | ** create the rds 67 | #+BEGIN_SRC bash 68 | egrep '^RDS_HOSTNAME=(.+)$' envs/aws || (./lambda/rds-create.sh | egrep '^RDS_HOSTNAME=' | tee -a envs/aws 69 | echo 'DBURIADMIN="postgres://postgres:$RDS_PASSWORD@$RDS_HOSTNAME/template1"' | tee -a envs/aws 70 | echo 'DBURI="postgres://postgres:$RDS_PASSWORD@$RDS_HOSTNAME/"' | tee -a envs/aws 71 | ) 72 | ./setenv.sh aws 73 | #+END_SRC 74 | ** create & deploy the lambda func 75 | #+BEGIN_SRC bash 76 | source .env 77 | egrep '^LAMBDA_ROLE=(.+)$' envs/aws || ( 78 | aws iam create-role --role-name $APPNAME --assume-role-policy-document file://lambda/trust-policy.tpl.json | tee envs/$ENV.role.json 79 | aws iam attach-role-policy --role-name $APPNAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole 80 | ) 81 | egrep '^LAMBDA_ROLE=(.+)$' envs/aws && echo 'LAMBDA_ROLE_ALREADY_SET' || (test -f envs/$ENV.role.json && (echo LAMBDA_ROLE=$(jq .Role.Arn envs/$ENV.role.json -r) | tee -a envs/aws) || echo 'NO_ROLE_FILE') 82 | ./setenv.sh aws 83 | source .env 84 | lambda/pack.sh 85 | aws lambda create-function --function-name $APPNAME-postgrest --runtime nodejs14.x --role "$LAMBDA_ROLE" --zip-file fileb://lambda/function.zip --handler index.handler --timeout 15 | tee envs/$ENV.lambda.json 86 | egrep '^AWS_POSTGREST_LAMBDA_FUNC=(.+)$' envs/aws || echo 'AWS_POSTGREST_LAMBDA_FUNC='$(jq .FunctionName envs/$ENV.lambda.json -r) | tee -a envs/aws 87 | aws lambda create-function-url-config --function-name $APPNAME-postgrest --auth-type NONE --cors 'AllowOrigins=*' | tee envs/$ENV.lambda.url.json 88 | test -f envs/$ENV.lambda.url.json && sed -i -E 's/^POSTGREST_BASE_URI=(.*)$/POSTGREST_BASE_URI="'$(jq .FunctionUrl envs/$ENV.lambda.url.json -r | sed -E 's/\//\\\//g')'"/g' envs/aws 89 | #+END_SRC 90 | * database initialization 91 | #+BEGIN_SRC bash 92 | source .env 93 | echo 'DBNAME:'$DBNAME 94 | ./db_init.sh 95 | #+END_SRC 96 | * launch 97 | #+BEGIN_SRC bash 98 | ./tmux.sh 99 | #+END_SRC 100 | 101 | -------------------------------------------------------------------------------- /app/.env: -------------------------------------------------------------------------------- 1 | ../.env -------------------------------------------------------------------------------- /app/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /app/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: ['eslint:recommended', 'prettier'], 4 | plugins: ['svelte3'], 5 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 6 | parserOptions: { 7 | sourceType: 'module', 8 | ecmaVersion: 2020 9 | }, 10 | env: { 11 | browser: true, 12 | es2017: true, 13 | node: true 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /functions 5 | /.svelte-kit 6 | /package 7 | .env 8 | .env.* 9 | !.env.example 10 | vite.config.js.timestamp-* 11 | vite.config.ts.timestamp-* 12 | -------------------------------------------------------------------------------- /app/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /app/.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "tabWidth": 2, 7 | "plugins": ["prettier-plugin-svelte"], 8 | "pluginSearchDirs": ["."], 9 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 10 | } 11 | -------------------------------------------------------------------------------- /app/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files 14 | // 15 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 16 | // from the referenced tsconfig.json - TypeScript does not merge them in 17 | } 18 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TODO", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "dev": "vite dev", 6 | "build": "vite build", 7 | "preview": "vite preview", 8 | "check": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json", 9 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./jsconfig.json --watch", 10 | "upload_s3": "bash -c 'source .env && aws s3 sync --acl public-read build s3://$APP_S3_BUCKET && echo UPLOADED'", 11 | "lint": "prettier --plugin-search-dir . --check . && eslint .", 12 | "format": "prettier --plugin-search-dir . --write ." 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/adapter-auto": "^1.0.0", 16 | "@sveltejs/kit": "^1.0.0", 17 | "eslint": "^8.28.0", 18 | "eslint-config-prettier": "^8.5.0", 19 | "eslint-plugin-svelte3": "^4.0.0", 20 | "prettier": "^2.8.0", 21 | "prettier-plugin-svelte": "^2.8.1", 22 | "svelte": "^3.54.0", 23 | "svelte-check": "^2.9.2", 24 | "typescript": "^4.9.3", 25 | "vite": "^4.0.0" 26 | }, 27 | "type": "module", 28 | "dependencies": { 29 | "@beyonk/svelte-social-auth": "^2.1.1", 30 | "dotenv": "^10.0.0", 31 | "jwt-decode": "^3.1.2", 32 | "lorem-ipsum": "^2.0.3", 33 | "qs": "^6.10.1", 34 | "rollup-plugin-babel": "^4.4.0", 35 | "rollup-plugin-svelte": "^7.1.0" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | // and what to do when importing types 4 | declare namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | -------------------------------------------------------------------------------- /app/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 25 | %sveltekit.head% 26 | 27 | 28 | 30 |
%sveltekit.body%
31 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/components/Auth.svelte: -------------------------------------------------------------------------------- 1 | 202 | 203 |
204 |
205 | {#if mode === 'validate'} 206 | {#if !validationProcess} 207 |

Email validation

208 | {#if !token} 209 |

No valid token provided

210 | 212 | {:else} 213 |

214 | Your token is either invalid or has been used. Please login 215 | or 216 | reset your password. 217 |

218 | {/if} 219 | {/if} 220 | {:else if mode === 'login'} 221 |
222 |
223 | Don't have user? Register now 224 |    Forgotten your password? 225 | Reset it 226 |
227 |
228 |
229 | {#if error}
230 | {error} 231 |
232 | Reset password 233 |
{/if} 234 | 235 |
236 | 240 | 241 | 245 | 246 | 247 |
248 |
249 | {:else if mode === 'register'} 250 |
251 |
252 | Login 253 |  Forgotten your password?  254 | Reset it 255 |
256 |
257 |

Registration

258 |
259 |
260 | 265 | 274 | 286 | 289 |
290 |
291 | {:else if mode === 'unvalidated'} 292 |

293 | Welcome, {$authDataStore.email} 294 | ! 295 |
296 | You have not yet validated your email. Please follow the link that should be in your inbox in 297 | order to complete validation. 298 |
299 |
300 | If you haven’t received an email from us, we can try & re-send it: 301 | resend validation link. 302 |

303 | 304 | {:else if mode === 'unapproved'} 305 |

306 | Welcome, {$authDataStore.email} 307 | ! 308 |
309 | You account is on review. Please wait. 310 |

311 | 312 | {:else if authData && !authData.is_expired} 313 |
314 | {#if thankYou} 315 |

THANK YOU FOR VALIDATING!

316 | {/if} 317 |

Logged in

318 | Role: {authData.role} 319 | Email: {authData.email} 320 | Approved: {authData.approved} 321 | Validated: {authData.validated} 322 | 323 | Expiry: {((new Date(authData.exp) - new Date()) / 1000 / 3600).toLocaleString()}h 324 | 325 | Is expired: {authData.is_expired} 326 | Reset password 327 | 328 |
329 | {:else if mode === 'social'} 330 |

Google sign-in

331 | 332 | 333 | 339 | {:else} 340 |

Not logged in?

341 |

Sign in

342 |

New here?

343 |

Register

344 | {/if} 345 |
346 |
347 | 348 | 394 | -------------------------------------------------------------------------------- /app/src/components/Nav.svelte: -------------------------------------------------------------------------------- 1 | 110 | 111 | 128 | 129 | 130 | 136 | -------------------------------------------------------------------------------- /app/src/components/PassReset.svelte: -------------------------------------------------------------------------------- 1 | 63 | 64 |
65 |
66 |

Restore password

67 |
68 | {#if mode === 'send-email'} 69 | {#if pass_reset_success} 70 |
71 |
Check your email for a link to reset your password
72 | {:else} 73 |
74 |

75 | Enter your user account's verified email address and we will send you a password reset 76 | link. 77 |

78 |
79 | 80 | {#if error}
{error}
{/if} 81 | 84 |
85 | {/if} 86 | {:else if mode === 'new-pass'} 87 | {#if pass_reset_success} 88 |
89 |
90 | Successfuly changed. 91 | {#if !$authDataStore} 92 | Sign in 93 | {/if} 94 |
95 | {:else} 96 |
97 | 98 |
99 | 105 |
106 | {confirm_pass && pass !== confirm_pass ? "Passwords don't match" : ''} 107 |
108 |
109 |
110 | 113 | {#if error}
{error}
{/if} 114 |
115 |
116 | {/if} 117 | {/if} 118 |
119 |
120 |
121 | 122 | 158 | -------------------------------------------------------------------------------- /app/src/components/Presentation.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /app/src/components/SideBarLayout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 |
Sidebar
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /app/src/components/layout/Box.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 | 30 |
31 | 32 | 37 | -------------------------------------------------------------------------------- /app/src/components/layout/Bracket.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 |
30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 |
38 |
39 | 40 | 58 | -------------------------------------------------------------------------------- /app/src/components/layout/Cluster.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | 25 |
26 | 27 | 37 | -------------------------------------------------------------------------------- /app/src/components/layout/Cover.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | 32 |
33 |
34 | 35 | 54 | -------------------------------------------------------------------------------- /app/src/components/layout/Frame.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 | 17 |
18 | 19 | 43 | -------------------------------------------------------------------------------- /app/src/components/layout/Grid.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | 18 |
19 | 20 | 25 | -------------------------------------------------------------------------------- /app/src/components/layout/Imposter.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 22 |
23 | 24 | 33 | -------------------------------------------------------------------------------- /app/src/components/layout/Reel.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 22 |
23 | 24 | 37 | -------------------------------------------------------------------------------- /app/src/components/layout/Sidebar.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 |
34 |
35 | {#if sidebarIs === 'left'} 36 | 39 |
40 | 41 |
42 | {:else} 43 |
44 | 45 |
46 | 49 | {/if} 50 |
51 |
52 | 53 | 73 | -------------------------------------------------------------------------------- /app/src/components/layout/Stack.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 |
28 | 29 |
30 | 31 | 43 | -------------------------------------------------------------------------------- /app/src/components/layout/Switcher.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 |
32 |
33 | 34 |
35 |
36 | 37 | 48 | -------------------------------------------------------------------------------- /app/src/components/layout/index.js: -------------------------------------------------------------------------------- 1 | import Box from './Box.svelte'; 2 | import Bracket from './Bracket.svelte'; 3 | import Cluster from './Cluster.svelte'; 4 | import Cover from './Cover.svelte'; 5 | import Frame from './Frame.svelte'; 6 | import Grid from './Grid.svelte'; 7 | import Imposter from './Imposter.svelte'; 8 | import Reel from './Reel.svelte'; 9 | import Sidebar from './Sidebar.svelte'; 10 | import Stack from './Stack.svelte'; 11 | import Switcher from './Switcher.svelte'; 12 | 13 | export { Box, Bracket, Cluster, Cover, Frame, Grid, Imposter, Reel, Sidebar, Stack, Switcher }; 14 | -------------------------------------------------------------------------------- /app/src/components/lib/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Function to turn props of components into css variables or values. 3 | */ 4 | function cssValue(...values) { 5 | return values.map((v) => `var(--${sanitize(v)}, ${v})`).join(' '); 6 | } 7 | 8 | /** 9 | * Function to generate 'sanitized' Id. 10 | */ 11 | function buildId(...values) { 12 | return values.map((v) => (typeof v === 'string' ? sanitize(v, '_') : String(v))).join('-'); 13 | } 14 | 15 | /** 16 | * Function to get px value of variable or relative unit on DOM node. 17 | */ 18 | function pxValue(node, value) { 19 | const style = node.style; 20 | const currentValue = style.width; 21 | 22 | style.width = cssValue(value); 23 | const pixels = getComputedStyle(node).width; 24 | style.width = currentValue; 25 | 26 | return pixels.replace('px', ''); 27 | } 28 | 29 | /** 30 | * Function to make string CSS variable- and class-name compatible. 31 | */ 32 | function sanitize(string, replacement = '') { 33 | return string.replace(/[^a-zA-Z0-9-_]/g, replacement); 34 | } 35 | 36 | export { cssValue, buildId, pxValue }; 37 | -------------------------------------------------------------------------------- /app/src/lib/notifications.js: -------------------------------------------------------------------------------- 1 | import io from 'socket.io-client'; 2 | import { getCookie } from '../../../common/postgrest.js'; 3 | 4 | let _socket = null; 5 | const getSocket = () => { 6 | if (!_socket) { 7 | _socket = io(import.meta.env.VITE_SITE_SOCKETIO_URI, { 8 | auth: { 9 | token: typeof document !== 'undefined' ? getCookie() : null 10 | } 11 | }); 12 | } 13 | return _socket; 14 | }; 15 | 16 | let subscribers = {}; 17 | 18 | getSocket().on('update', async (payload) => { 19 | const parsedPayload = JSON.parse(payload.message.payload); 20 | const payloadChannel = payload.message.channel; 21 | Object.entries(subscribers).forEach(([subscriber, { channels, callback }]) => { 22 | if (channels.includes(payloadChannel)) { 23 | callback(parsedPayload); 24 | } 25 | }); 26 | }); 27 | 28 | export const subscribe = async ({ subscriber, channels, callback }) => { 29 | subscribers[subscriber] = { channels, callback }; 30 | }; 31 | -------------------------------------------------------------------------------- /app/src/lib/stores.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | import { browser } from '$app/environment'; 3 | import { getCookie, getAuthData } from '../../../common/postgrest'; 4 | export const authDataStore = writable(0); 5 | const l = console.log; 6 | export const parseToken = () => { 7 | const token = browser && getCookie(); 8 | //l('token=',token,'pb',browser); 9 | let no; 10 | try { 11 | const add = token && getAuthData(token); 12 | const ad = add && add.auth_decoded; 13 | no = ad && { ...ad, exp: add.exp, is_expired: add.is_expired, token }; 14 | } catch (err) { 15 | no = {}; 16 | } 17 | authDataStore.set(no); 18 | }; 19 | -------------------------------------------------------------------------------- /app/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 |

23 | the grid (you're here now) 24 |

25 | 26 | 27 |
Stack
28 | 29 |
    30 |
  • some

  • 31 |
  • equally

  • 32 |
  • spaced

  • 33 |
  • content

  • 34 |
35 |
36 |
37 | 38 | 39 |
Box
40 | 41 |
42 | 43 | 44 |
Bracket
45 | 46 |
47 | 48 |
49 | 50 |
51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 | 59 | 60 | 61 |
Cluster
62 | 63 |
    64 |
  • Content

  • 65 |
  • Content

  • 66 |
  • Content

  • 67 |
  • Content

  • 68 |
  • Content

  • 69 |
70 |
71 |
72 | 73 | 74 |
Sidebar
75 | 76 |
77 | 78 | 79 |

A

80 |

B

81 |
82 |
83 |
84 |
85 | 86 |

Main Content

87 |
88 |
89 |
90 |
91 | 92 | 93 |
Switcher
94 | 95 | 96 | 97 | 98 | 99 |
100 | 101 | 102 |
Cover
103 | 104 |
105 | 106 |
107 | 108 |
109 | 110 |
111 |
112 |
113 | 114 | 115 |
Grid
116 |
117 | 118 | X 119 | o 120 | o 121 | o 122 | X 123 | o 124 | o 125 | o 126 | X 127 | 128 |
129 |
130 | 131 | 132 |
Frame
133 | 134 | 135 | favicon 136 | 137 | 138 |
139 | 140 | 141 |
Reel
142 | 143 |
144 | 145 | favicon 146 | favicon 147 | favicon 148 | favicon 149 | favicon 150 | favicon 151 | favicon 152 | favicon 153 | favicon 154 | favicon 155 | 156 |
157 |
158 |
159 | 160 | 161 |
Imposter
162 | 163 |
164 |

165 | Some obscured text. Some obscured text. Some obscured text. Some obscured text. Some 166 | obscured text. 167 |

168 | 169 | 170 |

I'm infront!

171 |
172 |
173 |
174 |
175 |

And scrollable!

176 |
177 |
178 |
179 |
180 |
181 | 182 | 183 | 188 | -------------------------------------------------------------------------------- /app/src/routes/__layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |