├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── .zshrc
├── README.md
├── demo.env
├── jsconfig.json
├── package.json
├── secrets
└── .gitignore
├── src
├── app.css
├── app.html
├── hooks.server.js
├── lib
│ ├── Home.svelte
│ ├── Input.svelte
│ ├── Loader.svelte
│ ├── Message.svelte
│ ├── Nav.svelte
│ ├── NetworkError.svelte
│ ├── Noti.svelte
│ ├── Tabs.svelte
│ ├── images
│ │ ├── Gighub.svelte
│ │ └── Logo.svelte
│ ├── paginate
│ │ ├── PaginationNav.svelte
│ │ ├── generateNavigationOptions.js
│ │ ├── index.js
│ │ ├── paginate.js
│ │ └── symbolTypes.js
│ ├── themes
│ │ ├── DarkModeToggle.svelte
│ │ └── themeStore.js
│ └── utils
│ │ ├── api.js
│ │ ├── auth.js
│ │ ├── browser.js
│ │ ├── timeAgo.js
│ │ ├── username.js
│ │ ├── validation.js
│ │ └── variables.js
└── routes
│ ├── +error.svelte
│ ├── +layout.server.js
│ ├── +layout.svelte
│ ├── +page.svelte
│ ├── admin
│ ├── +page.js
│ ├── +page.svelte
│ ├── user
│ │ └── [id]
│ │ │ ├── +page.js
│ │ │ └── +page.svelte
│ └── users
│ │ └── [p]
│ │ ├── +page.js
│ │ └── +page.svelte
│ ├── forgot
│ └── +page.svelte
│ ├── login
│ ├── +page.js
│ └── +page.svelte
│ ├── register
│ ├── +page.js
│ └── +page.svelte
│ └── user
│ ├── activation
│ └── [token]
│ │ └── +page.svelte
│ ├── profile
│ ├── +page.js
│ └── +page.svelte
│ └── reset
│ └── [token]
│ └── +page.svelte
├── static
├── favicon.ico
├── img
│ ├── 1.webp
│ ├── 404.gif
│ ├── 502.gif
│ └── github.svg
└── robots.txt
├── svelte.config.js
└── vite.config.js
/.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: 2019
9 | },
10 | env: {
11 | browser: true,
12 | es2017: true,
13 | node: true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .svelte
4 | .env
5 | .idea
6 | .svelte-kit
7 | package-lock.json
8 | .build
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .svelte/**
2 | static/**
3 | build/**
4 | node_modules/**
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "semi": false,
6 | "printWidth": 100
7 | }
8 |
--------------------------------------------------------------------------------
/.zshrc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mylastore/svelte-kit/bf1c6b13fa78404d786c71c4d6d7b21b9d2fcf02/.zshrc
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sveltekit Template
2 |
3 | Sveltekit template - inspired by [Hackathon Starter](https://hackathon-starter.walcony.com)
4 |
5 | Koa API with authentication, refresh token, password reset - repo can be found here [koa-api](https://github.com/mylastore/koa-blog-api)
6 |
7 | ## DEMO
8 | [Demo App](https://sveltekit.mylastore.com/)
9 |
10 | ## Included
11 |
12 | - Bootstrap 5 CSS (Bootstrap 5 is now Modular)
13 | - Formatting with ESLint and Prettier
14 | - User authentication with JWT token (register users must confirm email to create an account)
15 | - User profile page with [gravatar](https://en.gravatar.com/) if available else displays a default image
16 | - User forgot password
17 | - User roles (customer, admin)
18 | - Admin panel section displaying all register users and stats
19 | - Pagination inspired by [svelte-paginate](https://github.com/TahaSh/svelte-paginate#readme)
20 |
21 | ## Getting started
22 | - Rename the demo.env to .env enter your info
23 | - Create certs directory inside the secrets directory and generate local certs inside. Secure cookie are used on local development (to simulate production issue).
24 |
25 | git clone https://github.com/mylastore/svelte-kit
26 |
27 | npm install && npm start
28 |
29 | Now head over to your favorite browser and open up `localhost:3001` and you are ready to go.
30 |
31 | ## IMPORTANT! Start the [API](https://github.com/mylastore/koa-blog-api) repository and follow the instructions on how to seed the sample users data
32 |
33 | Login as ADMIN me@me.com and Password1
34 |
35 | Login as customer me1@me.com Password1
36 |
37 | ## License
38 |
39 | [MIT](http://opensource.org/licenses/MIT)
40 |
--------------------------------------------------------------------------------
/demo.env:
--------------------------------------------------------------------------------
1 |
2 | VITE_NODE_ENV=development
3 | VITE_APP_NAME="SVELTE KIT"
4 | VITE_API_DEVELOPMENT_PATH="http://localhost:8001/api"
5 | VITE_API_PRODUCTION_PATH="https://production.com/api"
6 | VITE_API_ANALYTICS="UA-12345678-9"
7 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": ".",
5 | "allowJs": true,
6 | "checkJs": true,
7 | "esModuleInterop": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "resolveJsonModule": true,
10 | "skipLibCheck": true,
11 | "sourceMap": true,
12 | "strict": true,
13 | "paths": {
14 | "$lib": [
15 | "src/lib"
16 | ],
17 | "$lib/*": [
18 | "src/lib/*"
19 | ]
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sveltekit",
3 | "version": "0.1.0",
4 | "scripts": {
5 | "start": "vite dev --port 3001",
6 | "build": "NODE_ENV=production vite build",
7 | "live": "NODE_ENV=production PORT=3001 node build",
8 | "test": "NODE_ENV=production PORT=3001 node build",
9 | "preview": "vite preview --port 3001",
10 | "lint": "prettier --check . && eslint --ignore-path .gitignore .",
11 | "format": "prettier --write ."
12 | },
13 | "dependencies": {
14 | "bootstrap": "^5.1.3",
15 | "cookie": "^0.4.2",
16 | "jwt-decode": "^3.1.2",
17 | "luxon": "^1.28.0",
18 | "svelte-feather-icons": "^3.6.0"
19 | },
20 | "type": "module",
21 | "devDependencies": {
22 | "@sveltejs/adapter-node": "^1.2.3",
23 | "@sveltejs/kit": "^1.12.0",
24 | "eslint": "^7.32.0",
25 | "eslint-config-prettier": "^8.5.0",
26 | "eslint-plugin-svelte3": "^3.4.1",
27 | "fetch-ponyfill": "^7.1.0",
28 | "prettier": "~2.2.1",
29 | "prettier-plugin-svelte": "^2.2.0",
30 | "svelte": "^3.50.0",
31 | "svelte-check": "^2.7.1",
32 | "vite": "^4.2.0"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/secrets/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | /*
4 | # Except this file
5 | !.gitignore
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | overflow: scroll;
3 | }
4 |
5 | :root {
6 | --bs-danger: #e92228;
7 | --bs-warning: gold;
8 | --bs-success: green;
9 | }
10 |
11 | .site {
12 | display: flex;
13 | min-height: 98vh;
14 | flex-direction: column;
15 | }
16 |
17 | .site-content {
18 | flex: 1;
19 | }
20 |
21 | .btn-danger, .alert-danger {
22 | background-color: var(--bs-danger);
23 | }
24 |
25 | .btn-warning, .alert-warning {
26 | background-color: var(--bs-warning);
27 | }
28 |
29 | .btn-success, .alert-success {
30 | background-color: var(--bs-success);
31 | }
32 |
33 | .alert-danger {
34 | color: white;
35 | border-color: var(--bs-danger);
36 | }
37 |
38 | .alert-warning {
39 | color: #333;
40 | border-color: var(--bs-warning)
41 | }
42 |
43 | .alert-success {
44 | color: white;
45 | border-color: var(--bs-success)
46 | }
47 |
48 | @media all {
49 | body.dark {
50 | background: #2B2A32;
51 | color: white;
52 | }
53 |
54 | body.dark .card {
55 | background: #42414C;
56 | border-color: #4d4d4d;
57 | }
58 |
59 | body.dark .card-body {
60 | color: white;
61 | }
62 |
63 | body.dark .bg-white,
64 | body.dark .bg-light {
65 | background: #212020 !important;
66 | }
67 |
68 | body.dark .card-footer a,
69 | body.dark .text-black-50 {
70 | color: gray !important;
71 | }
72 |
73 | body.dark .page-link {
74 | background: #42414c;
75 | border-color: #42414c;
76 | }
77 |
78 | body.dark .disabled > .page-link {
79 | background: #565656;
80 | border-color: #565656;
81 | color: #444;
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/hooks.server.js:
--------------------------------------------------------------------------------
1 | import * as cookie from 'cookie'
2 |
3 | export async function handle({ event, resolve }) {
4 | const cookies = cookie.parse(event.request.headers.get('cookie') || '')
5 | event.locals.token = cookies.token ? cookies.token : ''
6 | event.locals.user = cookies.user ? JSON.parse(cookies.user) : null
7 | return await resolve(event)
8 | }
9 |
--------------------------------------------------------------------------------
/src/lib/Home.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
Simple starter template to build svelte apps
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |

21 |
22 |
Power by Svelte
23 |
SvelteKit is an application framework powered by Svelte — build bigger apps with a
24 | smaller footprint and no headaches
25 |
Learn more
26 |
27 |
28 |
29 |
30 |
31 |

32 |
33 |
SvelteKit
34 |
A framework for building web applications, with a simple development experience and
35 | flexible filesystem-based routing
36 |
Go somewhere
37 |
38 |
39 |
40 |
41 |
42 |

43 |
44 |
Build fast
45 |
Hit the ground running with advanced routing, server-side rendering, code-splitting,
46 | offline support and more
47 |
Read docs
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
82 |
--------------------------------------------------------------------------------
/src/lib/Input.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 | {#if controlType === 'textarea'}
20 |
28 | {#if help}
29 |
{help}
30 | {/if}
31 | {/if}
32 | {#if controlType === 'input'}
33 |
(touched = true)}
41 | />
42 | {#if help}
43 |
{help}
44 | {/if}
45 | {/if}
46 | {#if validityMessage && !valid && touched}
47 |
{validityMessage}
48 | {/if}
49 |
50 |
51 |
65 |
--------------------------------------------------------------------------------
/src/lib/Loader.svelte:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Loading...
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/lib/Message.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | {message}
12 | {#if closeMessage}
13 |
16 | {/if}
17 |
18 |
19 |
46 |
--------------------------------------------------------------------------------
/src/lib/Nav.svelte:
--------------------------------------------------------------------------------
1 |
37 |
38 |
122 |
123 |
209 |
--------------------------------------------------------------------------------
/src/lib/NetworkError.svelte:
--------------------------------------------------------------------------------
1 |
2 | Network Error!
3 |
4 |
5 |
6 |
7 |
8 |
Oops!
9 |
10 | We are working to resolve this issue.
11 |
12 |

13 |
14 |
15 |
16 |
26 |
--------------------------------------------------------------------------------
/src/lib/Noti.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
34 |
35 | {#if $notifications[0]}
36 |
37 |
38 | {$notifications[0].message}
39 |
40 |
41 | {/if}
42 |
43 |
--------------------------------------------------------------------------------
/src/lib/Tabs.svelte:
--------------------------------------------------------------------------------
1 |
4 |
5 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/src/lib/images/Gighub.svelte:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/lib/images/Logo.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
--------------------------------------------------------------------------------
/src/lib/paginate/PaginationNav.svelte:
--------------------------------------------------------------------------------
1 |
34 |
35 |
100 |
101 |
118 |
--------------------------------------------------------------------------------
/src/lib/paginate/generateNavigationOptions.js:
--------------------------------------------------------------------------------
1 | import { PREVIOUS_PAGE, NEXT_PAGE, ELLIPSIS } from './symbolTypes'
2 |
3 | export default function ({
4 | totalItems,
5 | pageSize,
6 | currentPage,
7 | limit = null,
8 | showStepOptions = false
9 | }) {
10 | const totalPages = Math.ceil(totalItems / pageSize)
11 | const limitThreshold = getLimitThreshold({ limit })
12 | const limited = limit && totalPages > limitThreshold
13 | let options = limited
14 | ? generateLimitedOptions({ totalPages, limit, currentPage })
15 | : generateUnlimitedOptions({ totalPages })
16 | return showStepOptions ? addStepOptions({ options, currentPage, totalPages }) : options
17 | }
18 |
19 | function generateUnlimitedOptions({ totalPages }) {
20 | return new Array(totalPages).fill(null).map((value, index) => ({
21 | type: 'number',
22 | value: index + 1
23 | }))
24 | }
25 |
26 | function generateLimitedOptions({ totalPages, limit, currentPage }) {
27 | const boundarySize = limit * 2 + 2
28 | const firstBoundary = 1 + boundarySize
29 | const lastBoundary = totalPages - boundarySize
30 | const totalShownPages = firstBoundary + 2
31 |
32 | if (currentPage <= firstBoundary - limit) {
33 | return Array(totalShownPages)
34 | .fill(null)
35 | .map((value, index) => {
36 | if (index === totalShownPages - 1) {
37 | return {
38 | type: 'number',
39 | value: totalPages
40 | }
41 | } else if (index === totalShownPages - 2) {
42 | return {
43 | type: 'symbol',
44 | symbol: ELLIPSIS,
45 | value: firstBoundary + 1
46 | }
47 | }
48 | return {
49 | type: 'number',
50 | value: index + 1
51 | }
52 | })
53 | } else if (currentPage >= lastBoundary + limit) {
54 | return Array(totalShownPages)
55 | .fill(null)
56 | .map((value, index) => {
57 | if (index === 0) {
58 | return {
59 | type: 'number',
60 | value: 1
61 | }
62 | } else if (index === 1) {
63 | return {
64 | type: 'symbol',
65 | symbol: ELLIPSIS,
66 | value: lastBoundary - 1
67 | }
68 | }
69 | return {
70 | type: 'number',
71 | value: lastBoundary + index - 2
72 | }
73 | })
74 | } else if (currentPage >= firstBoundary - limit && currentPage <= lastBoundary + limit) {
75 | return Array(totalShownPages)
76 | .fill(null)
77 | .map((value, index) => {
78 | if (index === 0) {
79 | return {
80 | type: 'number',
81 | value: 1
82 | }
83 | } else if (index === 1) {
84 | return {
85 | type: 'symbol',
86 | symbol: ELLIPSIS,
87 | value: currentPage - limit + (index - 2)
88 | }
89 | } else if (index === totalShownPages - 1) {
90 | return {
91 | type: 'number',
92 | value: totalPages
93 | }
94 | } else if (index === totalShownPages - 2) {
95 | return {
96 | type: 'symbol',
97 | symbol: ELLIPSIS,
98 | value: currentPage + limit + 1
99 | }
100 | }
101 | return {
102 | type: 'number',
103 | value: currentPage - limit + (index - 2)
104 | }
105 | })
106 | }
107 | }
108 |
109 | function addStepOptions({ options, currentPage, totalPages }) {
110 | return [
111 | {
112 | type: 'symbol',
113 | symbol: PREVIOUS_PAGE,
114 | value: currentPage <= 1 ? 1 : currentPage - 1
115 | },
116 | ...options,
117 | {
118 | type: 'symbol',
119 | symbol: NEXT_PAGE,
120 | value: currentPage >= totalPages ? totalPages : currentPage + 1
121 | }
122 | ]
123 | }
124 |
125 | function getLimitThreshold({ limit }) {
126 | const maximumUnlimitedPages = 3 // This means we cannot limit 3 pages or less
127 | const numberOfBoundaryPages = 2 // The first and last pages are always shown
128 | return limit * 2 + maximumUnlimitedPages + numberOfBoundaryPages
129 | }
130 |
--------------------------------------------------------------------------------
/src/lib/paginate/index.js:
--------------------------------------------------------------------------------
1 | export { default as paginate } from './paginate.js'
2 | export { default as PaginationNav } from './PaginationNav.svelte'
3 |
--------------------------------------------------------------------------------
/src/lib/paginate/paginate.js:
--------------------------------------------------------------------------------
1 | export default function ({ items, pageSize, currentPage }) {
2 | return items.slice((currentPage - 1) * pageSize, (currentPage - 1) * pageSize + pageSize)
3 | }
4 |
--------------------------------------------------------------------------------
/src/lib/paginate/symbolTypes.js:
--------------------------------------------------------------------------------
1 | export const PREVIOUS_PAGE = 'PREVIOUS_PAGE'
2 | export const NEXT_PAGE = 'NEXT_PAGE'
3 | export const ELLIPSIS = 'ELLIPSIS'
4 |
--------------------------------------------------------------------------------
/src/lib/themes/DarkModeToggle.svelte:
--------------------------------------------------------------------------------
1 |
40 |
41 |
47 |
48 |
143 |
--------------------------------------------------------------------------------
/src/lib/themes/themeStore.js:
--------------------------------------------------------------------------------
1 | import {writable} from "svelte/store";
2 |
3 | export const theme = writable('dark' )
4 |
--------------------------------------------------------------------------------
/src/lib/utils/api.js:
--------------------------------------------------------------------------------
1 | import fetchPonyfill from "fetch-ponyfill"
2 | import {variables} from "$lib/utils/variables.js"
3 | import {logout} from "$lib/utils/auth.js"
4 | import {notifications} from "$lib/Noti.svelte"
5 | import {browser} from "$lib/utils/browser.js"
6 |
7 | const {fetch} = fetchPonyfill()
8 | const apiPath = variables.env === 'development' ? variables.apiDevPath : variables.apiLivePath
9 |
10 | export const api = async (method, path, data) => {
11 | const gotData = typeof data === 'object' && !Array.isArray(data) && data !== null
12 | const formData = data instanceof FormData
13 | try{
14 | const response = (await fetch(`${apiPath}/${path}`, {
15 | method: method,
16 | 'credentials': 'include',
17 | headers: {
18 | Accept: 'application/json',
19 | 'Content-Type': 'application/json'
20 | },
21 | // if FormData we set body: data else JSON stringify(data) & we don't set body when no data
22 | ...(gotData ? { body: !formData ? JSON.stringify(data) : data } : null)
23 | }))
24 | const res = await response.json()
25 | // logout user when no token is found on the BE
26 | if (res.status === 440) return await logout()
27 | // display all error messages
28 | if (res.status >= 400) {
29 | return browser && notifications.push(res.message)
30 | } else {
31 | return res
32 | }
33 |
34 | }catch (err){
35 | if (err && err instanceof Error) {
36 | // server error
37 | return browser && notifications.push('Something went wrong. Please try later.')
38 | } else {
39 | throw err
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/lib/utils/auth.js:
--------------------------------------------------------------------------------
1 | import {browser} from "$app/environment"
2 |
3 | export const logout = async () => {
4 | if (browser) {
5 | await localStorage.removeItem('username')
6 | return window.location.replace('/')
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/lib/utils/browser.js:
--------------------------------------------------------------------------------
1 |
2 | export const browser = (typeof window !== 'undefined' && typeof document !== 'undefined')
3 |
--------------------------------------------------------------------------------
/src/lib/utils/timeAgo.js:
--------------------------------------------------------------------------------
1 | import { DateTime } from 'luxon'
2 |
3 | const units = ['year', 'month', 'week', 'day', 'hour', 'minute', 'second']
4 |
5 | const timeAgo = (date) => {
6 | if(!date) return 'invalid date'
7 | let dateTime = DateTime.fromISO(date)
8 | const diff = dateTime.diffNow().shiftTo(...units)
9 | const unit = units.find((unit) => diff.get(unit) !== 0) || 'second'
10 |
11 | const relativeFormatter = new Intl.RelativeTimeFormat('en', {
12 | numeric: 'auto'
13 | })
14 | return relativeFormatter.format(Math.trunc(diff.as(unit)), unit)
15 | }
16 |
17 | export default timeAgo
18 |
--------------------------------------------------------------------------------
/src/lib/utils/username.js:
--------------------------------------------------------------------------------
1 | import {writable} from "svelte/store"
2 | import {browser} from "$app/environment"
3 |
4 | const userName = browser && localStorage.getItem('username')
5 |
6 | export const username = writable( (browser && userName && (JSON.parse(userName)) || '') )
7 | username.subscribe((v)=> browser && ( localStorage.username = JSON.stringify(v)) )
8 |
--------------------------------------------------------------------------------
/src/lib/utils/validation.js:
--------------------------------------------------------------------------------
1 | export function isRequire (str) {
2 | if (str) {
3 | return typeof str === 'string' && str !== '';
4 | }
5 | }
6 |
7 | // 8 characters or more, one capital letter and one special character
8 | export function isPassword (val) {
9 | return new RegExp('^(?=.*[a-z])(?=.*[A-Z])(.{8,50})$').test(val)
10 | }
11 |
12 | export function isEmail (val) {
13 | return new RegExp('^\\w+([-+.\']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$').test(val)
14 | }
15 |
16 | export function isUrl (val) {
17 | return new RegExp(
18 | /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/
19 | ).test(val)
20 | }
21 |
--------------------------------------------------------------------------------
/src/lib/utils/variables.js:
--------------------------------------------------------------------------------
1 | export const variables = {
2 | env: import.meta.env.VITE_NODE_ENV,
3 | apiDevPath: import.meta.env.VITE_API_DEVELOPMENT_PATH,
4 | apiLivePath: import.meta.env.VITE_API_PRODUCTION_PATH,
5 | appName: import.meta.env.VITE_APP_NAME,
6 | currencyLocation: {symbol: "$", code: 'USA'},
7 | analyticsID: import.meta.env.VITE_APP_ANALYTICS
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/+error.svelte:
--------------------------------------------------------------------------------
1 |
2 | Not Found!
3 |
4 |
5 |
6 |
7 |
8 |

9 |
10 | Not Found!
11 |
12 |
13 |
14 |
15 |
24 |
--------------------------------------------------------------------------------
/src/routes/+layout.server.js:
--------------------------------------------------------------------------------
1 | export const load = ({locals}) => {
2 | return {
3 | token: locals.token,
4 | user: locals.user
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 | {#if $theme && (typeof window != 'undefined')}
14 |
27 | {/if}
28 |
29 |
35 |
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 | {variables.appName} | Marketplace for the new generation
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/routes/admin/+page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit'
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (!session.user || session.user && session.user.role !== 'admin') {
6 | throw redirect(302, '/')
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/admin/+page.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 | Admin Panel
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
{userCount}
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/routes/admin/user/[id]/+page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit';
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (!session.user || session.user && session.user.role !== 'admin') {
6 | throw redirect(302, '/')
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/admin/user/[id]/+page.svelte:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 | Admin User Profile
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |

57 |
58 |
59 | {#if name}
Name: {name}
{/if}
60 | {#if userGender}
Gender: {userGender}
{/if}
61 | {#if userLocation}
Location: {userLocation}
{/if}
62 | {#if userWebsite}
Website: {userWebsite}
{/if}
63 | {#if userAbout}
About: {userAbout}
{/if}
64 |
Role: {userRole}
65 |
Member Since: {memberSince}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
84 |
--------------------------------------------------------------------------------
/src/routes/admin/users/[p]/+page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit';
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (!session.user || session.user && session.user.role !== 'admin') {
6 | throw redirect(302, '/')
7 | }
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/src/routes/admin/users/[p]/+page.svelte:
--------------------------------------------------------------------------------
1 |
57 |
58 |
59 | Admin Panel
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | Role
74 | |
75 |
76 | Image
77 | |
78 |
79 | Name
80 | |
81 |
82 | Gender
83 | |
84 |
85 | Website
86 | |
87 |
88 | Location
89 | |
90 |
91 | Member Since
92 | |
93 |
94 | Action
95 | |
96 |
97 |
98 |
99 | {#each users as user, i}
100 |
101 | {user.role} |
102 |
103 | {#if user.avatar}
104 |
105 | {:else}
106 |
107 | {/if}
108 | |
109 | {user.name} |
110 | {user.gender} |
111 | {user.website} |
112 | {user.location} |
113 | {timeAgo(user.createdAt)} |
114 |
115 |
116 |
117 |
133 |
134 |
135 | |
136 |
137 | {/each}
138 |
139 |
140 |
141 |
handleSetPage(e)}
148 | />
149 |
150 |
151 |
152 |
153 |
154 |
183 |
--------------------------------------------------------------------------------
/src/routes/forgot/+page.svelte:
--------------------------------------------------------------------------------
1 |
27 |
28 |
29 | Forgot Password
30 |
31 |
32 |
33 |
34 |
61 |
62 |
63 |
68 |
--------------------------------------------------------------------------------
/src/routes/login/+page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit';
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (session.user) {
6 | throw redirect(302, '/');
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/login/+page.svelte:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 |
45 | Login Form
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Sing In
55 |
We are glad you are here.
56 |
57 | (email = event.target.value)}
65 | />
66 | (password = event.target.value)}
75 | />
76 |
77 |
82 |
83 |
90 |
91 |
92 |
95 |
96 |
97 |
98 |
99 |
100 |
103 |
104 |
Admin: me1@me.com password1
105 |
User: me2@me.com password1
106 |
107 |
108 |
109 |
110 |
111 |
112 |
122 |
--------------------------------------------------------------------------------
/src/routes/register/+page.js:
--------------------------------------------------------------------------------
1 | import { redirect } from '@sveltejs/kit';
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (session.user) {
6 | throw redirect(302, '/');
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/register/+page.svelte:
--------------------------------------------------------------------------------
1 |
41 |
42 |
43 |
44 |
45 | Register Form
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
Sing Up
56 |
We only offer our services in the United States.
57 |
58 |
111 |
112 |
115 |
116 |
117 |
118 |
119 |
120 |
128 |
--------------------------------------------------------------------------------
/src/routes/user/activation/[token]/+page.svelte:
--------------------------------------------------------------------------------
1 |
28 |
29 |
30 | Account Activation
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
Activate account for {email}
39 |
40 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/routes/user/profile/+page.js:
--------------------------------------------------------------------------------
1 | import {redirect} from '@sveltejs/kit'
2 |
3 | export async function load({parent}) {
4 | const session = await parent()
5 | if (!session.user) {
6 | throw redirect(302, '/')
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/routes/user/profile/+page.svelte:
--------------------------------------------------------------------------------
1 |
108 |
109 |
110 | Profile Page
111 |
112 |
113 |
114 |
115 |
116 | {#if user}
117 |
118 |
119 |
120 |
124 |

130 |
131 | {#if name}
132 |
133 | Name:
134 | {name}
135 |
136 | {/if}
137 |
138 | Email:
139 | {email}
140 |
141 | {#if website}
142 |
143 | Website:
144 | {website}
145 |
146 | {/if}
147 | {#if location}
148 |
149 | Location:
150 | {location}
151 |
152 | {/if}
153 | {#if gender}
154 |
155 | Gender:
156 | {gender}
157 |
158 | {/if}
159 | {#if about}
160 |
161 | About:
162 | {about}
163 |
164 | {/if}
165 |
166 | Role:
167 | {role}
168 |
169 |
170 |
176 |
177 |
178 |
179 |
180 |
249 |
250 |
251 |
281 |
282 |
294 |
295 |
296 |
297 | {/if}
298 |
299 |
300 |
301 |
302 |
307 |
--------------------------------------------------------------------------------
/src/routes/user/reset/[token]/+page.svelte:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 | Password Reset
37 |
38 |
39 |
40 |
41 |
76 |
77 |
--------------------------------------------------------------------------------
/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mylastore/svelte-kit/bf1c6b13fa78404d786c71c4d6d7b21b9d2fcf02/static/favicon.ico
--------------------------------------------------------------------------------
/static/img/1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mylastore/svelte-kit/bf1c6b13fa78404d786c71c4d6d7b21b9d2fcf02/static/img/1.webp
--------------------------------------------------------------------------------
/static/img/404.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mylastore/svelte-kit/bf1c6b13fa78404d786c71c4d6d7b21b9d2fcf02/static/img/404.gif
--------------------------------------------------------------------------------
/static/img/502.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mylastore/svelte-kit/bf1c6b13fa78404d786c71c4d6d7b21b9d2fcf02/static/img/502.gif
--------------------------------------------------------------------------------
/static/img/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-node'
2 |
3 | /** @type {import('@sveltejs/kit').Config} */
4 | const config = {
5 | kit: {
6 | adapter: adapter(),
7 | }
8 | };
9 |
10 | export default config;
11 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from "vite"
2 | import {sveltekit} from '@sveltejs/kit/vite'
3 | import fs from "fs"
4 |
5 | export default defineConfig(({ command }) => {
6 | if (command === 'serve') {
7 | const key = fs.readFileSync('secrets/certs/localhost.key')
8 | const pem = fs.readFileSync('secrets/certs/localhost.crt')
9 |
10 | return {
11 | server: {
12 | https: {
13 | key: key,
14 | cert: pem,
15 | },
16 | host: 'localhost',
17 | port: '3007',
18 | strictPort: true
19 | },
20 | plugins: [sveltekit()]
21 | }
22 | } else {
23 | return {
24 | server: {
25 | host: 'localhost',
26 | port: '3007',
27 | strictPort: true
28 | },
29 | plugins: [sveltekit()]
30 | }
31 | }
32 | })
33 |
--------------------------------------------------------------------------------