Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 65583b1564 |
Binary file not shown.
1
client/app/components.d.ts
vendored
1
client/app/components.d.ts
vendored
@@ -17,7 +17,6 @@ declare module 'vue' {
|
||||
PrimeFloatLabel: typeof import('primevue/floatlabel')['default']
|
||||
PrimeInputText: typeof import('primevue/inputtext')['default']
|
||||
PrimePassword: typeof import('primevue/password')['default']
|
||||
PrimeProgressBar: typeof import('primevue/progressbar')['default']
|
||||
PrimeScrollPanel: typeof import('primevue/scrollpanel')['default']
|
||||
PrimeSelect: typeof import('primevue/select')['default']
|
||||
PrimeSelectButton: typeof import('primevue/selectbutton')['default']
|
||||
|
||||
@@ -6,29 +6,19 @@
|
||||
'bg-surface-800': expanded,
|
||||
}"
|
||||
>
|
||||
<div class="p-3" @click="toggleExpand">
|
||||
<div class="flex items-center gap-3">
|
||||
<PrimeAvatar
|
||||
size="small"
|
||||
class="shrink-0"
|
||||
:class="{
|
||||
'outline-1 outline-primary outline-offset-2': speaking,
|
||||
}"
|
||||
>
|
||||
<template #icon>
|
||||
<User :size="20" />
|
||||
</template>
|
||||
</PrimeAvatar>
|
||||
<div class="p-3 flex items-center gap-3" @click="toggleExpand">
|
||||
<PrimeAvatar size="small">
|
||||
<template #icon>
|
||||
<User :size="20" />
|
||||
</template>
|
||||
</PrimeAvatar>
|
||||
|
||||
<p class="flex-1 text-sm leading-5 font-medium text-color truncate w-0">
|
||||
{{ client.displayName || client.username }}
|
||||
</p>
|
||||
|
||||
<Component :is="expanded ? ChevronUp : ChevronDown" v-if="!isMe" :size="20" class="text-muted-color" />
|
||||
<div class="flex-1 overflow-hidden text-sm leading-5 font-medium text-color whitespace-nowrap overflow-ellipsis">
|
||||
{{ client.displayName || client.username }}
|
||||
</div>
|
||||
|
||||
<div v-if="hasBadges" class="flex justify-end align-center gap-1 mt-2">
|
||||
<PrimeBadge v-if="streaming" v-tooltip.top="'Watch'" severity="success" value="Streaming" size="small" @click.stop="watchStream" />
|
||||
<div class="flex align-center gap-1">
|
||||
<PrimeBadge v-if="!!shareConsumer" v-tooltip.top="'Watch'" severity="success" value="Streaming" size="small" @click.stop="watchStream" />
|
||||
|
||||
<PrimeBadge v-if="premuted" severity="danger" value="Muted" size="small" />
|
||||
<PrimeBadge v-else-if="client.outputMuted" severity="info" value="No sound" size="small" />
|
||||
@@ -36,6 +26,8 @@
|
||||
|
||||
<PrimeBadge v-if="isMe" severity="secondary" value="You" size="small" class="shrink-0" />
|
||||
</div>
|
||||
|
||||
<Component :is="expanded ? ChevronUp : ChevronDown" v-if="!isMe" :size="20" class="text-muted-color" />
|
||||
</div>
|
||||
|
||||
<CollapseTransition v-if="!isMe">
|
||||
@@ -60,6 +52,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { ChadClient } from '#shared/types'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import { ChevronDown, ChevronUp, User } from 'lucide-vue-next'
|
||||
import CollapseTransition from '~/components/CollapseTransition.vue'
|
||||
|
||||
@@ -74,34 +67,34 @@ const { show } = useFullscreenVideo()
|
||||
|
||||
const expanded = ref(false)
|
||||
|
||||
const {
|
||||
volume,
|
||||
premuted,
|
||||
speaking,
|
||||
audioConsumers,
|
||||
videoConsumers,
|
||||
shareConsumers,
|
||||
streaming,
|
||||
} = useClient(toRef(() => props.client.socketId))
|
||||
const volume = useLocalStorage<number>(computed(() => `CLIENT_VOLUME_${props.client.userId}`), 100, { writeDefaults: false })
|
||||
const premuted = useLocalStorage<boolean>(computed(() => `CLIENT_PREMUTED_${props.client.userId}`), false, { writeDefaults: false })
|
||||
|
||||
const isMe = computed(() => {
|
||||
return me.value && props.client.userId === me.value.userId
|
||||
})
|
||||
|
||||
const consumers = computed(() => {
|
||||
return allConsumers.value.values().filter(consumer => consumer.appData.socketId === props.client.socketId).toArray()
|
||||
})
|
||||
|
||||
const audioConsumer = computed(() => {
|
||||
return audioConsumers.value[0]
|
||||
return consumers.value.find(consumer => consumer.track.kind === 'audio')
|
||||
})
|
||||
|
||||
const videoConsumers = computed(() => {
|
||||
return consumers.value.filter(consumer => consumer.track.kind === 'video')
|
||||
})
|
||||
|
||||
const shareConsumer = computed(() => {
|
||||
return videoConsumers.value.find(consumer => consumer.appData.source === 'share')
|
||||
})
|
||||
|
||||
const audioTrack = computed(() => {
|
||||
return audioConsumer.value?.raw.track
|
||||
return audioConsumer.value?.track
|
||||
})
|
||||
|
||||
const audioConsumerPaused = computed(() => {
|
||||
if (Object.keys(allConsumers.value).length === 0)
|
||||
return false
|
||||
|
||||
return audioConsumer.value?.paused ?? false
|
||||
})
|
||||
const audioConsumerPaused = ref(false)
|
||||
|
||||
const inputMuted = computed(() => {
|
||||
if (isMe.value)
|
||||
@@ -110,12 +103,8 @@ const inputMuted = computed(() => {
|
||||
return premuted.value || audioConsumerPaused.value
|
||||
})
|
||||
|
||||
const hasBadges = computed(() => {
|
||||
return shareConsumers.value.length > 0
|
||||
|| premuted.value
|
||||
|| inputMuted.value
|
||||
|| props.client.outputMuted
|
||||
|| isMe.value
|
||||
watch(allConsumers, () => {
|
||||
audioConsumerPaused.value = audioConsumer.value?.paused ?? false
|
||||
})
|
||||
|
||||
const { setGain } = useAudioContext(audioTrack)
|
||||
@@ -132,11 +121,9 @@ function toggleExpand() {
|
||||
}
|
||||
|
||||
function watchStream() {
|
||||
if (!streaming.value)
|
||||
if (!shareConsumer.value)
|
||||
return
|
||||
|
||||
const consumer = [...videoConsumers.value, ...shareConsumers.value][0]!
|
||||
|
||||
show(new MediaStream([consumer.raw.track]))
|
||||
show(new MediaStream([shareConsumer.value.track]))
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<div class="text-sm overflow-x-auto">
|
||||
<p class="text-muted-color">
|
||||
{{ consumer.id }}
|
||||
</p>
|
||||
|
||||
<p>paused: {{ consumer.paused }}</p>
|
||||
|
||||
<p v-for="[key, value] in appData" :key="key">
|
||||
{{ key }}: {{ value }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Consumer } from 'mediasoup-client/types'
|
||||
|
||||
const props = defineProps<{
|
||||
consumer: Consumer
|
||||
}>()
|
||||
|
||||
const appData = computed(() => {
|
||||
return Object.entries(props.consumer.appData)
|
||||
})
|
||||
</script>
|
||||
@@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div ref="root" class="fullscreen-gallery">
|
||||
{{ videoConsumers.length + shareConsumers.length }}
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
|
||||
const rootRef = useTemplateRef('root')
|
||||
|
||||
const { enter } = useFullscreen(rootRef)
|
||||
const { videoConsumers, shareConsumers } = useMediasoup()
|
||||
|
||||
onMounted(() => {
|
||||
// enter()
|
||||
})
|
||||
</script>
|
||||
@@ -1,13 +0,0 @@
|
||||
<template>
|
||||
<div class="fullscreen-gallery-card">
|
||||
sasd
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<div class="cursor-pointer hover:outline outline-primary rounded overflow-hidden flex items-center justify-center">
|
||||
<video :srcObject="mediaStream" muted autoplay />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Consumer } from '#shared/types'
|
||||
|
||||
const props = defineProps<{
|
||||
consumer: Consumer
|
||||
}>()
|
||||
|
||||
const mediaStream = computed(() => {
|
||||
return new MediaStream([props.consumer.raw.track])
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -31,36 +31,28 @@ export const useApp = createGlobalState(() => {
|
||||
|
||||
const outputMuted = ref(false)
|
||||
|
||||
const videoEnabled = computed(() => {
|
||||
return !!mediasoup.videoProducer.value
|
||||
})
|
||||
|
||||
const sharingEnabled = computed(() => {
|
||||
return !!mediasoup.shareProducer.value
|
||||
})
|
||||
|
||||
const somebodyStreamingVideo = computed(() => {
|
||||
return mediasoup.videoConsumers.value.length > 0 || mediasoup.shareConsumers.value.length > 0
|
||||
})
|
||||
|
||||
async function muteInput() {
|
||||
if (inputMuted.value || !mediasoup.micProducer.value)
|
||||
if (inputMuted.value)
|
||||
return
|
||||
|
||||
await mediasoup.pauseProducer(mediasoup.micProducer.value)
|
||||
await mediasoup.pauseProducer('microphone')
|
||||
|
||||
toast.add({ severity: 'info', summary: 'Microphone muted', closable: false, life: 1000 })
|
||||
}
|
||||
|
||||
async function unmuteInput() {
|
||||
if (!inputMuted.value || !mediasoup.micProducer.value)
|
||||
if (!inputMuted.value)
|
||||
return
|
||||
|
||||
if (outputMuted.value) {
|
||||
await unmuteOutput()
|
||||
}
|
||||
|
||||
await mediasoup.resumeProducer(mediasoup.micProducer.value)
|
||||
await mediasoup.resumeProducer('microphone')
|
||||
|
||||
toast.add({ severity: 'info', summary: 'Microphone activated', closable: false, life: 1000 })
|
||||
}
|
||||
@@ -109,21 +101,12 @@ export const useApp = createGlobalState(() => {
|
||||
await muteOutput()
|
||||
}
|
||||
|
||||
async function toggleVideo() {
|
||||
if (!mediasoup.videoProducer.value) {
|
||||
await mediasoup.enableVideo()
|
||||
}
|
||||
else {
|
||||
await mediasoup.disableProducer(mediasoup.videoProducer.value)
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleShare() {
|
||||
if (!mediasoup.shareProducer.value) {
|
||||
await mediasoup.enableShare()
|
||||
}
|
||||
else {
|
||||
await mediasoup.disableProducer(mediasoup.shareProducer.value)
|
||||
await mediasoup.disableProducer('share')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,13 +121,10 @@ export const useApp = createGlobalState(() => {
|
||||
muteOutput,
|
||||
unmuteOutput,
|
||||
toggleOutput,
|
||||
toggleVideo,
|
||||
version,
|
||||
isTauri,
|
||||
commitSha,
|
||||
toggleShare,
|
||||
videoEnabled,
|
||||
sharingEnabled,
|
||||
somebodyStreamingVideo,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import type { ChadClient } from '#shared/types'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
|
||||
export function useClient(socketId: MaybeRef<ChadClient['socketId']>) {
|
||||
const mediasoup = useMediasoup()
|
||||
const { getClient } = useClients()
|
||||
|
||||
const client = computed(() => getClient(unref(socketId))!)
|
||||
|
||||
const volume = useLocalStorage<number>(computed(() => `CLIENT_VOLUME_${client.value.userId}`), 100, { writeDefaults: false })
|
||||
const premuted = useLocalStorage<boolean>(computed(() => `CLIENT_PREMUTED_${client.value.userId}`), false, { writeDefaults: false })
|
||||
|
||||
const consumers = computed(() => {
|
||||
return Object.values(mediasoup.consumers.value).filter(consumer => consumer.appData.socketId === client.value.socketId)
|
||||
})
|
||||
|
||||
const producers = computed(() => {
|
||||
return mediasoup.producers.value.values().filter(producer => producer.appData.socketId === client.value.socketId).toArray()
|
||||
})
|
||||
|
||||
const audioConsumers = computed(() => {
|
||||
return mediasoup.audioConsumers.value.filter(consumer => consumer.appData.socketId === client.value.socketId)
|
||||
})
|
||||
|
||||
const videoConsumers = computed(() => {
|
||||
return mediasoup.videoConsumers.value.filter(consumer => consumer.appData.socketId === client.value.socketId)
|
||||
})
|
||||
|
||||
const shareConsumers = computed(() => {
|
||||
return mediasoup.shareConsumers.value.filter(consumer => consumer.appData.socketId === client.value.socketId)
|
||||
})
|
||||
|
||||
const streaming = computed(() => {
|
||||
return videoConsumers.value.length > 0 || shareConsumers.value.length > 0
|
||||
})
|
||||
|
||||
const speaking = computed(() => {
|
||||
return mediasoup.speakingClients.value.some(speaker => speaker.clientId === client.value.socketId)
|
||||
})
|
||||
|
||||
return {
|
||||
volume,
|
||||
premuted,
|
||||
consumers,
|
||||
producers,
|
||||
audioConsumers,
|
||||
videoConsumers,
|
||||
shareConsumers,
|
||||
streaming,
|
||||
speaking,
|
||||
}
|
||||
}
|
||||
@@ -19,16 +19,7 @@ export const useDevices = createGlobalState(() => {
|
||||
})
|
||||
}
|
||||
|
||||
;(async () => {
|
||||
if (permissionGranted.value)
|
||||
return
|
||||
|
||||
await ensurePermissions()
|
||||
})()
|
||||
|
||||
return {
|
||||
ensurePermissions,
|
||||
permissionGranted,
|
||||
videoInputs: computed<MediaDeviceInfo[]>(() => JSON.parse(JSON.stringify(videoInputs.value))),
|
||||
audioInputs: computed<MediaDeviceInfo[]>(() => JSON.parse(JSON.stringify(audioInputs.value))),
|
||||
audioOutputs: computed<MediaDeviceInfo[]>(() => JSON.parse(JSON.stringify(audioOutputs.value))),
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
|
||||
export const useFullscreenGallery = createSharedComposable(() => {
|
||||
return {}
|
||||
})
|
||||
@@ -1,18 +1,11 @@
|
||||
import type { ChadClient, Consumer, Producer } from '#shared/types'
|
||||
import type { MediaKind, ProducerOptions } from 'mediasoup-client/types'
|
||||
import { createSharedComposable } from '@vueuse/core'
|
||||
import * as mediasoupClient from 'mediasoup-client'
|
||||
import { shallowRef } from 'vue'
|
||||
import { useDevices } from '~/composables/use-devices'
|
||||
import { usePreferences } from '~/composables/use-preferences'
|
||||
import { useSignaling } from '~/composables/use-signaling'
|
||||
|
||||
type ProducerType = 'microphone' | 'video' | 'share'
|
||||
|
||||
interface SpeakingClient {
|
||||
clientId: ChadClient['socketId']
|
||||
volume: number
|
||||
}
|
||||
type ProducerType = 'microphone' | 'camera' | 'share'
|
||||
|
||||
const ICE_SERVERS: RTCIceServer[] = [
|
||||
{ urls: 'stun:stun.l.google.com:19302' },
|
||||
@@ -40,42 +33,12 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
const sendTransport = shallowRef<mediasoupClient.types.Transport>()
|
||||
const recvTransport = shallowRef<mediasoupClient.types.Transport>()
|
||||
|
||||
const consumers = ref<Record<Consumer['id'], Consumer>>({})
|
||||
const producers = ref<Record<Producer['id'], Producer>>({})
|
||||
const micProducer = shallowRef<mediasoupClient.types.Producer>()
|
||||
const cameraProducer = shallowRef<mediasoupClient.types.Producer>()
|
||||
const shareProducer = shallowRef<mediasoupClient.types.Producer>()
|
||||
|
||||
const consumersArray = computed(() => {
|
||||
return Object.values(consumers.value)
|
||||
})
|
||||
|
||||
const audioConsumers = computed(() => {
|
||||
return consumersArray.value.filter(consumer => consumer.raw.kind === 'audio')
|
||||
})
|
||||
|
||||
const videoConsumers = computed(() => {
|
||||
return consumersArray.value.filter(consumer => consumer.raw.kind === 'video' && consumer.appData.source !== 'share')
|
||||
})
|
||||
|
||||
const shareConsumers = computed(() => {
|
||||
return consumersArray.value.filter(consumer => consumer.raw.kind === 'video' && consumer.appData.source === 'share')
|
||||
})
|
||||
|
||||
const producersArray = computed(() => {
|
||||
return Object.values(producers.value)
|
||||
})
|
||||
|
||||
const micProducer = computed(() => {
|
||||
return producersArray.value.find(producer => producer.raw.kind === 'audio' && producer.raw.appData.source === 'mic-video')
|
||||
})
|
||||
|
||||
const videoProducer = computed(() => {
|
||||
return producersArray.value.find(producer => producer.raw.kind === 'video' && producer.raw.appData.source !== 'share')
|
||||
})
|
||||
|
||||
const shareProducer = computed(() => {
|
||||
return producersArray.value.find(producer => producer.raw.kind === 'video' && producer.raw.appData.source === 'share')
|
||||
})
|
||||
|
||||
const speakingClients = shallowRef<SpeakingClient[]>([])
|
||||
const consumers = shallowRef<Map<string, mediasoupClient.types.Consumer>>(new Map())
|
||||
const producers = shallowRef<Map<string, mediasoupClient.types.Producer>>(new Map())
|
||||
|
||||
watch(signaling.socket, (socket) => {
|
||||
if (!socket)
|
||||
@@ -196,35 +159,20 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
producerId,
|
||||
kind,
|
||||
rtpParameters,
|
||||
streamId: `${socketId}-${appData.source || 'stream'}`,
|
||||
streamId: `${socketId}-${appData.source === 'share' ? 'share' : 'mic-webcam'}`,
|
||||
appData: { ...appData, socketId },
|
||||
})
|
||||
|
||||
if (producerPaused)
|
||||
consumer.pause()
|
||||
|
||||
consumers.value[consumer.id] = {
|
||||
id: consumer.id,
|
||||
paused: consumer.paused,
|
||||
appData: consumer.appData,
|
||||
raw: markRaw(consumer),
|
||||
}
|
||||
|
||||
consumer.observer.on('resume', () => {
|
||||
consumers.value[consumer.id]!.paused = false
|
||||
consumer.on('transportclose', () => {
|
||||
if (consumers.value.delete(consumer.id))
|
||||
triggerRef(consumers)
|
||||
})
|
||||
|
||||
consumer.observer.on('pause', () => {
|
||||
consumers.value[consumer.id]!.paused = true
|
||||
})
|
||||
|
||||
consumer.observer.on('close', () => {
|
||||
delete consumers.value[consumer.id]
|
||||
})
|
||||
|
||||
consumer.on('trackended', () => {
|
||||
consumer.close()
|
||||
})
|
||||
consumers.value.set(consumer.id, consumer)
|
||||
triggerRef(consumers)
|
||||
|
||||
cb()
|
||||
},
|
||||
@@ -235,37 +183,11 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
async (
|
||||
{ consumerId },
|
||||
) => {
|
||||
const consumer = consumers.value[consumerId]
|
||||
|
||||
if (!consumer)
|
||||
return
|
||||
|
||||
consumer.raw.close()
|
||||
if (consumers.value.delete(consumerId))
|
||||
triggerRef(consumers)
|
||||
},
|
||||
)
|
||||
|
||||
socket.on('consumerPaused', ({ consumerId }) => {
|
||||
const consumer = consumers.value[consumerId]
|
||||
|
||||
if (!consumer)
|
||||
return
|
||||
|
||||
consumer.raw.pause()
|
||||
})
|
||||
|
||||
socket.on('consumerResumed', ({ consumerId }) => {
|
||||
const consumer = consumers.value[consumerId]
|
||||
|
||||
if (!consumer)
|
||||
return
|
||||
|
||||
consumer.raw.resume()
|
||||
})
|
||||
|
||||
socket.on('speakingPeers', (value: SpeakingClient[]) => {
|
||||
speakingClients.value = value
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
device.value = undefined
|
||||
rtpCapabilities.value = undefined
|
||||
@@ -276,12 +198,43 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
recvTransport.value?.close()
|
||||
recvTransport.value = undefined
|
||||
|
||||
consumers.value = {}
|
||||
producers.value = {}
|
||||
micProducer.value = undefined
|
||||
cameraProducer.value = undefined
|
||||
shareProducer.value = undefined
|
||||
|
||||
consumers.value = new Map()
|
||||
producers.value = new Map()
|
||||
})
|
||||
|
||||
socket.on('consumerPaused', ({ consumerId }) => {
|
||||
const consumer = consumers.value.get(consumerId)
|
||||
|
||||
if (!consumer)
|
||||
return
|
||||
|
||||
consumer.pause()
|
||||
|
||||
triggerRef(consumers)
|
||||
})
|
||||
|
||||
socket.on('consumerResumed', ({ consumerId }) => {
|
||||
const consumer = consumers.value.get(consumerId)
|
||||
|
||||
if (!consumer)
|
||||
return
|
||||
|
||||
consumer.resume()
|
||||
|
||||
triggerRef(consumers)
|
||||
})
|
||||
}, { immediate: true, flush: 'sync' })
|
||||
|
||||
async function createProducer(options: ProducerOptions) {
|
||||
async function enableProducer(type: ProducerType, options: ProducerOptions) {
|
||||
const producer = getProducerByType(type)
|
||||
|
||||
if (producer.value)
|
||||
return
|
||||
|
||||
if (!device.value || !sendTransport.value)
|
||||
return
|
||||
|
||||
@@ -291,54 +244,47 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
if (!device.value.canProduce(options.track.kind as MediaKind))
|
||||
return
|
||||
|
||||
const producer = await sendTransport.value.produce({ disableTrackOnPause: true, ...options })
|
||||
producer.value = await sendTransport.value.produce(options)
|
||||
|
||||
producers.value[producer.id] = {
|
||||
id: producer.id,
|
||||
paused: producer.paused,
|
||||
appData: producer.appData,
|
||||
raw: markRaw(producer),
|
||||
}
|
||||
producers.value.set(producer.value.id, producer.value)
|
||||
triggerRef(producers)
|
||||
triggerRef(producer)
|
||||
|
||||
producer.observer.on('pause', () => {
|
||||
producers.value[producer.id]!.paused = true
|
||||
producer.value.on('transportclose', () => {
|
||||
micProducer.value = undefined
|
||||
})
|
||||
|
||||
producer.observer.on('resume', () => {
|
||||
producers.value[producer.id]!.paused = false
|
||||
})
|
||||
|
||||
producer.observer.on('close', () => {
|
||||
delete producers.value[producer.id]
|
||||
})
|
||||
|
||||
producer.on('trackended', () => {
|
||||
disableProducer(producers.value[producer.id]!)
|
||||
producer.value.on('trackended', () => {
|
||||
disableProducer(type)
|
||||
})
|
||||
}
|
||||
|
||||
async function disableProducer(producer: Producer) {
|
||||
if (!signaling.socket.value)
|
||||
async function disableProducer(type: ProducerType) {
|
||||
const producer = getProducerByType(type)
|
||||
|
||||
if (!signaling.socket.value || !producer.value)
|
||||
return
|
||||
|
||||
producers.value.delete(producer.value.id)
|
||||
|
||||
try {
|
||||
producer.raw.close()
|
||||
producer.value.close()
|
||||
|
||||
await signaling.socket.value.emitWithAck('closeProducer', {
|
||||
producerId: producer.id,
|
||||
producerId: producer.value.id,
|
||||
})
|
||||
}
|
||||
catch {
|
||||
}
|
||||
finally {
|
||||
delete producers.value[producer.id]
|
||||
triggerRef(producers)
|
||||
triggerRef(producer)
|
||||
}
|
||||
|
||||
producer.value = undefined
|
||||
}
|
||||
|
||||
async function enableMic() {
|
||||
if (micProducer.value)
|
||||
return
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
deviceId: { exact: preferences.inputDeviceId.value },
|
||||
@@ -353,64 +299,21 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
if (!track)
|
||||
return
|
||||
|
||||
await createProducer({
|
||||
await enableProducer('microphone', {
|
||||
track,
|
||||
streamId: 'mic-video',
|
||||
codecOptions: {
|
||||
opusStereo: true,
|
||||
opusDtx: true, // Меньше пакетов летит когда тишина
|
||||
opusFec: false, // Фиксит пакет лос
|
||||
},
|
||||
appData: {
|
||||
source: 'mic-video',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function disableMic() {
|
||||
if (!micProducer.value)
|
||||
return
|
||||
|
||||
await disableProducer(micProducer.value)
|
||||
}
|
||||
|
||||
async function enableVideo() {
|
||||
if (videoProducer.value)
|
||||
return
|
||||
|
||||
if (!device.value)
|
||||
return
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
video: {
|
||||
deviceId: { exact: preferences.videoDeviceId.value },
|
||||
},
|
||||
})
|
||||
|
||||
const track = stream.getVideoTracks()[0]
|
||||
|
||||
if (!track)
|
||||
return
|
||||
|
||||
await createProducer({
|
||||
track,
|
||||
streamId: 'mic-video',
|
||||
codec: device.value.rtpCapabilities.codecs?.find(
|
||||
c => c.mimeType.toLowerCase() === 'video/AV1',
|
||||
),
|
||||
// codecOptions: {
|
||||
// videoGoogleStartBitrate: 1000,
|
||||
// },
|
||||
appData: {
|
||||
source: 'mic-video',
|
||||
},
|
||||
})
|
||||
await disableProducer('microphone')
|
||||
}
|
||||
|
||||
async function enableShare() {
|
||||
if (shareProducer.value)
|
||||
return
|
||||
|
||||
if (!device.value)
|
||||
return
|
||||
|
||||
@@ -421,54 +324,85 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
if (!track)
|
||||
return
|
||||
|
||||
await createProducer({
|
||||
await enableProducer('share', {
|
||||
track,
|
||||
streamId: 'share',
|
||||
codec: device.value.rtpCapabilities.codecs?.find(
|
||||
c => c.mimeType.toLowerCase() === 'video/AV1',
|
||||
c => c.mimeType.toLowerCase() === 'video/h264',
|
||||
),
|
||||
codecOptions: {
|
||||
videoGoogleStartBitrate: 1000,
|
||||
},
|
||||
zeroRtpOnPause: true,
|
||||
appData: {
|
||||
source: 'share',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function pauseProducer(producer: Producer) {
|
||||
async function pauseProducer(type: ProducerType) {
|
||||
if (!signaling.socket.value)
|
||||
return
|
||||
|
||||
if (producer.paused)
|
||||
const producer = getProducerByType(type)
|
||||
|
||||
if (!producer.value)
|
||||
return
|
||||
|
||||
if (producer.value.paused)
|
||||
return
|
||||
|
||||
try {
|
||||
producer.raw.pause()
|
||||
producer.value.pause()
|
||||
|
||||
await signaling.socket.value.emitWithAck('pauseProducer', {
|
||||
producerId: producer.id,
|
||||
producerId: producer.value.id,
|
||||
})
|
||||
}
|
||||
catch {
|
||||
producer.raw.resume()
|
||||
producer.value.resume()
|
||||
}
|
||||
finally {
|
||||
triggerRef(producers)
|
||||
triggerRef(producer)
|
||||
}
|
||||
}
|
||||
|
||||
async function resumeProducer(producer: Producer) {
|
||||
async function resumeProducer(type: ProducerType) {
|
||||
if (!signaling.socket.value)
|
||||
return
|
||||
|
||||
const producer = getProducerByType(type)
|
||||
|
||||
if (!producer.value)
|
||||
return
|
||||
|
||||
try {
|
||||
producer.raw.resume()
|
||||
producer.value.resume()
|
||||
|
||||
await signaling.socket.value.emitWithAck('resumeProducer', {
|
||||
producerId: producer.id,
|
||||
producerId: producer.value.id,
|
||||
})
|
||||
}
|
||||
catch {
|
||||
producer.raw.pause()
|
||||
producer.value.pause()
|
||||
}
|
||||
finally {
|
||||
triggerRef(producers)
|
||||
triggerRef(producer)
|
||||
}
|
||||
}
|
||||
|
||||
async function init() {
|
||||
signaling.connect()
|
||||
}
|
||||
|
||||
function getProducerByType(type: ProducerType) {
|
||||
switch (type) {
|
||||
case 'microphone':
|
||||
return micProducer
|
||||
case 'camera':
|
||||
return cameraProducer
|
||||
case 'share':
|
||||
return shareProducer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,22 +421,18 @@ export const useMediasoup = createSharedComposable(() => {
|
||||
})
|
||||
|
||||
return {
|
||||
init,
|
||||
consumers,
|
||||
audioConsumers,
|
||||
videoConsumers,
|
||||
shareConsumers,
|
||||
producers,
|
||||
speakingClients,
|
||||
sendTransport,
|
||||
recvTransport,
|
||||
rtpCapabilities,
|
||||
device,
|
||||
micProducer,
|
||||
videoProducer,
|
||||
cameraProducer,
|
||||
shareProducer,
|
||||
pauseProducer,
|
||||
resumeProducer,
|
||||
enableVideo,
|
||||
enableShare,
|
||||
disableProducer,
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@ export interface SyncedPreferences {
|
||||
export const usePreferences = createGlobalState(() => {
|
||||
const { videoInputs, audioInputs, audioOutputs } = useDevices()
|
||||
|
||||
const synced = ref(false)
|
||||
const fetched = ref(false)
|
||||
|
||||
const inputDeviceId = useLocalStorage<MediaDeviceInfo['deviceId']>('INPUT_DEVICE_ID', 'default')
|
||||
const outputDeviceId = useLocalStorage<MediaDeviceInfo['deviceId']>('OUTPUT_DEVICE_ID', 'default')
|
||||
const videoDeviceId = useLocalStorage<MediaDeviceInfo['deviceId']>('VIDEO_DEVICE_ID', 'default')
|
||||
|
||||
const autoGainControl = useLocalStorage('AUTO_GAIN_CONTROL', false)
|
||||
const noiseSuppression = useLocalStorage('NOISE_SUPPRESSION', true)
|
||||
@@ -33,10 +32,6 @@ export const usePreferences = createGlobalState(() => {
|
||||
return audioOutputs.value.some(device => device.deviceId === outputDeviceId.value)
|
||||
})
|
||||
|
||||
const videoDeviceExist = computed(() => {
|
||||
return videoInputs.value.some(device => device.deviceId === videoDeviceId.value)
|
||||
})
|
||||
|
||||
watchDebounced(
|
||||
[toggleInputHotkey, toggleOutputHotkey],
|
||||
async ([toggleInputHotkey, toggleOutputHotkey]) => {
|
||||
@@ -58,10 +53,9 @@ export const usePreferences = createGlobalState(() => {
|
||||
)
|
||||
|
||||
return {
|
||||
synced,
|
||||
fetched,
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
videoDeviceId,
|
||||
autoGainControl,
|
||||
noiseSuppression,
|
||||
echoCancellation,
|
||||
@@ -70,6 +64,5 @@ export const usePreferences = createGlobalState(() => {
|
||||
toggleOutputHotkey,
|
||||
inputDeviceExist,
|
||||
outputDeviceExist,
|
||||
videoDeviceExist,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-[1fr_1.25fr] gap-2 p-2 h-screen grid-rows-[auto_1fr] max-w-full">
|
||||
<div class="grid grid-cols-[1fr_1.25fr] gap-2 p-2 h-screen grid-rows-[auto_1fr]">
|
||||
<div
|
||||
class="flex items-center justify-between gap-2 rounded-xl p-3 bg-surface-950"
|
||||
>
|
||||
@@ -21,12 +21,6 @@
|
||||
</PrimeButton>
|
||||
</PrimeButtonGroup>
|
||||
|
||||
<PrimeButton :severity="videoEnabled ? 'success' : undefined" outlined @click="toggleVideo">
|
||||
<template #icon>
|
||||
<Component :is="videoEnabled ? CameraOff : Camera" />
|
||||
</template>
|
||||
</PrimeButton>
|
||||
|
||||
<PrimeButton :severity="sharingEnabled ? 'success' : undefined" outlined @click="toggleShare">
|
||||
<template #icon>
|
||||
<Component :is="sharingEnabled ? ScreenShareOff : ScreenShare" />
|
||||
@@ -50,33 +44,28 @@
|
||||
</PrimeSelectButton>
|
||||
</div>
|
||||
|
||||
<PrimeScrollPanel class="bg-surface-900 rounded-xl overflow-hidden" style="min-height: 0">
|
||||
<PrimeScrollPanel class="bg-surface-900 rounded-xl" style="min-height: 0">
|
||||
<div v-auto-animate class="p-3 space-y-1">
|
||||
<ClientRow v-for="client of clients" :key="client.userId" :client="client" />
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
|
||||
<PrimeScrollPanel class="bg-surface-900 rounded-xl overflow-hidden" style="min-height: 0">
|
||||
<PrimeScrollPanel class="bg-surface-900 rounded-xl" style="min-height: 0">
|
||||
<div class="p-3">
|
||||
<slot />
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
</div>
|
||||
|
||||
<FullscreenGallery />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Camera,
|
||||
CameraOff,
|
||||
MessageCircle,
|
||||
Mic,
|
||||
MicOff,
|
||||
ScreenShare,
|
||||
ScreenShareOff,
|
||||
Settings,
|
||||
TvMinimalPlay,
|
||||
UserPen,
|
||||
Volume2,
|
||||
VolumeOff,
|
||||
@@ -87,12 +76,9 @@ const {
|
||||
clients,
|
||||
inputMuted,
|
||||
outputMuted,
|
||||
videoEnabled,
|
||||
sharingEnabled,
|
||||
somebodyStreamingVideo,
|
||||
toggleInput,
|
||||
toggleOutput,
|
||||
toggleVideo,
|
||||
toggleShare,
|
||||
} = useApp()
|
||||
const { connect, connected } = useSignaling()
|
||||
@@ -105,47 +91,31 @@ interface Tab {
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const tabs = computed<Tab[]>(() => {
|
||||
const result = []
|
||||
|
||||
if (somebodyStreamingVideo.value) {
|
||||
result.push({
|
||||
id: 'Gallery',
|
||||
icon: TvMinimalPlay,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Gallery' })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
result.push(
|
||||
{
|
||||
id: 'Index',
|
||||
icon: MessageCircle,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Index' })
|
||||
},
|
||||
const tabs: Tab[] = [
|
||||
{
|
||||
id: 'Index',
|
||||
icon: MessageCircle,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Index' })
|
||||
},
|
||||
{
|
||||
id: 'Profile',
|
||||
icon: UserPen,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Profile' })
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Profile',
|
||||
icon: UserPen,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Profile' })
|
||||
},
|
||||
{
|
||||
id: 'Preferences',
|
||||
icon: Settings,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Preferences' })
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'Preferences',
|
||||
icon: Settings,
|
||||
onClick: () => {
|
||||
navigateTo({ name: 'Preferences' })
|
||||
},
|
||||
)
|
||||
},
|
||||
]
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
const activeTab = ref<Tab>(tabs.value.find(tab => tab.id === route.name) ?? tabs.value.find(tab => tab.id === 'Index')!)
|
||||
const activeTab = ref<Tab>(tabs.find(tab => tab.id === route.name) ?? tabs[0]!)
|
||||
|
||||
watch(activeTab, (activeTab) => {
|
||||
activeTab.onClick()
|
||||
|
||||
@@ -7,9 +7,9 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
if (!me.value)
|
||||
return
|
||||
|
||||
const { synced, toggleInputHotkey, toggleOutputHotkey } = usePreferences()
|
||||
const { fetched, toggleInputHotkey, toggleOutputHotkey } = usePreferences()
|
||||
|
||||
if (synced.value)
|
||||
if (fetched.value)
|
||||
return
|
||||
|
||||
try {
|
||||
@@ -20,7 +20,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
|
||||
toggleInputHotkey.value = preferences.toggleInputHotkey ?? toggleInputHotkey.value
|
||||
toggleOutputHotkey.value = preferences.toggleOutputHotkey ?? toggleOutputHotkey.value
|
||||
synced.value = true
|
||||
fetched.value = true
|
||||
}
|
||||
catch {}
|
||||
})
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-[1fr_1fr] gap-2">
|
||||
<template v-for="(group, id) in groupedConsumers" :key="id">
|
||||
<GalleryCard
|
||||
v-for="consumer in group"
|
||||
:key="consumer.id"
|
||||
:consumer="consumer"
|
||||
@click="watch(consumer)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Consumer } from '#shared/types'
|
||||
|
||||
definePageMeta({
|
||||
name: 'Gallery',
|
||||
})
|
||||
|
||||
const { videoConsumers, shareConsumers } = useMediasoup()
|
||||
const fullscreenVideo = useFullscreenVideo()
|
||||
|
||||
const groupedConsumers = computed<Partial<Record<string, Consumer[]>>>(() => {
|
||||
if (fullscreenVideo.visible.value)
|
||||
return {}
|
||||
|
||||
const consumers = [...videoConsumers.value, ...shareConsumers.value]
|
||||
|
||||
return Object.groupBy(consumers, (consumer) => {
|
||||
return consumer.appData.socketId!
|
||||
})
|
||||
})
|
||||
|
||||
function watch(consumer: Consumer) {
|
||||
fullscreenVideo.show(new MediaStream([consumer.raw.track]))
|
||||
}
|
||||
</script>
|
||||
@@ -11,7 +11,6 @@
|
||||
option-label="label"
|
||||
option-value="deviceId"
|
||||
input-id="inputDevice"
|
||||
placeholder="No input device"
|
||||
fluid
|
||||
:invalid="!inputDeviceExist"
|
||||
/>
|
||||
@@ -52,29 +51,12 @@
|
||||
Video
|
||||
</PrimeDivider>
|
||||
|
||||
<PrimeFloatLabel variant="on">
|
||||
<PrimeSelect
|
||||
v-model="videoDeviceId"
|
||||
:options="videoInputs"
|
||||
option-label="label"
|
||||
option-value="deviceId"
|
||||
input-id="videoDevice"
|
||||
placeholder="No video device"
|
||||
fluid
|
||||
:invalid="!videoDeviceExist"
|
||||
/>
|
||||
<label for="inputDevice">Input device</label>
|
||||
</PrimeFloatLabel>
|
||||
|
||||
<PrimeDivider align="left">
|
||||
Screen sharing
|
||||
</PrimeDivider>
|
||||
|
||||
<div>
|
||||
<p class="text-sm mb-2 text-center">
|
||||
FPS
|
||||
</p>
|
||||
<PrimeSelectButton v-model="shareFps" :options="[5, 30, 60]" fluid size="small" />
|
||||
<div class="flex justify-between text-sm mb-3">
|
||||
<span>FPS</span>
|
||||
<span>{{ shareFps }}</span>
|
||||
</div>
|
||||
<PrimeSlider v-model="shareFps" class="mx-[10px]" :min="30" :max="60" :step="5" />
|
||||
</div>
|
||||
|
||||
<template v-if="isTauri">
|
||||
@@ -136,11 +118,10 @@ definePageMeta({
|
||||
})
|
||||
const { isTauri, version, commitSha } = useApp()
|
||||
const { checking, checkForUpdates, lastUpdate } = useUpdater()
|
||||
const { audioInputs, audioOutputs, videoInputs } = useDevices()
|
||||
const { audioInputs, audioOutputs } = useDevices()
|
||||
const {
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
videoDeviceId,
|
||||
autoGainControl,
|
||||
noiseSuppression,
|
||||
echoCancellation,
|
||||
@@ -148,7 +129,6 @@ const {
|
||||
toggleOutputHotkey,
|
||||
inputDeviceExist,
|
||||
outputDeviceExist,
|
||||
videoDeviceExist,
|
||||
shareFps,
|
||||
} = usePreferences()
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ export default defineNuxtConfig({
|
||||
strictPort: true,
|
||||
proxy: {
|
||||
'/api': {
|
||||
// target: 'http://localhost:4000/chad',
|
||||
target: 'https://api.koptilnya.xyz/chad',
|
||||
target: 'http://localhost:4000/chad',
|
||||
// target: 'https://api.koptilnya.xyz/chad',
|
||||
ws: true,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "nuxt build",
|
||||
"dev": "nuxt dev --host",
|
||||
"dev": "nuxt dev",
|
||||
"generate": "nuxt generate",
|
||||
"preview": "nuxt preview",
|
||||
"postinstall": "nuxt prepare"
|
||||
@@ -20,7 +20,7 @@
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"hotkeys-js": "^4.0.0",
|
||||
"lucide-vue-next": "^0.562.0",
|
||||
"mediasoup-client": "^3.18.6",
|
||||
"mediasoup-client": "^3.16.7",
|
||||
"nuxt": "^4.2.2",
|
||||
"postcss": "^8.5.6",
|
||||
"primeicons": "^7.0.0",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { Consumer as MediasoupConsumer, Producer as MediasoupProducer } from 'mediasoup-client/types'
|
||||
|
||||
export interface ChadClient {
|
||||
socketId: string
|
||||
userId: string
|
||||
@@ -7,30 +5,6 @@ export interface ChadClient {
|
||||
displayName: string
|
||||
inputMuted?: boolean
|
||||
outputMuted?: boolean
|
||||
|
||||
consumers: unknown[]
|
||||
producers: unknown[]
|
||||
volume: number
|
||||
isDominant: boolean
|
||||
}
|
||||
|
||||
export interface AppData {
|
||||
socketId?: ChadClient['socketId']
|
||||
source?: 'share' | 'mic-video'
|
||||
}
|
||||
|
||||
export interface Consumer {
|
||||
id: MediasoupConsumer['id']
|
||||
paused: MediasoupConsumer['paused']
|
||||
appData: AppData
|
||||
raw: MediasoupConsumer
|
||||
}
|
||||
|
||||
export interface Producer {
|
||||
id: MediasoupProducer['id']
|
||||
paused: MediasoupProducer['paused']
|
||||
appData: AppData
|
||||
raw: MediasoupProducer
|
||||
}
|
||||
|
||||
export type UpdatedClient = Omit<ChadClient, 'socketId' | 'userId' | 'isMe'>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "chad",
|
||||
"version": "0.2.23",
|
||||
"version": "0.2.19",
|
||||
"identifier": "xyz.koptilnya.chad",
|
||||
"build": {
|
||||
"frontendDist": "../.output/public",
|
||||
|
||||
@@ -4060,7 +4060,7 @@ __metadata:
|
||||
eslint-plugin-format: "npm:^1.0.2"
|
||||
hotkeys-js: "npm:^4.0.0"
|
||||
lucide-vue-next: "npm:^0.562.0"
|
||||
mediasoup-client: "npm:^3.18.6"
|
||||
mediasoup-client: "npm:^3.16.7"
|
||||
nuxt: "npm:^4.2.2"
|
||||
postcss: "npm:^8.5.6"
|
||||
primeicons: "npm:^7.0.0"
|
||||
@@ -6176,12 +6176,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"h264-profile-level-id@npm:^2.3.2":
|
||||
version: 2.3.2
|
||||
resolution: "h264-profile-level-id@npm:2.3.2"
|
||||
"h264-profile-level-id@npm:^2.3.1":
|
||||
version: 2.3.1
|
||||
resolution: "h264-profile-level-id@npm:2.3.1"
|
||||
dependencies:
|
||||
debug: "npm:^4.4.3"
|
||||
checksum: 10c0/75bd12ff36707ffacf379c31c403d4508f3116ef2065e375deadcfafd4f7d163521cf0c70ae5385ebac970fa0acc07f9dd497c4248cfc1ee5623b4533707731d
|
||||
checksum: 10c0/c3459549bb28e456db62428c79885cffd4958ce282099c4181b09576f8e5ad90b42395a77209fff4f20a7cb920aaeb660f73902f08343daead0f5527faeb4015
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -7302,9 +7302,9 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mediasoup-client@npm:^3.18.6":
|
||||
version: 3.18.6
|
||||
resolution: "mediasoup-client@npm:3.18.6"
|
||||
"mediasoup-client@npm:^3.16.7":
|
||||
version: 3.16.7
|
||||
resolution: "mediasoup-client@npm:3.16.7"
|
||||
dependencies:
|
||||
"@types/debug": "npm:^4.1.12"
|
||||
"@types/events-alias": "npm:@types/events@^3.0.3"
|
||||
@@ -7312,10 +7312,10 @@ __metadata:
|
||||
debug: "npm:^4.4.3"
|
||||
events-alias: "npm:events@^3.3.0"
|
||||
fake-mediastreamtrack: "npm:^2.2.1"
|
||||
h264-profile-level-id: "npm:^2.3.2"
|
||||
sdp-transform: "npm:^3.0.0"
|
||||
h264-profile-level-id: "npm:^2.3.1"
|
||||
sdp-transform: "npm:^2.15.0"
|
||||
supports-color: "npm:^10.2.2"
|
||||
checksum: 10c0/f5baff9139afccf88de5db767c1139efa5cdd68f4871e2fa9d6ff94d2e71d2365dc40e9ba6e903cde5fbb51a2d82972e738da656be9f6fc7006640fdd82dd5da
|
||||
checksum: 10c0/da44c6de8889963192c5b0b7907ed628e04d48be73b7bbfbf18012d66b07ede9d7367c0723466e496a87c7002c07f1af432d854c4c5e16cbd0887013870d8abe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -9829,12 +9829,12 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"sdp-transform@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "sdp-transform@npm:3.0.0"
|
||||
"sdp-transform@npm:^2.15.0":
|
||||
version: 2.15.0
|
||||
resolution: "sdp-transform@npm:2.15.0"
|
||||
bin:
|
||||
sdp-verify: checker.js
|
||||
checksum: 10c0/828a4595041ba64c86b29075aa4007ab384519b1fa29882db59ccb83b54b2b2a33b60848293f8da537fe151c52f5844fc17c8325396cac309fb19e2e81ec5bf4
|
||||
checksum: 10c0/96c060f113a3d5418defa168db609f7e23e5bd7954fa1cf7784f103dbe702e24d667e5310d2ac6d88abdb32322af83d6ebd0df08e07f4f172d5ed5888f921386
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
14
server/dto/channel.dto.ts
Normal file
14
server/dto/channel.dto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// dto/channel.dto.ts
|
||||
import type { Prisma } from '../prisma/generated/client'
|
||||
|
||||
export const channelPublicSelect = {
|
||||
id: true,
|
||||
name: true,
|
||||
owner_id: true,
|
||||
persistent: true,
|
||||
maxClients: true,
|
||||
} satisfies Prisma.ChannelSelect
|
||||
|
||||
export type ChannelPublicDTO = Prisma.ChannelGetPayload<{
|
||||
select: typeof channelPublicSelect
|
||||
}>
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "server",
|
||||
"scripts": {
|
||||
"start": "ts-node --transpile-only server.ts",
|
||||
"db:deploy": "npx prisma migrate deploy && npx prisma generate"
|
||||
"db:deploy": "npx prisma migrate deploy && npx prisma db seed && npx prisma generate"
|
||||
},
|
||||
"type": "module",
|
||||
"packageManager": "yarn@4.10.3",
|
||||
@@ -11,7 +11,8 @@
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@fastify/cors": "^11.1.0",
|
||||
"@lucia-auth/adapter-prisma": "^4.0.1",
|
||||
"@prisma/client": "^6.17.0",
|
||||
"@prisma/adapter-better-sqlite3": "^7.2.0",
|
||||
"@prisma/client": "7",
|
||||
"bcrypt": "^6.0.0",
|
||||
"consola": "^3.4.2",
|
||||
"dotenv": "^17.2.3",
|
||||
@@ -19,7 +20,7 @@
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"lucia": "^3.2.2",
|
||||
"mediasoup": "^3.19.3",
|
||||
"prisma": "^6.17.0",
|
||||
"prisma": "7",
|
||||
"socket.io": "^4.8.1",
|
||||
"ws": "^8.18.3",
|
||||
"zod": "^4.1.12"
|
||||
|
||||
@@ -22,8 +22,9 @@ export default fp<Partial<ServerOptions>>(
|
||||
await fastify.io.close()
|
||||
})
|
||||
|
||||
fastify.ready(async () => {
|
||||
await registerWebrtcSocket(fastify.io, fastify.mediasoupRouter)
|
||||
fastify.ready(() => {
|
||||
registerWebrtcSocket(fastify.io, fastify.mediasoupRouter)
|
||||
registerChannelSocket(fastify.io, fastify.mediasoupRouter)
|
||||
})
|
||||
},
|
||||
{ name: 'socket-io', dependencies: ['mediasoup-worker', 'mediasoup-router'] },
|
||||
|
||||
13
server/prisma.config.ts
Normal file
13
server/prisma.config.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { defineConfig, env } from 'prisma/config'
|
||||
import 'dotenv/config'
|
||||
|
||||
export default defineConfig({
|
||||
schema: './prisma/schema.prisma',
|
||||
migrations: {
|
||||
path: './prisma/migrations',
|
||||
seed: 'ts-node ./prisma/seed.ts',
|
||||
},
|
||||
datasource: {
|
||||
url: env('DATABASE_URL'),
|
||||
},
|
||||
})
|
||||
@@ -1,6 +1,14 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
|
||||
import { env } from 'prisma/config'
|
||||
import { PrismaClient } from './generated/client/index.js'
|
||||
import 'dotenv/config'
|
||||
|
||||
const client = new PrismaClient({
|
||||
adapter: new PrismaBetterSqlite3({
|
||||
url: env('DATABASE_URL'),
|
||||
}, {
|
||||
timestampFormat: 'unixepoch-ms',
|
||||
}),
|
||||
log: ['query', 'error', 'warn'],
|
||||
})
|
||||
|
||||
|
||||
39
server/prisma/generated/client/browser.ts
Normal file
39
server/prisma/generated/client/browser.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* This file should be your main import to use Prisma-related types and utilities in a browser.
|
||||
* Use it to get access to models, enums, and input types.
|
||||
*
|
||||
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
|
||||
* See `client.ts` for the standard, server-side entry point.
|
||||
*
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
|
||||
import * as Prisma from './internal/prismaNamespaceBrowser.ts'
|
||||
export { Prisma }
|
||||
export * as $Enums from './enums.ts'
|
||||
export * from './enums.ts';
|
||||
/**
|
||||
* Model User
|
||||
*
|
||||
*/
|
||||
export type User = Prisma.UserModel
|
||||
/**
|
||||
* Model Session
|
||||
*
|
||||
*/
|
||||
export type Session = Prisma.SessionModel
|
||||
/**
|
||||
* Model UserPreferences
|
||||
*
|
||||
*/
|
||||
export type UserPreferences = Prisma.UserPreferencesModel
|
||||
/**
|
||||
* Model Channel
|
||||
*
|
||||
*/
|
||||
export type Channel = Prisma.ChannelModel
|
||||
1
server/prisma/generated/client/client.d.ts
vendored
Normal file
1
server/prisma/generated/client/client.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./index"
|
||||
5
server/prisma/generated/client/client.js
Normal file
5
server/prisma/generated/client/client.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('.') }
|
||||
61
server/prisma/generated/client/client.ts
Normal file
61
server/prisma/generated/client/client.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
||||
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
||||
*
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
|
||||
import * as process from 'node:process'
|
||||
import * as path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
import * as runtime from "@prisma/client/runtime/client"
|
||||
import * as $Enums from "./enums.ts"
|
||||
import * as $Class from "./internal/class.ts"
|
||||
import * as Prisma from "./internal/prismaNamespace.ts"
|
||||
|
||||
export * as $Enums from './enums.ts'
|
||||
export * from "./enums.ts"
|
||||
/**
|
||||
* ## Prisma Client
|
||||
*
|
||||
* Type-safe database client for TypeScript
|
||||
* @example
|
||||
* ```
|
||||
* const prisma = new PrismaClient()
|
||||
* // Fetch zero or more Users
|
||||
* const users = await prisma.user.findMany()
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/client).
|
||||
*/
|
||||
export const PrismaClient = $Class.getPrismaClientClass()
|
||||
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||
export { Prisma }
|
||||
|
||||
/**
|
||||
* Model User
|
||||
*
|
||||
*/
|
||||
export type User = Prisma.UserModel
|
||||
/**
|
||||
* Model Session
|
||||
*
|
||||
*/
|
||||
export type Session = Prisma.SessionModel
|
||||
/**
|
||||
* Model UserPreferences
|
||||
*
|
||||
*/
|
||||
export type UserPreferences = Prisma.UserPreferencesModel
|
||||
/**
|
||||
* Model Channel
|
||||
*
|
||||
*/
|
||||
export type Channel = Prisma.ChannelModel
|
||||
298
server/prisma/generated/client/commonInputTypes.ts
Normal file
298
server/prisma/generated/client/commonInputTypes.ts
Normal file
@@ -0,0 +1,298 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
|
||||
*
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
|
||||
import type * as runtime from "@prisma/client/runtime/client"
|
||||
import * as $Enums from "./enums.ts"
|
||||
import type * as Prisma from "./internal/prismaNamespace.ts"
|
||||
|
||||
|
||||
export type StringFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||
}
|
||||
|
||||
export type DateTimeFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||
}
|
||||
|
||||
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type StringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type SortOrderInput = {
|
||||
sort: Prisma.SortOrder
|
||||
nulls?: Prisma.NullsOrder
|
||||
}
|
||||
|
||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type IntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type BoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
}
|
||||
|
||||
export type IntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedStringFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||
}
|
||||
|
||||
export type NestedDateTimeFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||
}
|
||||
|
||||
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
in?: string[]
|
||||
notIn?: string[]
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedIntFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
in?: number[]
|
||||
notIn?: number[]
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||
}
|
||||
|
||||
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
in?: Date[] | string[]
|
||||
notIn?: Date[] | string[]
|
||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||
}
|
||||
|
||||
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||
in?: string[] | null
|
||||
notIn?: string[] | null
|
||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedIntNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||
}
|
||||
|
||||
export type NestedIntNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedIntNullableWithAggregatesFilter<$PrismaModel> | number | null
|
||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_avg?: Prisma.NestedFloatNullableFilter<$PrismaModel>
|
||||
_sum?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
export type NestedFloatNullableFilter<$PrismaModel = never> = {
|
||||
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel> | null
|
||||
in?: number[] | null
|
||||
notIn?: number[] | null
|
||||
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedFloatNullableFilter<$PrismaModel> | number | null
|
||||
}
|
||||
|
||||
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||
}
|
||||
|
||||
|
||||
1
server/prisma/generated/client/default.d.ts
vendored
Normal file
1
server/prisma/generated/client/default.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./index"
|
||||
5
server/prisma/generated/client/default.js
Normal file
5
server/prisma/generated/client/default.js
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('#main-entry-point') }
|
||||
1
server/prisma/generated/client/edge.d.ts
vendored
Normal file
1
server/prisma/generated/client/edge.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./default"
|
||||
165
server/prisma/generated/client/edge.js
Normal file
165
server/prisma/generated/client/edge.js
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
PrismaClientKnownRequestError,
|
||||
PrismaClientUnknownRequestError,
|
||||
PrismaClientRustPanicError,
|
||||
PrismaClientInitializationError,
|
||||
PrismaClientValidationError,
|
||||
getPrismaClient,
|
||||
sqltag,
|
||||
empty,
|
||||
join,
|
||||
raw,
|
||||
skip,
|
||||
Decimal,
|
||||
Debug,
|
||||
DbNull,
|
||||
JsonNull,
|
||||
AnyNull,
|
||||
NullTypes,
|
||||
makeStrictEnum,
|
||||
Extensions,
|
||||
warnOnce,
|
||||
defineDmmfProperty,
|
||||
Public,
|
||||
getRuntime,
|
||||
createParam,
|
||||
} = require('./runtime/wasm-compiler-edge.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 7.2.0
|
||||
* Query Engine version: 0c8ef2ce45c83248ab3df073180d5eda9e8be7a3
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "7.2.0",
|
||||
engine: "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
||||
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
||||
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
||||
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
||||
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = sqltag
|
||||
Prisma.empty = empty
|
||||
Prisma.join = join
|
||||
Prisma.raw = raw
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = Extensions.getExtensionContext
|
||||
Prisma.defineExtension = Extensions.defineExtension
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = DbNull
|
||||
Prisma.JsonNull = JsonNull
|
||||
Prisma.AnyNull = AnyNull
|
||||
|
||||
Prisma.NullTypes = NullTypes
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
displayName: 'displayName',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SessionScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
expiresAt: 'expiresAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserPreferencesScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
toggleInputHotkey: 'toggleInputHotkey',
|
||||
toggleOutputHotkey: 'toggleOutputHotkey'
|
||||
};
|
||||
|
||||
exports.Prisma.ChannelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
maxClients: 'maxClients',
|
||||
persistent: 'persistent',
|
||||
owner_id: 'owner_id'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
User: 'User',
|
||||
Session: 'Session',
|
||||
UserPreferences: 'UserPreferences',
|
||||
Channel: 'Channel'
|
||||
};
|
||||
/**
|
||||
* Create the Client
|
||||
*/
|
||||
const config = {
|
||||
"previewFeatures": [],
|
||||
"clientVersion": "7.2.0",
|
||||
"engineVersion": "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3",
|
||||
"activeProvider": "sqlite",
|
||||
"inlineSchema": "datasource db {\n provider = \"sqlite\"\n}\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\nmodel User {\n id String @id @default(cuid())\n username String @unique\n password String\n displayName String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n Session Session[]\n UserPreferences UserPreferences?\n channels Channel[]\n}\n\nmodel Session {\n id String @id\n userId String\n expiresAt DateTime\n user User @relation(references: [id], fields: [userId], onDelete: Cascade)\n\n @@index([userId])\n}\n\nmodel UserPreferences {\n userId String @id\n toggleInputHotkey String? @default(\"\")\n toggleOutputHotkey String? @default(\"\")\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel Channel {\n id String @id\n name String\n maxClients Int?\n persistent Boolean @default(false)\n owner_id String?\n owner User? @relation(fields: [owner_id], references: [id], onDelete: Cascade)\n}\n"
|
||||
}
|
||||
|
||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"displayName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"Session\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"UserPreferences\",\"kind\":\"object\",\"type\":\"UserPreferences\",\"relationName\":\"UserToUserPreferences\"},{\"name\":\"channels\",\"kind\":\"object\",\"type\":\"Channel\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"UserPreferences\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleInputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleOutputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserPreferences\"}],\"dbName\":null},\"Channel\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"maxClients\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"persistent\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"owner_id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"owner\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
||||
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
||||
config.compilerWasm = {
|
||||
getRuntime: async () => require('./query_compiler_bg.js'),
|
||||
getQueryCompilerWasmModule: async () => {
|
||||
const loader = (await import('#wasm-compiler-loader')).default
|
||||
const compiler = (await loader).default
|
||||
return compiler
|
||||
}
|
||||
}
|
||||
if (typeof globalThis !== 'undefined' && globalThis['DEBUG'] || (typeof process !== 'undefined' && process.env && process.env.DEBUG) || undefined) {
|
||||
Debug.enable(typeof globalThis !== 'undefined' && globalThis['DEBUG'] || (typeof process !== 'undefined' && process.env && process.env.DEBUG) || undefined)
|
||||
}
|
||||
|
||||
const PrismaClient = getPrismaClient(config)
|
||||
exports.PrismaClient = PrismaClient
|
||||
Object.assign(exports, Prisma)
|
||||
15
server/prisma/generated/client/enums.ts
Normal file
15
server/prisma/generated/client/enums.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* This file exports all enum related types from the schema.
|
||||
*
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// This file is empty because there are no enums in the schema.
|
||||
export {}
|
||||
196
server/prisma/generated/client/index-browser.js
Normal file
196
server/prisma/generated/client/index-browser.js
Normal file
@@ -0,0 +1,196 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
Decimal,
|
||||
DbNull,
|
||||
JsonNull,
|
||||
AnyNull,
|
||||
NullTypes,
|
||||
makeStrictEnum,
|
||||
Public,
|
||||
getRuntime,
|
||||
skip
|
||||
} = require('./runtime/index-browser.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 7.2.0
|
||||
* Query Engine version: 0c8ef2ce45c83248ab3df073180d5eda9e8be7a3
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "7.2.0",
|
||||
engine: "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientKnownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)};
|
||||
Prisma.PrismaClientUnknownRequestError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientUnknownRequestError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientRustPanicError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientRustPanicError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientInitializationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientInitializationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.PrismaClientValidationError = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`PrismaClientValidationError is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`sqltag is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.empty = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`empty is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.join = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`join is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.raw = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`raw is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.getExtensionContext is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
Prisma.defineExtension = () => {
|
||||
const runtimeName = getRuntime().prettyName;
|
||||
throw new Error(`Extensions.defineExtension is unable to run in this browser environment, or has been bundled for the browser (running in ${runtimeName}).
|
||||
In case this error is unexpected for you, please report it in https://pris.ly/prisma-prisma-bug-report`,
|
||||
)}
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = DbNull
|
||||
Prisma.JsonNull = JsonNull
|
||||
Prisma.AnyNull = AnyNull
|
||||
|
||||
Prisma.NullTypes = NullTypes
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
displayName: 'displayName',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SessionScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
expiresAt: 'expiresAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserPreferencesScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
toggleInputHotkey: 'toggleInputHotkey',
|
||||
toggleOutputHotkey: 'toggleOutputHotkey'
|
||||
};
|
||||
|
||||
exports.Prisma.ChannelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
maxClients: 'maxClients',
|
||||
persistent: 'persistent',
|
||||
owner_id: 'owner_id'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
User: 'User',
|
||||
Session: 'Session',
|
||||
UserPreferences: 'UserPreferences',
|
||||
Channel: 'Channel'
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a stub Prisma Client that will error at runtime if called.
|
||||
*/
|
||||
class PrismaClient {
|
||||
constructor() {
|
||||
return new Proxy(this, {
|
||||
get(target, prop) {
|
||||
let message
|
||||
const runtime = getRuntime()
|
||||
if (runtime.isEdge) {
|
||||
message = `PrismaClient is not configured to run in ${runtime.prettyName}. In order to run Prisma Client on edge runtime, either:
|
||||
- Use Prisma Accelerate: https://pris.ly/d/accelerate
|
||||
- Use Driver Adapters: https://pris.ly/d/driver-adapters
|
||||
`;
|
||||
} else {
|
||||
message = 'PrismaClient is unable to run in this browser environment, or has been bundled for the browser (running in `' + runtime.prettyName + '`).'
|
||||
}
|
||||
|
||||
message += `
|
||||
If this is unexpected, please open an issue: https://pris.ly/prisma-prisma-bug-report`
|
||||
|
||||
throw new Error(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
exports.PrismaClient = PrismaClient
|
||||
|
||||
Object.assign(exports, Prisma)
|
||||
6981
server/prisma/generated/client/index.d.ts
vendored
Normal file
6981
server/prisma/generated/client/index.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
165
server/prisma/generated/client/index.js
Normal file
165
server/prisma/generated/client/index.js
Normal file
@@ -0,0 +1,165 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
const {
|
||||
PrismaClientKnownRequestError,
|
||||
PrismaClientUnknownRequestError,
|
||||
PrismaClientRustPanicError,
|
||||
PrismaClientInitializationError,
|
||||
PrismaClientValidationError,
|
||||
getPrismaClient,
|
||||
sqltag,
|
||||
empty,
|
||||
join,
|
||||
raw,
|
||||
skip,
|
||||
Decimal,
|
||||
Debug,
|
||||
DbNull,
|
||||
JsonNull,
|
||||
AnyNull,
|
||||
NullTypes,
|
||||
makeStrictEnum,
|
||||
Extensions,
|
||||
warnOnce,
|
||||
defineDmmfProperty,
|
||||
Public,
|
||||
getRuntime,
|
||||
createParam,
|
||||
} = require('./runtime/client.js')
|
||||
|
||||
|
||||
const Prisma = {}
|
||||
|
||||
exports.Prisma = Prisma
|
||||
exports.$Enums = {}
|
||||
|
||||
/**
|
||||
* Prisma Client JS version: 7.2.0
|
||||
* Query Engine version: 0c8ef2ce45c83248ab3df073180d5eda9e8be7a3
|
||||
*/
|
||||
Prisma.prismaVersion = {
|
||||
client: "7.2.0",
|
||||
engine: "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3"
|
||||
}
|
||||
|
||||
Prisma.PrismaClientKnownRequestError = PrismaClientKnownRequestError;
|
||||
Prisma.PrismaClientUnknownRequestError = PrismaClientUnknownRequestError
|
||||
Prisma.PrismaClientRustPanicError = PrismaClientRustPanicError
|
||||
Prisma.PrismaClientInitializationError = PrismaClientInitializationError
|
||||
Prisma.PrismaClientValidationError = PrismaClientValidationError
|
||||
Prisma.Decimal = Decimal
|
||||
|
||||
/**
|
||||
* Re-export of sql-template-tag
|
||||
*/
|
||||
Prisma.sql = sqltag
|
||||
Prisma.empty = empty
|
||||
Prisma.join = join
|
||||
Prisma.raw = raw
|
||||
Prisma.validator = Public.validator
|
||||
|
||||
/**
|
||||
* Extensions
|
||||
*/
|
||||
Prisma.getExtensionContext = Extensions.getExtensionContext
|
||||
Prisma.defineExtension = Extensions.defineExtension
|
||||
|
||||
/**
|
||||
* Shorthand utilities for JSON filtering
|
||||
*/
|
||||
Prisma.DbNull = DbNull
|
||||
Prisma.JsonNull = JsonNull
|
||||
Prisma.AnyNull = AnyNull
|
||||
|
||||
Prisma.NullTypes = NullTypes
|
||||
|
||||
|
||||
|
||||
|
||||
const path = require('path')
|
||||
|
||||
/**
|
||||
* Enums
|
||||
*/
|
||||
exports.Prisma.TransactionIsolationLevel = makeStrictEnum({
|
||||
Serializable: 'Serializable'
|
||||
});
|
||||
|
||||
exports.Prisma.UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
displayName: 'displayName',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
};
|
||||
|
||||
exports.Prisma.SessionScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
expiresAt: 'expiresAt'
|
||||
};
|
||||
|
||||
exports.Prisma.UserPreferencesScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
toggleInputHotkey: 'toggleInputHotkey',
|
||||
toggleOutputHotkey: 'toggleOutputHotkey'
|
||||
};
|
||||
|
||||
exports.Prisma.ChannelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
maxClients: 'maxClients',
|
||||
persistent: 'persistent',
|
||||
owner_id: 'owner_id'
|
||||
};
|
||||
|
||||
exports.Prisma.SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
};
|
||||
|
||||
exports.Prisma.NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
};
|
||||
|
||||
|
||||
exports.Prisma.ModelName = {
|
||||
User: 'User',
|
||||
Session: 'Session',
|
||||
UserPreferences: 'UserPreferences',
|
||||
Channel: 'Channel'
|
||||
};
|
||||
/**
|
||||
* Create the Client
|
||||
*/
|
||||
const config = {
|
||||
"previewFeatures": [],
|
||||
"clientVersion": "7.2.0",
|
||||
"engineVersion": "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3",
|
||||
"activeProvider": "sqlite",
|
||||
"inlineSchema": "datasource db {\n provider = \"sqlite\"\n}\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"./generated/client\"\n}\n\nmodel User {\n id String @id @default(cuid())\n username String @unique\n password String\n displayName String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n Session Session[]\n UserPreferences UserPreferences?\n channels Channel[]\n}\n\nmodel Session {\n id String @id\n userId String\n expiresAt DateTime\n user User @relation(references: [id], fields: [userId], onDelete: Cascade)\n\n @@index([userId])\n}\n\nmodel UserPreferences {\n userId String @id\n toggleInputHotkey String? @default(\"\")\n toggleOutputHotkey String? @default(\"\")\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel Channel {\n id String @id\n name String\n maxClients Int?\n persistent Boolean @default(false)\n owner_id String?\n owner User? @relation(fields: [owner_id], references: [id], onDelete: Cascade)\n}\n"
|
||||
}
|
||||
|
||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"displayName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"Session\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"UserPreferences\",\"kind\":\"object\",\"type\":\"UserPreferences\",\"relationName\":\"UserToUserPreferences\"},{\"name\":\"channels\",\"kind\":\"object\",\"type\":\"Channel\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"UserPreferences\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleInputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleOutputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserPreferences\"}],\"dbName\":null},\"Channel\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"maxClients\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"persistent\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"owner_id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"owner\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
||||
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
||||
config.compilerWasm = {
|
||||
getRuntime: async () => require('./query_compiler_bg.js'),
|
||||
getQueryCompilerWasmModule: async () => {
|
||||
const { Buffer } = require('node:buffer')
|
||||
const { wasm } = require('./query_compiler_bg.wasm-base64.js')
|
||||
const queryCompilerWasmFileBytes = Buffer.from(wasm, 'base64')
|
||||
|
||||
return new WebAssembly.Module(queryCompilerWasmFileBytes)
|
||||
}
|
||||
}
|
||||
|
||||
const PrismaClient = getPrismaClient(config)
|
||||
exports.PrismaClient = PrismaClient
|
||||
Object.assign(exports, Prisma)
|
||||
220
server/prisma/generated/client/internal/class.ts
Normal file
220
server/prisma/generated/client/internal/class.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* WARNING: This is an internal file that is subject to change!
|
||||
*
|
||||
* 🛑 Under no circumstances should you import this file directly! 🛑
|
||||
*
|
||||
* Please import the `PrismaClient` class from the `client.ts` file instead.
|
||||
*/
|
||||
|
||||
import * as runtime from "@prisma/client/runtime/client"
|
||||
import type * as Prisma from "./prismaNamespace.ts"
|
||||
|
||||
|
||||
const config: runtime.GetPrismaClientConfig = {
|
||||
"previewFeatures": [],
|
||||
"clientVersion": "7.2.0",
|
||||
"engineVersion": "0c8ef2ce45c83248ab3df073180d5eda9e8be7a3",
|
||||
"activeProvider": "sqlite",
|
||||
"inlineSchema": "datasource db {\n provider = \"sqlite\"\n // url = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n provider = \"prisma-client\"\n output = \"./generated/client\"\n}\n\nmodel User {\n id String @id @default(cuid())\n username String @unique\n password String\n displayName String\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n Session Session[]\n UserPreferences UserPreferences?\n channels Channel[]\n}\n\nmodel Session {\n id String @id\n userId String\n expiresAt DateTime\n user User @relation(references: [id], fields: [userId], onDelete: Cascade)\n\n @@index([userId])\n}\n\nmodel UserPreferences {\n userId String @id\n toggleInputHotkey String? @default(\"\")\n toggleOutputHotkey String? @default(\"\")\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n}\n\nmodel Channel {\n id String @id @default(cuid())\n name String\n maxClients Int?\n persistent Boolean @default(false)\n owner_id String?\n owner User? @relation(fields: [owner_id], references: [id], onDelete: Cascade)\n}\n",
|
||||
"runtimeDataModel": {
|
||||
"models": {},
|
||||
"enums": {},
|
||||
"types": {}
|
||||
}
|
||||
}
|
||||
|
||||
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"displayName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"Session\",\"kind\":\"object\",\"type\":\"Session\",\"relationName\":\"SessionToUser\"},{\"name\":\"UserPreferences\",\"kind\":\"object\",\"type\":\"UserPreferences\",\"relationName\":\"UserToUserPreferences\"},{\"name\":\"channels\",\"kind\":\"object\",\"type\":\"Channel\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null},\"Session\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"expiresAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"SessionToUser\"}],\"dbName\":null},\"UserPreferences\":{\"fields\":[{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleInputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"toggleOutputHotkey\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"UserToUserPreferences\"}],\"dbName\":null},\"Channel\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"maxClients\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"persistent\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"owner_id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"owner\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ChannelToUser\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
||||
|
||||
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
|
||||
const { Buffer } = await import('node:buffer')
|
||||
const wasmArray = Buffer.from(wasmBase64, 'base64')
|
||||
return new WebAssembly.Module(wasmArray)
|
||||
}
|
||||
|
||||
config.compilerWasm = {
|
||||
getRuntime: async () => await import("@prisma/client/runtime/query_compiler_bg.sqlite.mjs"),
|
||||
|
||||
getQueryCompilerWasmModule: async () => {
|
||||
const { wasm } = await import("@prisma/client/runtime/query_compiler_bg.sqlite.wasm-base64.mjs")
|
||||
return await decodeBase64AsWasm(wasm)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export type LogOptions<ClientOptions extends Prisma.PrismaClientOptions> =
|
||||
'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array<Prisma.LogLevel | Prisma.LogDefinition> ? Prisma.GetEvents<ClientOptions['log']> : never : never
|
||||
|
||||
export interface PrismaClientConstructor {
|
||||
/**
|
||||
* ## Prisma Client
|
||||
*
|
||||
* Type-safe database client for TypeScript
|
||||
* @example
|
||||
* ```
|
||||
* const prisma = new PrismaClient()
|
||||
* // Fetch zero or more Users
|
||||
* const users = await prisma.user.findMany()
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/client).
|
||||
*/
|
||||
|
||||
new <
|
||||
Options extends Prisma.PrismaClientOptions = Prisma.PrismaClientOptions,
|
||||
LogOpts extends LogOptions<Options> = LogOptions<Options>,
|
||||
OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { omit: infer U } ? U : Prisma.PrismaClientOptions['omit'],
|
||||
ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
|
||||
>(options: Prisma.Subset<Options, Prisma.PrismaClientOptions> ): PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||
}
|
||||
|
||||
/**
|
||||
* ## Prisma Client
|
||||
*
|
||||
* Type-safe database client for TypeScript
|
||||
* @example
|
||||
* ```
|
||||
* const prisma = new PrismaClient()
|
||||
* // Fetch zero or more Users
|
||||
* const users = await prisma.user.findMany()
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/client).
|
||||
*/
|
||||
|
||||
export interface PrismaClient<
|
||||
in LogOpts extends Prisma.LogLevel = never,
|
||||
in out OmitOpts extends Prisma.PrismaClientOptions['omit'] = undefined,
|
||||
in out ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
|
||||
> {
|
||||
[K: symbol]: { types: Prisma.TypeMap<ExtArgs>['other'] }
|
||||
|
||||
$on<V extends LogOpts>(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient;
|
||||
|
||||
/**
|
||||
* Connect with the database
|
||||
*/
|
||||
$connect(): runtime.Types.Utils.JsPromise<void>;
|
||||
|
||||
/**
|
||||
* Disconnect from the database
|
||||
*/
|
||||
$disconnect(): runtime.Types.Utils.JsPromise<void>;
|
||||
|
||||
/**
|
||||
* Executes a prepared raw query and returns the number of affected rows.
|
||||
* @example
|
||||
* ```
|
||||
* const result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/raw-queries).
|
||||
*/
|
||||
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<number>;
|
||||
|
||||
/**
|
||||
* Executes a raw query and returns the number of affected rows.
|
||||
* Susceptible to SQL injections, see documentation.
|
||||
* @example
|
||||
* ```
|
||||
* const result = await prisma.$executeRawUnsafe('UPDATE User SET cool = $1 WHERE email = $2 ;', true, 'user@email.com')
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/raw-queries).
|
||||
*/
|
||||
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<number>;
|
||||
|
||||
/**
|
||||
* Performs a prepared raw query and returns the `SELECT` data.
|
||||
* @example
|
||||
* ```
|
||||
* const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/raw-queries).
|
||||
*/
|
||||
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>;
|
||||
|
||||
/**
|
||||
* Performs a raw query and returns the `SELECT` data.
|
||||
* Susceptible to SQL injections, see documentation.
|
||||
* @example
|
||||
* ```
|
||||
* const result = await prisma.$queryRawUnsafe('SELECT * FROM User WHERE id = $1 OR email = $2;', 1, 'user@email.com')
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://pris.ly/d/raw-queries).
|
||||
*/
|
||||
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<T>;
|
||||
|
||||
|
||||
/**
|
||||
* Allows the running of a sequence of read/write operations that are guaranteed to either succeed or fail as a whole.
|
||||
* @example
|
||||
* ```
|
||||
* const [george, bob, alice] = await prisma.$transaction([
|
||||
* prisma.user.create({ data: { name: 'George' } }),
|
||||
* prisma.user.create({ data: { name: 'Bob' } }),
|
||||
* prisma.user.create({ data: { name: 'Alice' } }),
|
||||
* ])
|
||||
* ```
|
||||
*
|
||||
* Read more in our [docs](https://www.prisma.io/docs/concepts/components/prisma-client/transactions).
|
||||
*/
|
||||
$transaction<P extends Prisma.PrismaPromise<any>[]>(arg: [...P], options?: { isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<runtime.Types.Utils.UnwrapTuple<P>>
|
||||
|
||||
$transaction<R>(fn: (prisma: Omit<PrismaClient, runtime.ITXClientDenyList>) => runtime.Types.Utils.JsPromise<R>, options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<R>
|
||||
|
||||
$extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb<OmitOpts>, ExtArgs, runtime.Types.Utils.Call<Prisma.TypeMapCb<OmitOpts>, {
|
||||
extArgs: ExtArgs
|
||||
}>>
|
||||
|
||||
/**
|
||||
* `prisma.user`: Exposes CRUD operations for the **User** model.
|
||||
* Example usage:
|
||||
* ```ts
|
||||
* // Fetch zero or more Users
|
||||
* const users = await prisma.user.findMany()
|
||||
* ```
|
||||
*/
|
||||
get user(): Prisma.UserDelegate<ExtArgs, { omit: OmitOpts }>;
|
||||
|
||||
/**
|
||||
* `prisma.session`: Exposes CRUD operations for the **Session** model.
|
||||
* Example usage:
|
||||
* ```ts
|
||||
* // Fetch zero or more Sessions
|
||||
* const sessions = await prisma.session.findMany()
|
||||
* ```
|
||||
*/
|
||||
get session(): Prisma.SessionDelegate<ExtArgs, { omit: OmitOpts }>;
|
||||
|
||||
/**
|
||||
* `prisma.userPreferences`: Exposes CRUD operations for the **UserPreferences** model.
|
||||
* Example usage:
|
||||
* ```ts
|
||||
* // Fetch zero or more UserPreferences
|
||||
* const userPreferences = await prisma.userPreferences.findMany()
|
||||
* ```
|
||||
*/
|
||||
get userPreferences(): Prisma.UserPreferencesDelegate<ExtArgs, { omit: OmitOpts }>;
|
||||
|
||||
/**
|
||||
* `prisma.channel`: Exposes CRUD operations for the **Channel** model.
|
||||
* Example usage:
|
||||
* ```ts
|
||||
* // Fetch zero or more Channels
|
||||
* const channels = await prisma.channel.findMany()
|
||||
* ```
|
||||
*/
|
||||
get channel(): Prisma.ChannelDelegate<ExtArgs, { omit: OmitOpts }>;
|
||||
}
|
||||
|
||||
export function getPrismaClientClass(): PrismaClientConstructor {
|
||||
return runtime.getPrismaClient(config) as unknown as PrismaClientConstructor
|
||||
}
|
||||
1000
server/prisma/generated/client/internal/prismaNamespace.ts
Normal file
1000
server/prisma/generated/client/internal/prismaNamespace.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* WARNING: This is an internal file that is subject to change!
|
||||
*
|
||||
* 🛑 Under no circumstances should you import this file directly! 🛑
|
||||
*
|
||||
* All exports from this file are wrapped under a `Prisma` namespace object in the browser.ts file.
|
||||
* While this enables partial backward compatibility, it is not part of the stable public API.
|
||||
*
|
||||
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
|
||||
* model files in the `model` directory!
|
||||
*/
|
||||
|
||||
import * as runtime from "@prisma/client/runtime/index-browser"
|
||||
|
||||
export type * from '../models.ts'
|
||||
export type * from './prismaNamespace.ts'
|
||||
|
||||
export const Decimal = runtime.Decimal
|
||||
|
||||
|
||||
export const NullTypes = {
|
||||
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
|
||||
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
|
||||
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
|
||||
}
|
||||
/**
|
||||
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
|
||||
*
|
||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||
*/
|
||||
export const DbNull = runtime.DbNull
|
||||
|
||||
/**
|
||||
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
|
||||
*
|
||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||
*/
|
||||
export const JsonNull = runtime.JsonNull
|
||||
|
||||
/**
|
||||
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
|
||||
*
|
||||
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
|
||||
*/
|
||||
export const AnyNull = runtime.AnyNull
|
||||
|
||||
|
||||
export const ModelName = {
|
||||
User: 'User',
|
||||
Session: 'Session',
|
||||
UserPreferences: 'UserPreferences',
|
||||
Channel: 'Channel'
|
||||
} as const
|
||||
|
||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||
|
||||
/*
|
||||
* Enums
|
||||
*/
|
||||
|
||||
export const TransactionIsolationLevel = {
|
||||
Serializable: 'Serializable'
|
||||
} as const
|
||||
|
||||
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||
|
||||
|
||||
export const UserScalarFieldEnum = {
|
||||
id: 'id',
|
||||
username: 'username',
|
||||
password: 'password',
|
||||
displayName: 'displayName',
|
||||
createdAt: 'createdAt',
|
||||
updatedAt: 'updatedAt'
|
||||
} as const
|
||||
|
||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||
|
||||
|
||||
export const SessionScalarFieldEnum = {
|
||||
id: 'id',
|
||||
userId: 'userId',
|
||||
expiresAt: 'expiresAt'
|
||||
} as const
|
||||
|
||||
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
|
||||
|
||||
|
||||
export const UserPreferencesScalarFieldEnum = {
|
||||
userId: 'userId',
|
||||
toggleInputHotkey: 'toggleInputHotkey',
|
||||
toggleOutputHotkey: 'toggleOutputHotkey'
|
||||
} as const
|
||||
|
||||
export type UserPreferencesScalarFieldEnum = (typeof UserPreferencesScalarFieldEnum)[keyof typeof UserPreferencesScalarFieldEnum]
|
||||
|
||||
|
||||
export const ChannelScalarFieldEnum = {
|
||||
id: 'id',
|
||||
name: 'name',
|
||||
maxClients: 'maxClients',
|
||||
persistent: 'persistent',
|
||||
owner_id: 'owner_id'
|
||||
} as const
|
||||
|
||||
export type ChannelScalarFieldEnum = (typeof ChannelScalarFieldEnum)[keyof typeof ChannelScalarFieldEnum]
|
||||
|
||||
|
||||
export const SortOrder = {
|
||||
asc: 'asc',
|
||||
desc: 'desc'
|
||||
} as const
|
||||
|
||||
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
|
||||
|
||||
|
||||
export const NullsOrder = {
|
||||
first: 'first',
|
||||
last: 'last'
|
||||
} as const
|
||||
|
||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||
|
||||
15
server/prisma/generated/client/models.ts
Normal file
15
server/prisma/generated/client/models.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
// @ts-nocheck
|
||||
/*
|
||||
* This is a barrel export file for all models and their related types.
|
||||
*
|
||||
* 🟢 You can import this file directly.
|
||||
*/
|
||||
export type * from './models/User.ts'
|
||||
export type * from './models/Session.ts'
|
||||
export type * from './models/UserPreferences.ts'
|
||||
export type * from './models/Channel.ts'
|
||||
export type * from './commonInputTypes.ts'
|
||||
1412
server/prisma/generated/client/models/Channel.ts
Normal file
1412
server/prisma/generated/client/models/Channel.ts
Normal file
File diff suppressed because it is too large
Load Diff
1267
server/prisma/generated/client/models/Session.ts
Normal file
1267
server/prisma/generated/client/models/Session.ts
Normal file
File diff suppressed because it is too large
Load Diff
1597
server/prisma/generated/client/models/User.ts
Normal file
1597
server/prisma/generated/client/models/User.ts
Normal file
File diff suppressed because it is too large
Load Diff
1228
server/prisma/generated/client/models/UserPreferences.ts
Normal file
1228
server/prisma/generated/client/models/UserPreferences.ts
Normal file
File diff suppressed because it is too large
Load Diff
144
server/prisma/generated/client/package.json
Normal file
144
server/prisma/generated/client/package.json
Normal file
@@ -0,0 +1,144 @@
|
||||
{
|
||||
"name": "prisma-client-bc59e606b3f744a8b15c18a26f89a0eb5328cd0b7e5ee2c8265028eef68b0317",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"browser": "default.js",
|
||||
"exports": {
|
||||
"./client": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./extension": {
|
||||
"types": "./extension.d.ts",
|
||||
"require": "./extension.js",
|
||||
"import": "./extension.js",
|
||||
"default": "./extension.js"
|
||||
},
|
||||
"./index-browser": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index-browser.js",
|
||||
"import": "./index-browser.js",
|
||||
"default": "./index-browser.js"
|
||||
},
|
||||
"./index": {
|
||||
"types": "./index.d.ts",
|
||||
"require": "./index.js",
|
||||
"import": "./index.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./edge": {
|
||||
"types": "./edge.d.ts",
|
||||
"require": "./edge.js",
|
||||
"import": "./edge.js",
|
||||
"default": "./edge.js"
|
||||
},
|
||||
"./runtime/client": {
|
||||
"types": "./runtime/client.d.ts",
|
||||
"node": {
|
||||
"require": "./runtime/client.js",
|
||||
"default": "./runtime/client.js"
|
||||
},
|
||||
"require": "./runtime/client.js",
|
||||
"import": "./runtime/client.mjs",
|
||||
"default": "./runtime/client.mjs"
|
||||
},
|
||||
"./runtime/wasm-compiler-edge": {
|
||||
"types": "./runtime/wasm-compiler-edge.d.ts",
|
||||
"require": "./runtime/wasm-compiler-edge.js",
|
||||
"import": "./runtime/wasm-compiler-edge.mjs",
|
||||
"default": "./runtime/wasm-compiler-edge.mjs"
|
||||
},
|
||||
"./runtime/index-browser": {
|
||||
"types": "./runtime/index-browser.d.ts",
|
||||
"require": "./runtime/index-browser.js",
|
||||
"import": "./runtime/index-browser.mjs",
|
||||
"default": "./runtime/index-browser.mjs"
|
||||
},
|
||||
"./generator-build": {
|
||||
"require": "./generator-build/index.js",
|
||||
"import": "./generator-build/index.js",
|
||||
"default": "./generator-build/index.js"
|
||||
},
|
||||
"./sql": {
|
||||
"require": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.js",
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"import": {
|
||||
"types": "./sql.d.ts",
|
||||
"node": "./sql.mjs",
|
||||
"default": "./sql.mjs"
|
||||
},
|
||||
"default": "./sql.js"
|
||||
},
|
||||
"./*": "./*"
|
||||
},
|
||||
"version": "7.2.0",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@prisma/client-runtime-utils": "7.2.0"
|
||||
},
|
||||
"imports": {
|
||||
"#wasm-compiler-loader": {
|
||||
"edge-light": "./wasm-edge-light-loader.mjs",
|
||||
"workerd": "./wasm-worker-loader.mjs",
|
||||
"worker": "./wasm-worker-loader.mjs",
|
||||
"default": "./wasm-worker-loader.mjs"
|
||||
},
|
||||
"#main-entry-point": {
|
||||
"require": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"import": {
|
||||
"node": "./index.js",
|
||||
"edge-light": "./edge.js",
|
||||
"workerd": "./edge.js",
|
||||
"worker": "./edge.js",
|
||||
"browser": "./index-browser.js",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"default": "./index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
2
server/prisma/generated/client/query_compiler_bg.js
Normal file
2
server/prisma/generated/client/query_compiler_bg.js
Normal file
@@ -0,0 +1,2 @@
|
||||
"use strict";var h=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var D=(e,t)=>{for(var n in t)h(e,n,{get:t[n],enumerable:!0})},O=(e,t,n,_)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of M(t))!j.call(e,r)&&r!==n&&h(e,r,{get:()=>t[r],enumerable:!(_=T(t,r))||_.enumerable});return e};var B=e=>O(h({},"__esModule",{value:!0}),e);var xe={};D(xe,{QueryCompiler:()=>F,__wbg_Error_e83987f665cf5504:()=>q,__wbg_Number_bb48ca12f395cd08:()=>C,__wbg_String_8f0eb39a4a4c2f66:()=>k,__wbg___wbindgen_boolean_get_6d5a1ee65bab5f68:()=>W,__wbg___wbindgen_debug_string_df47ffb5e35e6763:()=>V,__wbg___wbindgen_in_bb933bd9e1b3bc0f:()=>z,__wbg___wbindgen_is_object_c818261d21f283a4:()=>L,__wbg___wbindgen_is_string_fbb76cb2940daafd:()=>P,__wbg___wbindgen_is_undefined_2d472862bd29a478:()=>Q,__wbg___wbindgen_jsval_loose_eq_b664b38a2f582147:()=>Y,__wbg___wbindgen_number_get_a20bf9b85341449d:()=>G,__wbg___wbindgen_string_get_e4f06c90489ad01b:()=>J,__wbg___wbindgen_throw_b855445ff6a94295:()=>X,__wbg_entries_e171b586f8f6bdbf:()=>H,__wbg_getTime_14776bfb48a1bff9:()=>K,__wbg_get_7bed016f185add81:()=>Z,__wbg_get_with_ref_key_1dc361bd10053bfe:()=>v,__wbg_instanceof_ArrayBuffer_70beb1189ca63b38:()=>ee,__wbg_instanceof_Uint8Array_20c8e73002f7af98:()=>te,__wbg_isSafeInteger_d216eda7911dde36:()=>ne,__wbg_length_69bca3cb64fc8748:()=>re,__wbg_length_cdd215e10d9dd507:()=>_e,__wbg_new_0_f9740686d739025c:()=>oe,__wbg_new_1acc0b6eea89d040:()=>ce,__wbg_new_5a79be3ab53b8aa5:()=>ie,__wbg_new_68651c719dcda04e:()=>se,__wbg_new_e17d9f43105b08be:()=>ue,__wbg_prototypesetcall_2a6620b6922694b2:()=>fe,__wbg_set_3f1d0b984ed272ed:()=>be,__wbg_set_907fb406c34a251d:()=>de,__wbg_set_c213c871859d6500:()=>ae,__wbg_set_message_82ae475bb413aa5c:()=>ge,__wbg_set_wasm:()=>N,__wbindgen_cast_2241b6af4c4b2941:()=>le,__wbindgen_cast_4625c577ab2ec9ee:()=>we,__wbindgen_cast_9ae0607507abb057:()=>pe,__wbindgen_cast_d6cd19b81560fd6e:()=>ye,__wbindgen_init_externref_table:()=>me});module.exports=B(xe);var A=()=>{};A.prototype=A;let o;function N(e){o=e}let p=null;function a(){return(p===null||p.byteLength===0)&&(p=new Uint8Array(o.memory.buffer)),p}let y=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});y.decode();const U=2146435072;let S=0;function R(e,t){return S+=t,S>=U&&(y=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),y.decode(),S=t),y.decode(a().subarray(e,e+t))}function m(e,t){return e=e>>>0,R(e,t)}let f=0;const g=new TextEncoder;"encodeInto"in g||(g.encodeInto=function(e,t){const n=g.encode(e);return t.set(n),{read:e.length,written:n.length}});function l(e,t,n){if(n===void 0){const i=g.encode(e),d=t(i.length,1)>>>0;return a().subarray(d,d+i.length).set(i),f=i.length,d}let _=e.length,r=t(_,1)>>>0;const s=a();let c=0;for(;c<_;c++){const i=e.charCodeAt(c);if(i>127)break;s[r+c]=i}if(c!==_){c!==0&&(e=e.slice(c)),r=n(r,_,_=c+e.length*3,1)>>>0;const i=a().subarray(r+c,r+_),d=g.encodeInto(e,i);c+=d.written,r=n(r,_,c,1)>>>0}return f=c,r}let b=null;function u(){return(b===null||b.buffer.detached===!0||b.buffer.detached===void 0&&b.buffer!==o.memory.buffer)&&(b=new DataView(o.memory.buffer)),b}function x(e){return e==null}function I(e){const t=typeof e;if(t=="number"||t=="boolean"||e==null)return`${e}`;if(t=="string")return`"${e}"`;if(t=="symbol"){const r=e.description;return r==null?"Symbol":`Symbol(${r})`}if(t=="function"){const r=e.name;return typeof r=="string"&&r.length>0?`Function(${r})`:"Function"}if(Array.isArray(e)){const r=e.length;let s="[";r>0&&(s+=I(e[0]));for(let c=1;c<r;c++)s+=", "+I(e[c]);return s+="]",s}const n=/\[object ([^\]]+)\]/.exec(toString.call(e));let _;if(n&&n.length>1)_=n[1];else return toString.call(e);if(_=="Object")try{return"Object("+JSON.stringify(e)+")"}catch{return"Object"}return e instanceof Error?`${e.name}: ${e.message}
|
||||
${e.stack}`:_}function $(e,t){return e=e>>>0,a().subarray(e/1,e/1+t)}function w(e){const t=o.__wbindgen_externrefs.get(e);return o.__externref_table_dealloc(e),t}const E=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(e=>o.__wbg_querycompiler_free(e>>>0,1));class F{__destroy_into_raw(){const t=this.__wbg_ptr;return this.__wbg_ptr=0,E.unregister(this),t}free(){const t=this.__destroy_into_raw();o.__wbg_querycompiler_free(t,0)}compileBatch(t){const n=l(t,o.__wbindgen_malloc,o.__wbindgen_realloc),_=f,r=o.querycompiler_compileBatch(this.__wbg_ptr,n,_);if(r[2])throw w(r[1]);return w(r[0])}constructor(t){const n=o.querycompiler_new(t);if(n[2])throw w(n[1]);return this.__wbg_ptr=n[0]>>>0,E.register(this,this.__wbg_ptr,this),this}compile(t){const n=l(t,o.__wbindgen_malloc,o.__wbindgen_realloc),_=f,r=o.querycompiler_compile(this.__wbg_ptr,n,_);if(r[2])throw w(r[1]);return w(r[0])}}Symbol.dispose&&(F.prototype[Symbol.dispose]=F.prototype.free);function q(e,t){return Error(m(e,t))}function C(e){return Number(e)}function k(e,t){const n=String(t),_=l(n,o.__wbindgen_malloc,o.__wbindgen_realloc),r=f;u().setInt32(e+4*1,r,!0),u().setInt32(e+4*0,_,!0)}function W(e){const t=e,n=typeof t=="boolean"?t:void 0;return x(n)?16777215:n?1:0}function V(e,t){const n=I(t),_=l(n,o.__wbindgen_malloc,o.__wbindgen_realloc),r=f;u().setInt32(e+4*1,r,!0),u().setInt32(e+4*0,_,!0)}function z(e,t){return e in t}function L(e){const t=e;return typeof t=="object"&&t!==null}function P(e){return typeof e=="string"}function Q(e){return e===void 0}function Y(e,t){return e==t}function G(e,t){const n=t,_=typeof n=="number"?n:void 0;u().setFloat64(e+8*1,x(_)?0:_,!0),u().setInt32(e+4*0,!x(_),!0)}function J(e,t){const n=t,_=typeof n=="string"?n:void 0;var r=x(_)?0:l(_,o.__wbindgen_malloc,o.__wbindgen_realloc),s=f;u().setInt32(e+4*1,s,!0),u().setInt32(e+4*0,r,!0)}function X(e,t){throw new Error(m(e,t))}function H(e){return Object.entries(e)}function K(e){return e.getTime()}function Z(e,t){return e[t>>>0]}function v(e,t){return e[t]}function ee(e){let t;try{t=e instanceof ArrayBuffer}catch{t=!1}return t}function te(e){let t;try{t=e instanceof Uint8Array}catch{t=!1}return t}function ne(e){return Number.isSafeInteger(e)}function re(e){return e.length}function _e(e){return e.length}function oe(){return new Date}function ce(){return new Object}function ie(e){return new Uint8Array(e)}function se(){return new Map}function ue(){return new Array}function fe(e,t,n){Uint8Array.prototype.set.call($(e,t),n)}function be(e,t,n){e[t]=n}function de(e,t,n){return e.set(t,n)}function ae(e,t,n){e[t>>>0]=n}function ge(e,t){global.PRISMA_WASM_PANIC_REGISTRY.set_message(m(e,t))}function le(e,t){return m(e,t)}function we(e){return BigInt.asUintN(64,e)}function pe(e){return e}function ye(e){return e}function me(){const e=o.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}0&&(module.exports={QueryCompiler,__wbg_Error_e83987f665cf5504,__wbg_Number_bb48ca12f395cd08,__wbg_String_8f0eb39a4a4c2f66,__wbg___wbindgen_boolean_get_6d5a1ee65bab5f68,__wbg___wbindgen_debug_string_df47ffb5e35e6763,__wbg___wbindgen_in_bb933bd9e1b3bc0f,__wbg___wbindgen_is_object_c818261d21f283a4,__wbg___wbindgen_is_string_fbb76cb2940daafd,__wbg___wbindgen_is_undefined_2d472862bd29a478,__wbg___wbindgen_jsval_loose_eq_b664b38a2f582147,__wbg___wbindgen_number_get_a20bf9b85341449d,__wbg___wbindgen_string_get_e4f06c90489ad01b,__wbg___wbindgen_throw_b855445ff6a94295,__wbg_entries_e171b586f8f6bdbf,__wbg_getTime_14776bfb48a1bff9,__wbg_get_7bed016f185add81,__wbg_get_with_ref_key_1dc361bd10053bfe,__wbg_instanceof_ArrayBuffer_70beb1189ca63b38,__wbg_instanceof_Uint8Array_20c8e73002f7af98,__wbg_isSafeInteger_d216eda7911dde36,__wbg_length_69bca3cb64fc8748,__wbg_length_cdd215e10d9dd507,__wbg_new_0_f9740686d739025c,__wbg_new_1acc0b6eea89d040,__wbg_new_5a79be3ab53b8aa5,__wbg_new_68651c719dcda04e,__wbg_new_e17d9f43105b08be,__wbg_prototypesetcall_2a6620b6922694b2,__wbg_set_3f1d0b984ed272ed,__wbg_set_907fb406c34a251d,__wbg_set_c213c871859d6500,__wbg_set_message_82ae475bb413aa5c,__wbg_set_wasm,__wbindgen_cast_2241b6af4c4b2941,__wbindgen_cast_4625c577ab2ec9ee,__wbindgen_cast_9ae0607507abb057,__wbindgen_cast_d6cd19b81560fd6e,__wbindgen_init_externref_table});
|
||||
BIN
server/prisma/generated/client/query_compiler_bg.wasm
Normal file
BIN
server/prisma/generated/client/query_compiler_bg.wasm
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
3317
server/prisma/generated/client/runtime/client.d.ts
vendored
Normal file
3317
server/prisma/generated/client/runtime/client.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
86
server/prisma/generated/client/runtime/client.js
Normal file
86
server/prisma/generated/client/runtime/client.js
Normal file
File diff suppressed because one or more lines are too long
87
server/prisma/generated/client/runtime/index-browser.d.ts
vendored
Normal file
87
server/prisma/generated/client/runtime/index-browser.d.ts
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
import { AnyNull } from '@prisma/client-runtime-utils';
|
||||
import { DbNull } from '@prisma/client-runtime-utils';
|
||||
import { Decimal } from '@prisma/client-runtime-utils';
|
||||
import { isAnyNull } from '@prisma/client-runtime-utils';
|
||||
import { isDbNull } from '@prisma/client-runtime-utils';
|
||||
import { isJsonNull } from '@prisma/client-runtime-utils';
|
||||
import { JsonNull } from '@prisma/client-runtime-utils';
|
||||
import { NullTypes } from '@prisma/client-runtime-utils';
|
||||
|
||||
export { AnyNull }
|
||||
|
||||
declare type Args<T, F extends Operation> = T extends {
|
||||
[K: symbol]: {
|
||||
types: {
|
||||
operations: {
|
||||
[K in F]: {
|
||||
args: any;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
} ? T[symbol]['types']['operations'][F]['args'] : any;
|
||||
|
||||
export { DbNull }
|
||||
|
||||
export { Decimal }
|
||||
|
||||
declare type Exact<A, W> = (A extends unknown ? (W extends A ? {
|
||||
[K in keyof A]: Exact<A[K], W[K]>;
|
||||
} : W) : never) | (A extends Narrowable ? A : never);
|
||||
|
||||
export declare function getRuntime(): GetRuntimeOutput;
|
||||
|
||||
declare type GetRuntimeOutput = {
|
||||
id: RuntimeName;
|
||||
prettyName: string;
|
||||
isEdge: boolean;
|
||||
};
|
||||
|
||||
export { isAnyNull }
|
||||
|
||||
export { isDbNull }
|
||||
|
||||
export { isJsonNull }
|
||||
|
||||
export { JsonNull }
|
||||
|
||||
/**
|
||||
* Generates more strict variant of an enum which, unlike regular enum,
|
||||
* throws on non-existing property access. This can be useful in following situations:
|
||||
* - we have an API, that accepts both `undefined` and `SomeEnumType` as an input
|
||||
* - enum values are generated dynamically from DMMF.
|
||||
*
|
||||
* In that case, if using normal enums and no compile-time typechecking, using non-existing property
|
||||
* will result in `undefined` value being used, which will be accepted. Using strict enum
|
||||
* in this case will help to have a runtime exception, telling you that you are probably doing something wrong.
|
||||
*
|
||||
* Note: if you need to check for existence of a value in the enum you can still use either
|
||||
* `in` operator or `hasOwnProperty` function.
|
||||
*
|
||||
* @param definition
|
||||
* @returns
|
||||
*/
|
||||
export declare function makeStrictEnum<T extends Record<PropertyKey, string | number>>(definition: T): T;
|
||||
|
||||
declare type Narrowable = string | number | bigint | boolean | [];
|
||||
|
||||
export { NullTypes }
|
||||
|
||||
declare type Operation = 'findFirst' | 'findFirstOrThrow' | 'findUnique' | 'findUniqueOrThrow' | 'findMany' | 'create' | 'createMany' | 'createManyAndReturn' | 'update' | 'updateMany' | 'updateManyAndReturn' | 'upsert' | 'delete' | 'deleteMany' | 'aggregate' | 'count' | 'groupBy' | '$queryRaw' | '$executeRaw' | '$queryRawUnsafe' | '$executeRawUnsafe' | 'findRaw' | 'aggregateRaw' | '$runCommandRaw';
|
||||
|
||||
declare namespace Public {
|
||||
export {
|
||||
validator
|
||||
}
|
||||
}
|
||||
export { Public }
|
||||
|
||||
declare type RuntimeName = 'workerd' | 'deno' | 'netlify' | 'node' | 'bun' | 'edge-light' | '';
|
||||
|
||||
declare function validator<V>(): <S>(select: Exact<S, V>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation>(client: C, model: M, operation: O): <S>(select: Exact<S, Args<C[M], O>>) => S;
|
||||
|
||||
declare function validator<C, M extends Exclude<keyof C, `$${string}`>, O extends keyof C[M] & Operation, P extends keyof Args<C[M], O>>(client: C, model: M, operation: O, prop: P): <S>(select: Exact<S, Args<C[M], O>[P]>) => S;
|
||||
|
||||
export { }
|
||||
6
server/prisma/generated/client/runtime/index-browser.js
Normal file
6
server/prisma/generated/client/runtime/index-browser.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
"use strict";var s=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var a=(e,t)=>{for(var n in t)s(e,n,{get:t[n],enumerable:!0})},y=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of p(t))!f.call(e,i)&&i!==n&&s(e,i,{get:()=>t[i],enumerable:!(r=g(t,i))||r.enumerable});return e};var x=e=>y(s({},"__esModule",{value:!0}),e);var O={};a(O,{AnyNull:()=>o.AnyNull,DbNull:()=>o.DbNull,Decimal:()=>m.Decimal,JsonNull:()=>o.JsonNull,NullTypes:()=>o.NullTypes,Public:()=>l,getRuntime:()=>c,isAnyNull:()=>o.isAnyNull,isDbNull:()=>o.isDbNull,isJsonNull:()=>o.isJsonNull,makeStrictEnum:()=>u});module.exports=x(O);var l={};a(l,{validator:()=>d});function d(...e){return t=>t}var b=new Set(["toJSON","$$typeof","asymmetricMatch",Symbol.iterator,Symbol.toStringTag,Symbol.isConcatSpreadable,Symbol.toPrimitive]);function u(e){return new Proxy(e,{get(t,n){if(n in t)return t[n];if(!b.has(n))throw new TypeError("Invalid enum value: ".concat(String(n)))}})}var N=()=>{var e,t;return((t=(e=globalThis.process)==null?void 0:e.release)==null?void 0:t.name)==="node"},S=()=>{var e,t;return!!globalThis.Bun||!!((t=(e=globalThis.process)==null?void 0:e.versions)!=null&&t.bun)},E=()=>!!globalThis.Deno,R=()=>typeof globalThis.Netlify=="object",h=()=>typeof globalThis.EdgeRuntime=="object",C=()=>{var e;return((e=globalThis.navigator)==null?void 0:e.userAgent)==="Cloudflare-Workers"};function k(){var n;return(n=[[R,"netlify"],[h,"edge-light"],[C,"workerd"],[E,"deno"],[S,"bun"],[N,"node"]].flatMap(r=>r[0]()?[r[1]]:[]).at(0))!=null?n:""}var M={node:"Node.js",workerd:"Cloudflare Workers",deno:"Deno and Deno Deploy",netlify:"Netlify Edge Functions","edge-light":"Edge Runtime (Vercel Edge Functions, Vercel Edge Middleware, Next.js (Pages Router) Edge API Routes, Next.js (App Router) Edge Route Handlers or Next.js Middleware)"};function c(){let e=k();return{id:e,prettyName:M[e]||e,isEdge:["workerd","deno","netlify","edge-light"].includes(e)}}var o=require("@prisma/client-runtime-utils"),m=require("@prisma/client-runtime-utils");0&&(module.exports={AnyNull,DbNull,Decimal,JsonNull,NullTypes,Public,getRuntime,isAnyNull,isDbNull,isJsonNull,makeStrictEnum});
|
||||
//# sourceMappingURL=index-browser.js.map
|
||||
76
server/prisma/generated/client/runtime/wasm-compiler-edge.js
Normal file
76
server/prisma/generated/client/runtime/wasm-compiler-edge.js
Normal file
File diff suppressed because one or more lines are too long
45
server/prisma/generated/client/schema.prisma
Normal file
45
server/prisma/generated/client/schema.prisma
Normal file
@@ -0,0 +1,45 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
output = "./generated/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
username String @unique
|
||||
password String
|
||||
displayName String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
Session Session[]
|
||||
UserPreferences UserPreferences?
|
||||
channels Channel[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id
|
||||
userId String
|
||||
expiresAt DateTime
|
||||
user User @relation(references: [id], fields: [userId], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model UserPreferences {
|
||||
userId String @id
|
||||
toggleInputHotkey String? @default("")
|
||||
toggleOutputHotkey String? @default("")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model Channel {
|
||||
id String @id
|
||||
name String
|
||||
maxClients Int?
|
||||
persistent Boolean @default(false)
|
||||
owner_id String?
|
||||
owner User? @relation(fields: [owner_id], references: [id], onDelete: Cascade)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_compiler_bg.wasm?module')
|
||||
5
server/prisma/generated/client/wasm-worker-loader.mjs
Normal file
5
server/prisma/generated/client/wasm-worker-loader.mjs
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
export default import('./query_compiler_bg.wasm')
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `volumes` on the `UserPreferences` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- CreateTable
|
||||
CREATE TABLE "Channel" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"name" TEXT NOT NULL,
|
||||
"maxClients" INTEGER,
|
||||
"persistent" BOOLEAN NOT NULL DEFAULT false,
|
||||
"owner_id" TEXT,
|
||||
CONSTRAINT "Channel_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
CREATE TABLE "new_UserPreferences" (
|
||||
"userId" TEXT NOT NULL PRIMARY KEY,
|
||||
"toggleInputHotkey" TEXT DEFAULT '',
|
||||
"toggleOutputHotkey" TEXT DEFAULT '',
|
||||
CONSTRAINT "UserPreferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_UserPreferences" ("toggleInputHotkey", "toggleOutputHotkey", "userId") SELECT "toggleInputHotkey", "toggleOutputHotkey", "userId" FROM "UserPreferences";
|
||||
DROP TABLE "UserPreferences";
|
||||
ALTER TABLE "new_UserPreferences" RENAME TO "UserPreferences";
|
||||
PRAGMA foreign_keys=ON;
|
||||
PRAGMA defer_foreign_keys=OFF;
|
||||
@@ -1,11 +1,11 @@
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
url = env("DATABASE_URL")
|
||||
// url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
// output = "./generated/client"
|
||||
provider = "prisma-client"
|
||||
output = "./generated/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
@@ -15,16 +15,15 @@ model User {
|
||||
displayName String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
Session Session[]
|
||||
UserPreferences UserPreferences?
|
||||
channels Channel[]
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id
|
||||
userId String
|
||||
expiresAt DateTime
|
||||
|
||||
user User @relation(references: [id], fields: [userId], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
@@ -34,7 +33,14 @@ model UserPreferences {
|
||||
userId String @id
|
||||
toggleInputHotkey String? @default("")
|
||||
toggleOutputHotkey String? @default("")
|
||||
volumes Json? @default("{}")
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model Channel {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
maxClients Int?
|
||||
persistent Boolean @default(false)
|
||||
owner_id String?
|
||||
owner User? @relation(fields: [owner_id], references: [id], onDelete: Cascade)
|
||||
}
|
||||
23
server/prisma/seed.ts
Normal file
23
server/prisma/seed.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import prisma from '../prisma/client.ts'
|
||||
|
||||
async function main() {
|
||||
await prisma.channel.upsert({
|
||||
where: { id: 'default' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'default',
|
||||
name: 'Default',
|
||||
persistent: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
.then(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
.catch(async (e) => {
|
||||
console.error(e)
|
||||
await prisma.$disconnect()
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { Namespace } from '../types/webrtc.ts'
|
||||
import type { Namespace } from '../types/socket.ts'
|
||||
import { z } from 'zod'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { socketToClient } from '../utils/socket-to-client.ts'
|
||||
|
||||
449
server/socket/channel.ts
Normal file
449
server/socket/channel.ts
Normal file
@@ -0,0 +1,449 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { Server as SocketServer } from 'socket.io'
|
||||
import type {
|
||||
Namespace,
|
||||
SomeSocket,
|
||||
} from '../types/socket.ts'
|
||||
import { consola } from 'consola'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { socketToClient } from '../utils/socket-to-client.ts'
|
||||
|
||||
export default function (io: SocketServer<Namespace>) {
|
||||
io.on('connection', async (socket) => {
|
||||
consola.info('[WebRtc]', 'Client connected', socket.id)
|
||||
|
||||
socket.data.joined = false
|
||||
|
||||
socket.data.inputMuted = false
|
||||
socket.data.outputMuted = false
|
||||
|
||||
socket.data.transports = new Map()
|
||||
socket.data.producers = new Map()
|
||||
socket.data.consumers = new Map()
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: socket.handshake.auth.userId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
displayName: true,
|
||||
},
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
socket.disconnect()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const { id, username, displayName } = user
|
||||
|
||||
socket.data.userId = id
|
||||
socket.data.username = username
|
||||
socket.data.displayName = displayName
|
||||
|
||||
socket.emit('authenticated', { channels })
|
||||
|
||||
socket.on('join', async ({ rtpCapabilities }, cb) => {
|
||||
if (socket.data.joined) {
|
||||
consola.error('[WebRtc]', 'Already joined')
|
||||
cb({ error: 'Already joined' })
|
||||
}
|
||||
|
||||
socket.data.joined = true
|
||||
socket.data.rtpCapabilities = rtpCapabilities
|
||||
|
||||
const joinedSockets = await getJoinedSockets()
|
||||
|
||||
cb(joinedSockets.map(socketToClient))
|
||||
|
||||
for (const joinedSocket of joinedSockets.filter(joinedSocket => joinedSocket.id !== socket.id)) {
|
||||
for (const producer of joinedSocket.data.producers.values()) {
|
||||
createConsumer(
|
||||
socket,
|
||||
joinedSocket,
|
||||
producer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
socket.broadcast.emit('newPeer', socketToClient(socket))
|
||||
})
|
||||
|
||||
socket.on('getRtpCapabilities', (cb) => {
|
||||
cb(router.rtpCapabilities)
|
||||
})
|
||||
|
||||
socket.on('createTransport', async ({ producing, consuming }, cb) => {
|
||||
try {
|
||||
const transport = await router.createWebRtcTransport({
|
||||
listenInfos: [
|
||||
{
|
||||
protocol: 'udp',
|
||||
ip: '0.0.0.0',
|
||||
announcedAddress: process.env.ANNOUNCED_ADDRESS || '127.0.0.1',
|
||||
portRange: {
|
||||
min: 40000,
|
||||
max: 40100,
|
||||
},
|
||||
},
|
||||
],
|
||||
enableUdp: true,
|
||||
preferUdp: true,
|
||||
appData: {
|
||||
producing,
|
||||
consuming,
|
||||
},
|
||||
})
|
||||
|
||||
socket.data.transports.set(transport.id, transport)
|
||||
|
||||
cb({
|
||||
id: transport.id,
|
||||
iceParameters: transport.iceParameters,
|
||||
iceCandidates: transport.iceCandidates,
|
||||
dtlsParameters: transport.dtlsParameters,
|
||||
})
|
||||
|
||||
transport.on('icestatechange', (iceState: types.IceState) => {
|
||||
if (iceState === 'disconnected' || iceState === 'closed') {
|
||||
consola.info('[WebRtc]', '[WebRtcTransport]', `"icestatechange" event [iceState:${iceState}], closing peer`, transport.id)
|
||||
|
||||
socket.disconnect()
|
||||
}
|
||||
})
|
||||
|
||||
transport.on('dtlsstatechange', (dtlsState: types.DtlsState) => {
|
||||
if (dtlsState === 'failed' || dtlsState === 'closed') {
|
||||
consola.warn('WebRtcTransport "dtlsstatechange" event [dtlsState:%s], closing peer', dtlsState)
|
||||
|
||||
socket.disconnect()
|
||||
}
|
||||
})
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
consola.error('[WebRtc]', '[createTransport]', error.message)
|
||||
cb({ error: error.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('connectTransport', async ({ transportId, dtlsParameters }, cb) => {
|
||||
const transport = socket.data.transports.get(transportId)
|
||||
|
||||
if (!transport) {
|
||||
consola.error('[WebRtc]', '[connectTransport]', `Transport with id ${transportId} not found`)
|
||||
cb({ error: 'Transport not found' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await transport.connect({ dtlsParameters })
|
||||
|
||||
cb({ ok: true })
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
consola.error('[WebRtc]', '[connectTransport]', error.message)
|
||||
cb({ error: error.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('produce', async ({ transportId, kind, rtpParameters, appData }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const transport = socket.data.transports.get(transportId)
|
||||
|
||||
if (!transport) {
|
||||
consola.error('[WebRtc]', '[produce]', `Transport with id ${transportId} not found`)
|
||||
cb({ error: 'Transport not found' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const producer = await transport.produce({ kind, rtpParameters, appData: { ...appData, socketId: socket.id } })
|
||||
|
||||
socket.data.producers.set(producer.id, producer)
|
||||
|
||||
cb({ id: producer.id })
|
||||
|
||||
const otherSockets = await getJoinedSockets(socket.id)
|
||||
|
||||
for (const otherSocket of otherSockets) {
|
||||
createConsumer(
|
||||
otherSocket,
|
||||
socket,
|
||||
producer,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: Add into the AudioLevelObserver and ActiveSpeakerObserver.
|
||||
// https://github.com/versatica/mediasoup-demo/blob/v3/server/lib/Room.js#L1276
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
consola.error('[WebRtc]', '[produce]', error.message)
|
||||
cb({ error: error.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('closeProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const producer = socket.data.producers.get(producerId)
|
||||
|
||||
if (!producer) {
|
||||
consola.error(`producer with id "${producerId}" not found`)
|
||||
cb({ error: `producer with id "${producerId}" not found` })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
producer.close()
|
||||
|
||||
socket.data.producers.delete(producerId)
|
||||
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('pauseProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const producer = socket.data.producers.get(producerId)
|
||||
|
||||
if (!producer) {
|
||||
consola.error(`producer with id "${producerId}" not found`)
|
||||
cb({ error: `producer with id "${producerId}" not found` })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (producer.paused)
|
||||
return
|
||||
|
||||
await producer.pause()
|
||||
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('resumeProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const producer = socket.data.producers.get(producerId)
|
||||
|
||||
if (!producer) {
|
||||
consola.error(`producer with id "${producerId}" not found`)
|
||||
cb({ error: `producer with id "${producerId}" not found` })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await producer.resume()
|
||||
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('pauseConsumer', async ({ consumerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const consumer = socket.data.consumers.get(consumerId)
|
||||
|
||||
if (!consumer) {
|
||||
consola.error(`consumer with id "${consumerId}" not found`)
|
||||
cb({ error: `consumer with id "${consumerId}" not found` })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await consumer.pause()
|
||||
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('resumeConsumer', async ({ consumerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const consumer = socket.data.consumers.get(consumerId)
|
||||
|
||||
if (!consumer) {
|
||||
consola.error(`consumer with id "${consumerId}" not found`)
|
||||
cb({ error: `consumer with id "${consumerId}" not found` })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
await consumer.resume()
|
||||
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('updateClient', async (updatedClient, cb) => {
|
||||
if (typeof updatedClient.inputMuted === 'boolean') {
|
||||
socket.data.inputMuted = updatedClient.inputMuted
|
||||
}
|
||||
|
||||
if (typeof updatedClient.outputMuted === 'boolean') {
|
||||
socket.data.outputMuted = updatedClient.outputMuted
|
||||
}
|
||||
|
||||
cb(socketToClient(socket))
|
||||
|
||||
namespace.emit('clientChanged', socket.id, socketToClient(socket))
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
consola.info('Client disconnected:', socket.id)
|
||||
|
||||
if (socket.data.joined) {
|
||||
socket.broadcast.emit('peerClosed', socket.id)
|
||||
}
|
||||
|
||||
for (const transport of socket.data.transports.values()) {
|
||||
transport.close()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
async function getJoinedSockets(excludeId?: string) {
|
||||
const sockets = await namespace.fetchSockets()
|
||||
|
||||
return sockets.filter(socket => socket.data.joined && (excludeId ? excludeId !== socket.id : true))
|
||||
}
|
||||
|
||||
async function createConsumer(
|
||||
consumerSocket: SomeSocket,
|
||||
producerSocket: SomeSocket,
|
||||
producer: types.Producer,
|
||||
) {
|
||||
if (
|
||||
!consumerSocket.data.rtpCapabilities
|
||||
|| !router.canConsume(
|
||||
{
|
||||
producerId: producer.id,
|
||||
rtpCapabilities: consumerSocket.data.rtpCapabilities,
|
||||
},
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const transport = Array.from(consumerSocket.data.transports.values())
|
||||
.find(t => t.appData.consuming)
|
||||
|
||||
if (!transport) {
|
||||
consola.error('createConsumer() | Transport for consuming not found')
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let consumer: types.Consumer
|
||||
|
||||
try {
|
||||
consumer = await transport.consume(
|
||||
{
|
||||
producerId: producer.id,
|
||||
rtpCapabilities: consumerSocket.data.rtpCapabilities,
|
||||
// Enable NACK for OPUS.
|
||||
enableRtx: true,
|
||||
paused: true,
|
||||
ignoreDtx: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
catch (error) {
|
||||
consola.error('_createConsumer() | transport.consume():%o', error)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
consumerSocket.data.consumers.set(consumer.id, consumer)
|
||||
|
||||
consumer.on('transportclose', () => {
|
||||
consumerSocket.data.consumers.delete(consumer.id)
|
||||
})
|
||||
|
||||
consumer.on('producerclose', () => {
|
||||
consumerSocket.data.consumers.delete(consumer.id)
|
||||
|
||||
consumerSocket.emit('consumerClosed', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('producerpause', () => {
|
||||
consumerSocket.emit('consumerPaused', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('producerresume', () => {
|
||||
consumerSocket.emit('consumerResumed', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('score', (score: types.ConsumerScore) => {
|
||||
consumerSocket.emit('consumerScore', { consumerId: consumer.id, score })
|
||||
})
|
||||
|
||||
try {
|
||||
await consumerSocket.emitWithAck(
|
||||
'newConsumer',
|
||||
{
|
||||
socketId: producerSocket.id,
|
||||
producerId: producer.id,
|
||||
id: consumer.id,
|
||||
kind: consumer.kind,
|
||||
rtpParameters: consumer.rtpParameters,
|
||||
type: consumer.type,
|
||||
appData: producer.appData,
|
||||
producerPaused: consumer.producerPaused,
|
||||
},
|
||||
)
|
||||
|
||||
await consumer.resume()
|
||||
|
||||
consumerSocket.emit(
|
||||
'consumerScore',
|
||||
{
|
||||
consumerId: consumer.id,
|
||||
score: consumer.score,
|
||||
},
|
||||
)
|
||||
}
|
||||
catch (error) {
|
||||
consola.error('_createConsumer() | failed:%o', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,14 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { Server as SocketServer } from 'socket.io'
|
||||
import type {
|
||||
ChadClient,
|
||||
Namespace,
|
||||
SomeSocket,
|
||||
} from '../types/webrtc.ts'
|
||||
} from '../types/socket.ts'
|
||||
import { consola } from 'consola'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { socketToClient } from '../utils/socket-to-client.ts'
|
||||
|
||||
export default async function (io: SocketServer, router: types.Router) {
|
||||
const namespace: Namespace = io.of('/webrtc')
|
||||
|
||||
const audioLevelObserver = await router.createAudioLevelObserver({
|
||||
maxEntries: 10,
|
||||
threshold: -80,
|
||||
interval: 800,
|
||||
})
|
||||
|
||||
const activeSpeakerObserver = await router.createActiveSpeakerObserver()
|
||||
|
||||
audioLevelObserver.on('volumes', async (volumes: types.AudioLevelObserverVolume[]) => {
|
||||
namespace.emit('speakingPeers', volumes.map(({ producer, volume }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
return {
|
||||
clientId: socketId,
|
||||
volume,
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
audioLevelObserver.on('silence', () => {
|
||||
namespace.emit('speakingPeers', [])
|
||||
namespace.emit('activeSpeaker', undefined)
|
||||
})
|
||||
|
||||
activeSpeakerObserver.on('dominantspeaker', ({ producer }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
namespace.emit('activeSpeaker', socketId)
|
||||
})
|
||||
|
||||
namespace.on('connection', async (socket) => {
|
||||
export default function (io: SocketServer, router: types.Router) {
|
||||
io.on('connection', async (socket) => {
|
||||
consola.info('[WebRtc]', 'Client connected', socket.id)
|
||||
|
||||
socket.data.joined = false
|
||||
@@ -54,7 +20,7 @@ export default async function (io: SocketServer, router: types.Router) {
|
||||
socket.data.producers = new Map()
|
||||
socket.data.consumers = new Map()
|
||||
|
||||
const { id, username, displayName } = await prisma.user.findUnique({
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: socket.handshake.auth.userId,
|
||||
},
|
||||
@@ -65,11 +31,19 @@ export default async function (io: SocketServer, router: types.Router) {
|
||||
},
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
socket.disconnect()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const { id, username, displayName } = user
|
||||
|
||||
socket.data.userId = id
|
||||
socket.data.username = username
|
||||
socket.data.displayName = displayName
|
||||
|
||||
socket.emit('authenticated')
|
||||
socket.emit('authenticated', { channels })
|
||||
|
||||
socket.on('join', async ({ rtpCapabilities }, cb) => {
|
||||
if (socket.data.joined) {
|
||||
@@ -213,8 +187,8 @@ export default async function (io: SocketServer, router: types.Router) {
|
||||
)
|
||||
}
|
||||
|
||||
await audioLevelObserver.addProducer({ producerId: producer.id })
|
||||
await activeSpeakerObserver.addProducer({ producerId: producer.id })
|
||||
// TODO: Add into the AudioLevelObserver and ActiveSpeakerObserver.
|
||||
// https://github.com/versatica/mediasoup-demo/blob/v3/server/lib/Room.js#L1276
|
||||
}
|
||||
catch (error) {
|
||||
if (error instanceof Error) {
|
||||
@@ -350,7 +324,7 @@ export default async function (io: SocketServer, router: types.Router) {
|
||||
|
||||
cb(socketToClient(socket))
|
||||
|
||||
namespace.emit('clientChanged', socket.id, socketToClient(socket))
|
||||
io.emit('clientChanged', socket.id, socketToClient(socket))
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
@@ -367,7 +341,7 @@ export default async function (io: SocketServer, router: types.Router) {
|
||||
})
|
||||
|
||||
async function getJoinedSockets(excludeId?: string) {
|
||||
const sockets = await namespace.fetchSockets()
|
||||
const sockets = await io.fetchSockets()
|
||||
|
||||
return sockets.filter(socket => socket.data.joined && (excludeId ? excludeId !== socket.id : true))
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2016",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "nodenext",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"allowImportingTsExtensions": true
|
||||
"allowImportingTsExtensions": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"target": "ES2023",
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { RemoteSocket, Socket, Namespace as SocketNamespace } from 'socket.io'
|
||||
import type { User } from '../prisma/client'
|
||||
import type { RemoteSocket, Server, Socket } from 'socket.io'
|
||||
import type { ChannelModel, UserModel } from '../prisma/generated/client/models.ts'
|
||||
|
||||
export interface ServerInfo {
|
||||
owner_id: UserModel['id']
|
||||
channels: ChannelModel[]
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
}
|
||||
|
||||
export interface ChadClient {
|
||||
socketId: string
|
||||
userId: User['id']
|
||||
username: User['username']
|
||||
displayName: User['displayName']
|
||||
userId: UserModel['id']
|
||||
username: UserModel['username']
|
||||
displayName: UserModel['displayName']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
}
|
||||
@@ -96,7 +102,7 @@ export interface ClientToServerEvents {
|
||||
}
|
||||
|
||||
export interface ServerToClientEvents {
|
||||
authenticated: () => void
|
||||
authenticated: (arg: ServerInfo) => void
|
||||
newPeer: (arg: ChadClient) => void
|
||||
producers: (arg: ProducerShort[]) => void
|
||||
newConsumer: (
|
||||
@@ -118,17 +124,15 @@ export interface ServerToClientEvents {
|
||||
consumerResumed: (arg: { consumerId: string }) => void
|
||||
consumerScore: (arg: { consumerId: string, score: types.ConsumerScore }) => void
|
||||
clientChanged: (clientId: ChadClient['socketId'], client: ChadClient) => void
|
||||
speakingPeers: (arg: { clientId: ChadClient['socketId'], volume: types.AudioLevelObserverVolume['volume'] }[]) => void
|
||||
activeSpeaker: (clientId?: ChadClient['socketId']) => void
|
||||
}
|
||||
|
||||
export interface InterServerEvent {}
|
||||
|
||||
export interface SocketData {
|
||||
joined: boolean
|
||||
userId: User['id']
|
||||
username: User['username']
|
||||
displayName: User['displayName']
|
||||
userId: UserModel['id']
|
||||
username: UserModel['username']
|
||||
displayName: UserModel['displayName']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
@@ -137,6 +141,7 @@ export interface SocketData {
|
||||
consumers: Map<types.Consumer['id'], types.Consumer>
|
||||
}
|
||||
|
||||
export type SomeSocket = Socket<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData> | RemoteSocket<ServerToClientEvents, SocketData>
|
||||
export type SomeSocket = Socket<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
| RemoteSocket<ServerToClientEvents, SocketData>
|
||||
|
||||
export type Namespace = SocketNamespace<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
export type SocketServer = Server<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChadClient, SomeSocket } from '../types/webrtc.ts'
|
||||
import type { ChadClient, SomeSocket } from '../types/socket.ts'
|
||||
|
||||
export function socketToClient(socket: SomeSocket): ChadClient {
|
||||
return {
|
||||
|
||||
825
server/yarn.lock
825
server/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user