├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc ├── README.md ├── package-lock.json ├── package.json ├── src ├── app.d.ts ├── app.html ├── hooks.server.ts ├── lib │ ├── classes │ │ ├── cacheSession.ts │ │ ├── enhancedFunc.ts │ │ ├── fetchHandler.ts │ │ └── formHandler.ts │ ├── components │ │ ├── Button.svelte │ │ ├── Card.svelte │ │ ├── CenterLayout.svelte │ │ ├── ErrorParagraph.svelte │ │ ├── Form.svelte │ │ ├── FormCustomEnhanced.svelte │ │ ├── FormEnhanced.svelte │ │ ├── InputText.svelte │ │ └── theme.ts │ ├── configs.ts │ └── types │ │ └── types.d.ts └── routes │ ├── +error.svelte │ ├── +layout.svelte │ ├── +page.svelte │ ├── account │ ├── +page.server.ts │ └── +page.svelte │ ├── check-locals │ └── +server.ts │ ├── logout │ └── +server.ts │ ├── signin │ ├── +page.server.ts │ └── +page.svelte │ └── signup │ ├── +page.server.ts │ └── +page.svelte ├── static └── favicon.png ├── svelte.config.js ├── tsconfig.json └── vite.config.ts /.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 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | .vscode 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "pluginSearchDirs": ["."], 7 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 |
8 |

9 | Open Source - Template Auth UI 10 |

11 | 12 | # What is? 🔍 13 | 14 | Is a template UI with an authentication and authorization system.
15 | 16 |
17 | preview 18 |
19 | 20 | # Who is this for? 💻 21 | programmers or developers: front-end or full stack that already have backend API. 22 | 23 | # Why should I use this? 🤔 24 | 25 | It's a faster way to start build your website, if you want to streamline your work while saving coding time using an already created template, well, this is for you. 26 | It will continuous be updated to newests versions of SvelteKit. 27 | 28 | 29 | # Features ✔️ 30 | Clean Code ✅ 31 | Fast to configure ✅ 32 | Easy to understand ✅ 33 | Token handlers ✅ 34 | Cookies handlers ✅ 35 | Session/LocalStorage Cache handlers ✅ 36 | 37 | # How to use? 📝 38 | 39 | First steps:
40 | 41 |
46 | 47 |
48 | Install packages
49 | 50 | npm install 51 | 52 |
53 | Turn on-line
54 | 55 | npm run dev -- --open 56 | 57 |
58 | 59 | ## ⚠️ Expected JSON data from API ⚠️
60 | 61 | /account
62 | 63 | { "username": "Person Name", "email": "person@email.com" } 64 | 65 | /login
66 | 67 | { "token": "Bearer whateverToken" } 68 |
69 |
70 | 71 | ## Required: 🔴 72 |

73 | you must change API [url, endpoints]
74 | easy change config.ts , available on ../src/lib/components/ file
75 |

76 | 77 |
78 | preview 79 |
80 | 81 |
82 |
83 | 84 |

85 | Optional
86 | easy change theme.ts , available on ../src/lib/components/ file 87 |

88 | 89 |
90 | preview 91 |
92 | 93 | 94 | # Design Architecture ♻️ 95 |

96 | Requests 97 |

98 |
99 | preview 100 |
101 | 102 | More coming soon 103 | 104 | 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-kit", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vite dev", 7 | "build": "vite build", 8 | "preview": "vite preview", 9 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 10 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 11 | "lint": "prettier --check . && eslint .", 12 | "format": "prettier --write ." 13 | }, 14 | "devDependencies": { 15 | "@sveltejs/adapter-auto": "next", 16 | "@sveltejs/kit": "next", 17 | "@types/cookie": "^0.5.1", 18 | "@typescript-eslint/eslint-plugin": "^5.27.0", 19 | "@typescript-eslint/parser": "^5.27.0", 20 | "eslint": "^8.16.0", 21 | "eslint-config-prettier": "^8.3.0", 22 | "eslint-plugin-svelte3": "^4.0.0", 23 | "prettier": "^2.6.2", 24 | "prettier-plugin-svelte": "^2.7.0", 25 | "svelte": "^3.44.0", 26 | "svelte-check": "^2.7.1", 27 | "svelte-preprocess": "^4.10.6", 28 | "tslib": "^2.3.1", 29 | "typescript": "^4.7.4", 30 | "vite": "^3.1.0" 31 | }, 32 | "type": "module" 33 | } 34 | -------------------------------------------------------------------------------- /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 Locals { 6 | user 7 | } 8 | // interface PageData {} 9 | // interface PageError {} 10 | // interface Platform {} 11 | } 12 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from "@sveltejs/kit"; 2 | import * as cookie from 'cookie' 3 | 4 | /** @type {import('@sveltejs/kit').Handle} */ 5 | 6 | 7 | export const handle: Handle = async ({ event, resolve }) => { 8 | 9 | // Wraps request 10 | const getCookiesFromRequest = event.request.headers.get('cookie') ?? ''; 11 | const cookies = cookie.parse(getCookiesFromRequest); 12 | 13 | event.locals.user = {token: cookies?.token} 14 | 15 | 16 | const response = await resolve(event); 17 | return response; 18 | } -------------------------------------------------------------------------------- /src/lib/classes/cacheSession.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | import { browser } from '$app/environment'; 3 | 4 | 5 | /** 6 | * Class responsible for store/cache data in browser 7 | */ 8 | export class cacheSession { 9 | 10 | 11 | public setItem = (key: string, value: string) => { 12 | browser ? sessionStorage.setItem(key, value): null; 13 | } 14 | 15 | /** 16 | * For each element you pass in array, 17 | * it will save in sessionStorage as example: 18 | * key: email, value: ickynho7@outlook.com 19 | * @param formData : request.formData() 20 | * @param arrayOfInputs : [ 'email', 'username' ] 21 | */ 22 | public async saveFormData(formData: any, arrayOfInputs: any) { 23 | arrayOfInputs.forEach( (element: string) => { 24 | const key = element; 25 | const value = String(formData.get(key)) ?? ""; 26 | browser ? sessionStorage.setItem(key, value) : ""; 27 | 28 | 29 | }); 30 | }; 31 | 32 | /** 33 | * You should use this method in +page.svelte to get values stored 34 | * in sessionStorage client-side. 35 | * @param key : You should select which key you want to get values 36 | * @returns value from a key stored in sessionStorage 37 | */ 38 | public getItem = (key: string) => browser ? sessionStorage.getItem(key) ?? '' : null; 39 | 40 | } 41 | 42 | export default new cacheSession(); 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/lib/classes/enhancedFunc.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atrikx/sveltekit-auth-template/f4f7cb2d04e9c773f6e4e23f41af032f9e075f7e/src/lib/classes/enhancedFunc.ts -------------------------------------------------------------------------------- /src/lib/classes/fetchHandler.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | 3 | 4 | /** 5 | * This is function is closed for modifications. 6 | * The second principle of SOLID: read more on https://en.wikipedia.org/wiki/SOLID 7 | * Changes are made on .env file 8 | */ 9 | 10 | 11 | /** 12 | * This class refeers to your "custom" API. 13 | * 14 | */ 15 | 16 | 17 | export class Fetch { 18 | 19 | private url: string = import.meta.env.VITE_MYAPI; 20 | 21 | async headers (token?:string) { 22 | return { 23 | 'Authorization': "Bearer " + token, 24 | 'Content-Type': "application/json", 25 | 'accept': "application/json" 26 | } 27 | } 28 | 29 | // function method post 30 | async post(endpoint: string, data: any, token?: string,) { 31 | 32 | const headers = await this.headers(token); 33 | const request = await fetch(endpoint, { 34 | method: 'POST', 35 | headers: headers, 36 | body: JSON.stringify(data) 37 | }); 38 | const response = await request; 39 | return response; 40 | } 41 | 42 | // function method get 43 | async get(endpoint: string, token: string) { 44 | 45 | const request = await fetch(endpoint, { 46 | method: 'GET', 47 | headers: { 48 | 'Authorization': "Bearer " + token, 49 | "Content-Type": "application/json" 50 | } 51 | }); 52 | const response = await request; 53 | return response; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/classes/formHandler.ts: -------------------------------------------------------------------------------- 1 | import { applyAction } from "$app/forms"; 2 | 3 | /** 4 | * Class responsible to handler with forms. 5 | */ 6 | export class formHandler { 7 | formObject 8 | 9 | constructor ( formInputsObjects: { name: string }[] ) { 10 | this.formObject = formInputsObjects; 11 | 12 | } 13 | 14 | public enhanceFunction = ( { form, data, cancel }: ActionParams, 15 | showEmptyFieldsTrue: VoidFunction, 16 | showEmptyFieldsFalse: VoidFunction, 17 | ) => { 18 | const formActions = { form, data, cancel }; 19 | 20 | // Validation EmptyFields 21 | const findEmptyFields = this.checkEmptyFieldsBeforeSend(formActions); 22 | findEmptyFields ? showEmptyFieldsTrue() : showEmptyFieldsFalse() ; 23 | 24 | // TODO Validation findPasswordMatch 25 | // TODO Validation findInvalidCharactersInLogin? 26 | return async ({ result }: any) => (result.type === 'redirect') ? applyAction(result) : null; 27 | } 28 | 29 | // 30 | /** 31 | * This method function's is to handle a validation for enhance in forms, 32 | * that was built to work within FormActions Feature; 33 | * It is supposed to run inside a enhance function. 34 | * Check on: https://kit.svelte.dev/docs/form-actions 35 | * 36 | * @param param0: pass { form, data, cancel } 37 | * @param formInputsObjects: Which input fields you want to check if its empty 38 | * @returns true if foundEmptyValue false if not 39 | */ 40 | 41 | // TODO converts to private method 42 | public checkEmptyFieldsBeforeSend = ( { form, data, cancel }: ActionParams ) => { 43 | 44 | let foundEmptyValue = false; 45 | 46 | this.formObject.forEach( (element) => { 47 | if (data.get(element.name) === '') { 48 | cancel(); 49 | foundEmptyValue = true; 50 | } 51 | }); 52 | 53 | return foundEmptyValue ? true : false; 54 | }} 55 | -------------------------------------------------------------------------------- /src/lib/components/Button.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | 32 | 50 | -------------------------------------------------------------------------------- /src/lib/components/Card.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 |

{title}

8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /src/lib/components/CenterLayout.svelte: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 |
5 | 6 | 7 | -------------------------------------------------------------------------------- /src/lib/components/ErrorParagraph.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

{msg}

6 | 7 | -------------------------------------------------------------------------------- /src/lib/components/Form.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
9 | 10 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /src/lib/components/FormCustomEnhanced.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
9 | 10 | 11 | 12 | 20 | -------------------------------------------------------------------------------- /src/lib/components/FormEnhanced.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
11 | 12 | 13 | 14 | 22 | -------------------------------------------------------------------------------- /src/lib/components/InputText.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | 18 |
19 | 20 | 45 | -------------------------------------------------------------------------------- /src/lib/components/theme.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains all colors from components. 3 | */ 4 | 5 | // Button 6 | export const themeButtonInnerTextColor = '#f6f7f8'; 7 | export const themeButtonBoxShadow = '#132b50'; 8 | export const themeBgButtonHover = '#4D4D4D'; 9 | export const themeBgButton = '#1D3153'; 10 | 11 | // Form and other components coming soon... 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/configs.ts: -------------------------------------------------------------------------------- 1 | // Svelte forms 2 | export const formObjectSignUp: InputText[] = [ 3 | {name: 'username', placeholder: 'Username', type: "text"}, 4 | {name: 'email', placeholder: 'Email', type: "text"}, 5 | {name: 'password', placeholder: 'Password', type: "password"} 6 | ] 7 | export const formObjectSignIn: InputText[] = [ 8 | {name: 'email', placeholder: 'Email', type: "text"}, 9 | {name: 'password', placeholder: 'Password', type: "password"} 10 | ] 11 | export const buttonsWidth = "50%" 12 | export const inputsWidth = "60%" 13 | 14 | 15 | // Svelte endpoints 16 | export const urlMyAccount = '/account' 17 | export const urlSignUp = '/signup' 18 | export const urlSignIn = '/signin' 19 | 20 | 21 | // API endpoints 22 | export const apiUrl = 'http://127.0.0.1:8000' 23 | export const apiNewAccount = apiUrl + '/criar-conta' 24 | export const apiMyAccount = apiUrl + '/minha-conta' 25 | export const apiLogin = apiUrl + '/login' 26 | 27 | -------------------------------------------------------------------------------- /src/lib/types/types.d.ts: -------------------------------------------------------------------------------- 1 | interface ActionParams { 2 | form: HTMLFormElement, 3 | data: FormData, 4 | cancel: VoidFunction 5 | } 6 | 7 | interface InputText { 8 | name: string, 9 | placeholder: string, 10 | type: string 11 | } -------------------------------------------------------------------------------- /src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 |

Oooops...

-------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |

Welcome to Auth Template!

21 | 22 | 23 | -------------------------------------------------------------------------------- /src/routes/account/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { Fetch } from "$lib/classes/fetchHandler"; 2 | import { apiMyAccount, urlSignIn } from "$lib/configs"; 3 | import { redirect } from "@sveltejs/kit"; 4 | 5 | import type { ServerLoadEvent, RequestEvent } from "@sveltejs/kit"; 6 | 7 | /** @type {import('./$types').PageServerLoad} */ 8 | /** 9 | * This will load all informations from user... 10 | * 11 | */ 12 | export async function load({ locals, cookies }: ServerLoadEvent) { 13 | 14 | // Define Constants 15 | const url: string = apiMyAccount; 16 | const token = locals.user?.token; 17 | 18 | // If user isn't logged in, redirect. 19 | if (!token) {throw redirect(301, urlSignIn);} 20 | 21 | // TODO add security layer (if token !== type token throw redirect) 22 | 23 | // Fetch 24 | const response = await new Fetch().get(url, token).catch(() => {throw redirect(301, "/APIisOffline")}); 25 | if (!response?.ok) { 26 | cookies.set('token', ''); 27 | throw redirect(301, urlSignIn); 28 | } 29 | 30 | const dataReceived = await response.json(); 31 | 32 | return { 33 | username: dataReceived.username, 34 | email: dataReceived.email 35 | }; 36 | } 37 | 38 | /** @type {import('./$types').Actions} */ 39 | export const actions = { 40 | logout: async ({ cookies }: RequestEvent) => { 41 | 42 | // reset token 43 | cookies.set('token', ''); 44 | 45 | // loggedOut 46 | throw redirect(301, urlSignIn) 47 | 48 | } 49 | }; -------------------------------------------------------------------------------- /src/routes/account/+page.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | My Account 21 | 22 | 23 | 24 | 25 | 26 |

Username: {username.replace(/"/g, "")}

27 |

Email: {email.replace(/"/g, "")}

28 | 46 | 47 |
48 |
49 |
-------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atrikx/sveltekit-auth-template/f4f7cb2d04e9c773f6e4e23f41af032f9e075f7e/static/favicon.png -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import preprocess from 'svelte-preprocess'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: preprocess(), 9 | 10 | kit: { 11 | adapter: adapter() 12 | } 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /tsconfig.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 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 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import type { UserConfig } from 'vite'; 3 | 4 | const config: UserConfig = { 5 | plugins: [sveltekit()] 6 | }; 7 | 8 | export default config; 9 | --------------------------------------------------------------------------------