Compare commits
27 commits
8ae492a9f1
...
1d79f680df
Author | SHA1 | Date | |
---|---|---|---|
1d79f680df | |||
8ce2d52044 | |||
646ae78776 | |||
68cd8e10ed | |||
2f0ff0521f | |||
c9decc585e | |||
136ca93ce8 | |||
591599f499 | |||
b900655896 | |||
64c5f99963 | |||
2e3a4ae10d | |||
1085687c00 | |||
89cd8ec1bf | |||
358b950af4 | |||
3d1d1151bc | |||
6d4b3d51bc | |||
fc9d21d4df | |||
986c5a90eb | |||
1c680e85e3 | |||
fecf6fb6e0 | |||
4364e9fa3b | |||
e1f2a5a591 | |||
a4b98ba58a | |||
6aa725fb77 | |||
f17aab4a6a | |||
2bfd1aa833 | |||
f04c88b392 |
29 changed files with 1051 additions and 521 deletions
17
app.vue
17
app.vue
|
@ -16,4 +16,21 @@ body {
|
|||
a {
|
||||
color: aquamarine;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.bottom-border {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
.left-border {
|
||||
border-left: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
.right-border {
|
||||
border-right: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 20 KiB |
|
@ -1 +0,0 @@
|
|||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" fill="#000" fill-opacity="1"></path><g class="" style="" transform="translate(0,0)"><path d="M17.47 250.9C88.82 328.1 158 397.6 224.5 485.5c72.3-143.8 146.3-288.1 268.4-444.37L460 26.06C356.9 135.4 276.8 238.9 207.2 361.9c-48.4-43.6-126.62-105.3-174.38-137z" fill="#fff" fill-opacity="1"></path></g></svg>
|
Before Width: | Height: | Size: 430 B |
|
@ -1 +0,0 @@
|
|||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" fill="#000" fill-opacity="1"></path><g class="" style="" transform="translate(0,0)"><path d="M182.78 80.125c3.367 19.498 10.608 52.67 19.126 84.813 8.126 30.663 17.73 60.122 25.063 75.062 57.968-.962 148.212-16.707 252.343-46.344-91.756-70.023-188.486-99.376-296.532-113.53zM164.657 85c-65.62 51.243-106.43 120.106-138.5 196.25 54.866-38.51 111.644-60.42 169.313-70.906-3.995-12.636-7.88-26.486-11.626-40.625-8.425-31.79-15.554-64.12-19.188-84.72zm322.281 125.906c-74.123 21.218-141.43 35.68-196.25 42.813 24.018 51.794 36.448 106.688 43.688 160.936 70.634-58.76 125.36-118.495 152.563-203.75zM201.53 228.28c-56.563 9.917-111.78 30.946-165.56 68.907 89.478 61.396 189.91 97.037 279.874 119.844-7.362-55.057-20.104-109.997-44.75-161.03-18.39 1.897-35.134 2.875-49.938 2.875h-5.344l-2.718-4.625c-3.898-6.69-7.77-15.598-11.563-25.97z" fill="#fff" fill-opacity="1"></path></g></svg>
|
Before Width: | Height: | Size: 1,003 B |
|
@ -1 +0,0 @@
|
|||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" fill="#000" fill-opacity="1"></path><g class="" style="" transform="translate(0,0)"><path d="M256 19.27L25.637 249.638 19.27 256 32 268.73l6.363-6.367L256 44.727l217.637 217.636L480 268.73 492.73 256l-6.367-6.363zM96 48v107.273l64-64.002V48zm160 20.727l-192 192V486h64V320h96v166h224V260.727zM288 320h96v80h-96z" fill="#fff" fill-opacity="1"></path></g></svg>
|
Before Width: | Height: | Size: 484 B |
|
@ -1 +0,0 @@
|
|||
<svg style="height: 512px; width: 512px;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 0h512v512H0z" fill="#000" fill-opacity="1"></path><g class="" style="" transform="translate(0,0)"><path d="M41 25v78h430V25H41zm254 23h18v32h-18V48zm121 0a16 16 0 0 1 16 16 16 16 0 0 1-16 16 16 16 0 0 1-16-16 16 16 0 0 1 16-16zM64 55h48v18H64V55zm80 0h48v18h-48V55zm80 0h48v18h-48V55zm-119 66v30h302v-30H105zm-64 48v78h430v-78H41zm254 23h18v32h-18v-32zm121 0a16 16 0 0 1 16 16 16 16 0 0 1-16 16 16 16 0 0 1-16-16 16 16 0 0 1 16-16zm-352 7h48v18H64v-18zm80 0h48v18h-48v-18zm80 0h48v18h-48v-18zm-119 66v30h302v-30H105zm-64 48v78h430v-78H41zm254 23h18v32h-18v-32zm121 0a16 16 0 0 1 16 16 16 16 0 0 1-16 16 16 16 0 0 1-16-16 16 16 0 0 1 16-16zm-352 7h48v18H64v-18zm80 0h48v18h-48v-18zm80 0h48v18h-48v-18zm13 66v30h38v-30h-38zM25 457v30h130.2l20-30H25zm171.8 0l-20 30h158.4l-20-30H196.8zm140 0l20 30H487v-30H336.8z" fill="#fff" fill-opacity="1"></path></g></svg>
|
Before Width: | Height: | Size: 968 B |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 6.3 KiB |
19
classes/Message.ts
Normal file
19
classes/Message.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import type { MessageResponse } from "~/types/interfaces";
|
||||
|
||||
export default class Message {
|
||||
uuid: string;
|
||||
channelUuid: string;
|
||||
userUuid: string;
|
||||
message: string;
|
||||
|
||||
constructor({ uuid, channel_uuid, user_uuid, message }: MessageResponse) {
|
||||
this.uuid = uuid;
|
||||
this.channelUuid = channel_uuid;
|
||||
this.userUuid = user_uuid;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
getTimestamp() {
|
||||
return uuidToTimestamp(this.uuid);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,19 @@
|
|||
<template>
|
||||
<div>
|
||||
<a class="channel-list-link" :href="href">
|
||||
{{ name }}
|
||||
</a>
|
||||
<div v-if="current" class="channel-list-link-container">
|
||||
<NuxtLink class="channel-list-link" :href="props.href">
|
||||
# {{ name }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div v-else class="channel-list-link-container current-channel">
|
||||
<NuxtLink class="channel-list-link" :href="props.href">
|
||||
# {{ name }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const props = defineProps<{ name: string, href: string }>();
|
||||
const props = defineProps<{ name: string, href: string, current?: boolean }>();
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -16,5 +21,18 @@ const props = defineProps<{ name: string, href: string }>();
|
|||
.channel-list-link {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
padding-left: .5dvw;
|
||||
padding-right: .5dvw;
|
||||
}
|
||||
|
||||
.channel-list-link-container {
|
||||
text-align: left;
|
||||
display: flex;
|
||||
height: 4dvh;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.current-channel {
|
||||
background-color: rgb(70, 70, 70);
|
||||
}
|
||||
</style>
|
40
components/InvitePopup.vue
Normal file
40
components/InvitePopup.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<template>
|
||||
<div id="invite-popup">
|
||||
<div v-if="invite">
|
||||
<p>{{ invite }}</p>
|
||||
<button @click="copyInvite">Copy Link</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button @click="generateInvite">Generate Invite</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { InviteResponse } from '~/types/interfaces';
|
||||
|
||||
|
||||
const invite = ref<string>();
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
async function generateInvite(): Promise<void> {
|
||||
const createdInvite: InviteResponse | undefined = await fetchWithApi(
|
||||
`/servers/${route.params.serverId}/invites`,
|
||||
{ method: "POST", body: { custom_id: "oijewfoiewf" } }
|
||||
);
|
||||
|
||||
invite.value = createdInvite?.id;
|
||||
return;
|
||||
}
|
||||
|
||||
function copyInvite() {
|
||||
const inviteUrl = URL.parse(`invite/${invite.value}`, `${window.location.protocol}//${window.location.host}`);
|
||||
navigator.clipboard.writeText(inviteUrl!.href);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
13
components/Loading.vue
Normal file
13
components/Loading.vue
Normal file
|
@ -0,0 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
Loading...
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
|
@ -1,13 +1,20 @@
|
|||
<template>
|
||||
<div class="message">
|
||||
<img class="message-author-pfp" :src="img" :alt="username">
|
||||
<div>
|
||||
<img v-if="props.img" class="message-author-avatar" :src="img" :alt="username">
|
||||
<Icon v-else name="lucide:user" class="message-author-avatar" />
|
||||
</div>
|
||||
<div class="message-data">
|
||||
<div class="message-metadata">
|
||||
<span class="message-author-username">
|
||||
{{ username }}
|
||||
</span>
|
||||
<span class="message-date">
|
||||
<span class="message-date" :title="date.toString()">
|
||||
{{ messageDate }}
|
||||
<!--
|
||||
<div class="message-date-hover" v-if="showHover">
|
||||
</div>
|
||||
-->
|
||||
</span>
|
||||
</div>
|
||||
<div class="message-text">
|
||||
|
@ -21,6 +28,7 @@
|
|||
const props = defineProps<{ class?: string, img?: string, username: string, text: string, timestamp: number, format: "12" | "24" }>();
|
||||
|
||||
const messageDate = ref<string>();
|
||||
const showHover = ref(false);
|
||||
|
||||
const date = new Date(props.timestamp);
|
||||
const now = new Date()
|
||||
|
@ -32,12 +40,16 @@ if (now.getUTCHours() >= 0) {
|
|||
if (dateHour > 12) {
|
||||
dateHour = dateHour - 12;
|
||||
}
|
||||
messageDate.value = `${dateHour}:${dateMinute} ${dateHour > 0 && dateHour < 13 ? "AM" : "PM"}`
|
||||
messageDate.value = `${dateHour}:${dateMinute < 10 ? "0" + dateMinute : dateMinute} ${dateHour > 0 && dateHour < 13 ? "AM" : "PM"}`
|
||||
} else {
|
||||
messageDate.value = `${dateHour}:${dateMinute}`
|
||||
messageDate.value = `${dateHour}:${dateMinute < 10 ? "0" + dateMinute : dateMinute}`
|
||||
}
|
||||
}
|
||||
|
||||
//function toggleTooltip(e: Event) {
|
||||
// showHover.value = !showHover.value;
|
||||
//}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -46,7 +58,8 @@ if (now.getUTCHours() >= 0) {
|
|||
/* border: 1px solid lightcoral; */
|
||||
margin-bottom: 1dvh;
|
||||
display: grid;
|
||||
grid-template-columns: 3em auto;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-metadata {
|
||||
|
@ -65,7 +78,7 @@ if (now.getUTCHours() >= 0) {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.message-author-pfp {
|
||||
.message-author-avatar {
|
||||
margin-right: 1dvw;
|
||||
width: 3em;
|
||||
}
|
||||
|
@ -79,4 +92,15 @@ if (now.getUTCHours() >= 0) {
|
|||
font-size: small;
|
||||
color: rgb(150, 150, 150);
|
||||
}
|
||||
|
||||
.message-date:hover {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
.message-date-tooltip {
|
||||
height: 20px;;
|
||||
width: 20px;
|
||||
}
|
||||
*/
|
||||
</style>
|
122
components/MessageArea.vue
Normal file
122
components/MessageArea.vue
Normal file
|
@ -0,0 +1,122 @@
|
|||
<template>
|
||||
<div id="message-area">
|
||||
<div id="messages">
|
||||
<Message v-for="message of messages" :username="displayName" :text="message.message"
|
||||
:timestamp="uuidToTimestamp(message.uuid)" format="12" />
|
||||
</div>
|
||||
<div id="message-box">
|
||||
<form id="message-form" @submit="sendMessage">
|
||||
<input v-model="messageInput" type="text" name="message-input" id="message-box-input">
|
||||
<button type="submit">
|
||||
<Icon name="lucide:send" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { MessageResponse } from '~/types/interfaces';
|
||||
|
||||
const props = defineProps<{ channelUrl: string, amount?: number, offset?: number, reverse?: boolean }>();
|
||||
|
||||
const messagesRes: MessageResponse[] | undefined = await fetchWithApi(
|
||||
`${props.channelUrl}/messages`,
|
||||
{ query: { "amount": props.amount ?? 100, "offset": props.offset ?? 0 } }
|
||||
);
|
||||
if (messagesRes && props.reverse) {
|
||||
messagesRes.reverse();
|
||||
}
|
||||
|
||||
const messages = ref(messagesRes);
|
||||
|
||||
const { fetchUser } = useAuth();
|
||||
|
||||
const user = await fetchUser();
|
||||
const displayName = user!.display_name ?? user!.username
|
||||
|
||||
const messageInput = ref<string>();
|
||||
|
||||
const accessToken = useCookie("access_token").value;
|
||||
const apiBase = useCookie("api_base").value;
|
||||
const { refresh } = useAuth();
|
||||
|
||||
let ws: WebSocket;
|
||||
|
||||
if (accessToken && apiBase) {
|
||||
console.log("channel url:", `${apiBase.replace("http", "ws")}/${props.channelUrl}/socket`);
|
||||
console.log("access token:", accessToken);
|
||||
do {
|
||||
console.log("Trying to connect to channel WebSocket...");
|
||||
ws = new WebSocket(`${apiBase.replace("http", "ws").replace("3000", "8080")}/${props.channelUrl}/socket`,
|
||||
["Authorization", accessToken]
|
||||
);
|
||||
if (ws) break;
|
||||
await sleep(10000);
|
||||
} while (!ws);
|
||||
|
||||
ws.addEventListener("open", (event) => {
|
||||
console.log("WebSocket connected!");
|
||||
});
|
||||
|
||||
ws.addEventListener("message", (event) => {
|
||||
console.log("event data:", event.data);
|
||||
messages.value?.push(
|
||||
JSON.parse(event.data)
|
||||
)
|
||||
});
|
||||
} else {
|
||||
await refresh();
|
||||
}
|
||||
|
||||
function sendMessage(e: Event) {
|
||||
e.preventDefault();
|
||||
const text = messageInput.value;
|
||||
console.log("text:", text);
|
||||
if (text) {
|
||||
ws.send(text);
|
||||
messageInput.value = "";
|
||||
console.log("MESSAGE SENT!!!");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
#message-area {
|
||||
padding-top: 3dvh;
|
||||
}
|
||||
|
||||
#message-area {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#message-box {
|
||||
border: 1px solid rgb(70, 70, 70);
|
||||
margin-bottom: 1dvh;
|
||||
height: 7%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#message-form {
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#message-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#messages {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
</style>
|
100
composables/auth.ts
Normal file
100
composables/auth.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import type { UserResponse } from "~/types/interfaces";
|
||||
|
||||
export const useAuth = () => {
|
||||
const accessToken = useCookie("access_token");
|
||||
const user = useState<UserResponse | null>("user", () => null);
|
||||
|
||||
async function clearAuth() {
|
||||
accessToken.value = null;
|
||||
user.value = null;
|
||||
}
|
||||
|
||||
async function register(username: string, email: string, password: string) {
|
||||
const hashedPass = await hashPassword(password);
|
||||
const res = await fetchWithApi("/auth/register", {
|
||||
method: "POST", body:
|
||||
{
|
||||
email, identifier: username, password: hashedPass, device_name: "Linux Laptop"
|
||||
}
|
||||
}) as { access_token: string, refresh_token: string };
|
||||
//authStore.setAccessToken(accessToken);
|
||||
accessToken.value = res.access_token;
|
||||
}
|
||||
|
||||
async function login(username: string, password: string, device_name: string) {
|
||||
const hashedPass = await hashPassword(password);
|
||||
console.log("hashedPass:", hashedPass);
|
||||
//authStore.setAccessToken(accessToken);
|
||||
const res = await fetchWithApi("/auth/login", {
|
||||
method: "POST", body:
|
||||
{
|
||||
username, password: hashedPass, device_name: "Linux Laptop"
|
||||
}
|
||||
}) as { access_token: string, refresh_token: string }; fetch
|
||||
console.log("hi");
|
||||
accessToken.value = res.access_token;
|
||||
console.log("access token:", accessToken.value);
|
||||
await fetchUser();
|
||||
}
|
||||
|
||||
async function logout(password: string) {
|
||||
console.log("password:", password);
|
||||
console.log("access:", accessToken.value);
|
||||
const hashedPass = await hashPassword(password);
|
||||
console.log("hashed");
|
||||
|
||||
const res = await fetchWithApi("/auth/revoke", {
|
||||
method: "POST",
|
||||
body:
|
||||
{
|
||||
password: hashedPass, device_name: "Linux Laptop"
|
||||
}
|
||||
});
|
||||
|
||||
clearAuth();
|
||||
}
|
||||
|
||||
async function revoke() {
|
||||
clearAuth();
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
console.log("refreshing");
|
||||
try {
|
||||
const res = await fetchWithApi("/auth/refresh", {
|
||||
method: "POST"
|
||||
}) as { access_token: string };
|
||||
accessToken.value = res.access_token;
|
||||
console.log("set new access token");
|
||||
} catch (error) {
|
||||
console.error("refresh error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchUser() {
|
||||
if (!accessToken.value) return;
|
||||
const res = await fetchWithApi("/users/me") as UserResponse;
|
||||
user.value = res;
|
||||
return user.value;
|
||||
}
|
||||
|
||||
async function getUser() {
|
||||
if (!accessToken) return;
|
||||
if (!user.value) {
|
||||
await fetchUser();
|
||||
}
|
||||
return user.value;
|
||||
}
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
register,
|
||||
login,
|
||||
logout,
|
||||
revoke,
|
||||
refresh,
|
||||
getUser,
|
||||
fetchUser,
|
||||
user
|
||||
}
|
||||
}
|
123
layouts/auth.vue
123
layouts/auth.vue
|
@ -1,40 +1,133 @@
|
|||
<template>
|
||||
<div id="main-container">
|
||||
<div id="auth-form-container">
|
||||
<div id="root-container" style="margin-top: 5dvh;">
|
||||
<Loading v-if="!mounted" />
|
||||
<div v-else id="main-container">
|
||||
<div v-if="!instanceUrl">
|
||||
<div v-if="instanceError" style="color: red;">
|
||||
{{ instanceError }}
|
||||
</div>
|
||||
<form @submit="selectInstance">
|
||||
<div>
|
||||
<label for="instance-url">Instance URL</label>
|
||||
<br>
|
||||
<input type="url" name="instance-url" id="instance-url" required v-model="instanceUrlInput">
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Next</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div v-else id="auth-form-container">
|
||||
<slot />
|
||||
</div>
|
||||
<div v-if="accessToken">
|
||||
<div v-if="auth.accessToken.value">
|
||||
You're logged in!
|
||||
<button @click="logout">Log out</button>
|
||||
<form @submit="logout">
|
||||
<div>
|
||||
<label for="logout-password">Password</label>
|
||||
<br>
|
||||
<input type="password" name="logout-password" id="logout-password" v-model="form.password" required>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Log out</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<button @click="refresh">Refresh</button>
|
||||
</div>
|
||||
<div>
|
||||
<button @click="showUser">Show user</button>
|
||||
</div>
|
||||
<div>
|
||||
<button @click="getUser">Get me</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="res">
|
||||
Response:
|
||||
<p>
|
||||
{{ res }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { FetchError } from 'ofetch';
|
||||
|
||||
const mounted = ref(false);
|
||||
const redirectTo = useRoute().query.redirect_to;
|
||||
|
||||
const apiVersion = useRuntimeConfig().public.apiVersion;
|
||||
const instanceUrl = ref<string | null | undefined>(null);
|
||||
const instanceUrlInput = ref<string>();
|
||||
const instanceError = ref<string>();
|
||||
|
||||
const accessToken = useCookie("access_token");
|
||||
const auth = useAuth();
|
||||
|
||||
const res = ref();
|
||||
if (auth.accessToken.value) {
|
||||
//navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
mounted.value = true;
|
||||
const cookie = useCookie("instance_url").value;
|
||||
instanceUrl.value = cookie;
|
||||
console.log(cookie);
|
||||
console.log("set instance url to:", instanceUrl.value);
|
||||
});
|
||||
|
||||
async function selectInstance(e: Event) {
|
||||
e.preventDefault();
|
||||
if (instanceUrlInput.value) {
|
||||
const instanceUrlObj = new URL(`api/v${apiVersion}/stats`, instanceUrlInput.value.endsWith("/") ? instanceUrlInput.value : instanceUrlInput.value + "/");
|
||||
try {
|
||||
const res = await $fetch.raw(instanceUrlObj.href);
|
||||
console.log("instance res:", res);
|
||||
instanceError.value = "";
|
||||
const origin = new URL(res.url).origin;
|
||||
localStorage.setItem("instanceUrl", origin);
|
||||
instanceUrl.value = origin;
|
||||
useCookie("instance_url").value = origin;
|
||||
useCookie("api_base").value = origin + `/api/v${apiVersion}`;
|
||||
localStorage.setItem("apiBase", origin + `/api/v${apiVersion}`);
|
||||
} catch (error: any) {
|
||||
if (error instanceof FetchError) {
|
||||
console.log("Status code:", error.response?.status);
|
||||
if (error.response?.status == 404) {
|
||||
instanceError.value = "An instance with that URL does not exist or is currently down.";
|
||||
}
|
||||
}
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
password: ""
|
||||
});
|
||||
|
||||
async function logout(e: Event) {
|
||||
e.preventDefault();
|
||||
accessToken.value = null;
|
||||
useCookie("refresh_token").value = null;
|
||||
res.value = await $fetch(`/api/v${apiVersion}/auth/revoke`, { credentials: "include" });
|
||||
await auth.logout(form.password);
|
||||
console.log("logout");
|
||||
}
|
||||
|
||||
async function refresh(e: Event) {
|
||||
e.preventDefault();
|
||||
await auth.refresh();
|
||||
console.log("refreshed");
|
||||
}
|
||||
|
||||
async function getUser(e: Event) {
|
||||
e.preventDefault();
|
||||
await auth.getUser();
|
||||
console.log("user:", auth.user.value);
|
||||
}
|
||||
|
||||
async function showUser(e: Event) {
|
||||
e.preventDefault();
|
||||
console.log("user:", auth.user.value);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#main-container {
|
||||
#root-container, #main-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
@ -1,59 +1,43 @@
|
|||
<template>
|
||||
<div id="client-root">
|
||||
<div id="home" class=".homebar-item">
|
||||
<NuxtLink href="/web">
|
||||
<img src="~/assets/img/house.svg" alt="Home">
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div id="current-channel" class=".homebar-item">
|
||||
<div id="homebar">
|
||||
<div class="homebar-item">
|
||||
main bar
|
||||
</div>
|
||||
<div id="test" class=".homebar-item">test</div>
|
||||
<div id="test2" class=".homebar-item">test2</div>
|
||||
</div>
|
||||
<div id="left-column">
|
||||
<NuxtLink id="home-button" href="/">
|
||||
<Icon name="lucide:house" class="white" size="2rem" />
|
||||
</NuxtLink>
|
||||
<div id="servers-list">
|
||||
<NuxtLink v-for="server of servers" :href="'web' + server.url">
|
||||
<img src="~/assets/img/server.svg" :alt="server.name">
|
||||
<NuxtLink v-for="server of servers" :href="`/servers/${server.uuid}`">
|
||||
<Icon name="lucide:server" class="white" size="2rem" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div id="channels-list" class="main-grid-row">
|
||||
<Channel v-for="channel of channels" :name="channel.name" :href="`${useRoute().path}/${channel.id}`" />
|
||||
</div>
|
||||
<div id="message-history" class="main-grid-row">
|
||||
<Message v-for="message of messages" :img="pfp" :username="message.author.username" :text="message.text"
|
||||
:timestamp="message.timestamp" format="12" />
|
||||
<div id="message-box" class="main-grid-row">
|
||||
<input type="text" name="message-box-input" id="message-box-input">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<div id="members-list">
|
||||
<div class="member-item" v-for="member of members">
|
||||
<img src="~/assets/img/tiger-head.svg" :alt="member.displayName" width="30dvw">
|
||||
<span class="member-display-name">{{ member.displayName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<NuxtPage />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import pfp from "~/assets/img/tiger-head.svg";
|
||||
|
||||
const servers = [
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
uuid: "0197088b-e4e8-7033-8e6b-7ceb065e9acd"
|
||||
},
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
uuid: "0197088b-e4e8-7033-8e6b-7ceb065e9acd"
|
||||
},
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
uuid: "0197088b-e4e8-7033-8e6b-7ceb065e9acd"
|
||||
}
|
||||
];
|
||||
|
||||
//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
|
||||
//console.log("servers:", servers);
|
||||
const members = [
|
||||
{
|
||||
id: "3287484395",
|
||||
|
@ -93,59 +77,13 @@ const members = [
|
|||
}
|
||||
];
|
||||
|
||||
const messages = [
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "hello gamers!",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "yo what's up!",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "how are you guys?",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "im doing well",
|
||||
timestamp: 1745948498000
|
||||
}
|
||||
]
|
||||
|
||||
const channels = [
|
||||
{
|
||||
name: "#super-cool-channel",
|
||||
id: "8gh9548rg44"
|
||||
},
|
||||
{
|
||||
name: "#super-lame-channel",
|
||||
id: "hgff45387hy"
|
||||
},
|
||||
{
|
||||
name: "#secret-channel",
|
||||
id: "g8f734h87gt"
|
||||
},
|
||||
]
|
||||
function sendMessage(e: Event) {
|
||||
e.preventDefault();
|
||||
const textInput = document.getElementById("message-box-input") as HTMLInputElement;
|
||||
const text = textInput.value;
|
||||
console.log("MESSAGE SENT!!!");
|
||||
console.log("text:", text);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -155,20 +93,24 @@ const channels = [
|
|||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 4fr 18fr 4fr;
|
||||
grid-template-rows: 8dvh auto;
|
||||
grid-template-rows: 4dvh auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#homebar {
|
||||
grid-row: 1;
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
padding-left: 5dvw;
|
||||
padding-right: 5dvw;
|
||||
}
|
||||
|
||||
#client-root>div:nth-child(-n+4) {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#client-root div {
|
||||
/* border: 1px solid cyan; */
|
||||
}
|
||||
|
||||
#main-bar {}
|
||||
|
||||
#__nuxt {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
|
@ -180,8 +122,8 @@ const channels = [
|
|||
}
|
||||
|
||||
#home {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
padding-left: .5dvw;
|
||||
padding-right: .5dvw;
|
||||
}
|
||||
|
||||
#current-info {
|
||||
|
@ -194,64 +136,50 @@ const channels = [
|
|||
grid-row: 1;
|
||||
}
|
||||
|
||||
#utilities {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 3dvh;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#left-sidebar-container,
|
||||
#right-sidebar-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bottom-border {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
#message-history,
|
||||
#members-list {
|
||||
padding-top: 3dvh;
|
||||
}
|
||||
|
||||
.left-border {
|
||||
border-left: 1px solid rgb(70, 70, 70);
|
||||
#message-history {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-left: 3dvw;
|
||||
padding-right: 3dvw;
|
||||
}
|
||||
|
||||
.right-border {
|
||||
#left-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2dvh;
|
||||
padding-left: .5dvw;
|
||||
padding-right: .5dvw;
|
||||
border-right: 1px solid rgb(70, 70, 70);
|
||||
padding-top: 1.5dvh;
|
||||
}
|
||||
|
||||
#middle-left-column {
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
border-right: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#main-content {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 15fr 30fr 2fr;
|
||||
text-align: center;
|
||||
margin-left: 1dvw;
|
||||
#home-button {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
padding-bottom: 1dvh;
|
||||
}
|
||||
|
||||
#message-box {
|
||||
border: 1px solid rgb(70, 70, 70);
|
||||
width: 100%;
|
||||
margin-bottom: 1dvh;
|
||||
#servers-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1dvh;
|
||||
}
|
||||
|
||||
#message-box-input {
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-grid-row {
|
||||
/* border: 1px solid cyan; */
|
||||
}
|
||||
|
||||
#main-bar {}
|
||||
|
||||
#servers-list,
|
||||
#channels-list,
|
||||
#message-history,
|
||||
#members-list {
|
||||
margin-top: 3dvh;
|
||||
}
|
||||
</style>
|
|
@ -2,18 +2,28 @@
|
|||
export default defineNuxtConfig({
|
||||
compatibilityDate: '2024-11-01',
|
||||
devtools: { enabled: true },
|
||||
modules: ['@nuxt/eslint', '@nuxt/image', "@pinia/nuxt"],
|
||||
modules: ['@nuxt/eslint', '@nuxt/image', "@pinia/nuxt", "@nuxt/icon"],
|
||||
app: {
|
||||
/*
|
||||
Defines what prefix the client runs on
|
||||
E.g.: baseURL set to "/web" would host at https://gorb.app/web
|
||||
Default is "/" (aka root), which hosts at https://gorb.app/
|
||||
*/
|
||||
baseURL: "/",
|
||||
baseURL: "/"
|
||||
},
|
||||
runtimeConfig: {
|
||||
public: {
|
||||
apiVersion: 1
|
||||
}
|
||||
},
|
||||
/* nitro: {
|
||||
devProxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8080/api",
|
||||
changeOrigin: true,
|
||||
prependPath: true,
|
||||
ws: true
|
||||
}
|
||||
}
|
||||
} */
|
||||
})
|
|
@ -11,6 +11,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@nuxt/eslint": "^1.3.0",
|
||||
"@nuxt/icon": "1.13.0",
|
||||
"@nuxt/image": "1.10.0",
|
||||
"@pinia/nuxt": "0.11.0",
|
||||
"nuxt": "^3.17.0",
|
||||
|
@ -20,9 +21,10 @@
|
|||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.5.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39",
|
||||
"packageManager": "pnpm@10.11.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@iconify-json/lucide": "^1.2.44",
|
||||
"@types/node": "^22.15.3",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.2.6"
|
||||
|
|
268
pages/index.vue
268
pages/index.vue
|
@ -1,268 +0,0 @@
|
|||
<template>
|
||||
<div id="client-root">
|
||||
<div id="home" class=".homebar-item">
|
||||
<NuxtLink href="/web">
|
||||
<img src="~/assets/img/house.svg" alt="Home">
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div id="current-channel" class=".homebar-item">
|
||||
main bar
|
||||
</div>
|
||||
<div id="test" class=".homebar-item">test</div>
|
||||
<div id="test2" class=".homebar-item">test2</div>
|
||||
<div id="servers-list">
|
||||
<NuxtLink v-for="server of servers" :href="'web' + server.url">
|
||||
<img src="~/assets/img/server.svg" :alt="server.name">
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div id="channels-list" class="main-grid-row">
|
||||
<Channel v-for="channel of channels" :name="channel.name" :href="`${useRoute().path}/${channel.id}`" />
|
||||
</div>
|
||||
<div id="message-history" class="main-grid-row">
|
||||
<div id="messages">
|
||||
<Message v-for="message of messages" :img="pfp" :username="message.author.username" :text="message.text"
|
||||
:timestamp="message.timestamp" format="12" />
|
||||
</div>
|
||||
<div id="message-box" class="main-grid-row">
|
||||
<form @submit="sendMessage">
|
||||
<input type="text" name="message-box-input" id="message-box-input">
|
||||
<button type="submit">
|
||||
<img :src="checkmark" alt="Send" width="20">
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="members-list">
|
||||
<div class="member-item" v-for="member of members">
|
||||
<img src="~/assets/img/tiger-head.svg" :alt="member.displayName" width="30dvw">
|
||||
<span class="member-display-name">{{ member.displayName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
import pfp from "~/assets/img/tiger-head.svg";
|
||||
import checkmark from "~/assets/img/check-mark.png";
|
||||
|
||||
const servers = [
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
},
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
},
|
||||
{
|
||||
name: "Test",
|
||||
url: "/servers/284103257435"
|
||||
}
|
||||
];
|
||||
|
||||
const members = [
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed"
|
||||
}
|
||||
];
|
||||
|
||||
const messages = [
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "hello gamers!",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "yo what's up!",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "how are you guys?",
|
||||
timestamp: 1745948498000
|
||||
},
|
||||
{
|
||||
author: {
|
||||
id: "3287484395",
|
||||
username: "SauceyRed",
|
||||
avatar: "~/assets/img/tiger-head.svg"
|
||||
},
|
||||
text: "im doing well",
|
||||
timestamp: 1745948498000
|
||||
}
|
||||
]
|
||||
|
||||
const channels = [
|
||||
{
|
||||
name: "#super-cool-channel",
|
||||
id: "8gh9548rg44"
|
||||
},
|
||||
{
|
||||
name: "#super-lame-channel",
|
||||
id: "hgff45387hy"
|
||||
},
|
||||
{
|
||||
name: "#secret-channel",
|
||||
id: "g8f734h87gt"
|
||||
},
|
||||
]
|
||||
|
||||
function sendMessage(e: Event) {
|
||||
e.preventDefault();
|
||||
const textInput = document.getElementById("message-box-input") as HTMLInputElement;
|
||||
const text = textInput.value;
|
||||
console.log("MESSAGE SENT!!!");
|
||||
console.log("text:", text);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#client-root {
|
||||
/* border: 1px solid white; */
|
||||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 4fr 18fr 4fr;
|
||||
grid-template-rows: 8dvh auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#client-root>div:nth-child(-n+4) {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#__nuxt {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.grid-column {
|
||||
padding-top: 1dvh;
|
||||
}
|
||||
|
||||
#home {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
#current-info {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
#test {
|
||||
grid-column: 3;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
#utilities {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 3dvh;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#left-sidebar-container,
|
||||
#right-sidebar-container {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.member-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bottom-border {
|
||||
border-bottom: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
.left-border {
|
||||
border-left: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
.right-border {
|
||||
border-right: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#main-content {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 15fr 30fr 2fr;
|
||||
text-align: center;
|
||||
margin-left: 1dvw;
|
||||
}
|
||||
|
||||
#message-box {
|
||||
border: 1px solid rgb(70, 70, 70);
|
||||
width: 100%;
|
||||
margin-bottom: 1dvh;
|
||||
height: 5%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#message-box-input {
|
||||
width: 80%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#servers-list,
|
||||
#channels-list,
|
||||
#message-history,
|
||||
#members-list {
|
||||
margin-top: 3dvh;
|
||||
}
|
||||
|
||||
#message-history {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<NuxtLayout>
|
||||
<form @submit="login">
|
||||
<form @submit="formLogin">
|
||||
<div>
|
||||
<label for="username">Username/Email</label>
|
||||
<br>
|
||||
|
@ -18,12 +18,6 @@
|
|||
<div>
|
||||
Don't have an account? <NuxtLink href="/register">Register</NuxtLink> one!
|
||||
</div>
|
||||
<div v-if="response">
|
||||
Response:
|
||||
<p>
|
||||
{{ response }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
|
@ -38,44 +32,14 @@ const form = reactive({
|
|||
password: "",
|
||||
});
|
||||
|
||||
const response = ref();
|
||||
|
||||
//const authStore = useAuthStore();
|
||||
const accessToken = useCookie("access_token");
|
||||
const refreshToken = useCookie("refresh_token");
|
||||
const redirectTo = useRoute().query.redirect_to;
|
||||
|
||||
console.log("access token:", accessToken.value);
|
||||
console.log("refresh token:", refreshToken.value);
|
||||
const { login } = useAuth();
|
||||
|
||||
onMounted(() => {
|
||||
console.log("accessToken:", accessToken.value);
|
||||
console.log("refreshToken:", refreshToken.value);
|
||||
|
||||
if (accessToken.value) {
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
});
|
||||
|
||||
const apiVersion = useRuntimeConfig().public.apiVersion;
|
||||
|
||||
async function login(e: Event) {
|
||||
async function formLogin(e: Event) {
|
||||
e.preventDefault();
|
||||
console.log("Sending login data");
|
||||
const hashedPass = await hashPassword(form.password);
|
||||
console.log("hashedPass:", hashedPass);
|
||||
//authStore.setAccessToken(accessToken);
|
||||
const res = await $fetch(`/api/v${apiVersion}/auth/login`, {
|
||||
method: "POST", body:
|
||||
{
|
||||
username: form.username, password: hashedPass
|
||||
}
|
||||
}) as { access_token: string, refresh_token: string };
|
||||
response.value = res;
|
||||
accessToken.value = res.access_token;
|
||||
console.log("set access token:", accessToken.value);
|
||||
const refreshToken = useCookie("refresh_token", { secure: true, httpOnly: false });
|
||||
refreshToken.value = res.refresh_token;
|
||||
await login(form.username, form.password, "Linux Laptop");
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<input type="password" name="password" id="password" v-model="form.password">
|
||||
</div>
|
||||
<div>
|
||||
<label for="repeat-password">Password</label>
|
||||
<label for="repeat-password">Repeat password</label>
|
||||
<br>
|
||||
<input type="password" name="repeat-password" id="repeat-password" v-model="form.repeatPassword">
|
||||
</div>
|
||||
|
@ -35,12 +35,6 @@
|
|||
<div>
|
||||
Already have an account? <NuxtLink href="/login">Log in</NuxtLink>!
|
||||
</div>
|
||||
<div v-if="response">
|
||||
Response:
|
||||
<p>
|
||||
{{ response }}
|
||||
</p>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
|
@ -57,8 +51,6 @@ const form = reactive({
|
|||
repeatPassword: ""
|
||||
});
|
||||
|
||||
const response = ref();
|
||||
|
||||
/*
|
||||
const errorMessages = reactive({
|
||||
username: {
|
||||
|
@ -81,18 +73,11 @@ const errorMessages = reactive({
|
|||
*/
|
||||
|
||||
//const authStore = useAuthStore();
|
||||
const accessToken = useCookie("access_token");
|
||||
const refreshToken = useCookie("refresh_token");
|
||||
const auth = useAuth();
|
||||
const redirectTo = useRoute().query.redirect_to;
|
||||
|
||||
console.log("access token:", accessToken.value);
|
||||
console.log("refresh token:", refreshToken.value);
|
||||
|
||||
onMounted(() => {
|
||||
console.log("accessToken:", accessToken.value);
|
||||
console.log("refreshToken:", refreshToken.value);
|
||||
|
||||
if (accessToken.value) {
|
||||
if (auth.accessToken.value) {
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
});
|
||||
|
@ -135,19 +120,7 @@ const apiVersion = useRuntimeConfig().public.apiVersion;
|
|||
async function register(e: Event) {
|
||||
e.preventDefault();
|
||||
console.log("Sending registration data");
|
||||
const hashedPass = await hashPassword(form.password);
|
||||
const res = await $fetch(`/api/v${apiVersion}/auth/register`, {
|
||||
method: "POST", body:
|
||||
{
|
||||
email: form.email, username: form.username, password: hashedPass
|
||||
}
|
||||
}) as { access_token: string, refresh_token: string };
|
||||
response.value = res;
|
||||
//authStore.setAccessToken(accessToken);
|
||||
accessToken.value = res.access_token;
|
||||
console.log("set access token:", accessToken.value);
|
||||
const refreshToken = useCookie("refresh_token", { secure: true, httpOnly: false });
|
||||
refreshToken.value = res.refresh_token;
|
||||
await auth.register(form.username, form.email, form.password);
|
||||
//return navigateTo(redirectTo ? redirectTo as string : useAppConfig().baseURL as string);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,144 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
<NuxtLayout name="client">
|
||||
<div id="middle-left-column" class="main-grid-row">
|
||||
<div id="server-title">
|
||||
<h3>
|
||||
{{ server?.name }}
|
||||
<span>
|
||||
<button @click="showServerSettings">
|
||||
<Icon name="lucide:settings" />
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button @click="toggleInvitePopup">
|
||||
<Icon name="lucide:share-2" />
|
||||
</button>
|
||||
</span>
|
||||
<InvitePopup v-if="showInvitePopup" />
|
||||
</h3>
|
||||
</div>
|
||||
<div id="channels-list">
|
||||
<Channel v-for="channel of channels" v-if="route.params.channelId == channel?.uuid" :name="channel.name"
|
||||
:href="route.path" :current="true" />
|
||||
<Channel v-for="channel of channels" v-else :name="channel.name"
|
||||
:href="`/servers/${route.params.serverId}/channels/${channel.uuid}`" />
|
||||
</div>
|
||||
</div>
|
||||
<MessageArea :channel-url="channelUrlPath" />
|
||||
<div id="members-list">
|
||||
<div class="member-item" v-for="member of members">
|
||||
<img v-if="member.avatar" :src="member.avatar" :alt="member.displayName" height="30" />
|
||||
<Icon v-else name="lucide:user" size="30" />
|
||||
<span class="member-display-name">{{ member.displayName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLayout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const server: GuildResponse | undefined = await fetchWithApi(`servers/${route.params.serverId}`);
|
||||
|
||||
const channels: ChannelResponse[] | undefined = await fetchWithApi(
|
||||
`servers/${route.params.serverId}/channels`
|
||||
);
|
||||
const channel: ChannelResponse | undefined = await fetchWithApi(
|
||||
route.path
|
||||
);
|
||||
|
||||
const channelUrlPath = `servers/${route.params.serverId}/channels/${route.params.channelId}`;
|
||||
|
||||
console.log("channel:", channel);
|
||||
|
||||
const showInvitePopup = ref(false);
|
||||
|
||||
import type { ChannelResponse, GuildResponse, MessageResponse } from "~/types/interfaces";
|
||||
|
||||
//const servers = await fetchWithApi("/servers") as { uuid: string, name: string, description: string }[];
|
||||
//console.log("servers:", servers);
|
||||
const members = [
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
},
|
||||
{
|
||||
id: "3287484395",
|
||||
displayName: "SauceyRed",
|
||||
avatar: ""
|
||||
}
|
||||
];
|
||||
|
||||
function showServerSettings() { }
|
||||
|
||||
function toggleInvitePopup(e: Event) {
|
||||
e.preventDefault();
|
||||
showInvitePopup.value = !showInvitePopup.value;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.member-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: .5em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#members-list {
|
||||
padding-top: 3dvh;
|
||||
}
|
||||
|
||||
#middle-left-column {
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
border-right: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#members-list {
|
||||
padding-left: 1dvw;
|
||||
padding-right: 1dvw;
|
||||
border-left: 1px solid rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,10 +1,12 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
Hello
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const server = await fetchWithApi(`/servers/${useRoute().params.serverId}`);
|
||||
console.log("server:", server);
|
||||
|
||||
</script>
|
||||
|
||||
|
|
207
pnpm-lock.yaml
generated
207
pnpm-lock.yaml
generated
|
@ -11,6 +11,9 @@ importers:
|
|||
'@nuxt/eslint':
|
||||
specifier: ^1.3.0
|
||||
version: 1.3.0(@vue/compiler-sfc@3.5.13)(eslint@9.25.1(jiti@2.4.2))(magicast@0.3.5)(typescript@5.8.3)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1))
|
||||
'@nuxt/icon':
|
||||
specifier: 1.13.0
|
||||
version: 1.13.0(magicast@0.3.5)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))
|
||||
'@nuxt/image':
|
||||
specifier: 1.10.0
|
||||
version: 1.10.0(@netlify/blobs@8.2.0)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)
|
||||
|
@ -36,6 +39,9 @@ importers:
|
|||
specifier: ^4.5.1
|
||||
version: 4.5.1(vue@3.5.13(typescript@5.8.3))
|
||||
devDependencies:
|
||||
'@iconify-json/lucide':
|
||||
specifier: ^1.2.44
|
||||
version: 1.2.44
|
||||
'@types/node':
|
||||
specifier: ^22.15.3
|
||||
version: 22.15.3
|
||||
|
@ -55,6 +61,9 @@ packages:
|
|||
'@antfu/install-pkg@1.0.0':
|
||||
resolution: {integrity: sha512-xvX6P/lo1B3ej0OsaErAjqgFYzYVcJpamjLAFLYh9vRJngBrMoUG7aVnrGTeqM7yxbyTD5p3F2+0/QUEh8Vzhw==}
|
||||
|
||||
'@antfu/utils@8.1.1':
|
||||
resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==}
|
||||
|
||||
'@apidevtools/json-schema-ref-parser@11.9.3':
|
||||
resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==}
|
||||
engines: {node: '>= 16'}
|
||||
|
@ -578,6 +587,23 @@ packages:
|
|||
resolution: {integrity: sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@iconify-json/lucide@1.2.44':
|
||||
resolution: {integrity: sha512-lOwXoOtkFm4Zj1duPj5xqhPKKMUvxVRMfIHi0YDfwLjEEgrhzsJZ4JCpE119L8P8p6zi4on4b9cPZdVXCUuDoQ==}
|
||||
|
||||
'@iconify/collections@1.0.551':
|
||||
resolution: {integrity: sha512-5Jy+BoI4nsNE1nHqukQh5ZxxppGThyU3VR794PLmZtbF7YJGYYa4SETnGRl4hz/5lKiTe8OhlXf5Ns45ZGLz5w==}
|
||||
|
||||
'@iconify/types@2.0.0':
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
|
||||
'@iconify/utils@2.3.0':
|
||||
resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==}
|
||||
|
||||
'@iconify/vue@5.0.0':
|
||||
resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==}
|
||||
peerDependencies:
|
||||
vue: '>=3'
|
||||
|
||||
'@ioredis/commands@1.2.0':
|
||||
resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==}
|
||||
|
||||
|
@ -704,6 +730,11 @@ packages:
|
|||
peerDependencies:
|
||||
vite: '>=6.0'
|
||||
|
||||
'@nuxt/devtools-kit@2.4.1':
|
||||
resolution: {integrity: sha512-taA2Nm03JiV3I+SEYS/u1AfjvLm3V9PO8lh0xLsUk/2mlUnL6GZ9xLXrp8VRg11HHt7EPXERGQh8h4iSPU2bSQ==}
|
||||
peerDependencies:
|
||||
vite: '>=6.0'
|
||||
|
||||
'@nuxt/devtools-wizard@2.4.0':
|
||||
resolution: {integrity: sha512-3/5S2zpl79rE1b/lh8M/2lDNsYiYIXXHZmCwsYPuFJA6DilLQo/VY44oq6cY0Q1up32HYB3h1Te/q3ELbsb+ag==}
|
||||
hasBin: true
|
||||
|
@ -740,6 +771,9 @@ packages:
|
|||
vite-plugin-eslint2:
|
||||
optional: true
|
||||
|
||||
'@nuxt/icon@1.13.0':
|
||||
resolution: {integrity: sha512-Sft1DZj/U5Up60DMnhp+hRDNDXRkMhwHocxgDkDkAPBxqR8PRyvzxcMIy3rjGMu0s+fB6ZdLs6vtaWzjWuerQQ==}
|
||||
|
||||
'@nuxt/image@1.10.0':
|
||||
resolution: {integrity: sha512-/B58GeEmme7bkmQUrXzEw8P9sJb9BkMaYZqLDtq8ZdDLEddE3P4nVya8RQPB+p4b7EdqWajpPqdy1A2ZPLev/A==}
|
||||
engines: {node: '>=18.20.6'}
|
||||
|
@ -748,10 +782,18 @@ packages:
|
|||
resolution: {integrity: sha512-+aS+Enqqo2qSbyl0APPPxX8BPYsaRcZ8dFRbpCOfK38lv2ckoHKCWNkT8L/7q2w+1pjNZaxlUoW9Mku1vdEb/A==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
'@nuxt/kit@3.17.4':
|
||||
resolution: {integrity: sha512-l+hY8sy2XFfg3PigZj+PTu6+KIJzmbACTRimn1ew/gtCz+F38f6KTF4sMRTN5CUxiB8TRENgEonASmkAWfpO9Q==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
'@nuxt/schema@3.17.0':
|
||||
resolution: {integrity: sha512-BwHD1NBtZRlk+qPZYvNzzdp7MG8s4i5gmTQ+12hbxc9x09osB9RivAU2ekwMMLfykx90wDszDu0DJ5Zec4Svgg==}
|
||||
engines: {node: ^14.18.0 || >=16.10.0}
|
||||
|
||||
'@nuxt/schema@3.17.4':
|
||||
resolution: {integrity: sha512-bsfJdWjKNYLkVQt7Ykr9YsAql1u8Tuo6iecSUOltTIhsvAIYsknRFPHoNKNmaiv/L6FgCQgUgQppPTPUAXiJQQ==}
|
||||
engines: {node: ^14.18.0 || >=16.10.0}
|
||||
|
||||
'@nuxt/telemetry@2.6.6':
|
||||
resolution: {integrity: sha512-Zh4HJLjzvm3Cq9w6sfzIFyH9ozK5ePYVfCUzzUQNiZojFsI2k1QkSBrVI9BGc6ArKXj/O6rkI6w7qQ+ouL8Cag==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
@ -1409,6 +1451,9 @@ packages:
|
|||
'@vue/shared@3.5.13':
|
||||
resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
|
||||
|
||||
'@vue/shared@3.5.14':
|
||||
resolution: {integrity: sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==}
|
||||
|
||||
'@whatwg-node/disposablestack@0.0.6':
|
||||
resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
@ -1650,6 +1695,14 @@ packages:
|
|||
magicast:
|
||||
optional: true
|
||||
|
||||
c12@3.0.4:
|
||||
resolution: {integrity: sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==}
|
||||
peerDependencies:
|
||||
magicast: ^0.3.5
|
||||
peerDependenciesMeta:
|
||||
magicast:
|
||||
optional: true
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -2546,6 +2599,10 @@ packages:
|
|||
resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
globals@15.15.0:
|
||||
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
globals@16.0.0:
|
||||
resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -2873,6 +2930,9 @@ packages:
|
|||
knitwork@1.2.0:
|
||||
resolution: {integrity: sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==}
|
||||
|
||||
kolorist@1.8.0:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
|
||||
|
||||
kuler@2.0.0:
|
||||
resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
|
||||
|
||||
|
@ -3917,6 +3977,11 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.2:
|
||||
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
send@1.2.0:
|
||||
resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
|
||||
engines: {node: '>= 18'}
|
||||
|
@ -4287,6 +4352,10 @@ packages:
|
|||
resolution: {integrity: sha512-8jL3T+FKDg+qLFX55X9j92uFRqH5vWrNlf/eJb5IQlQB5q5wjooXQDXP1ulhJJQHbosBmlKhBo/ZVS5jHlcJGA==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
unimport@5.0.1:
|
||||
resolution: {integrity: sha512-1YWzPj6wYhtwHE+9LxRlyqP4DiRrhGfJxdtH475im8ktyZXO3jHj/3PZ97zDdvkYoovFdi0K4SKl3a7l92v3sQ==}
|
||||
engines: {node: '>=18.12.0'}
|
||||
|
||||
unixify@1.0.0:
|
||||
resolution: {integrity: sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -4684,6 +4753,8 @@ snapshots:
|
|||
package-manager-detector: 0.2.11
|
||||
tinyexec: 0.3.2
|
||||
|
||||
'@antfu/utils@8.1.1': {}
|
||||
|
||||
'@apidevtools/json-schema-ref-parser@11.9.3':
|
||||
dependencies:
|
||||
'@jsdevtools/ono': 7.1.3
|
||||
|
@ -5143,6 +5214,34 @@ snapshots:
|
|||
|
||||
'@humanwhocodes/retry@0.4.2': {}
|
||||
|
||||
'@iconify-json/lucide@1.2.44':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify/collections@1.0.551':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify/types@2.0.0': {}
|
||||
|
||||
'@iconify/utils@2.3.0':
|
||||
dependencies:
|
||||
'@antfu/install-pkg': 1.0.0
|
||||
'@antfu/utils': 8.1.1
|
||||
'@iconify/types': 2.0.0
|
||||
debug: 4.4.0
|
||||
globals: 15.15.0
|
||||
kolorist: 1.8.0
|
||||
local-pkg: 1.1.1
|
||||
mlly: 1.7.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@iconify/vue@5.0.0(vue@3.5.13(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
vue: 3.5.13(typescript@5.8.3)
|
||||
|
||||
'@ioredis/commands@1.2.0': {}
|
||||
|
||||
'@isaacs/cliui@8.0.2':
|
||||
|
@ -5377,6 +5476,15 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/devtools-kit@2.4.1(magicast@0.3.5)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1))':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
'@nuxt/schema': 3.17.4
|
||||
execa: 8.0.1
|
||||
vite: 6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1)
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/devtools-wizard@2.4.0':
|
||||
dependencies:
|
||||
consola: 3.4.2
|
||||
|
@ -5492,6 +5600,28 @@ snapshots:
|
|||
- utf-8-validate
|
||||
- vite
|
||||
|
||||
'@nuxt/icon@1.13.0(magicast@0.3.5)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1))(vue@3.5.13(typescript@5.8.3))':
|
||||
dependencies:
|
||||
'@iconify/collections': 1.0.551
|
||||
'@iconify/types': 2.0.0
|
||||
'@iconify/utils': 2.3.0
|
||||
'@iconify/vue': 5.0.0(vue@3.5.13(typescript@5.8.3))
|
||||
'@nuxt/devtools-kit': 2.4.1(magicast@0.3.5)(vite@6.3.3(@types/node@22.15.3)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.1))
|
||||
'@nuxt/kit': 3.17.4(magicast@0.3.5)
|
||||
consola: 3.4.2
|
||||
local-pkg: 1.1.1
|
||||
mlly: 1.7.4
|
||||
ohash: 2.0.11
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
std-env: 3.9.0
|
||||
tinyglobby: 0.2.13
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
- supports-color
|
||||
- vite
|
||||
- vue
|
||||
|
||||
'@nuxt/image@1.10.0(@netlify/blobs@8.2.0)(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.0(magicast@0.3.5)
|
||||
|
@ -5555,6 +5685,33 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/kit@3.17.4(magicast@0.3.5)':
|
||||
dependencies:
|
||||
c12: 3.0.4(magicast@0.3.5)
|
||||
consola: 3.4.2
|
||||
defu: 6.1.4
|
||||
destr: 2.0.5
|
||||
errx: 0.1.0
|
||||
exsolve: 1.0.5
|
||||
ignore: 7.0.4
|
||||
jiti: 2.4.2
|
||||
klona: 2.0.6
|
||||
knitwork: 1.2.0
|
||||
mlly: 1.7.4
|
||||
ohash: 2.0.11
|
||||
pathe: 2.0.3
|
||||
pkg-types: 2.1.0
|
||||
scule: 1.3.0
|
||||
semver: 7.7.2
|
||||
std-env: 3.9.0
|
||||
tinyglobby: 0.2.13
|
||||
ufo: 1.6.1
|
||||
unctx: 2.4.1
|
||||
unimport: 5.0.1
|
||||
untyped: 2.0.0
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
'@nuxt/schema@3.17.0':
|
||||
dependencies:
|
||||
consola: 3.4.2
|
||||
|
@ -5562,6 +5719,14 @@ snapshots:
|
|||
pathe: 2.0.3
|
||||
std-env: 3.9.0
|
||||
|
||||
'@nuxt/schema@3.17.4':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.14
|
||||
consola: 3.4.2
|
||||
defu: 6.1.4
|
||||
pathe: 2.0.3
|
||||
std-env: 3.9.0
|
||||
|
||||
'@nuxt/telemetry@2.6.6(magicast@0.3.5)':
|
||||
dependencies:
|
||||
'@nuxt/kit': 3.17.0(magicast@0.3.5)
|
||||
|
@ -6270,6 +6435,8 @@ snapshots:
|
|||
|
||||
'@vue/shared@3.5.13': {}
|
||||
|
||||
'@vue/shared@3.5.14': {}
|
||||
|
||||
'@whatwg-node/disposablestack@0.0.6':
|
||||
dependencies:
|
||||
'@whatwg-node/promise-helpers': 1.3.1
|
||||
|
@ -6520,6 +6687,23 @@ snapshots:
|
|||
optionalDependencies:
|
||||
magicast: 0.3.5
|
||||
|
||||
c12@3.0.4(magicast@0.3.5):
|
||||
dependencies:
|
||||
chokidar: 4.0.3
|
||||
confbox: 0.2.2
|
||||
defu: 6.1.4
|
||||
dotenv: 16.5.0
|
||||
exsolve: 1.0.5
|
||||
giget: 2.0.0
|
||||
jiti: 2.4.2
|
||||
ohash: 2.0.11
|
||||
pathe: 2.0.3
|
||||
perfect-debounce: 1.0.0
|
||||
pkg-types: 2.1.0
|
||||
rc9: 2.1.2
|
||||
optionalDependencies:
|
||||
magicast: 0.3.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
|
||||
call-bind-apply-helpers@1.0.2:
|
||||
|
@ -7505,6 +7689,8 @@ snapshots:
|
|||
|
||||
globals@14.0.0: {}
|
||||
|
||||
globals@15.15.0: {}
|
||||
|
||||
globals@16.0.0: {}
|
||||
|
||||
globby@11.1.0:
|
||||
|
@ -7824,6 +8010,8 @@ snapshots:
|
|||
|
||||
knitwork@1.2.0: {}
|
||||
|
||||
kolorist@1.8.0: {}
|
||||
|
||||
kuler@2.0.0: {}
|
||||
|
||||
lambda-local@2.2.0:
|
||||
|
@ -9030,6 +9218,8 @@ snapshots:
|
|||
|
||||
semver@7.7.1: {}
|
||||
|
||||
semver@7.7.2: {}
|
||||
|
||||
send@1.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
|
@ -9475,6 +9665,23 @@ snapshots:
|
|||
unplugin: 2.3.2
|
||||
unplugin-utils: 0.2.4
|
||||
|
||||
unimport@5.0.1:
|
||||
dependencies:
|
||||
acorn: 8.14.1
|
||||
escape-string-regexp: 5.0.0
|
||||
estree-walker: 3.0.3
|
||||
local-pkg: 1.1.1
|
||||
magic-string: 0.30.17
|
||||
mlly: 1.7.4
|
||||
pathe: 2.0.3
|
||||
picomatch: 4.0.2
|
||||
pkg-types: 2.1.0
|
||||
scule: 1.3.0
|
||||
strip-literal: 3.0.0
|
||||
tinyglobby: 0.2.13
|
||||
unplugin: 2.3.2
|
||||
unplugin-utils: 0.2.4
|
||||
|
||||
unixify@1.0.0:
|
||||
dependencies:
|
||||
normalize-path: 2.1.1
|
||||
|
|
6
types.d.ts
vendored
Normal file
6
types.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
export interface GorbStats {
|
||||
accounts: number,
|
||||
uptime: number,
|
||||
version: string,
|
||||
build_number: string
|
||||
}
|
54
types/interfaces.ts
Normal file
54
types/interfaces.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
export interface ChannelPermissionResponse {
|
||||
channel_uuid: string,
|
||||
role_uuid: string,
|
||||
permissions: number
|
||||
}
|
||||
|
||||
export interface RoleResponse {
|
||||
uuid: string,
|
||||
guild_uuid: string,
|
||||
name: string,
|
||||
color: number,
|
||||
position: number,
|
||||
permissions: number
|
||||
}
|
||||
|
||||
export interface GuildResponse {
|
||||
uuid: string,
|
||||
name: string,
|
||||
description: string | null,
|
||||
icon: string | null,
|
||||
owner_uuid: string,
|
||||
roles: [],
|
||||
member_count: number
|
||||
}
|
||||
|
||||
export interface ChannelResponse {
|
||||
uuid: string,
|
||||
guild_uuid: string,
|
||||
name: string,
|
||||
description: string,
|
||||
permissions: ChannelPermissionResponse[]
|
||||
}
|
||||
|
||||
export interface MessageResponse {
|
||||
uuid: string
|
||||
channel_uuid: string
|
||||
user_uuid: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface InviteResponse {
|
||||
id: string,
|
||||
user_uuid: string,
|
||||
guild_uuid: string
|
||||
}
|
||||
|
||||
export interface UserResponse {
|
||||
uuid: string,
|
||||
username: string,
|
||||
display_name: string | null,
|
||||
avatar: string | null,
|
||||
email: string,
|
||||
email_verified: boolean
|
||||
}
|
71
utils/fetchWithApi.ts
Normal file
71
utils/fetchWithApi.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import type { NitroFetchRequest, NitroFetchOptions } from "nitropack";
|
||||
|
||||
export default async <T>(path: string, options: NitroFetchOptions<string> = {}) => {
|
||||
console.log("path received:", path);
|
||||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
if (path.endsWith("/")) {
|
||||
path = path.slice(0, path.lastIndexOf("/"));
|
||||
}
|
||||
console.log("formatted path:", path);
|
||||
try {
|
||||
const accessToken = useCookie("access_token");
|
||||
console.log("access token:", accessToken.value);
|
||||
const apiBase = useCookie("api_base").value;
|
||||
const apiVersion = useRuntimeConfig().public.apiVersion;
|
||||
console.log("heyoooo")
|
||||
console.log("apiBase:", apiBase);
|
||||
if (!apiBase) {
|
||||
console.log("no api base");
|
||||
return;
|
||||
}
|
||||
console.log("path:", path)
|
||||
const { revoke, refresh } = useAuth();
|
||||
console.log("access token 2:", accessToken.value);
|
||||
|
||||
let headers: HeadersInit = {};
|
||||
|
||||
if (accessToken.value) {
|
||||
headers = {
|
||||
...options.headers,
|
||||
"Authorization": `Bearer ${accessToken.value}`
|
||||
};
|
||||
} else {
|
||||
headers = {
|
||||
...options.headers
|
||||
};
|
||||
}
|
||||
|
||||
let reauthFailed = false;
|
||||
while (!reauthFailed) {
|
||||
try {
|
||||
console.log("fetching:", URL.parse(apiBase + path));
|
||||
const res = await $fetch<T>(URL.parse(apiBase + path)!.href, {
|
||||
...options,
|
||||
headers,
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 401) {
|
||||
if (!path.startsWith("/auth/refresh")) {
|
||||
try {
|
||||
await refresh();
|
||||
} catch (error: any) {
|
||||
if (error?.response?.status === 401) {
|
||||
reauthFailed = true;
|
||||
await revoke();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("error:", error);
|
||||
}
|
||||
}
|
3
utils/sleep.ts
Normal file
3
utils/sleep.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default async (ms: number): Promise<NodeJS.Timeout> => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
6
utils/uuidToTimestamp.ts
Normal file
6
utils/uuidToTimestamp.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default (uuid: string) => {
|
||||
const parts = uuid.split("-");
|
||||
const bits = parts[0] + parts[1].slice(0, 4);
|
||||
const timestamp = parseInt(bits, 16);
|
||||
return timestamp;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue