Compare commits

..

1 Commits

Author SHA1 Message Date
Patrick Cleavelin 2dbeb6ee9c we are docker now 2023-03-04 02:36:58 -06:00
18 changed files with 2048 additions and 491 deletions

View File

@ -48,7 +48,7 @@
docker = dockerTools.buildImage { docker = dockerTools.buildImage {
name = "memejoin-svelte"; name = "memejoin-svelte";
tag = "0.1.2_1"; tag = "0.1.0";
copyToRoot = buildEnv { copyToRoot = buildEnv {
name = "image-root"; name = "image-root";
paths = [ nodejs yarnPkg ]; paths = [ nodejs yarnPkg ];

1891
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,56 +2,34 @@
import { intros, member } from './store.ts'; import { intros, member } from './store.ts';
import { member_can, Permission } from './permissions.ts'; import { member_can, Permission } from './permissions.ts';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { uploadIntro } from './api.js';
let enteredUploadTitle = '';
let selectedUploadGuild = null;
let selectedFile = null;
let uploadPromise = null;
let enteredUrl = ''; let enteredUrl = '';
let enteredTitle = ''; let enteredTitle = '';
let selectedGuild = null; let selectedGuild = null;
let downloadPromise = null;
let canDownloadAny = false; let canDownloadAny = false;
let downloadPromise = null;
let allowedGuildList = []; let allowedGuildList = [];
$: allowedGuildList = $member.guilds $: allowedGuildList = $member.guilds
.filter((guild) => member_can(guild.permissions, Permission.CanDownload)) .filter((guild) => member_can(guild.permissions, Permission.CanDownload))
.map((guild) => guild); .map((guild) => guild.name);
$: canDownloadAny = allowedGuildList.length > 0; $: canDownloadAny = allowedGuildList.length > 0;
const download = () => { const download = () => {
if (!!selectedGuild) { if (!!selectedGuild) {
downloadPromise = (async () => { downloadPromise = (async () => {
await intros.addIntro(selectedGuild.id, enteredUrl, enteredTitle, $member.token); await intros.addIntro(selectedGuild, enteredUrl, enteredTitle, $member.token);
await intros.fetchIntros($member.guilds); await intros.fetchIntros($member.guilds);
})(); })();
} else { } else {
} }
}; };
const upload = () => {
// TODO: limit to 1 file
if (!!selectedUploadGuild) {
uploadPromise = (async () => {
await uploadIntro(selectedUploadGuild.id, enteredUploadTitle, selectedFile[0], $member.token);
await intros.fetchIntros($member.guilds);
})();
} else {
}
}
</script> </script>
{#if canDownloadAny} {#if canDownloadAny}
<div id="cardContent"> <div>
<div id="nestedCardContent">
<h3>Download New Intro</h3> <h3>Download New Intro</h3>
{#if !!downloadPromise} {#if !!downloadPromise}
{#await downloadPromise} {#await downloadPromise}
@ -66,9 +44,7 @@
{:else} {:else}
<select bind:value={selectedGuild}> <select bind:value={selectedGuild}>
{#each allowedGuildList as guild} {#each allowedGuildList as guild}
<option value={guild}> <option value={guild}>{guild}</option>
{guild.name}
</option>
{/each} {/each}
</select> </select>
<input bind:value={enteredTitle} placeholder='enter intro title'> <input bind:value={enteredTitle} placeholder='enter intro title'>
@ -76,51 +52,17 @@
<button on:click={download}>Download</button> <button on:click={download}>Download</button>
{/if} {/if}
</div> </div>
<div id="nestedCardContent">
<h3>Upload New Intro</h3>
{#if !!uploadPromise}
{#await uploadPromise}
<p>uploading...</p>
{:then result}
<p>Uploaded</p>
<button on:click={() => {uploadPromise = null}}>Add another</button>
{:catch err}
<p style='color: red'>{err}</p>
<button on:click={() => {uploadPromise = null}}>Ok</button>
{/await}
{:else}
<select bind:value={selectedUploadGuild} placeholder='Choose Guild'>
{#each allowedGuildList as guild}
<option value={guild}>{guild.name}</option>
{/each}
</select>
<input bind:value={enteredUploadTitle} placeholder='enter intro title'>
<label for="fileSelect" id="uploadLabel">
{#if !!selectedFile}
{selectedFile[0].name}
{:else}
Select Intro
{/if}
<input id="fileSelect" type="file" accept="audio/*, video/*" bind:files={selectedFile}>
</label>
<button on:click={upload}>Upload</button>
{/if}
</div>
</div>
{/if} {/if}
<style> <style>
div#uploader { div {
display: flex; display: flex;
position: relative; width: 80%;
width: 85%;
height: 85%;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background-color: #2a2a4a; background-color: #2a2a4a;
padding: 16px; padding: 1.5em;
box-shadow: 1px 3px 4px 1px #1f1f36; box-shadow: 1px 3px 4px 1px #1f1f36;
margin: 16px;
} }
h3 { h3 {
@ -128,47 +70,14 @@
margin-bottom: 2em; margin-bottom: 2em;
} }
select {
appearance: none;
background-color: #393963;
color: lightgrey;
width: 100%;
}
option {
text-align: center;
}
input, button { input, button {
margin: 0.5em; margin: 0.5em;
} }
label#uploadLabel {
width: 100%;
color: lightgray;
text-align: center;
border-style: none;
border-radius: 4px;
background-image: linear-gradient(0deg, #1f1f36, #23233d);
padding: 0.5em 0.5em 0.5em;
margin: 0 0 16px 0px;
user-select: none;
}
label#uploadLabel:hover {
background-image: linear-gradient(0deg, #34345b, #393963);
box-shadow: 1px 3px 15px 1px #1f1f36;
cursor: pointer;
}
input#fileSelect {
display: none;
}
input { input {
width: 100%;
border-style: solid; border-style: solid;
border-color: #323259; border-color: #323259;
width: 100%;
text-align: center; text-align: center;
color: lightgrey; color: lightgrey;
background-image: linear-gradient(0deg, #23233d, #1f1f36); background-image: linear-gradient(0deg, #23233d, #1f1f36);

View File

@ -11,7 +11,6 @@
export let exclude = null; export let exclude = null;
export let include = null; export let include = null;
export let btnLabel = 'Add'; export let btnLabel = 'Add';
export let btnDanger = false;
export let emptyMsg = null; export let emptyMsg = null;
let filteredIntroList = []; let filteredIntroList = [];
@ -83,7 +82,6 @@
let selectedIntros = []; let selectedIntros = [];
</script> </script>
<div id="nestedCardContent" class="cardLight cardNoShadow">
<div id="list"> <div id="list">
{#if !!filteredIntroList && filteredIntroList.length > 0} {#if !!filteredIntroList && filteredIntroList.length > 0}
{#each filteredIntroList as intro (intro.index)} {#each filteredIntroList as intro (intro.index)}
@ -95,23 +93,5 @@
{:else} {:else}
<p style="color: red">{emptyMsg}</p> <p style="color: red">{emptyMsg}</p>
{/if} {/if}
<button on:click={onConfirm}>{btnLabel}</button>
</div> </div>
<button style="background-image: linear-gradient(0deg, {btnDanger ? '#a81111' : '#1f1f36'}, {btnDanger ? '#db1616' : '#23233d'});"on:click={onConfirm}>{btnLabel}</button>
</div>
<style>
div#list {
display: flex;
width: 85%;
border-radius: 4px;
flex-direction: column;
align-items: center;
background-color: #323259;
padding: 1em;
overflow-x: hidden;
overflow-y: auto;
margin: 16px;
height: 256px;
}
</style>

View File

@ -1,60 +1,26 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { member, intros } from './store.ts'; import { member, intros } from './store.ts';
import { env } from '$env/dynamic/public';
const authorizeUri = const authorizeUri = "https://discord.com/api/oauth2/authorize?client_id=577634620728934400&redirect_uri=https%3A%2F%2Fspacegirl.nl%2Fmemes%2Fauth&response_type=code&scope=guilds.members.read%20guilds%20identify";
`https://discord.com/api/oauth2/authorize?client_id=577634620728934400&redirect_uri=${encodeURIComponent(env.PUBLIC_APP_BASE_URL + '/auth')}&response_type=code&scope=${encodeURIComponent('guilds.members.read guilds identify')}`;
let loginPromise = null; onMount(async () => {
let mounted = false;
onMount(() => {
loginPromise = (async () => {
const token = window.localStorage.getItem('token'); const token = window.localStorage.getItem('token');
if (!!token) { if (!!token) {
try {
await member.pullData(token); await member.pullData(token);
await intros.fetchIntros($member.guilds); await intros.fetchIntros($member.guilds);
return true;
} catch (err) {
console.table(err);
if (err.message === "User doesn't exist") {
return false;
} else {
throw err;
} }
}
} else {
return false;
}
})();
mounted = true;
}); });
const login = async (username) => { const login = async (username) => {
window.location = authorizeUri; window.location = authorizeUri;
} }
let loginPromise = null;
let enteredUsername = ''; let enteredUsername = '';
</script> </script>
{#if !!loginPromise} <p style="color:red;">You need to login first</p>
{#await loginPromise}
<p>Loading...</p>
{:then result}
{#if result}
<p>Success</p>
{:else}
<button on:click={() => loginPromise = login(enteredUsername)}>Login</button> <button on:click={() => loginPromise = login(enteredUsername)}>Login</button>
{/if}
{:catch}
<p style="color: red">An error occurred while contacting the server. Try refreshing</p>
{/await}
{:else if !mounted}
<p>Loading...</p>
{/if}

View File

@ -1,54 +0,0 @@
import { env } from '$env/dynamic/public';
export const apiCall = async (method, endpoint, token, body, contentType) => {
const headers = (() => {
if (!!token) {
return { 'token': token };
}
return {}
})();
return (await
fetch(
`${env.PUBLIC_API_URL}/${endpoint}`,
{ method: method, headers: { 'Content-Type': contentType, ...headers }, body: body })
);
};
export const authenticate = async (code) => {
return await apiCall('GET', `auth?code=${code}`, null);
};
export const getMe = async (token) => {
return await apiCall('GET', 'me', token);
};
export const getGuildIntros = async (guild, token) => {
return await apiCall('GET', `intros/${guild}`, token);
};
export const addGuildIntro = async (guild, url, title, token) => {
return await apiCall('GET', `intros/${guild}/add?url=${encodeURIComponent(url)}&name=${encodeURIComponent(title)}`, token);
};
export const chooseIntro = async (guild, channel, selectedIntros, token) => {
for (const intro of selectedIntros) {
await apiCall('POST', `intros/${guild}/${channel}/${intro}`, token);
}
};
export const removeIntro = async (guild, channel, selectedIntros, token) => {
for (const intro of selectedIntros) {
await apiCall('POST', `intros/${guild}/${channel}/${intro}/remove`, token);
}
};
export const uploadIntro = async (guild, name, file, token) => {
await apiCall('POST', `intros/${guild}/upload?name=${encodeURIComponent(name)}`, token, file);
}
export const deleteIntro = async (guild, selectedIntros, token) => {
await apiCall('DELETE', `intros/${guild}/delete`, token, JSON.stringify(selectedIntros), 'application/json');
}

View File

@ -4,7 +4,6 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>MemeJoin - Dashboard</title>
%sveltekit.head% %sveltekit.head%
</head> </head>
<style> <style>
@ -13,49 +12,48 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background-color: #313052; background-color: #313052;
width: max-content; font-family: 'Cantarell';
} }
h1, h2, h3, h4, h5, p, label, li { h1, h2, h3, h4, h5, p, label, li {
color: lightgrey; color: lightgrey;
} }
div#cardContent { div#intros {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
align-items: stretch;
justify-items: center;
justify-content: center;
background-color: #323259;
box-shadow: 1px 3px 15px 1px #1f1f36;
margin-bottom: 2em;
padding: 2em;
gap: 1em;
}
div#nestedCardContent {
display: flex; display: flex;
position: relative;
width: 85%;
height: fit-content;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
background-color: #323259;
padding: 0.5em;
box-shadow: 1px 3px 15px 1px #1f1f36;
}
div#guild-settings {
display: flex;
flex-direction: row;
align-items: center;
background-color: #2a2a4a; background-color: #2a2a4a;
padding: 16px; margin: 1em;
box-shadow: 1px 3px 4px 1px #1f1f36; box-shadow: 1px 3px 4px 1px #1f1f36;
} }
div#nestedCardContent.cardNoShadow{ div#channel-settings {
box-shadow: none; display: flex;
flex-direction: column;
align-items: center;
background-color: #2a2a4a;
margin: 0.5em;
} }
div#nestedCardContent.cardLight{ div#list {
background-color: #323259; display: inline-flex;
width: 85%;
flex-direction: column;
align-items: center;
} }
button, #list-item { button, #list-item {
color: lightgray; color: lightgray;
font-family: 'JetBrains Mono';
border-style: none; border-style: none;
border-radius: 4px; border-radius: 4px;
background-image: linear-gradient(0deg, #1f1f36, #23233d); background-image: linear-gradient(0deg, #1f1f36, #23233d);
@ -63,20 +61,21 @@
margin: 0 0 16px 0px; margin: 0 0 16px 0px;
width: 100%; width: 100%;
user-select: none; user-select: none;
overflow-wrap: break-word;
} }
button:hover, #list-item:hover { button:hover, #list-item:hover {
background-image: linear-gradient(0deg, #34345b, #393963); background-image: linear-gradient(0deg, #34345b, #393963);
box-shadow: 1px 3px 15px 1px #1f1f36; box-shadow: 1px 3px 15px 1px #1f1f36;
cursor: pointer;
} }
#list-item > input[type="checkbox"]:checked { #list-item > input[type="checkbox"] {
display: none;
} }
#list-item:has(input[type="checkbox"]:checked) { #list-item:has(input[type="checkbox"]:checked) {
background-image: linear-gradient(0deg, #40406e, #444475); background-image: linear-gradient(0deg, #40406e, #444475);
} }
</style> </style>
<body data-sveltekit-preload-data="hover" style="display: block; margin: 0; width: 100%; overflow-x: hidden; overflow-y: scroll;"><div>%sveltekit.body%</div></body> <body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html> </html>

View File

@ -1,38 +0,0 @@
<script>
import { member, intros } from '../store.ts';
import { deleteIntro } from '../api.js';
import IntroSelector from '../IntroSelector.svelte';
export let guilds = [];
let deletePromise = null;
const apiDeleteIntro = async (guild, selectedIntros) => {
await deleteIntro(guild, selectedIntros, $member.token);
await member.pullData($member.token);
await intros.fetchIntros($member.guilds);
};
const deleteIntros = (event) => {
deletePromise = apiDeleteIntro(event.detail.guild, event.detail.intros);
};
</script>
{#if !!deletePromise}
{#await deletePromise}
{:catch err}
<p style="color: red">{err}</p>
{/await}
{/if}
<div id="nestedCardContent">
{#each guilds as guild}
<h4>{guild.name}</h4>
<IntroSelector
guild={guild.id}
exclude={[]}
on:confirm={deleteIntros}
btnLabel="Delete Intro From Guild"
btnDanger={true}
emptyMsg="Your guild doesn't have any intros, try adding some below"
/>
{/each}
</div>

View File

@ -1,48 +0,0 @@
<script>
import { member, intros } from '../store.ts';
import IntroSelector from '../IntroSelector.svelte';
import { slide } from 'svelte/transition';
import DeleteSelector from './DeleteSelector.svelte';
import Permissions from './Permissions.svelte';
import { member_can, Permission } from '../permissions.ts';
let dashboardVisible = false;
let allowedGuildList = [];
$: allowedGuildList = $member.guilds
.filter((guild) => member_can(guild.permissions, Permission.CanDelete))
.map((guild) => guild);
$: isModerator = allowedGuildList.length > 0;
</script>
{#if isModerator}
<h3>Wow you're a moderator, here is a cool dashboard for you to use</h3>
<div id="cardContent" class="noGrid">
<div id="nestedCardContent" class="cardLight cardNoShadow">
{#if dashboardVisible}
<div id="nestedCardContent" class="cardLight cardNoShadow" transition:slide>
<button on:click={() => dashboardVisible = false}>
Hide dashboard ^
</button>
<DeleteSelector guilds={allowedGuildList}/>
<!--<Permissions />-->
</div>
{:else}
<button on:click={() => dashboardVisible = true}>
Show dashboard v
</button>
{/if}
</div>
</div>
{/if}
<style>
h3 {
text-align: center;
}
</style>

View File

@ -1 +0,0 @@
<h3>Permissions</h3>

View File

@ -1,7 +1,6 @@
export const Permission = { export const Permission = {
None: 0, None: 0,
CanDownload: 1, CanDownload: 1,
CanDelete: 2,
} }
export const member_can = (permissions, perm) => { export const member_can = (permissions, perm) => {

View File

@ -1,5 +0,0 @@
<script>
import './global.css';
</script>
<slot></slot>

View File

@ -1,61 +1,76 @@
<script> <script>
import { env } from '$env/dynamic/public';
import { member, intros } from '../store.ts'; import { member, intros } from '../store.ts';
import { member_can } from '../permissions.ts'; import { member_can } from '../permissions.ts';
import { chooseIntro, removeIntro } from '../api.js';
import Login from '../Login.svelte'; import Login from '../Login.svelte';
import IntroSelector from '../IntroSelector.svelte'; import IntroSelector from '../IntroSelector.svelte';
import IntroDownloader from '../IntroDownloader.svelte'; import IntroDownloader from '../IntroDownloader.svelte';
import ModDashboard from '../components/ModDashboard.svelte';
let addIntroPromise = null; let addIntroPromise = null;
let removeIntroPromise = null; let removeIntroPromise = null;
const apiAddIntro = async (guild, channel, username, selectedIntros) => { const apiAddIntro = async (guild, channel, username, selectedIntros) => {
await chooseIntro(guild, channel, selectedIntros, $member.token); for (const intro of selectedIntros) {
const response = await fetch(
`https://${process.env.API_URL}/memes/api/intros/${guild}/${channel}/${intro}`,
{ method: 'POST', headers: {"token": $member.token} }
);
if (!response.ok) {
const body = await response.json();
throw new Error(`${body}`);
}
}
await member.pullData($member.token); await member.pullData($member.token);
}; };
const apiRemoveIntro = async (guild, channel, username, selectedIntros) => { const apiRemoveIntro = async (guild, channel, username, selectedIntros) => {
await removeIntro(guild, channel, selectedIntros, $member.token); for (const intro of selectedIntros) {
const response = await fetch(
`https://${process.env.API_URL}/memes/api/intros/${guild}/${channel}/${intro}/remove`,
{ method: 'POST', headers: {"token": $member.token} }
);
if (!response.ok) {
const body = await response.json();
throw new Error(`${body}`);
}
}
await member.pullData($member.token); await member.pullData($member.token);
}; };
const addIntros = (event) => { const addIntros = (event) => {
addIntroPromise = apiAddIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros); addIntroPromise = apiAddIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros);
} }
const removeIntros = (event) => { const removeIntros = (event) => {
removeIntroPromise = apiRemoveIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros); removeIntroPromise = apiRemoveIntro(event.detail.guild, event.detail.channel, $member.username, event.detail.intros);
} }
</script> </script>
{#if !!$member}
<div id="mainContent">
<h1>MemeJoin - A bot for user intros</h1> <h1>MemeJoin - A bot for user intros</h1>
<p style='text-align:center;'>{$member.username}</p>
<ModDashboard /> {#if !!$member}
<IntroDownloader /> <p>{$member.username}</p>
<h3>Guild Settings</h3>
<div id="cardContent"> <h3>Your Intros</h3>
<div id="intros">
{#each $member.guilds as guild} {#each $member.guilds as guild}
<div id="nestedCardContent" class="cardLight cardNoShadow">
<h4>{guild.name}</h4> <h4>{guild.name}</h4>
<div id="nestedCardContent"> <IntroDownloader />
<div id="guild-settings">
{#each guild.channels as channel} {#each guild.channels as channel}
<div id="nestedCardContent" class="cardNoShadow"> <div id="channel-settings">
<h4>{channel.name}</h4> <h4>{channel.name}</h4>
{#await addIntroPromise then result} {#await addIntroPromise then result}
{:catch err} {:catch err}
<p style='color: red'>Failed to add intro {err}</p> <p style='color: red'>Failed to add intro</p>
{/await} {/await}
{#await removeIntroPromise then result} {#await removeIntroPromise then result}
{:catch err} {:catch err}
<p style='color: red'>Failed to remove intro</p> <p style='color: red'>Failed to remove intro</p>
{/await} {/await}
<h3>Your Current Intros</h3>
<IntroSelector <IntroSelector
guild={guild.id} guild={guild.name}
channel={channel.name} channel={channel.name}
include={channel.intros.map((x) => x.index)} include={channel.intros.map((x) => x.index)}
on:confirm={removeIntros} on:confirm={removeIntros}
@ -63,61 +78,21 @@
emptyMsg="You don't have any intros, try adding one" emptyMsg="You don't have any intros, try adding one"
/> />
<h3>Select Intros</h3> <h3>Add Intros</h3>
<IntroSelector <IntroSelector
guild={guild.id} guild={guild.name}
channel={channel.name} channel={channel.name}
exclude={channel.intros.map((x) => x.index)} exclude={channel.intros.map((x) => x.index)}
on:confirm={addIntros} on:confirm={addIntros}
btnLabel="Choose"
emptyMsg="There are no intros" emptyMsg="There are no intros"
/> />
</div> </div>
{/each} {/each}
</div> </div>
</div>
{/each} {/each}
</div> </div>
</div>
{:else} {:else}
<Login /> <Login />
{/if} {/if}
<style>
h1, h2, h3 {
text-align: center;
}
div#mainContent {
display: grid;
top: 10em;
margin: 16px;
}
div#guild {
display: flex;
flex-direction: column;
align-items: center;
}
div#guild-settings {
display: flex;
width: 85%;
flex-direction: row;
justify-content: center;
background-color: #2a2a4a;
margin: 1em;
box-shadow: 1px 3px 4px 1px #1f1f36;
}
div#channel-settings {
display: flex;
width: 85%;
height: 95%;
flex-direction: column;
align-items: center;
background-color: #2a2a4a;
margin: 1em;
}
</style>

View File

@ -1,29 +1,26 @@
<script> <script>
import { env } from '$env/dynamic/public';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { page } from '$app/stores'; import { page } from '$app/stores';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { member, intros } from '../../store.ts'; import { member, intros } from '../../store.ts';
import { authenticate } from '../../api.js';
const code = $page.url.searchParams.get('code'); const code = $page.url.searchParams.get('code');
let loginFailed = false; let loginFailed = false;
onMount(async () => { onMount(async () => {
const response = await authenticate(code); const response = await fetch(`https://${process.env.API_URL}/memes/api/auth?code=${code}`);
const body = await response.json(); const body = await response.json();
if (!response.ok) { if (!response.ok) {
loginFailed = true loginFailed = true
} else { } else {
window.localStorage.setItem('token', body.token);
await member.pullData(body.token); await member.pullData(body.token);
await intros.fetchIntros($member.guilds); await intros.fetchIntros($member.guilds);
window.localStorage.setItem('token', body.token);
goto(`${env.PUBLIC_APP_BASE_URL}`) goto('/')
} }
}); });
</script> </script>

View File

@ -1,8 +0,0 @@
@font-face {
font-family: 'JetBrains Mono';
src: url('/fonts/JetBrainsMono-Medium.woff2') format('woff');
}
body {
font-family: 'JetBrains Mono';
}

View File

@ -1,6 +1,5 @@
import { env } from '$env/dynamic/public';
import { readable, writable } from 'svelte/store'; import { readable, writable } from 'svelte/store';
import { getMe, getGuildIntros, addGuildIntro } from './api.js';
function createMemberStore() { function createMemberStore() {
const { subscribe, set, update } = writable(null) const { subscribe, set, update } = writable(null)
@ -10,13 +9,14 @@ function createMemberStore() {
set: set, set: set,
addIntro: (intro: IntroIndex) => { update((n) => n.intros.push(intro)); return intro }, addIntro: (intro: IntroIndex) => { update((n) => n.intros.push(intro)); return intro },
pullData: async (token) => { pullData: async (token) => {
const response = await getMe(token); const response = (await (await fetch(
`https://${process.env.API_URL}/memes/api/me`,
{ headers: {"token": token} })).json())
if (!response.ok) { if (response === "NoUserFound") {
throw new Error(await response.text()); return;
} else { } else {
const body = await response.json(); set({ token: token, ...response.Me })
set({ token: token, ...body.Me })
} }
} }
} }
@ -28,7 +28,8 @@ function createIntroStore() {
return { return {
subscribe: subscribe, subscribe: subscribe,
addIntro: async (guild, url, title, token) => { addIntro: async (guild, url, title, token) => {
const response = await addGuildIntro(guild, url, title, token); const response = await fetch(`https://${process.env.API_URL}/memes/api/intros/${guild}/add/${encodeURIComponent(url)}?name=${encodeURIComponent(title)}`,
{ method: 'GET', headers: { 'token': token } });
if (!response.ok) { if (!response.ok) {
throw new Error(await response.body); throw new Error(await response.body);
@ -38,13 +39,11 @@ function createIntroStore() {
let intros = new Map(); let intros = new Map();
for (const guild of guilds) { for (const guild of guilds) {
const response = await getGuildIntros(guild.id, null); const response = (await (await fetch(`https://${process.env.API_URL}/memes/api/intros/${guild.name}`)).json())
const body = await response.json();
if (response !== "NoGuildFound") { if (response !== "NoGuildFound") {
let guild_intros = new Map(); let guild_intros = new Map();
Object.entries(body.Intros).forEach(([index, intro]) => { Object.entries(response.Intros).forEach(([index, intro]) => {
if (!!intro.File) { if (!!intro.File) {
guild_intros.set(index, { name: intro.File.friendlyName, filename: intro.File.filename }); guild_intros.set(index, { name: intro.File.friendlyName, filename: intro.File.filename });
} else if (!!intro.Online) { } else if (!!intro.Online) {
@ -52,7 +51,7 @@ function createIntroStore() {
} }
}) })
intros.set(guild.id, guild_intros); intros.set(guild.name, guild_intros);
} }
} }

View File

@ -2,9 +2,5 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
server: {
host: "0.0.0.0",
port: "8080",
},
plugins: [sveltekit()] plugins: [sveltekit()]
}); });