Compare commits

...
Sign in to create a new pull request.

74 commits

Author SHA1 Message Date
Stefan Prechtl
809e7ce6d7
Update README.md 2024-10-01 10:00:32 +02:00
Jonas Weissengruber
576e65dca6
Update index.md 2022-11-10 11:11:27 +01:00
Jonas Weissengruber
e0505755da
Update index.md 2022-11-10 11:08:23 +01:00
Jonas Weissengruber
4fe86d33b6
Update index.md 2022-11-10 11:06:15 +01:00
Jonas Weissengruber
3cd93ac7da
Update index.md 2022-11-10 11:03:32 +01:00
Jonas Weissengruber
7c4ed1d2b2
image size in md? 2022-11-10 10:59:49 +01:00
Jonas Weissengruber
67395c9ebf
added text 2022-11-10 10:31:33 +01:00
Jonas Weissengruber
3ef834e103
Delete about_us.md 2022-11-10 10:26:56 +01:00
Jonas Weissengruber
cf9da4c6a0
moved team to index 2022-11-10 10:26:38 +01:00
Stefan Prechtl
58f2a53539
Update index.md 2022-11-10 10:08:58 +01:00
Stefan Prechtl
46e891d429
Delete CNAME 2022-11-03 11:34:53 +01:00
Stefan Prechtl
01d9377238
Update CNAME 2022-11-03 11:34:36 +01:00
Stefan Prechtl
7819b461b8
Create CNAME 2022-11-03 11:34:15 +01:00
Stefan Prechtl
f0e2c9ca94
Update index.md 2022-11-03 11:31:45 +01:00
j-weissen
ad94658aef about us page 2022-11-03 11:28:37 +01:00
Stefan Prechtl
1b52eaac46
Update index.md 2022-11-03 11:28:14 +01:00
Stefan Prechtl
e03b58057e
Update index.md 2022-11-03 11:24:29 +01:00
Stefan Prechtl
1ffd08082b
Update index.md 2022-11-03 11:22:37 +01:00
Stefan Prechtl
942ee3805d
Update index.md 2022-11-03 11:19:38 +01:00
Stefan Prechtl
75034e0602
Update index.md 2022-11-03 11:14:53 +01:00
Stefan Prechtl
5bd335dbd6
Update index.md 2022-11-03 11:12:09 +01:00
Stefan Prechtl
ffce155154
Delete test.md 2022-11-03 11:06:56 +01:00
Stefan Prechtl
245de43c78
Update _config.yml 2022-11-03 11:04:17 +01:00
j-weissen
bb9ee26bd2 Merge remote-tracking branch 'origin/master' 2022-11-03 11:03:13 +01:00
j-weissen
158d49d221 guckst du foto 2022-11-03 11:02:55 +01:00
Stefan Prechtl
fcd2f16401
Delete index.md 2022-11-03 10:54:21 +01:00
Stefan Prechtl
f1331157fd
Rename _config.yml to docs/_config.yml 2022-11-03 10:54:02 +01:00
Stefan Prechtl
f021209531
Create test.md 2022-11-03 10:52:33 +01:00
Stefan Prechtl
d5867c4f0d
Create index.md 2022-11-03 10:51:50 +01:00
Stefan Prechtl
b8c3603cbb
Delete web directory 2022-11-03 10:51:02 +01:00
Stefan Prechtl
dbde003fc2
Update index.md 2022-11-03 10:45:59 +01:00
Stefan Prechtl
239c73fb8e
Update index.md 2022-11-03 10:43:36 +01:00
Stefan Prechtl
9589a21f96
Update README.md 2022-11-03 10:42:48 +01:00
Stefan Prechtl
c1c6804a63
Create index.md 2022-11-03 10:41:35 +01:00
Stefan Prechtl
08166dda95
Create index.md 2022-11-03 10:39:54 +01:00
Stefan Prechtl
2e3af63c72
Update _config.yml 2022-11-03 10:37:20 +01:00
Stefan Prechtl
d438bc4b14
Update _config.yml 2022-11-03 10:36:13 +01:00
Stefan Prechtl
00f74d4228
Create _config.yml 2022-11-03 10:34:15 +01:00
j-weissen
2d06680bab Merge branch 'develop' into editor 2022-10-18 09:42:17 +02:00
j-weissen
39d8a2034f coloscheme fixed 2022-10-18 09:41:58 +02:00
j-weissen
8d63c8fd4e heading stop jumping 2022-10-18 09:36:54 +02:00
dhain
63e37061de Merge remote-tracking branch 'origin/develop' into develop 2022-10-18 09:11:55 +02:00
Jonas Weissengruber
319828b8b1
Update README.md 2022-10-18 09:10:42 +02:00
j-weissen
952917d415 added comments, fixed repo error 2022-10-18 09:00:19 +02:00
dhain
c85fbe915f Merge branch 'develop' into popup
# Conflicts:
#	frontend/svelte/src/routes/+page.svelte
2022-10-18 08:53:06 +02:00
j-weissen
4091f6c8f5 lastViewed bugfix findOne 2022-10-18 08:12:31 +02:00
dhain
e9c21409cd edited comments 2022-10-18 08:05:58 +02:00
j-weissen
7068ae402e editor styling added 2022-10-17 21:27:19 +02:00
dhain
a9bebd789e delete popup now working (1ms sleep) 2022-10-17 08:30:12 +02:00
j-weissen
fa94a99567 editor functionable 2022-10-17 00:32:27 +02:00
j-weissen
180a26344a updated path to StrapiNoteRepo 2022-10-17 00:04:10 +02:00
j-weissen
f7686371c5 Merge branch 'endpoints' into develop 2022-10-17 00:02:39 +02:00
j-weissen
51735f79dc fixed update 2022-10-17 00:01:21 +02:00
sprechtl
53854b23cd Fixed path 2022-10-16 23:56:59 +02:00
sprechtl
fa91d5f670 Merge branch 'user-repo' into develop 2022-10-16 23:44:18 +02:00
sprechtl
ad23d05000 No beauty but worky 2022-10-16 23:42:40 +02:00
j-weissen
154d496208 stores.ts added, StrapiNoteRepository using it, updated editor 2022-10-16 23:22:19 +02:00
dhain
36d373cdc3 added customBootstrap.css to main page 2022-10-16 23:10:38 +02:00
dhain
f266f3579c popup now working
note deletion via popup working
popup is not closing properly (1st close ok, after that not working (bcs 1 bool for all modals))
2022-10-16 23:08:15 +02:00
s-prechtl
6fd7726ae0 UserRepo Working 2022-10-13 08:54:24 +02:00
s-prechtl
bee5b04441 Merge branch 'develop' into user-repo
# Conflicts:
#	frontend/svelte/src/routes/+page.svelte
#	frontend/svelte/src/routes/login/+page.svelte
2022-10-11 13:14:41 +02:00
dhain
cdefc2e993 ? 2022-10-11 11:09:09 +02:00
dhain
7abdbe605b updated some pwa files (not working)
removed unnecessary code in listing
refactored function comments
2022-10-11 10:42:53 +02:00
s-prechtl
2d193969f7 UserRepo fixed oda so 2022-10-11 09:32:14 +02:00
dhain
22396d1a00 Merge branch 'listing' into develop 2022-10-11 08:52:38 +02:00
dhain
031a4e5259 Create new note in listing now working 2022-10-11 08:52:11 +02:00
s-prechtl
03b1695d9a Merge branch 'styling' into develop 2022-10-11 08:08:08 +02:00
s-prechtl
03e75ffe2a Styling 2022-10-11 08:06:37 +02:00
dhain
93f9979259 Merge branch 'develop' into listing 2022-10-10 20:31:02 +02:00
dhain
4125b433e5 PWA a bissi probiert 2022-10-10 20:28:51 +02:00
sprechtl
d66b9272b7 Repo for USER started 2022-10-10 01:21:53 +02:00
sprechtl
07e014cd8f new folder structure 2022-10-10 00:10:11 +02:00
dhain
7a5ba150f7 removed useless junk 2022-10-09 21:19:35 +02:00
dhain
2eeda6fc5c delete note do be working 2022-10-04 10:51:59 +02:00
40 changed files with 686 additions and 292 deletions

View file

@ -1,4 +1,13 @@
# 🚀 Getting started with PomeloNote
### **THIS REPOSITORY HAS DEPENDENCIES WITH SECURITY VULNERABILITIES. YOU MIGHT WANT TO UPDATE PACKAGES BEFORE USE.**
## Setup
- run `npm i`
- get the .env file and save it to the root directory of the project
- set up Strapi
- go to `localhost:1337/admin`
- register an admin user
- go to Settings => Users&Permissions Plugin => Roles => Authenticated => Note => Select all
- Save
### Starting the container with svelte and strapi:
``docker-compose up --build -d``

View file

@ -50,6 +50,9 @@ module.exports = createCoreController(noteUid, ({strapi}) => ({
lastViewed: Date.now()
}
})
entry = await strapi.entityService.findOne(noteUid, noteId, {
populate: ['owners'],
});
return JSON.stringify(entry);
} else {
ctx.response.status = 403;
@ -63,22 +66,26 @@ module.exports = createCoreController(noteUid, ({strapi}) => ({
async update(ctx) {
const noteId = getNoteIdFromUrl(ctx.request.url)
const userId = ctx.state.user.id;
const requestBody = ctx.request.body;
const requestBody = JSON.parse(ctx.request.body);
console.log(JSON.stringify(requestBody, null, 2))
const entry = await strapi.entityService.findOne(noteUid, noteId, {
populate: ['owners'],
});
const authorized = entry.owners.some(owner => owner.id === userId)
let allPreviousOwnersKept = false;
let allPreviousOwnersKept = true;
if (requestBody.data.hasOwnProperty("owners")) {
allPreviousOwnersKept = entry.owners.every(owner => requestBody.data.owners.includes(owner));
}
console.log({
"auth": authorized,
"allprev": allPreviousOwnersKept,
})
if (!authorized) {
ctx.response.status = 403;
} else if (!allPreviousOwnersKept) {
ctx.response.status = 400;
} else {
return super.update(ctx);
}
return await strapi.entityService.update(noteUid, noteId, requestBody);
},
/**
* Creates a new note, automatically sets owners to the user making the request and lastViewed
@ -87,7 +94,8 @@ module.exports = createCoreController(noteUid, ({strapi}) => ({
*/
async create(ctx) {
const userId = ctx.state.user.id;
const requestBody = ctx.request.body;
const requestBody = JSON.parse(ctx.request.body);
console.log(requestBody);
const response = await strapi.entityService.create(noteUid, {
data: {
title: requestBody.data.title,

View file

@ -0,0 +1,73 @@
{
"kind": "collectionType",
"collectionName": "up_users",
"info": {
"name": "user",
"description": "",
"singularName": "user",
"pluralName": "users",
"displayName": "User"
},
"options": {
"draftAndPublish": false,
"timestamps": true
},
"attributes": {
"username": {
"type": "string",
"minLength": 3,
"unique": true,
"configurable": false,
"required": true
},
"email": {
"type": "email",
"minLength": 6,
"configurable": false,
"required": true
},
"provider": {
"type": "string",
"configurable": false
},
"password": {
"type": "password",
"minLength": 6,
"configurable": false,
"private": true
},
"resetPasswordToken": {
"type": "string",
"configurable": false,
"private": true
},
"confirmationToken": {
"type": "string",
"configurable": false,
"private": true
},
"confirmed": {
"type": "boolean",
"default": false,
"configurable": false
},
"blocked": {
"type": "boolean",
"default": false,
"configurable": false
},
"role": {
"type": "relation",
"relation": "manyToOne",
"target": "plugin::users-permissions.role",
"inversedBy": "users",
"configurable": false
},
"notes": {
"type": "relation",
"relation": "manyToMany",
"target": "api::note.note",
"inversedBy": "owners"
}
}
}

3
docs/_config.yml Normal file
View file

@ -0,0 +1,3 @@
remote_theme: pages-themes/leap-day@v0.2.0
plugins:
- jekyll-remote-theme

4
docs/_data/devs.csv Normal file
View file

@ -0,0 +1,4 @@
name,github,image
Jonas Weissengruber,j-weissen,jowei
Stefan Prechtler,s-prechtl,stef
David Hain,d-hain,dave
1 name github image
2 Jonas Weissengruber j-weissen jowei
3 Stefan Prechtler s-prechtl stef
4 David Hain d-hain dave

BIN
docs/images/dave.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
docs/images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/images/editor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
docs/images/jowei.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
docs/images/listing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
docs/images/login.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/images/register.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/images/stef.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

31
docs/index.md Normal file
View file

@ -0,0 +1,31 @@
# Pomelo Note
This is the best open source note app you will ever find.
## Login
When first entering the app, you will need to login. If you haven't got an account you may consider [registering](#register), or just not using the app at all.
<br /><img src="images/login.png" width="50%" style="margin: auto; display: block;" />
## Register
A username, an email and a password that's all you need. If you are missing one of those, just don't use the app at all.
<br /><img src="images/register.png" width="50%" style="margin: auto; display: block;" />
## Editor
You can edit your notes with our minimalistic editor interface.
<br /><img src="images/editor.png" width="50%" style="margin: auto; display: block;" />
## Listing
Here you can see all your notes. Click on them to open the editor or hover and press the red "X" to delete them.
<br /><img src="images/listing.png" width="50%" style="margin: auto; display: block;" />
## Delete
Confirm the deletion.
<br /><img src="images/delete.png" width="50%" style="margin: auto; display: block;" />
# The Team
{% for dev in site.data.devs %}
{{ dev.name }}
[GitHub](https://github.com/{{ dev.github }})
![{{ dev.name }}](images/{{ dev.image }}.jpg)
{% endfor %}

View file

@ -9,7 +9,9 @@
"version": "0.0.1",
"dependencies": {
"bootstrap-icons": "^1.9.1",
"nookies": "^2.5.2"
"nookies": "^2.5.2",
"sv-popup": "^0.2.5",
"webworker": "^0.8.4"
},
"devDependencies": {
"@sveltejs/adapter-auto": "next",
@ -2083,6 +2085,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sv-popup": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/sv-popup/-/sv-popup-0.2.5.tgz",
"integrity": "sha512-JhBu4piXaauamT4vMEcFCydvxJ8e72G7c9F3caZVAPsiFqWPTYT3JDz99FlR+YCnbOp1emsZqqOPVvCwHgURog=="
},
"node_modules/svelte": {
"version": "3.50.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.50.1.tgz",
@ -2358,6 +2365,14 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true
},
"node_modules/webworker": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/webworker/-/webworker-0.8.4.tgz",
"integrity": "sha512-zzsVxtHf+mCn0WuYLarSWfRGmX7JiYKkKvso5FYC7rJ9G8svwGQA5a51Sjq9D2c/rKVU6U/kyBcaI7gUTVlsJg==",
"engines": {
"node": ">=0.4.3"
}
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
@ -3824,6 +3839,11 @@
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"sv-popup": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/sv-popup/-/sv-popup-0.2.5.tgz",
"integrity": "sha512-JhBu4piXaauamT4vMEcFCydvxJ8e72G7c9F3caZVAPsiFqWPTYT3JDz99FlR+YCnbOp1emsZqqOPVvCwHgURog=="
},
"svelte": {
"version": "3.50.1",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-3.50.1.tgz",
@ -3981,6 +4001,11 @@
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true
},
"webworker": {
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/webworker/-/webworker-0.8.4.tgz",
"integrity": "sha512-zzsVxtHf+mCn0WuYLarSWfRGmX7JiYKkKvso5FYC7rJ9G8svwGQA5a51Sjq9D2c/rKVU6U/kyBcaI7gUTVlsJg=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",

View file

@ -22,7 +22,9 @@
},
"type": "module",
"dependencies": {
"bootstrap-icons": "^1.9.1",
"nookies": "^2.5.2",
"bootstrap-icons": "^1.9.1"
"sv-popup": "^0.2.5",
"webworker": "^0.8.4"
}
}

View file

@ -1,12 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-theme="emerald">
<head>
<meta charset="utf-8" />
<link rel="icon" href="../src/resources/images/logo2.svg" />
<meta name="viewport" content="width=device-width" />
<link rel="manifest" href="../static/manifest.json">
%sveltekit.head%
</head>
<body>
<div>%sveltekit.body%</div>
</body>
</html>
</html>

View file

@ -0,0 +1,29 @@
html,
:root {
--main-txt-color: black;
--cross-txt-color: red;
--color-primary: #fff494;
--color-primary-600: #fff17a;
--color-primary-700: #ffec47;
--color-primary-800: #ffe714;
--color-primary-900: #e0c900;
}
.btn-primary {
background-color: var(--color-primary-800) !important;
border: var(--color-primary-800) !important;
color: var(--main-txt-color) !important;
}
.btn-primary:hover {
background-color: var(--color-primary-900) !important;
border: var(--color-primary-900) !important;
color: var(--main-txt-color) !important;
}
.btn-primary:disabled {
background-color: var(--color-primary-700) !important;
border: var(--color-primary-700) !important;
}

View file

@ -1,4 +1,5 @@
import {parseCookies} from "nookies";
import type {Authentication} from "./authentication";
import {createErrorToast} from "./customToasts";
/**
* Capitalises first letter of string.
@ -22,13 +23,14 @@ export async function bearerFetch(endpoint: string, jwt: string, baseUrl: string
});
}
const getJwtCookie = () => {
// @ts-ignore
return parseCookies("/").jwt;
};
/**
* JWT Cookie
*/
export const jwt: string = getJwtCookie();
export function handleErrorsFromResponseWithToast(response: Authentication) {
if (response.error != null) {
if (response.error.details.errors) {
for (const error of response.error.details.errors) {
createErrorToast(error.message);
}
} else {
createErrorToast(response.error.message);
}
}
}

View file

@ -1,4 +1,4 @@
import type {User} from "../../../models/user";
import type {User} from "./user";
/**
* User Login Auth.

View file

@ -1,4 +1,4 @@
import type {Note} from "./types";
import type {Note} from "../../types";
export interface NoteRepository {
getNotes(): Promise<Note[]>;

View file

@ -1,6 +1,8 @@
import type {Note} from "./types";
import type {Note} from "../../types";
import {parseCookies} from "nookies";
import type {NoteRepository} from "./NoteRepository";
import {currentNoteId} from "../../../stores";
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
@ -13,11 +15,21 @@ export class StrapiNoteRepository implements NoteRepository {
return this.instance;
}
private constructor() {}
private constructor() {
currentNoteId.subscribe((value) => (this._currentNoteId = value));
}
private currentNoteId: number | undefined;
private _currentNoteId: unknown;
private static apiNoteEndpoint: string = "http://localhost:1337/api/notes"
public set currentNoteId(value: number | undefined) {
currentNoteId.set(value || -1);
}
public get currentNoteId(): number {
return <number>this._currentNoteId;
}
public async getNotes(): Promise<Note[]>{
const response = await StrapiNoteRepository.fetchStrapiNoteEndpoint("/", 'GET');
return await response.json();
@ -29,7 +41,7 @@ export class StrapiNoteRepository implements NoteRepository {
}
public async getCurrentNote(): Promise<Note | void> {
if (this.currentNoteId === null || this.currentNoteId === undefined) {
if (this._currentNoteId === null || this._currentNoteId === undefined) {
return;
}
return await this.getNote(this.currentNoteId);
@ -53,7 +65,7 @@ export class StrapiNoteRepository implements NoteRepository {
let requestInit: RequestInit = {
method: method,
headers: {
authorization: StrapiNoteRepository.mockedGetAuthorizationHeader()
authorization: StrapiNoteRepository.getAuthorizationHeader()
}
};
if (body) {
@ -66,8 +78,9 @@ export class StrapiNoteRepository implements NoteRepository {
return "bearer TOKEN"
}
private static getAuthorizationHeader() {
const jwt = parseCookies().jwt;
static getAuthorizationHeader() {
// @ts-ignore
const jwt = parseCookies('/').jwt;
return `bearer ${jwt}`
}
}

View file

@ -0,0 +1,93 @@
import type {UserRepository} from "./UserRepository";
import type {Authentication} from "../../authentication";
import type {HttpMethod} from "@sveltejs/kit/types/private";
import {StrapiNoteRepository} from "../note/StrapiNoteRepository";
import {error} from "@sveltejs/kit";
import {User} from "../../user";
export class StrapiUserRepo implements UserRepository {
private static instance: StrapiUserRepo;
public static getInstance(verification: boolean = true): StrapiUserRepo {
if (this.instance === undefined || this.instance === null) {
this.instance = new StrapiUserRepo();
this.instance.verify().then(() => {
if (verification && !this.instance.verified) {
window.location.href = "/login";
}
});
}
return this.instance;
}
private verified: boolean = false;
private constructor() {
}
private static api: string = "http://localhost:1337/api"
private static apiUserEndpoint: string = StrapiUserRepo.api + "/auth/local"
/**
* Verifies the current users jwt.
* @private
*/
private async verify() {
this.verified = false;
let result = await this.getMe();
if (!result.error) {
this.verified = true;
}
}
async getMe(): Promise<Authentication> {
const response = await StrapiUserRepo.fetchStrapi("/me", "GET", null, true, "/users")
return await response.json();
}
async registerUser(email: string, username: string, password: string): Promise<Authentication> {
const payload = {
email: email,
password: password,
username: username
};
const response = await StrapiUserRepo.fetchStrapi("/register", "POST", payload, false);
return await response.json();
}
async loginUser(identifier: string, password: string): Promise<Authentication> {
const payload = {
identifier: identifier,
password: password
};
const response = await StrapiUserRepo.fetchStrapi("/", "POST", payload, false);
return response.json();
}
private static async fetchStrapi(path: string, method: HttpMethod, body: any | null = null, authorization: boolean = true, customPath: any = null): Promise<Response> {
let requestInit: RequestInit = {
method: method,
};
if (authorization && body) {
requestInit["headers"] = {
authorization: StrapiNoteRepository.getAuthorizationHeader() ?? '',
'Accept': 'application/json',
'Content-Type': 'application/json'
}
} else if (authorization) {
requestInit["headers"] = {
authorization: StrapiNoteRepository.getAuthorizationHeader() ?? '',
}
} else if (body) {
requestInit["headers"] = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
if (body) {
requestInit["body"] = JSON.stringify(body)
}
return await fetch((customPath) ? (this.api + customPath + path) : StrapiUserRepo.apiUserEndpoint + path, requestInit);
}
}

View file

@ -0,0 +1,19 @@
import type {Authentication} from "../../authentication";
export interface UserRepository {
/**
* Registers a new user.
* @param email
* @param username
* @param password
*/
registerUser(email: string, username: string, password: string): Promise<Authentication>;
/**
* Gets the current user.
* @param jwt
*/
getMe(jwt: string): Promise<Authentication>;
loginUser(identifier: string, password: string): Promise<Authentication>;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -1,87 +1,48 @@
<script lang="ts">
import type {Note} from "../models/types";
import {onMount} from "svelte";
import {bearerFetch, jwt} from "../models/PomeloUtils";
const endpoint = "/notes";
//
// //:TODO TEMP!!!
// const jsonStr = "[{\"id\":0,\"attributes\":{\"title\":\"mike\",\"content\":\"C Moasta\",\"lastViewed\":\"2022-09-27\"}},{\"id\":1,\"attributes\":{\"title\":\"samc\",\"content\":\"drupal gott\",\"lastViewed\":\"1999-09-09\"}},{\"id\":2,\"attributes\":{\"title\":\"DIO\",\"content\":\"in all CAPS\",\"lastViewed\":\"2022-09-27\"}},{\"id\":3,\"attributes\":{\"title\":\"Eren\",\"content\":\"Jäger\",\"lastViewed\":\"2022-09-27\"}},{\"id\":4,\"attributes\":{\"title\":\"stow\",\"content\":\"Beitn Chef\",\"lastViewed\":\"2022-09-27\"}},{\"id\":5,\"attributes\":{\"title\":\"Wonder of U\",\"content\":\"Umm... so, personally... this is the first time this has happened, so I'm a bit surprised. Only a centimeter away... I mean, I don't think there's ever been someone who's gotten that close to me... without a, you know... calamity occurring. I'm not really... not really sure what happens at one centimeter away... 'cause it's my first time. I don't really understand it either. Seriously. But in the flow of calamity... there's nobody who can attack me. Not a single person. That, I know for sure. Wonder of U.\",\"lastViewed\":\"2022-09-27\"}}]";
// //:TODO TEMP!!!
//
// let notes: Note[] = JSON.parse(jsonStr);
import {StrapiNoteRepository} from "../models/repos/note/StrapiNoteRepository";
import {StrapiUserRepo} from "../models/repos/user/StrapiUserRepo";
import {Content, Modal, Trigger} from "sv-popup";
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
const noteRepo: StrapiNoteRepository = StrapiNoteRepository.getInstance();
let notes: Note[];
onMount(async () => {
const response = await bearerFetch(endpoint, jwt);
let data = await response.json();
notes = data.data;
StrapiUserRepo.getInstance();
notes = await noteRepo.getNotes();
notes.forEach(note => {
note.attributes.lastViewed = new Date(note.attributes.lastViewed);
note.lastViewed = new Date(note.lastViewed);
});
console.log(notes);
});
/**
* Reloads the Notes Listing
* (by doing something very intelligent)
* Adds a Note with the title "New Note" and redirects to editor
*/
function reloadNotesListing() {
notes = notes.filter(i => i === i);
async function addNoteHandler() {
const newTitle = "New Note";
const newNote = await addNote(newTitle);
noteRepo.currentNoteId = newNote.id;
window.location = "/editor";
}
/**
* Gives the user a prompt to input the new title of the note and creates it if the title is valid
*/
function addNotePrompt() {
let newTitle = prompt('Name of the new Note');
console.log(notes)
if (newTitle != null && newTitle != '') {
addNote(newTitle);
console.log(notes)
reloadNotesListing();
}
}
/**
* Adds a new note to the "notes" Array with:
* * the latest id + 1 (or 0 if no notes exist)
* * no content
* * the current date as the "lastViewed" property
* Adds a new note to the Database
* @param title The title of the new Note
* @return The created Note Object
*/
function addNote(title: string) {
const date = new Date();
const newNoteId: number = (notes.length == 0) ? 0 : notes[notes.length - 1].id + 1
const note: Note = {
id: newNoteId,
attributes: {
title: title,
content: "",
lastViewed: date
}
};
notes.push(note);
async function addNote(title: string): Promise<Note> {
return await noteRepo.createNote({title: title,});
}
/**
* Gives the user a prompt if they are sure to delete this note and deletes it if they confirm
* @param note The note to be deleted
*/
function deleteNotePrompt(note) {
const reallyDelete = confirm("Do you really want to delete this Note?");
if (reallyDelete) {
deleteNote(note);
}
}
/**
* Deletes the note from the "notes" Array
* Deletes the note from the "notes" Array and the database
* @param note The note to be deleted
*/
function deleteNote(note) {
notes = notes.filter(i => i !== note);
noteRepo.deleteNote(note.id);
}
/**
@ -101,32 +62,44 @@
}
/**
* Handles a click on a note list element
* Sets the currentNoteId and redirects to the editor
* @param note The note the user clicked on
*/
function onNoteLiClick(note) {
noteRepo.currentNoteId = note.id;
window.location = "/editor";
note.attributes.lastViewed = new Date();
}
/**
* Closes the modal (popup for deletion)
*/
async function closeModal() {
closeModalBool = true;
await sleep(1);
closeModalBool = false;
}
let closeModalBool = false;
</script>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>PomeloNote | Home</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.3.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row">
<!-- Add Note Button -->
<div class="offset-md-7 col-md-1">
<button class="btn btn-primary" on:click={() => addNotePrompt()}>Add Note</button>
<button class="btn btn-primary" on:click={() => addNoteHandler()}>Add Note</button>
</div>
</div>
@ -141,18 +114,42 @@
<div class="row">
<div class="col-10" on:click={() => onNoteLiClick(note)}>
<div>
{note.attributes.title}
{note.title}
</div>
<div class="list-date-text">
{note.attributes.lastViewed.toLocaleDateString()}
{note.lastViewed.toLocaleDateString()}
</div>
</div>
<div class="col-1">
<button style="display: none" id={"noteButton" + note.id}
on:click={() => deleteNotePrompt(note)}>
<i class="bi bi-x"></i>
</button>
<!-- Delete Note Popup -->
<Modal basic small={true} button={false} close={closeModalBool}>
<Content>
<div class="row" style="margin-bottom: 10px;">
<h5>Do you really want to delete the "{note.title}" note?</h5>
</div>
<div class="row">
<div class="col-4 offset-2">
<button class="btn btn-primary"
on:click={() => deleteNote(note)}
on:click={() => closeModal()}>
<b>Yes</b>
</button>
</div>
<div class="col-4 offset-2">
<button class="btn btn-primary" autofocus
on:click={() => closeModal()}>
<b>No</b>
</button>
</div>
</div>
</Content>
<Trigger>
<button style="display: none" id={"noteButton" + note.id}>
<i class="bi bi-x"></i>
</button>
</Trigger>
</Modal>
</div>
</div>
</li>
@ -166,17 +163,11 @@
</html>
<style>
@import "../customBootstrap.css";
html,
:root {
--main-txt-color: black;
--sub-txt-color: gray;
--cross-txt-color: red;
--color-primary: #fff494;
--color-primary-600: #fff17a;
--color-primary-700: #ffec47;
--color-primary-800: #ffe714;
--color-primary-900: #e0c900;
}
body {
@ -230,18 +221,6 @@
color: var(--cross-txt-color);
}
.btn-primary {
background-color: var(--color-primary-800);
border: var(--color-primary-800);
color: var(--main-txt-color);
}
.btn-primary:hover {
background-color: var(--color-primary-900);
border: var(--color-primary-900);
color: var(--main-txt-color);
}
.list-date-text {
color: var(--sub-txt-color);
font-size: 0.8314159265358979323846264338rem;

View file

@ -1,20 +1,6 @@
import {bearerFetch, jwt} from "../models/PomeloUtils";
import {StrapiUserRepo} from "../models/repos/user/StrapiUserRepo";
/** @type {import('./$types').PageLoad} */
export async function load() {
let invalid = !jwt;
if (!invalid) {
const request = await bearerFetch("/users/me", jwt);
const response = await request.json();
invalid = "error" in response;
}
if (invalid) {
if (typeof window !== 'undefined') {
// @ts-ignore
window.location = "/login";
}
}
// StrapiUserRepo.getInstance();
}

View file

@ -1,24 +1,81 @@
<script lang="ts">
import type {Note} from "../../models/types";
import {StrapiNoteRepository} from "../../models/repos/note/StrapiNoteRepository";
import {onMount} from "svelte";
let notes: Note[] = JSON.parse(window.localStorage.getItem("notes"));
const clickedNoteId = window.localStorage.getItem("clickedNoteId");
let noteRepo: StrapiNoteRepository;
let currentNote: Note;
onMount(async () => {
noteRepo = StrapiNoteRepository.getInstance();
try {
currentNote = await noteRepo.getNote(noteRepo.currentNoteId);
} catch {
returnToListing();
}
title = (<Note>currentNote).title;
content = (<Note>currentNote).content;
})
/**
* saves the currently opened Note and returns to listing
*/
function saveAndQuit() {
noteRepo.updateNote(currentNote.id, {
"title": title,
"content": content,
})
returnToListing();
}
/**
* redirects to listing
*/
function returnToListing() {
window.location = "/";
}
export let title: string, content: string;
const currNote = notes.find((note)=>{
return note.id === parseInt(clickedNoteId);
});
</script>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{"Pomelonote | Edit " + currNote.title}</title>
<title>Editor</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
</head>
<html lang="en">
<div class="offset-3 col-6">
{currNote.content}
<div class="offset-3 col-6 wrapper">
<h1 class="">{title === "" ? "" : title}</h1>
<input bind:value={title} class="input"> <br/>
<textarea bind:value={content} class="input textarea"></textarea>
<div class="button-container">
<button on:click={() => saveAndQuit()} class="btn btn-primary">Save</button>
<button on:click={() => returnToListing()} class="btn btn-outline-danger">Cancel</button>
</div>
</div>
</html>
<style>
@import "../../customBootstrap.css";
.wrapper {
margin-top: 20px;
}
.input {
margin-bottom: 10px;
width: 100%;
}
.button-container {
float: right;
}
.textarea {
height: 300px;
}
</style>

View file

@ -1,9 +1,9 @@
<script lang="ts">
import {setCookie} from "nookies";
import type {Authentication} from "./models/authentication";
import { SvelteToast } from '@zerodevx/svelte-toast'
import {createErrorToast} from "../../models/customToasts";
import {SvelteToast} from '@zerodevx/svelte-toast'
import logo from "../../resources/images/logo2.svg";
import {handleErrorsFromResponseWithToast} from "../../models/PomeloUtils";
import {StrapiUserRepo} from "../../models/repos/user/StrapiUserRepo";
let user: string;
let password: string;
@ -14,31 +14,12 @@
* Handles the button click.
*/
async function handleSubmit() {
const endpoint = "http://localhost:1337/api/auth/local";
const payload = {
identifier: user,
password: password
};
const userRepo: StrapiUserRepo = StrapiUserRepo.getInstance(false);
const login = await fetch(endpoint, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
const response = await userRepo.loginUser(user, password);
const response: Authentication = await login.json();
if (response.error != null){
if (response.error.details.errors){
for (const error of response.error.details.errors) {
createErrorToast(error.message);
}
} else{
createErrorToast(response.error.message);
}
if (response.error != null) {
handleErrorsFromResponseWithToast(response);
} else {
if (rememberMe) {
setCookie(null, 'jwt', response.jwt, {
@ -56,34 +37,34 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>PomeloNote | Login</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" rel="stylesheet">
</head>
<body>
<main class="form-signin w-100 m-auto">
<img class="img-fluid" src="{logo}" alt="Logo">
<img alt="Logo" class="img-fluid" src="{logo}">
<h1 class="h3 mb-3 fw-normal">Please sign in</h1>
<div class="form-floating">
<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com" bind:value={user}>
<input bind:value={user} class="form-control" id="floatingInput" placeholder="name@example.com" type="text">
<label for="floatingInput">Email address or username</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" placeholder="Password" bind:value={password}>
<input bind:value={password} class="form-control" id="floatingPassword" placeholder="Password" type="password">
<label for="floatingPassword">Password</label>
</div>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="rememberMe" bind:checked={rememberMe}> Remember me
<input bind:checked={rememberMe} type="checkbox" value="rememberMe"> Remember me
</label>
</div>
<button class="w-100 btn btn-lg btn-primary" on:click={handleSubmit}>Sign in</button>
<a href="/register" class="opacity-75 d-flex justify-content-center text-center fs-6">No user yet? Register.</a>
<a class="opacity-75 d-flex justify-content-center text-center fs-6" href="/register">No user yet? Register.</a>
<p class="mt-5 mb-3 text-muted">&copy;2022</p>
</main>
@ -91,42 +72,7 @@
</body>
</html>
<style>
html,
body {
height: 100%;
}
body {
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
max-width: 330px;
padding: 15px;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.img-fluid{
margin-bottom: 15px;
}
@import "../../userInput.css";
@import "../../customBootstrap.css";
</style>

View file

@ -1,8 +1,8 @@
<script lang="ts">
import logo from "../../resources/images/logo2.svg";
import {SvelteToast} from "@zerodevx/svelte-toast";
import type {Authentication} from "../login/models/authentication";
import {createErrorToast} from "../../models/customToasts";
import {StrapiUserRepo} from "../../models/repos/user/StrapiUserRepo";
import {handleErrorsFromResponseWithToast} from "../../models/PomeloUtils";
let user: string;
let password: string;
@ -13,32 +13,12 @@
* Handles the button click.
*/
async function handleSubmit() {
const endpoint = "http://localhost:1337/api/auth/local/register";
const payload = {
email: email,
password: password,
username: user
};
const userRepo: StrapiUserRepo = StrapiUserRepo.getInstance(false);
const login = await fetch(endpoint, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
const response: Authentication = await login.json();
const response = await userRepo.registerUser(email, user, password);
if (response.error != null) {
if (response.error.details.errors) {
for (const error of response.error.details.errors) {
createErrorToast(error.message);
}
} else {
createErrorToast(response.error.message);
}
handleErrorsFromResponseWithToast(response);
} else {
window.location = "/login";
}
@ -49,29 +29,29 @@
lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>PomeloNote | Register</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" rel="stylesheet">
</head>
<body>
<main class="form-signin w-100 m-auto">
<img class="img-fluid" src="{logo}" alt="Logo">
<img alt="Logo" class="img-fluid" src="{logo}">
<h1 class="h3 mb-3 fw-normal">Register a new user</h1>
<div class="form-floating">
<input type="text" class="form-control" id="floatingUsr" placeholder="exampleUsername" bind:value={user}>
<input bind:value={user} class="form-control" id="floatingUsr" placeholder="exampleUsername" type="text">
<label for="floatingUsr">Username</label>
</div>
<div class="form-floating">
<input type="email" class="form-control" id="floatingInput" placeholder="name@example.com" bind:value={email}>
<input bind:value={email} class="form-control" id="floatingInput" placeholder="name@example.com" type="email">
<label for="floatingInput">Email address</label>
</div>
<div class="form-floating">
<input type="password" class="form-control" id="floatingPassword" placeholder="Password" bind:value={password}>
<input bind:value={password} class="form-control" id="floatingPassword" placeholder="Password" type="password">
<label for="floatingPassword">Password</label>
</div>
@ -79,7 +59,7 @@
Register user
{#if user}: {user} {/if}
</button>
<a href="/login" class="opacity-75 d-flex justify-content-center text-center fs-6">Already registered? Login.</a>
<a class="opacity-75 d-flex justify-content-center text-center fs-6" href="/login">Already registered? Login.</a>
<p class="mt-5 mb-3 text-muted">&copy;2022</p>
</main>
@ -88,46 +68,6 @@
</html>
<style>
html,
body {
height: 100%;
}
body {
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
max-width: 330px;
padding: 15px;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.img-fluid {
margin-bottom: 15px;
}
@import "../../userInput.css";
@import "../../customBootstrap.css";
</style>

View file

@ -0,0 +1,78 @@
/// <reference lib="webworker" />
import { build, files, version } from '$service-worker';
const worker = ServiceWorkerGlobalScope;
// const FILES = cache + version;
const to_cache = build.concat(files);
const staticAssets = new Set(to_cache);
worker.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(FILES)
.then((cache) => cache.addAll(to_cache))
.then(() => {
worker.skipWaiting();
})
);
});
worker.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then(async (keys) => {
// delete old caches
for (const key of keys) {
if (key !== FILES) await caches.delete(key);
}
worker.clients.claim();
})
);
});
/**
* Fetch the asset from the network and store it in the cache.
* Fall back to the cache if the user is offline.
*/
async function fetchAndCache(request) {
const cache = await caches.open(offline + version);
try {
const response = await fetch(request);
cache.put(request, response.clone());
return response;
} catch (err) {
const response = await cache.match(request);
if (response) return response;
throw err;
}
}
worker.addEventListener('fetch', (event) => {
if (event.request.method !== 'GET' || event.request.headers.has('range')) return;
const url = new URL(event.request.url);
// don't try to handle e.g. data: URIs
const isHttp = url.protocol.startsWith('http');
const isDevServerRequest =
url.hostname === self.location.hostname && url.port !== self.location.port;
const isStaticAsset = url.host === self.location.host && staticAssets.has(url.pathname);
const skipBecauseUncached = event.request.cache === 'only-if-cached' && !isStaticAsset;
if (isHttp && !isDevServerRequest && !skipBecauseUncached) {
event.respondWith(
(async () => {
// always serve static files and bundler-generated assets from cache.
// if your application has other URLs with data that will never change,
// set this variable to true for them and they will only be fetched once.
const cachedAsset = isStaticAsset && (await caches.match(event.request));
return cachedAsset || fetchAndCache(event.request);
})()
);
}
});

View file

@ -0,0 +1,7 @@
import {writable} from "svelte/store";
import {browser} from "$app/environment"
export const currentNoteId = writable<number>();
if (browser) {
currentNoteId.set(Number(localStorage.getItem("currentNoteId") || ""))
currentNoteId.subscribe(val => localStorage.setItem("currentNoteId", String(val)));
}

View file

@ -0,0 +1,41 @@
html,
body {
height: 100%;
}
body {
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.form-signin {
max-width: 330px;
padding: 15px;
}
.form-signin .form-floating:focus-within {
z-index: 2;
}
.form-signin input[type="text"] {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
.form-signin input[type="email"] {
margin-bottom: -1px;
border-radius: 0;
}
.form-signin input[type="password"] {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.img-fluid {
margin-bottom: 15px;
}

View file

@ -0,0 +1,41 @@
{
"lang": "en",
"dir": "/",
"name": "Pomelo Note",
"short_name": "Pomelo",
"description": "Best Note App",
"theme_color": "#000",
"background_color": "#000",
"display": "standalone",
"orientation": "portrait",
"prefer_related_applications": false,
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "../resources/icons/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "../resources/icons/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "../resources/icons/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "../resources/icons/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
}
],
"splash_pages": null
}

View file

@ -2,7 +2,14 @@ import { sveltekit } from '@sveltejs/kit/vite';
import type { UserConfig } from 'vite';
const config: UserConfig = {
plugins: [sveltekit()]
plugins: [sveltekit()],
server: {
fs: {
// Allow serving files from one level up to the project root
allow: ['..'],
},
},
};
export default config;