mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Rename Postgres tables for better recognition and add additional routes in web app
This commit is contained in:
@@ -1 +1,26 @@
|
||||
<form action=""></form>
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { form } = $props();
|
||||
</script>
|
||||
|
||||
<form method="POST" use:enhance>
|
||||
{#if form?.success}
|
||||
<p>Successfully logged in</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.success === false}
|
||||
<p>{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Username
|
||||
<input type="text" name="username" required />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
@@ -1 +1,26 @@
|
||||
<form action=""></form>
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { form } = $props();
|
||||
</script>
|
||||
|
||||
<form method="POST" use:enhance>
|
||||
{#if form?.success}
|
||||
<p>Successfully registered</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.success === false}
|
||||
<p>{form.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Username
|
||||
<input type="text" name="username" minlength="3" maxlength="16" required />
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" minlength="12" maxlength="128" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
107
web-app/src/routes/(authenticated)/+page.server.ts
Normal file
107
web-app/src/routes/(authenticated)/+page.server.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
export const load = async ({ fetch, cookies, url }) => {
|
||||
const searchQuery = url.searchParams.get("website_search_query");
|
||||
const sortBy = url.searchParams.get("website_sort");
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
const baseFetchUrl = "http://localhost:3000/website";
|
||||
|
||||
if (searchQuery) {
|
||||
params.append("website_title", `ilike.*${searchQuery}*`);
|
||||
}
|
||||
|
||||
switch (sortBy) {
|
||||
case "creation-time":
|
||||
params.append("order", "created_at.desc");
|
||||
break;
|
||||
case "last-modified":
|
||||
params.append("order", "last_modified_at.desc");
|
||||
break;
|
||||
case "title-a-to-z":
|
||||
params.append("order", "title.asc");
|
||||
break;
|
||||
case "title-z-to-a":
|
||||
params.append("order", "title.desc");
|
||||
break;
|
||||
}
|
||||
|
||||
const constructedFetchUrl = `${baseFetchUrl}?${params.toString()}`;
|
||||
|
||||
const websiteData = await fetch(constructedFetchUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
}
|
||||
});
|
||||
|
||||
const websites = await websiteData.json();
|
||||
|
||||
return {
|
||||
websites
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
createWebsite: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/rpc/create_website", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_type: data.get("content-type"),
|
||||
title: data.get("title")
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { createWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { createWebsite: { success: true, operation: "created" } };
|
||||
},
|
||||
updateWebsite: async ({ request, cookies, fetch }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/website?id=eq.${data.get("id")}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: data.get("title")
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { updateWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { updateWebsite: { success: true, operation: "updated" } };
|
||||
},
|
||||
deleteWebsite: async ({ request, cookies, fetch }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/website?id=eq.${data.get("id")}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
return { deleteWebsite: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
return { deleteWebsite: { success: true, operation: "deleted" } };
|
||||
}
|
||||
};
|
||||
103
web-app/src/routes/(authenticated)/+page.svelte
Normal file
103
web-app/src/routes/(authenticated)/+page.svelte
Normal file
@@ -0,0 +1,103 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
import DateTime from "$lib/components/DateTime.svelte";
|
||||
|
||||
const { form, data } = $props();
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h2>Create website</h2>
|
||||
|
||||
<form method="POST" action="?/createWebsite" use:enhance>
|
||||
{#if form?.createWebsite?.success}
|
||||
<p>Successfully created website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.createWebsite?.success === false}
|
||||
<p>{form.createWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
<label>
|
||||
Type
|
||||
<select name="content-type">
|
||||
<option value="Blog">Blog</option>
|
||||
<option value="Docs">Docs</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Title
|
||||
<input type="text" name="title" />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Your websites</h2>
|
||||
|
||||
{#if form?.deleteWebsite?.success}
|
||||
<p>Successfully deleted website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.deleteWebsite?.success === false}
|
||||
<p>{form.deleteWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
{#each data.websites as { id, content_type, title, created_at }}
|
||||
<article>
|
||||
<h3>
|
||||
<a href="/website/{id}">{title}</a>
|
||||
</h3>
|
||||
<p>
|
||||
<strong>Type:</strong>
|
||||
{content_type}
|
||||
</p>
|
||||
<p>
|
||||
<strong>Created at:</strong>
|
||||
<DateTime date={created_at} />
|
||||
</p>
|
||||
<details>
|
||||
<summary>Update</summary>
|
||||
<form
|
||||
method="POST"
|
||||
action="?/updateWebsite"
|
||||
use:enhance={() => {
|
||||
return async ({ update }) => {
|
||||
await update({ reset: false });
|
||||
};
|
||||
}}
|
||||
>
|
||||
{#if form?.updateWebsite?.success}
|
||||
<p>Successfully updated website</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.updateWebsite?.success === false}
|
||||
<p>{form.updateWebsite.message}</p>
|
||||
{/if}
|
||||
|
||||
<input type="hidden" name="id" value={id} />
|
||||
<label>
|
||||
Title
|
||||
<input type="text" name="title" value={title} />
|
||||
</label>
|
||||
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
</details>
|
||||
<details>
|
||||
<summary>Delete</summary>
|
||||
<!-- TODO: Needs to be password protected -->
|
||||
<form method="POST" action="?/deleteWebsite" use:enhance>
|
||||
<input type="hidden" name="id" value={id} />
|
||||
|
||||
<button type="submit">Delete</button>
|
||||
</form>
|
||||
</details>
|
||||
</article>
|
||||
{/each}
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Shared with you</h2>
|
||||
</section>
|
||||
36
web-app/src/routes/(authenticated)/account/+page.server.ts
Normal file
36
web-app/src/routes/(authenticated)/account/+page.server.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export const load = async ({ locals }) => {
|
||||
return {
|
||||
user: locals.user
|
||||
};
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
logout: async ({ cookies }) => {
|
||||
cookies.delete("session_token", { path: "/" });
|
||||
|
||||
return { logout: { success: true } };
|
||||
},
|
||||
deleteAccount: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/rpc/delete_account", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
password: data.get("password")
|
||||
})
|
||||
});
|
||||
|
||||
const response = await res.json();
|
||||
|
||||
if (!res.ok) {
|
||||
return { deleteAccount: { success: false, message: response.message } };
|
||||
}
|
||||
|
||||
cookies.delete("session_token", { path: "/" });
|
||||
return { deleteAccount: { success: true } };
|
||||
}
|
||||
};
|
||||
51
web-app/src/routes/(authenticated)/account/+page.svelte
Normal file
51
web-app/src/routes/(authenticated)/account/+page.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { enhance } from "$app/forms";
|
||||
|
||||
const { data, form } = $props();
|
||||
</script>
|
||||
|
||||
<section>
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>
|
||||
<strong>Username:</strong>
|
||||
{data.user.username}
|
||||
</p>
|
||||
<p>
|
||||
<strong>ID:</strong>
|
||||
{data.user.id}
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Logout</h2>
|
||||
|
||||
{#if form?.logout?.success}
|
||||
<p>Successfully logged out</p>
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="?/logout" use:enhance>
|
||||
<button type="submit">Logout</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Delete account</h2>
|
||||
|
||||
{#if form?.deleteAccount?.success}
|
||||
<p>Account was deleted</p>
|
||||
{/if}
|
||||
|
||||
{#if form?.deleteAccount?.success === false}
|
||||
<p>{form.deleteAccount.message}</p>
|
||||
{/if}
|
||||
|
||||
<form method="POST" action="?/deleteAccount" use:enhance>
|
||||
<label>
|
||||
Password
|
||||
<input type="password" name="password" required />
|
||||
</label>
|
||||
|
||||
<button type="submit">Delete account</button>
|
||||
</form>
|
||||
</section>
|
||||
@@ -1,5 +1,5 @@
|
||||
export const load = async ({ params, fetch, cookies }) => {
|
||||
const websiteData = await fetch(`http://localhost:3000/cms_content?id=eq.${params.websiteId}`, {
|
||||
const websiteData = await fetch(`http://localhost:3000/content?id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ALLOWED_MIME_TYPES } from "$lib/utils.js";
|
||||
|
||||
export const load = async ({ params, fetch, cookies, url }) => {
|
||||
const globalSettingsData = await fetch(
|
||||
`http://localhost:3000/cms_settings?content_id=eq.${params.websiteId}&select=*,cms_media(*)`,
|
||||
`http://localhost:3000/settings?website_id=eq.${params.websiteId}&select=*,media(*)`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -17,7 +17,7 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
);
|
||||
|
||||
const headerData = await fetch(
|
||||
`http://localhost:3000/cms_header?content_id=eq.${params.websiteId}&select=*,cms_media(*)`,
|
||||
`http://localhost:3000/header?website_id=eq.${params.websiteId}&select=*,media(*)`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
@@ -28,7 +28,7 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const homeData = await fetch(`http://localhost:3000/cms_home?content_id=eq.${params.websiteId}`, {
|
||||
const homeData = await fetch(`http://localhost:3000/home?website_id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -37,24 +37,21 @@ export const load = async ({ params, fetch, cookies, url }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const footerData = await fetch(
|
||||
`http://localhost:3000/cms_footer?content_id=eq.${params.websiteId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`,
|
||||
Accept: "application/vnd.pgrst.object+json"
|
||||
}
|
||||
const footerData = await fetch(`http://localhost:3000/footer?website_id=eq.${params.websiteId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`,
|
||||
Accept: "application/vnd.pgrst.object+json"
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
const searchQuery = url.searchParams.get("article_search_query");
|
||||
const sortBy = url.searchParams.get("article_sort");
|
||||
|
||||
const parameters = new URLSearchParams();
|
||||
|
||||
const baseFetchUrl = `http://localhost:3000/cms_article?content_id=eq.${params.websiteId}&select=*,cms_media(*)`;
|
||||
const baseFetchUrl = `http://localhost:3000/article?website_id=eq.${params.websiteId}&select=*,media(*)`;
|
||||
|
||||
if (searchQuery) {
|
||||
parameters.append("title", `ilike.*${searchQuery}*`);
|
||||
@@ -117,21 +114,18 @@ export const actions = {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`http://localhost:3000/cms_settings?content_id=eq.${params.websiteId}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
accent_color_light_theme: data.get("accent-color-light"),
|
||||
accent_color_dark_theme: data.get("accent-color-dark"),
|
||||
favicon_image: favicon?.content
|
||||
})
|
||||
}
|
||||
);
|
||||
const res = await fetch(`http://localhost:3000/settings?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
accent_color_light_theme: data.get("accent-color-light"),
|
||||
accent_color_dark_theme: data.get("accent-color-dark"),
|
||||
favicon_image: favicon?.content
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const response = await res.json();
|
||||
@@ -160,7 +154,7 @@ export const actions = {
|
||||
return logo;
|
||||
}
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_header?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/header?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -187,7 +181,7 @@ export const actions = {
|
||||
updateHome: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_home?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/home?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -208,7 +202,7 @@ export const actions = {
|
||||
updateFooter: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_footer?content_id=eq.${params.websiteId}`, {
|
||||
const res = await fetch(`http://localhost:3000/footer?website_id=eq.${params.websiteId}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -233,14 +227,14 @@ export const actions = {
|
||||
createArticle: async ({ request, fetch, cookies, params }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch("http://localhost:3000/cms_article", {
|
||||
const res = await fetch("http://localhost:3000/article", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${cookies.get("session_token")}`
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_id: params.websiteId,
|
||||
website_id: params.websiteId,
|
||||
title: data.get("title")
|
||||
})
|
||||
});
|
||||
@@ -268,7 +262,7 @@ export const actions = {
|
||||
return cover;
|
||||
}
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_article?id=eq.${data.get("article-id")}`, {
|
||||
const res = await fetch(`http://localhost:3000/article?id=eq.${data.get("article-id")}`, {
|
||||
method: "PATCH",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -294,7 +288,7 @@ export const actions = {
|
||||
deleteArticle: async ({ request, fetch, cookies }) => {
|
||||
const data = await request.formData();
|
||||
|
||||
const res = await fetch(`http://localhost:3000/cms_article?id=eq.${data.get("article-id")}`, {
|
||||
const res = await fetch(`http://localhost:3000/article?id=eq.${data.get("article-id")}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -348,7 +342,7 @@ const handleFileUpload = async (
|
||||
|
||||
const relativePath = relative(join(process.cwd(), "static"), filepath);
|
||||
|
||||
const res = await customFetch("http://localhost:3000/cms_media", {
|
||||
const res = await customFetch("http://localhost:3000/media", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -357,7 +351,7 @@ const handleFileUpload = async (
|
||||
Accept: "application/vnd.pgrst.object+json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content_id: contentId,
|
||||
website_id: contentId,
|
||||
user_id: userId,
|
||||
original_name: file.name,
|
||||
file_system_path: relativePath
|
||||
|
||||
28
web-app/src/routes/+layout.svelte
Normal file
28
web-app/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,28 @@
|
||||
<script lang="ts">
|
||||
import { page } from "$app/stores";
|
||||
const { data, children } = $props();
|
||||
</script>
|
||||
|
||||
<nav>
|
||||
<a href="/">archtika</a>
|
||||
{#if data.user}
|
||||
<a href="/account">Account</a>
|
||||
{:else}
|
||||
<a href="/register">Register</a>
|
||||
<a href="/login">Login</a>
|
||||
{/if}
|
||||
</nav>
|
||||
|
||||
<header>
|
||||
<h1>{$page.url.pathname}</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{@render children()}
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
<small>archtika</small>
|
||||
</p>
|
||||
</footer>
|
||||
@@ -1,11 +0,0 @@
|
||||
<section>
|
||||
<h2>Create website</h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Your websites</h2>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Shared with you</h2>
|
||||
</section>
|
||||
Reference in New Issue
Block a user