stream day 2
This commit is contained in:
30
App.vue
Normal file
30
App.vue
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<NuxtLayout>
|
||||||
|
<div class="flex h-screen max-h-screen">
|
||||||
|
<Nav />
|
||||||
|
<Sidebar />
|
||||||
|
<NuxtPage />
|
||||||
|
</div>
|
||||||
|
</NuxtLayout>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useUserStore } from '~/stores/user'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async setup() {
|
||||||
|
const userStore = useUserStore()
|
||||||
|
const sessionToken = useCookie('sessionToken')
|
||||||
|
console.log(sessionToken.value)
|
||||||
|
if (userStore.user.id === undefined && sessionToken.value) {
|
||||||
|
const user = await $fetch('/api/getCurrentUser')
|
||||||
|
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
userStore.setUser(user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
116
components/Nav.vue
Normal file
116
components/Nav.vue
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="p-4 bg-zinc-800 grid grid-cols-1 grid-rows-[56px_1fr_56px] h-screen text-white relative">
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
@click="openServer('@me')"
|
||||||
|
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300">
|
||||||
|
<svg width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 12c2-2.96 0-7-1-8c0 3.038-1.773 4.741-3 6c-1.226 1.26-2 3.24-2 5a6 6 0 1 0 12 0c0-1.532-1.056-3.94-2-5c-1.786 3-2.791 3-4 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overflow-y-scroll my-2 flex gap-2">
|
||||||
|
<div v-for="server in servers"
|
||||||
|
:key="server.id"
|
||||||
|
@click="openServer(server.id)"
|
||||||
|
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300 h-[56px] w-[56px]">
|
||||||
|
<svg width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 256 154">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="svgIDa"
|
||||||
|
x1="-2.778%"
|
||||||
|
x2="100%"
|
||||||
|
y1="32%"
|
||||||
|
y2="67.556%">
|
||||||
|
<stop offset="0%"
|
||||||
|
stop-color="#2298BD" />
|
||||||
|
<stop offset="100%"
|
||||||
|
stop-color="#0ED7B5" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<path fill="url(#svgIDa)"
|
||||||
|
d="M128 0C93.867 0 72.533 17.067 64 51.2C76.8 34.133 91.733 27.733 108.8 32c9.737 2.434 16.697 9.499 24.401 17.318C145.751 62.057 160.275 76.8 192 76.8c34.133 0 55.467-17.067 64-51.2c-12.8 17.067-27.733 23.467-44.8 19.2c-9.737-2.434-16.697-9.499-24.401-17.318C174.249 14.743 159.725 0 128 0ZM64 76.8C29.867 76.8 8.533 93.867 0 128c12.8-17.067 27.733-23.467 44.8-19.2c9.737 2.434 16.697 9.499 24.401 17.318C81.751 138.857 96.275 153.6 128 153.6c34.133 0 55.467-17.067 64-51.2c-12.8 17.067-27.733 23.467-44.8 19.2c-9.737-2.434-16.697-9.499-24.401-17.318C110.249 91.543 95.725 76.8 64 76.8Z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div @click="createServerModelOpen = true"
|
||||||
|
class="bg-zinc-600/80 p-3 rounded-full transition-all hover:rounded-2xl ease-in-out hover:bg-zinc-500/60 duration-300">
|
||||||
|
<svg width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M12 5v14m-7-7h14" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div v-if="createServerModelOpen"
|
||||||
|
class="absolute z-10 top-0 bottom-0 left-0 right-0">
|
||||||
|
<div class="bg-zinc-900/80 w-screen h-screen"
|
||||||
|
@click="createServerModelOpen = false">
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="p-4 z-20 absolute bg-zinc-800 shadow-md rounded-md -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 text-white">
|
||||||
|
<h2 class="font-semibold text-xl">
|
||||||
|
Create a server:
|
||||||
|
</h2>
|
||||||
|
<div>
|
||||||
|
<form @submit.prevent="createServer"
|
||||||
|
class="w-3/5">
|
||||||
|
<input v-model="serverName"
|
||||||
|
type="text"
|
||||||
|
class="py-2 px-3 rounded-md mb-2 bg-zinc-700 shadow-md border border-zinc-700/80"
|
||||||
|
placeholder="Server name" />
|
||||||
|
<input type="submit"
|
||||||
|
class="py-2 px-3 rounded-md bg-zinc-700 shadow-md border border-zinc-700/80" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
servers: useServerStore().servers,
|
||||||
|
createServerModelOpen: false,
|
||||||
|
serverName: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async createServer() {
|
||||||
|
const serverStore = useServerStore();
|
||||||
|
const { server } = await $fetch('/api/channel/create', { method: 'post', body: { serverName: this.serverName } })
|
||||||
|
this.createServerModelOpen = false;
|
||||||
|
this.serverName = '';
|
||||||
|
serverStore.addServer(server)
|
||||||
|
},
|
||||||
|
openServer(id: string): void {
|
||||||
|
const router = useRouter();
|
||||||
|
useServerStore().servers.find((e: unknown, i: number) => {
|
||||||
|
if (e.id === id) {
|
||||||
|
useServerStore().activeServer = i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
router.push({ path: `/channel/${id}` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
64
components/Sidebar.vue
Normal file
64
components/Sidebar.vue
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex bg-zinc-700 w-60 h-screen shadow-sm text-white select-none">
|
||||||
|
<div class="w-full"
|
||||||
|
v-if="server">
|
||||||
|
<div class="flex p-4 border-b border-zinc-600/80">
|
||||||
|
<h4 class="text-lg font-semibold w-fit ">
|
||||||
|
{{ server.name }}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-y-1.5 px-1.5 mt-2 flex-col">
|
||||||
|
<div class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm"
|
||||||
|
v-for="channel in server.channels"
|
||||||
|
:key="channel.id">
|
||||||
|
<svg width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
||||||
|
</svg> {{ channel.name }}
|
||||||
|
</div>
|
||||||
|
<div class="flex text-center hover:bg-zinc-600/70 px-2 py-1.5 w-full transition-colors rounded drop-shadow-sm"
|
||||||
|
v-for="channel in server.channels"
|
||||||
|
:key="channel.id">
|
||||||
|
<svg width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M5 9h14M5 15h14M11 4L7 20M17 4l-4 16" />
|
||||||
|
</svg> {{ channel.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useUserStore } from '~/stores/user'
|
||||||
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
user: useUserStore().user,
|
||||||
|
server: useServerStore().servers[useServerStore().activeServer]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
if (route.path.includes('@me')) {
|
||||||
|
this.server = useServerStore().dms[useServerStore().activeServer]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="hover:scale-105 cursor-pointer duration-500 flex flex-col justify-center items-center text-center rounded shadow-xl border-2 border-gray-500 h-full w-full p-6">
|
|
||||||
<h2 class="text-lg text-gray-700">{{name}}</h2>
|
|
||||||
<p class="text-sm text-gray-600">{{description}}</p>
|
|
||||||
<a
|
|
||||||
class="text-sm text-violet-500 underline decoration-dotted underline-offset-2 cursor-pointer mt-3"
|
|
||||||
href={{documentation}}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
props: ['name', 'description', 'documentation']
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -20,4 +20,18 @@ export default {
|
|||||||
autoprefixer: {},
|
autoprefixer: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
modules: [
|
||||||
|
[
|
||||||
|
'@pinia/nuxt',
|
||||||
|
{
|
||||||
|
autoImports: [
|
||||||
|
// automatically imports `defineStore`
|
||||||
|
'defineStore', // import { defineStore } from 'pinia'
|
||||||
|
// automatically imports `defineStore` as `definePiniaStore`
|
||||||
|
['defineStore', 'definePiniaStore'], // import { defineStore as definePiniaStore } from 'pinia'
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
90
package-lock.json
generated
90
package-lock.json
generated
@@ -5,9 +5,11 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/nuxt": "^0.4.6",
|
||||||
"@prisma/client": "^4.8.0",
|
"@prisma/client": "^4.8.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"nuxt": "^3.0.0",
|
"nuxt": "^3.0.0",
|
||||||
|
"pinia": "^2.0.28",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -831,6 +833,18 @@
|
|||||||
"vue": "^3.2.45"
|
"vue": "^3.2.45"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@pinia/nuxt": {
|
||||||
|
"version": "0.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pinia/nuxt/-/nuxt-0.4.6.tgz",
|
||||||
|
"integrity": "sha512-HjrYEfLdFpmsjhicPJgL36jVhzHWukIQPFFHGTSF84Cplu+f2nY2XHKqe9ToHzE9rLee2RjLOwAzOnXa/I/u6A==",
|
||||||
|
"dependencies": {
|
||||||
|
"@nuxt/kit": "^3.0.0",
|
||||||
|
"pinia": ">=2.0.27"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@prisma/client": {
|
"node_modules/@prisma/client": {
|
||||||
"version": "4.8.0",
|
"version": "4.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
||||||
@@ -5065,6 +5079,56 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pinia": {
|
||||||
|
"version": "2.0.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.28.tgz",
|
||||||
|
"integrity": "sha512-YClq9DkqCblq9rlyUual7ezMu/iICWdBtfJrDt4oWU9Zxpijyz7xB2xTwx57DaBQ96UGvvTMORzALr+iO5PVMw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.4.5",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.4.0",
|
||||||
|
"typescript": ">=4.4.4",
|
||||||
|
"vue": "^2.6.14 || ^3.2.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pinia/node_modules/vue-demi": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"bin": {
|
||||||
|
"vue-demi-fix": "bin/vue-demi-fix.js",
|
||||||
|
"vue-demi-switch": "bin/vue-demi-switch.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/antfu"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@vue/composition-api": "^1.0.0-rc.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.6.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@vue/composition-api": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pkg-types": {
|
"node_modules/pkg-types": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz",
|
||||||
@@ -8037,6 +8101,15 @@
|
|||||||
"vue-bundle-renderer": "^1.0.0"
|
"vue-bundle-renderer": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@pinia/nuxt": {
|
||||||
|
"version": "0.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pinia/nuxt/-/nuxt-0.4.6.tgz",
|
||||||
|
"integrity": "sha512-HjrYEfLdFpmsjhicPJgL36jVhzHWukIQPFFHGTSF84Cplu+f2nY2XHKqe9ToHzE9rLee2RjLOwAzOnXa/I/u6A==",
|
||||||
|
"requires": {
|
||||||
|
"@nuxt/kit": "^3.0.0",
|
||||||
|
"pinia": ">=2.0.27"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@prisma/client": {
|
"@prisma/client": {
|
||||||
"version": "4.8.0",
|
"version": "4.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.8.0.tgz",
|
||||||
@@ -11029,6 +11102,23 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
|
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
|
||||||
},
|
},
|
||||||
|
"pinia": {
|
||||||
|
"version": "2.0.28",
|
||||||
|
"resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.28.tgz",
|
||||||
|
"integrity": "sha512-YClq9DkqCblq9rlyUual7ezMu/iICWdBtfJrDt4oWU9Zxpijyz7xB2xTwx57DaBQ96UGvvTMORzALr+iO5PVMw==",
|
||||||
|
"requires": {
|
||||||
|
"@vue/devtools-api": "^6.4.5",
|
||||||
|
"vue-demi": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue-demi": {
|
||||||
|
"version": "0.13.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
|
||||||
|
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
|
||||||
|
"requires": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"pkg-types": {
|
"pkg-types": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz",
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
"prepare": "nuxi prepare"
|
"prepare": "nuxi prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/nuxt": "^0.4.6",
|
||||||
"@prisma/client": "^4.8.0",
|
"@prisma/client": "^4.8.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"nuxt": "^3.0.0",
|
"nuxt": "^3.0.0",
|
||||||
|
"pinia": "^2.0.28",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
16
pages/channel/@me/[dmId].vue
Normal file
16
pages/channel/@me/[dmId].vue
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<template>
|
||||||
|
hello world
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async setup() {
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { server } = await $fetch(`/api/channel/${route.params.dmId}`)
|
||||||
|
if (!useServerStore().dms.includes(server)) useServerStore().addDM(server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
26
pages/channel/@me/index.vue
Normal file
26
pages/channel/@me/index.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="startDM">
|
||||||
|
<input v-model="userId" />
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
userId: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async startDM() {
|
||||||
|
const { server } = await $fetch('/api/channel/createDM', { method: 'post', body: { partnerId: this.userId } })
|
||||||
|
|
||||||
|
useServerStore().addDM(server)
|
||||||
|
useRouter().push({ path: '/channel/@me/' + server.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
hello world
|
hello world
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup async>
|
||||||
$fetch('/api/getChannelById', { params: { test: 123 } })
|
import { useServerStore } from '~/stores/servers'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const { server } = await $fetch(`/api/channel/${route.params.id}`)
|
||||||
|
if (!useServerStore().servers.includes(server)) useServerStore().addServer(server);
|
||||||
</script>
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
Hello there
|
Hello there traveler
|
||||||
<nuxt-link to="/login">Login</nuxt-link>
|
<nuxt-link to="/login">Login</nuxt-link>
|
||||||
<nuxt-link to="/signup">Signup</nuxt-link>
|
<nuxt-link to="/signup">Signup</nuxt-link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useUserStore } from '~/stores/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -36,6 +38,8 @@ export default {
|
|||||||
userId.value = user.userId
|
userId.value = user.userId
|
||||||
const token = useCookie('sessionToken')
|
const token = useCookie('sessionToken')
|
||||||
token.value = user.token
|
token.value = user.token
|
||||||
|
|
||||||
|
useUserStore().setUser(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { useUserStore } from '~/stores/user'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -42,6 +44,8 @@ export default {
|
|||||||
userId.value = user.userId
|
userId.value = user.userId
|
||||||
const token = useCookie('sessionToken')
|
const token = useCookie('sessionToken')
|
||||||
token.value = user.token
|
token.value = user.token
|
||||||
|
|
||||||
|
useUserStore().setUser(user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,18 +22,24 @@ model Server {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
participants User[]
|
participants User[]
|
||||||
|
channels Channel[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Room {
|
model Channel {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
name String
|
name String
|
||||||
|
Server Server? @relation(fields: [serverId], references: [id])
|
||||||
|
serverId String?
|
||||||
|
Message Message[]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Message {
|
model Message {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
body String
|
body String
|
||||||
|
channel Channel @relation(fields: [channelId], references: [id])
|
||||||
creator User @relation(fields: [userId], references: [id])
|
creator User @relation(fields: [userId], references: [id])
|
||||||
userId String
|
userId String
|
||||||
|
channelId String
|
||||||
}
|
}
|
||||||
|
|
||||||
model Session {
|
model Session {
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { PrismaClient } from '@prisma/client'
|
|||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
if (!event.context.authenticated) return {
|
if (!event.context.user.authenticated) return {
|
||||||
message: 'You must be logged in to view a channel.'
|
message: 'You must be logged in to view a channel.'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event.context.params.channelId) {
|
if (!event.context.params.id) {
|
||||||
event.node.res.statusCode = 400;
|
event.node.res.statusCode = 400;
|
||||||
return {
|
return {
|
||||||
message: 'A channelId is required'
|
message: 'A channelId is required'
|
||||||
@@ -15,10 +15,21 @@ export default defineEventHandler(async (event) => {
|
|||||||
|
|
||||||
const server = await prisma.server.findFirst({
|
const server = await prisma.server.findFirst({
|
||||||
where: {
|
where: {
|
||||||
id: event.context.params.channelId
|
id: event.context.params.id
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
participants: true,
|
||||||
|
channels: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!server) {
|
||||||
|
event.node.res.statusCode = 404;
|
||||||
|
return {
|
||||||
|
message: `Channel with id "${event.context.params.id}" not found`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
server
|
server
|
||||||
}
|
}
|
||||||
55
server/api/channel/create.post.ts
Normal file
55
server/api/channel/create.post.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
|
message: 'You must be logged in to view a channel.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { serverName } = await readBody(event)
|
||||||
|
|
||||||
|
if (!serverName) {
|
||||||
|
event.node.res.statusCode = 400;
|
||||||
|
return {
|
||||||
|
message: 'channel name is required to create a channel.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const preExistingServer = await prisma.server.findFirst({
|
||||||
|
where: {
|
||||||
|
name: serverName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (preExistingServer) {
|
||||||
|
event.node.res.statusCode = 409;
|
||||||
|
return {
|
||||||
|
message: `Server with name ${serverName} already exists.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = await prisma.server.create({
|
||||||
|
data: {
|
||||||
|
name: serverName,
|
||||||
|
participants: { connect: [{ id: event.context.user.id }] },
|
||||||
|
channels: {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
name: 'general',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
channels: true,
|
||||||
|
participants: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
|
})
|
||||||
67
server/api/channel/createDM.post.ts
Normal file
67
server/api/channel/createDM.post.ts
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
if (!event.context.user.authenticated) {
|
||||||
|
event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
|
message: 'You must be logged in to view a channel.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { partnerId } = await readBody(event)
|
||||||
|
|
||||||
|
if (!partnerId) {
|
||||||
|
event.node.res.statusCode = 400;
|
||||||
|
return {
|
||||||
|
message: 'A friend is required to create a DM.'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const partner = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: partnerId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: event.context.user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const preExistingServer = await prisma.server.findFirst({
|
||||||
|
where: {
|
||||||
|
name: `${user.username} and ${partner.username}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (preExistingServer) {
|
||||||
|
event.node.res.statusCode = 409;
|
||||||
|
return {
|
||||||
|
message: `DM already exists.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = await prisma.server.create({
|
||||||
|
data: {
|
||||||
|
name: `${user.username} and ${partner.username}`,
|
||||||
|
participants: { connect: [{ id: event.context.user.id }, { id: partner.id }] },
|
||||||
|
channels: {
|
||||||
|
create: [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
channels: true,
|
||||||
|
participants: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
server
|
||||||
|
}
|
||||||
|
})
|
||||||
21
server/api/getCurrentUser.get.ts
Normal file
21
server/api/getCurrentUser.get.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
if (event.context.user === undefined || event.context.user.id === undefined) {
|
||||||
|
// event.node.res.statusCode = 401;
|
||||||
|
return {
|
||||||
|
message: "Unauthenticated"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: event.context.user.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
user.passwordhash = undefined;
|
||||||
|
|
||||||
|
return user
|
||||||
|
})
|
||||||
@@ -13,6 +13,26 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const preExistingUser = await prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
username: body.username
|
||||||
|
},
|
||||||
|
{
|
||||||
|
email: body.email
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (preExistingUser) {
|
||||||
|
event.node.res.statusCode = 409;
|
||||||
|
return {
|
||||||
|
message: `User with username ${body.username} or email ${body.email} already exists`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const passwordhash = await bcryptjs.hash(body.password, 10)
|
const passwordhash = await bcryptjs.hash(body.password, 10)
|
||||||
|
|
||||||
const user = await prisma.user.create({
|
const user = await prisma.user.create({
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ const prisma = new PrismaClient()
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const cookies = parseCookies(event)
|
const cookies = parseCookies(event)
|
||||||
|
|
||||||
if (!cookies.sessionToken) return;
|
console.log(cookies.sessionToken)
|
||||||
|
|
||||||
|
if (!cookies.sessionToken) {
|
||||||
|
event.context.user = { authenticated: false }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const session = await prisma.session.findFirst({
|
const session = await prisma.session.findFirst({
|
||||||
where: {
|
where: {
|
||||||
@@ -12,7 +17,10 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!session) return;
|
if (!session) {
|
||||||
|
event.context.user = { authenticated: false }
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
event.context.authenticated = true;
|
event.context.user = { authenticated: true, id: session.userId };
|
||||||
})
|
})
|
||||||
15
stores/servers.ts
Normal file
15
stores/servers.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export const useServerStore = defineStore('server', {
|
||||||
|
state: () => ({
|
||||||
|
servers: [],
|
||||||
|
dms: [],
|
||||||
|
activeServer: undefined
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
addServer(server) {
|
||||||
|
this.servers.push(server)
|
||||||
|
},
|
||||||
|
addDM(server) {
|
||||||
|
this.dms.push(server)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
10
stores/user.ts
Normal file
10
stores/user.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state: () => ({
|
||||||
|
user: {}
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setUser(user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
4
types/index.ts
Normal file
4
types/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface IUser {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user