Compare commits
4 Commits
channels
...
v0.3.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
| c966aa9c4b | |||
| 0915d3c64d | |||
| 9f39ee6430 | |||
| 3658975b93 |
Binary file not shown.
7
client/app/components.d.ts
vendored
7
client/app/components.d.ts
vendored
@@ -13,13 +13,20 @@ declare module 'vue' {
|
||||
PrimeButton: typeof import('primevue/button')['default']
|
||||
PrimeButtonGroup: typeof import('primevue/buttongroup')['default']
|
||||
PrimeCard: typeof import('primevue/card')['default']
|
||||
PrimeDivider: typeof import('primevue/divider')['default']
|
||||
PrimeFloatLabel: typeof import('primevue/floatlabel')['default']
|
||||
PrimeInputGroup: typeof import('primevue/inputgroup')['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']
|
||||
PrimeSlider: typeof import('primevue/slider')['default']
|
||||
PrimeTag: typeof import('primevue/tag')['default']
|
||||
PrimeTextarea: typeof import('primevue/textarea')['default']
|
||||
PrimeToast: typeof import('primevue/toast')['default']
|
||||
PrimeToggleSwitch: typeof import('primevue/toggleswitch')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getVersion } from '@tauri-apps/api/app'
|
||||
|
||||
import { openUrl as tauriOpenUrl } from '@tauri-apps/plugin-opener'
|
||||
import { computedAsync, createGlobalState } from '@vueuse/core'
|
||||
import { useClients } from '~/composables/use-clients'
|
||||
|
||||
@@ -134,6 +134,15 @@ export const useApp = createGlobalState(() => {
|
||||
}
|
||||
}
|
||||
|
||||
async function openUrl(href: string) {
|
||||
if (isTauri.value) {
|
||||
await tauriOpenUrl(href)
|
||||
}
|
||||
else {
|
||||
window.open(href, '_blank', 'noopener noreferrer')
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
ready,
|
||||
clients,
|
||||
@@ -153,5 +162,6 @@ export const useApp = createGlobalState(() => {
|
||||
videoEnabled,
|
||||
sharingEnabled,
|
||||
somebodyStreamingVideo,
|
||||
openUrl,
|
||||
}
|
||||
})
|
||||
|
||||
60
client/app/composables/use-chat.ts
Normal file
60
client/app/composables/use-chat.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { createGlobalState } from '@vueuse/core'
|
||||
|
||||
export interface ChatClientMessage {
|
||||
text: string
|
||||
replyTo?: {
|
||||
messageId: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
sender: string
|
||||
text: string
|
||||
createdAt: string
|
||||
replyTo?: {
|
||||
messageId: string
|
||||
sender: string
|
||||
text: string
|
||||
}
|
||||
}
|
||||
|
||||
export const useChat = createGlobalState(() => {
|
||||
const signaling = useSignaling()
|
||||
const { emit } = useEventBus()
|
||||
|
||||
const messages = shallowRef<ChatMessage[]>([])
|
||||
|
||||
watch(signaling.socket, (socket) => {
|
||||
if (!socket)
|
||||
return
|
||||
|
||||
socket.on('chat:new-message', (message: ChatMessage) => {
|
||||
messages.value.push(message)
|
||||
triggerRef(messages)
|
||||
|
||||
emit('chat:new-message')
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
messages.value = []
|
||||
})
|
||||
}, { immediate: true })
|
||||
|
||||
function sendMessage(message: ChatClientMessage) {
|
||||
if (!signaling.connected.value)
|
||||
return
|
||||
|
||||
message.text = message.text.trim()
|
||||
|
||||
if (!message.text.length)
|
||||
return
|
||||
|
||||
signaling.socket.value!.emit('chat:message', message)
|
||||
}
|
||||
|
||||
return {
|
||||
messages,
|
||||
sendMessage,
|
||||
}
|
||||
})
|
||||
@@ -29,6 +29,8 @@ export interface AppEvents extends Record<EventType, unknown> {
|
||||
'video:disabled': void
|
||||
'share:enabled': void
|
||||
'share:disabled': void
|
||||
|
||||
'chat:new-message': void
|
||||
}
|
||||
|
||||
const emitter = mitt<AppEvents>()
|
||||
|
||||
@@ -10,7 +10,7 @@ export interface SyncedPreferences {
|
||||
export const usePreferences = createGlobalState(() => {
|
||||
const { videoInputs, audioInputs, audioOutputs } = useDevices()
|
||||
|
||||
const fetched = ref(false)
|
||||
const synced = ref(false)
|
||||
|
||||
const inputDeviceId = useLocalStorage<MediaDeviceInfo['deviceId']>('INPUT_DEVICE_ID', 'default')
|
||||
const outputDeviceId = useLocalStorage<MediaDeviceInfo['deviceId']>('OUTPUT_DEVICE_ID', 'default')
|
||||
@@ -58,7 +58,7 @@ export const usePreferences = createGlobalState(() => {
|
||||
)
|
||||
|
||||
return {
|
||||
fetched,
|
||||
synced,
|
||||
inputDeviceId,
|
||||
outputDeviceId,
|
||||
videoDeviceId,
|
||||
|
||||
@@ -15,7 +15,7 @@ function hashStringToNumber(str: string, cap: number): number {
|
||||
|
||||
const oneShots: Howl[] = []
|
||||
|
||||
type SfxEvent = 'mic-on' | 'mic-off' | 'stream-on' | 'stream-off' | 'connection'
|
||||
type SfxEvent = 'mic-on' | 'mic-off' | 'stream-on' | 'stream-off' | 'connection' | 'message'
|
||||
|
||||
const EVENT_VOLUME: Record<SfxEvent, number> = {
|
||||
'mic-on': 0.2,
|
||||
|
||||
@@ -66,7 +66,7 @@ export const useSignaling = createSharedComposable(() => {
|
||||
|
||||
const uri = host ? `${protocol}//${host}` : ``
|
||||
|
||||
socket.value = io(`${uri}`, {
|
||||
socket.value = io(uri, {
|
||||
path: `${pathname}/ws`,
|
||||
transports: ['websocket'],
|
||||
withCredentials: true,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div v-auto-animate class="w-1/2 m-auto">
|
||||
<div class="text-center">
|
||||
<PrimeSelectButton :model-value="tab" class="mb-6" :options="options" option-label="label" :allow-empty="false" @update:model-value="onTabChanged" />
|
||||
<PrimeSelectButton v-model="tab" class="mb-6" :options="options" option-label="label" :allow-empty="false" />
|
||||
</div>
|
||||
|
||||
<slot />
|
||||
@@ -15,35 +15,28 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { version } = useApp()
|
||||
|
||||
interface Tab {
|
||||
label: string
|
||||
route: RouteLocationRaw
|
||||
}
|
||||
|
||||
const options = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: 'Login',
|
||||
route: { name: 'Login' },
|
||||
routeName: 'Login',
|
||||
},
|
||||
{
|
||||
label: 'Register',
|
||||
route: { name: 'Register' },
|
||||
routeName: 'Register',
|
||||
},
|
||||
] as Tab[]
|
||||
]
|
||||
})
|
||||
|
||||
const tab = computed(() => {
|
||||
return options.value.find(option => route.name === router.resolve(option.route)?.name)
|
||||
})
|
||||
const tab = shallowRef(options.value.find(option => route.name === option.routeName))
|
||||
|
||||
function onTabChanged(tab: Tab) {
|
||||
navigateTo(tab.route)
|
||||
}
|
||||
watch(tab, (tab) => {
|
||||
if (!tab)
|
||||
return
|
||||
|
||||
navigateTo({ name: tab.routeName })
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -56,11 +56,9 @@
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
|
||||
<PrimeScrollPanel class="bg-surface-900 rounded-xl overflow-hidden" style="min-height: 0">
|
||||
<div class="p-3">
|
||||
<div class="bg-surface-900 rounded-xl overflow-hidden p-3 flex flex-col min-h-full">
|
||||
<slot />
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
</div>
|
||||
|
||||
<FullscreenGallery />
|
||||
|
||||
@@ -7,9 +7,9 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
if (!me.value)
|
||||
return
|
||||
|
||||
const { fetched, toggleInputHotkey, toggleOutputHotkey } = usePreferences()
|
||||
const { synced, toggleInputHotkey, toggleOutputHotkey } = usePreferences()
|
||||
|
||||
if (fetched.value)
|
||||
if (synced.value)
|
||||
return
|
||||
|
||||
try {
|
||||
@@ -20,7 +20,7 @@ export default defineNuxtRouteMiddleware(async () => {
|
||||
|
||||
toggleInputHotkey.value = preferences.toggleInputHotkey ?? toggleInputHotkey.value
|
||||
toggleOutputHotkey.value = preferences.toggleOutputHotkey ?? toggleOutputHotkey.value
|
||||
fetched.value = true
|
||||
synced.value = true
|
||||
}
|
||||
catch {}
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="grid grid-cols-[1fr_1fr] gap-2">
|
||||
<PrimeScrollPanel class="grid grid-cols-[1fr_1fr] gap-2 min-h-0">
|
||||
<GalleryCard
|
||||
v-for="producer in producers"
|
||||
:key="`producer-${producer.id}`"
|
||||
@@ -13,7 +13,7 @@
|
||||
:client="consumer.client"
|
||||
:consumer="consumer.consumer"
|
||||
/>
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -1,17 +1,161 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex items-center justify-center">
|
||||
<PrimeCard>
|
||||
<template #content>
|
||||
The chat is under development.
|
||||
</template>
|
||||
</PrimeCard>
|
||||
<p v-if="!messages.length" class="text-muted-color text-center m-auto">
|
||||
Chat is empty
|
||||
</p>
|
||||
|
||||
<PrimeScrollPanel v-else ref="scroll" class="flex-1 min-h-0 overflow-x-hidden">
|
||||
<div class="flex flex-col gap-3">
|
||||
<div
|
||||
v-for="message in messages"
|
||||
:key="message.id"
|
||||
class="w-fit max-w-[60%]"
|
||||
:class="{
|
||||
'ml-auto': message.sender === me?.username,
|
||||
}"
|
||||
>
|
||||
<p
|
||||
v-if="message.sender !== me?.username"
|
||||
class="text-sm text-muted-color mb-1"
|
||||
>
|
||||
{{ message.sender }}
|
||||
</p>
|
||||
|
||||
<div
|
||||
class="px-3 py-2 rounded-lg"
|
||||
:class="{
|
||||
'bg-surface-800': message.sender !== me?.username,
|
||||
'bg-surface-700': message.sender === me?.username,
|
||||
}"
|
||||
>
|
||||
<p class="[&>a]:break-all" @click="handleMessageClick" v-html="parseMessageText(message.text)" />
|
||||
|
||||
<p class="mt-1 text-right text-sm text-muted-color" :title="formatDate(message.createdAt, 'dd.MM.yyyy, HH:mm')">
|
||||
{{ formatDate(message.createdAt) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
|
||||
<div class="mt-3 shrink-0">
|
||||
<PrimeInputGroup>
|
||||
<!-- <PrimeInputGroupAddon> -->
|
||||
<!-- <PrimeButton severity="secondary" class="shrink-0" disabled> -->
|
||||
<!-- <template #icon> -->
|
||||
<!-- <Paperclip /> -->
|
||||
<!-- </template> -->
|
||||
<!-- </PrimeButton> -->
|
||||
<!-- </PrimeInputGroupAddon> -->
|
||||
|
||||
<PrimeInputText
|
||||
v-model="text"
|
||||
placeholder="Write a message..."
|
||||
fluid
|
||||
autocomplete="off"
|
||||
@keydown.enter.exact="sendMessage"
|
||||
@vue:mounted="onInputMounted"
|
||||
/>
|
||||
|
||||
<PrimeButton class="shrink-0" label="Send" severity="contrast" @click="sendMessage" />
|
||||
</PrimeInputGroup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useEventBus } from '#imports'
|
||||
import { format } from 'date-fns'
|
||||
import linkifyStr from 'linkify-string'
|
||||
import { useChat } from '~/composables/use-chat'
|
||||
|
||||
definePageMeta({
|
||||
name: 'Index',
|
||||
})
|
||||
|
||||
const { openUrl } = useApp()
|
||||
const { me } = useClients()
|
||||
const chat = useChat()
|
||||
const eventBus = useEventBus()
|
||||
const { messages } = chat
|
||||
|
||||
const scrollRef = useTemplateRef('scroll')
|
||||
const contentRef = computed(() => scrollRef.value?.$refs.content)
|
||||
|
||||
const text = ref('')
|
||||
|
||||
eventBus.on('chat:new-message', onNewMessage)
|
||||
|
||||
onScopeDispose(() => {
|
||||
eventBus.off('chat:new-message', onNewMessage)
|
||||
})
|
||||
|
||||
function parseMessageText(text: string) {
|
||||
return linkifyStr(
|
||||
text,
|
||||
{
|
||||
className: 'underline',
|
||||
rel: 'noopener noreferrer',
|
||||
target: '_blank',
|
||||
},
|
||||
).replaceAll('\n', '<br>')
|
||||
}
|
||||
|
||||
function formatDate(date: string, formatStr = 'HH:mm') {
|
||||
return format(date, formatStr)
|
||||
}
|
||||
|
||||
function sendMessage() {
|
||||
if (!text.value)
|
||||
return
|
||||
|
||||
chat.sendMessage({
|
||||
text: text.value,
|
||||
})
|
||||
|
||||
text.value = ''
|
||||
}
|
||||
|
||||
function onInputMounted(ref: VNode) {
|
||||
ref.el?.focus()
|
||||
}
|
||||
|
||||
const ARRIVED_STATE_THRESHOLD_PIXELS = 1
|
||||
async function onNewMessage() {
|
||||
if (!contentRef.value)
|
||||
return
|
||||
|
||||
const arrivedBottom = contentRef.value.scrollTop + contentRef.value.clientHeight
|
||||
>= contentRef.value.scrollHeight - ARRIVED_STATE_THRESHOLD_PIXELS
|
||||
|
||||
const scrollable = contentRef.value.scrollHeight > contentRef.value.clientHeight
|
||||
|
||||
await nextTick()
|
||||
|
||||
if (scrollable && !arrivedBottom)
|
||||
return
|
||||
|
||||
contentRef.value.scrollTo({
|
||||
behavior: 'smooth',
|
||||
top: contentRef.value.scrollHeight,
|
||||
})
|
||||
}
|
||||
|
||||
function handleMessageClick({ target }: MouseEvent) {
|
||||
if (!target)
|
||||
return
|
||||
|
||||
if (target instanceof HTMLElement) {
|
||||
if (target.tagName === 'A') {
|
||||
target.addEventListener('click', onAnchorClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onAnchorClick(event: MouseEvent) {
|
||||
event.preventDefault()
|
||||
|
||||
const target = event.target as HTMLAnchorElement
|
||||
|
||||
console.log('yo')
|
||||
openUrl(target.href)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<PrimeScrollPanel class="min-h-0">
|
||||
<PrimeDivider align="left">
|
||||
Audio
|
||||
</PrimeDivider>
|
||||
@@ -81,7 +81,6 @@
|
||||
size="small"
|
||||
option-label="label"
|
||||
option-value="value"
|
||||
:allow-empty="false"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -133,7 +132,7 @@
|
||||
@click="checkForUpdates"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</PrimeScrollPanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
@@ -4,12 +4,12 @@ export default defineNuxtPlugin(() => {
|
||||
|
||||
// Connection sounds
|
||||
on('socket:authenticated', ({ socketId }) => {
|
||||
sfx.playRandomConnectionSound(socketId)
|
||||
sfx.playEvent('stream-on')
|
||||
})
|
||||
|
||||
// Client events
|
||||
on('client:added', (client) => {
|
||||
sfx.playRandomConnectionSound(client.socketId)
|
||||
sfx.playEvent('stream-on')
|
||||
})
|
||||
|
||||
on('client:removed', () => {
|
||||
@@ -54,4 +54,8 @@ export default defineNuxtPlugin(() => {
|
||||
sfx.playEvent('stream-off')
|
||||
}
|
||||
})
|
||||
|
||||
on('chat:new-message', () => {
|
||||
sfx.playEvent('message')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,12 +14,16 @@
|
||||
"@nuxt/fonts": "^0.11.4",
|
||||
"@primeuix/themes": "^1.2.5",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@tauri-apps/plugin-global-shortcut": "~2",
|
||||
"@tauri-apps/plugin-process": "~2",
|
||||
"@tauri-apps/plugin-updater": "~2",
|
||||
"@tauri-apps/plugin-global-shortcut": "^2.3.1",
|
||||
"@tauri-apps/plugin-opener": "~2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"hotkeys-js": "^4.0.0",
|
||||
"howler": "^2.2.4",
|
||||
"linkify-string": "^4.3.2",
|
||||
"linkifyjs": "^4.3.2",
|
||||
"lucide-vue-next": "^0.562.0",
|
||||
"mediasoup-client": "^3.18.6",
|
||||
"mitt": "^3.0.1",
|
||||
|
||||
2361
client/src-tauri/Cargo.lock
generated
2361
client/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,8 @@ log = "0.4"
|
||||
tauri = { version = "2.8.5", features = [] }
|
||||
tauri-plugin-log = "2"
|
||||
tauri-plugin-process = "2"
|
||||
windows = { version = "0.52", features = ["Win32_UI_Shell"] }
|
||||
tauri-plugin-opener = "2"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-global-shortcut = "2"
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"global-shortcut:allow-is-registered",
|
||||
"global-shortcut:allow-register",
|
||||
"global-shortcut:allow-unregister",
|
||||
"global-shortcut:allow-unregister-all"
|
||||
"global-shortcut:allow-unregister-all",
|
||||
"opener:allow-default-urls",
|
||||
"opener:allow-open-url"
|
||||
]
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_single_instance::init(|_, _, _| {}))
|
||||
// .plugin(tauri_plugin_single_instance::init(|_, _, _| {}))
|
||||
.setup(|app| {
|
||||
if cfg!(debug_assertions) {
|
||||
app.handle().plugin(
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn set_app_user_model_id() {
|
||||
use windows::core::HSTRING;
|
||||
use windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
|
||||
|
||||
unsafe {
|
||||
SetCurrentProcessExplicitAppUserModelID(&HSTRING::from("xyz.koptilnya.chad"))
|
||||
.ok()
|
||||
.expect("Failed to set AppUserModelID");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(target_os = "windows")]
|
||||
set_app_user_model_id();
|
||||
|
||||
app_lib::run();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||
"productName": "Chad",
|
||||
"version": "0.2.33",
|
||||
"version": "0.3.0-rc.2",
|
||||
"identifier": "xyz.koptilnya.chad",
|
||||
"build": {
|
||||
"frontendDist": "../.output/public",
|
||||
@@ -23,7 +23,7 @@
|
||||
"fullscreen": false,
|
||||
"center": true,
|
||||
"theme": "Dark",
|
||||
"additionalBrowserArgs": "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required",
|
||||
"additionalBrowserArgs": "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --autoplay-policy=no-user-gesture-required --lang=en",
|
||||
"incognito": false
|
||||
}
|
||||
],
|
||||
|
||||
@@ -2824,10 +2824,10 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tauri-apps/api@npm:^2.6.0":
|
||||
version: 2.8.0
|
||||
resolution: "@tauri-apps/api@npm:2.8.0"
|
||||
checksum: 10c0/fb111e4d7572372997b440ebe6879543fa8c4765151878e3fddfbfe809b18da29eed142ce83061d14a9ca6d896b3266dc8a4927c642d71cdc0b4277dc7e3aabf
|
||||
"@tauri-apps/api@npm:^2.10.1":
|
||||
version: 2.10.1
|
||||
resolution: "@tauri-apps/api@npm:2.10.1"
|
||||
checksum: 10c0/f3c0b2ba67a0b887440a7faa1e0589e847760ee30ec29b964f22573a46b817cb3af2199d6f5f7dfdda54d65b465ebaaa280454c610a5c53d808a0911fa15e45d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2959,7 +2959,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tauri-apps/plugin-global-shortcut@npm:~2":
|
||||
"@tauri-apps/plugin-global-shortcut@npm:^2.3.1":
|
||||
version: 2.3.1
|
||||
resolution: "@tauri-apps/plugin-global-shortcut@npm:2.3.1"
|
||||
dependencies:
|
||||
@@ -2968,21 +2968,30 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tauri-apps/plugin-process@npm:~2":
|
||||
version: 2.3.0
|
||||
resolution: "@tauri-apps/plugin-process@npm:2.3.0"
|
||||
"@tauri-apps/plugin-opener@npm:~2":
|
||||
version: 2.5.3
|
||||
resolution: "@tauri-apps/plugin-opener@npm:2.5.3"
|
||||
dependencies:
|
||||
"@tauri-apps/api": "npm:^2.6.0"
|
||||
checksum: 10c0/ef50344a7436d92278c2ef4526f72daaf3171c4d65743bbc1f7a00fa581644a8583bb8680f637a34af5c7e6a0e8722c22189290e903584fef70ed83b64b6e9c0
|
||||
"@tauri-apps/api": "npm:^2.8.0"
|
||||
checksum: 10c0/9ef2fae01e03f3bb16d8e55bfd921cf7c1d284e6459bd5b45777806304eb70ab0b50cbf03be76fc05e64ef70a37493e0cd90b0acc16eaee4a4fc2cfff7e43b71
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tauri-apps/plugin-updater@npm:~2":
|
||||
version: 2.9.0
|
||||
resolution: "@tauri-apps/plugin-updater@npm:2.9.0"
|
||||
"@tauri-apps/plugin-process@npm:^2.3.1":
|
||||
version: 2.3.1
|
||||
resolution: "@tauri-apps/plugin-process@npm:2.3.1"
|
||||
dependencies:
|
||||
"@tauri-apps/api": "npm:^2.6.0"
|
||||
checksum: 10c0/72ce83d1c241308a13b9929f0900e4d33453875877009166e3998e3e75a1003ac48c3641086b4d3230f0f18c64f475ad6c3556d1603fc641ca50dc9c18d61866
|
||||
"@tauri-apps/api": "npm:^2.8.0"
|
||||
checksum: 10c0/2e5086898f1c9f25f6426a752404c788727237142bbb7c8f418b97c76c5360874d06203150d136e51114df9e720022e4fa3681fd1d4cb6f777dc83c3553f8670
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tauri-apps/plugin-updater@npm:^2.10.1":
|
||||
version: 2.10.1
|
||||
resolution: "@tauri-apps/plugin-updater@npm:2.10.1"
|
||||
dependencies:
|
||||
"@tauri-apps/api": "npm:^2.10.1"
|
||||
checksum: 10c0/5d3813851ccbbf90253ad4647dbd97501c2bb75db864175693322fa6eb062b6e1bae03890810ad0bbe7f6da4e020af35e8c787e6999f8c7c645121164587dc29
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -4059,15 +4068,19 @@ __metadata:
|
||||
"@primevue/nuxt-module": "npm:^4.4.0"
|
||||
"@tailwindcss/vite": "npm:^4.1.14"
|
||||
"@tauri-apps/cli": "npm:^2.8.4"
|
||||
"@tauri-apps/plugin-global-shortcut": "npm:~2"
|
||||
"@tauri-apps/plugin-process": "npm:~2"
|
||||
"@tauri-apps/plugin-updater": "npm:~2"
|
||||
"@tauri-apps/plugin-global-shortcut": "npm:^2.3.1"
|
||||
"@tauri-apps/plugin-opener": "npm:~2"
|
||||
"@tauri-apps/plugin-process": "npm:^2.3.1"
|
||||
"@tauri-apps/plugin-updater": "npm:^2.10.1"
|
||||
"@types/howler": "npm:^2"
|
||||
"@vueuse/core": "npm:^13.9.0"
|
||||
date-fns: "npm:^4.1.0"
|
||||
eslint: "npm:^9.36.0"
|
||||
eslint-plugin-format: "npm:^1.0.2"
|
||||
hotkeys-js: "npm:^4.0.0"
|
||||
howler: "npm:^2.2.4"
|
||||
linkify-string: "npm:^4.3.2"
|
||||
linkifyjs: "npm:^4.3.2"
|
||||
lucide-vue-next: "npm:^0.562.0"
|
||||
mediasoup-client: "npm:^3.18.6"
|
||||
mitt: "npm:^3.0.1"
|
||||
@@ -4566,6 +4579,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"date-fns@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "date-fns@npm:4.1.0"
|
||||
checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"db0@npm:^0.3.4":
|
||||
version: 0.3.4
|
||||
resolution: "db0@npm:0.3.4"
|
||||
@@ -6953,6 +6973,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"linkify-string@npm:^4.3.2":
|
||||
version: 4.3.2
|
||||
resolution: "linkify-string@npm:4.3.2"
|
||||
peerDependencies:
|
||||
linkifyjs: ^4.0.0
|
||||
checksum: 10c0/674e908b46aa6da3ee7e5c0749464d8de55f4d44933d7e9dea4d2f9bb5af0137d45142494288cd120335e21d8298c76ec4524ab6e67edede4613adb5f17f7dc6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"linkifyjs@npm:^4.3.2":
|
||||
version: 4.3.2
|
||||
resolution: "linkifyjs@npm:4.3.2"
|
||||
checksum: 10c0/1a85e6b368304a4417567fe5e38651681e3e82465590836942d1b4f3c834cc35532898eb1e2479f6337d9144b297d418eb708b6be8ed0b3dc3954a3588e07971
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"listhen@npm:^1.9.0":
|
||||
version: 1.9.0
|
||||
resolution: "listhen@npm:1.9.0"
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Chad is a real-time voice/video chat server (think Discord-like) built with Fastify, Socket.IO, and mediasoup for WebRTC media handling. It uses SQLite via Prisma ORM and Lucia for session-based authentication. The client is a Tauri desktop app (separate repo).
|
||||
|
||||
## Commands
|
||||
|
||||
- **Start server:** `yarn start` (runs `ts-node --transpile-only server.ts`)
|
||||
- **Deploy DB (migrate + seed + generate):** `yarn db:deploy`
|
||||
- **Generate Prisma client after schema changes:** `npx prisma generate`
|
||||
- **Create a migration:** `npx prisma migrate dev --name <name>`
|
||||
- **Lint:** `npx eslint .`
|
||||
- **Package manager:** Yarn 4 (corepack). Do not use npm.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Entry Point & Plugin System
|
||||
|
||||
`server.ts` creates a Fastify instance and uses `@fastify/autoload` to auto-register everything in `plugins/` and `routes/` (prefixed under `/chad`). Plugins use `fastify-plugin` (`fp`) with named dependencies to control load order.
|
||||
|
||||
### Plugin Load Order (dependency chain)
|
||||
|
||||
1. `plugins/auth.ts` — Adds `req.user` / `req.session` via Lucia cookie validation on every request (preHandler hook)
|
||||
2. `plugins/mediasoup-worker.ts` — Creates a mediasoup Worker, decorates `fastify.mediasoupWorker`
|
||||
3. `plugins/mediasoup-router.ts` — Creates a mediasoup Router (depends on worker), decorates `fastify.mediasoupRouter`. Configures supported audio/video codecs (Opus, VP8, VP9, H.264, AV1)
|
||||
4. `plugins/socket.ts` — Creates Socket.IO server at `/chad/ws` (depends on worker + router), decorates `fastify.io`. Registers socket handlers on `fastify.ready()`
|
||||
|
||||
### Socket Handlers (`socket/`)
|
||||
|
||||
- `socket/webrtc.ts` — Main WebRTC signaling: join/leave, transport creation, producer/consumer lifecycle, audio level observation, active speaker detection
|
||||
- `socket/channel.ts` — Channel-based socket logic (in development on `channels` branch)
|
||||
|
||||
Both handlers authenticate by looking up `socket.handshake.auth.userId` against the DB.
|
||||
|
||||
### REST Routes (`routes/`)
|
||||
|
||||
All routes are prefixed with `/chad` via autoload config.
|
||||
|
||||
- `routes/auth.ts` — `/register`, `/login`, `/logout`, `/me`
|
||||
- `routes/user.ts` — `/preferences` (GET/PATCH), `/profile` (PATCH). Profile changes broadcast to connected socket peers via `clientChanged` event.
|
||||
|
||||
### Type System (`types/socket.ts`)
|
||||
|
||||
Fully typed Socket.IO events: `ClientToServerEvents`, `ServerToClientEvents`, `SocketData`. The `SomeSocket` union type covers both live `Socket` and `RemoteSocket` (from `fetchSockets()`).
|
||||
|
||||
### Database
|
||||
|
||||
- SQLite with Prisma 7 + `@prisma/adapter-better-sqlite3`
|
||||
- Schema at `prisma/schema.prisma`, generated client at `prisma/generated/client/`
|
||||
- Models: `User`, `Session` (Lucia), `UserPreferences`, `Channel`
|
||||
- Seed creates a persistent "Default" channel
|
||||
- Config in `prisma.config.ts` using `dotenv` for `DATABASE_URL`
|
||||
|
||||
### Key Patterns
|
||||
|
||||
- Request validation uses Zod schemas defined inline in route handlers
|
||||
- DTOs in `dto/` define Prisma select objects with `satisfies Prisma.*Select` for type-safe projections
|
||||
- `utils/socket-to-client.ts` maps socket data to the `ChadClient` interface sent to clients
|
||||
- ESLint uses `@antfu/eslint-config` (flat config) with `no-console` and `n/prefer-global/process` disabled
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `PORT` — Server port (default: 4000, Docker: 80)
|
||||
- `DATABASE_URL` — SQLite path (e.g., `file:../data/database.db`)
|
||||
- `ANNOUNCED_ADDRESS` — Public IP for WebRTC ICE candidates (default: `127.0.0.1`)
|
||||
- `CORS_ORIGIN` — Socket.IO CORS origin (default: `*`)
|
||||
|
||||
### Docker
|
||||
|
||||
`Dockerfile` requires python3 and build-essential for mediasoup native compilation. Runs `yarn db:deploy && yarn start`.
|
||||
@@ -1,14 +0,0 @@
|
||||
// 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,
|
||||
} as 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 db seed && npx prisma generate"
|
||||
"db:deploy": "npx prisma migrate deploy && npx prisma generate"
|
||||
},
|
||||
"type": "module",
|
||||
"packageManager": "yarn@4.10.3",
|
||||
@@ -10,9 +10,9 @@
|
||||
"@fastify/autoload": "^6.3.1",
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@fastify/cors": "^11.1.0",
|
||||
"@fastify/multipart": "^10.0.0",
|
||||
"@lucia-auth/adapter-prisma": "^4.0.1",
|
||||
"@prisma/adapter-better-sqlite3": "^7.2.0",
|
||||
"@prisma/client": "7",
|
||||
"@prisma/client": "^6.17.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"consola": "^3.4.2",
|
||||
"dotenv": "^17.2.3",
|
||||
@@ -20,8 +20,9 @@
|
||||
"fastify-plugin": "^5.1.0",
|
||||
"lucia": "^3.2.2",
|
||||
"mediasoup": "^3.19.3",
|
||||
"prisma": "7",
|
||||
"prisma": "^6.17.0",
|
||||
"socket.io": "^4.8.1",
|
||||
"uuid": "^13.0.0",
|
||||
"ws": "^8.18.3",
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { ServerOptions } from 'socket.io'
|
||||
import type { ChadClient } from '../types/socket.ts'
|
||||
import { consola } from 'consola'
|
||||
import fp from 'fastify-plugin'
|
||||
import { Server } from 'socket.io'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import registerChannelHandlers from '../socket/channel.ts'
|
||||
import registerWebrtcHandlers from '../socket/webrtc.ts'
|
||||
import registerChatSocket from '../socket/chat.ts'
|
||||
import registerWebrtcSocket from '../socket/webrtc.ts'
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyInstance {
|
||||
@@ -26,80 +23,9 @@ export default fp<Partial<ServerOptions>>(
|
||||
await fastify.io.close()
|
||||
})
|
||||
|
||||
fastify.ready(() => {
|
||||
const audioLevelObserver = await fastify.mediasoupRouter.createAudioLevelObserver({
|
||||
maxEntries: 10,
|
||||
threshold: -80,
|
||||
interval: 800,
|
||||
})
|
||||
|
||||
const activeSpeakerObserver = await fastify.mediasoupRouter.createActiveSpeakerObserver()
|
||||
|
||||
audioLevelObserver.on('volumes', async (volumes: types.AudioLevelObserverVolume[]) => {
|
||||
fastify.io.emit('webrtc:speaking-peers', volumes.map(({ producer, volume }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
return {
|
||||
clientId: socketId,
|
||||
volume,
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
audioLevelObserver.on('silence', () => {
|
||||
fastify.io.emit('webrtc:speaking-peers', [])
|
||||
fastify.io.emit('webrtc:active-speaker', undefined)
|
||||
})
|
||||
|
||||
activeSpeakerObserver.on('dominantspeaker', ({ producer }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
fastify.io.emit('webrtc:active-speaker', socketId)
|
||||
})
|
||||
|
||||
fastify.io.on('connection', async (socket) => {
|
||||
consola.info('New connection', socket.id)
|
||||
|
||||
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
|
||||
|
||||
consola.info('User authorized', ...Object.values(user))
|
||||
|
||||
const channel = await registerChannelHandlers(fastify.io, socket)
|
||||
const webrtc = await registerWebrtcHandlers(
|
||||
fastify.io,
|
||||
socket,
|
||||
fastify.mediasoupRouter,
|
||||
audioLevelObserver,
|
||||
activeSpeakerObserver,
|
||||
)
|
||||
|
||||
socket.emit('webrtc:authenticated', {
|
||||
...channel,
|
||||
...webrtc,
|
||||
rtpCapabilities: fastify.mediasoupRouter.rtpCapabilities,
|
||||
})
|
||||
})
|
||||
fastify.ready(async () => {
|
||||
await registerWebrtcSocket(fastify.io, fastify.mediasoupRouter)
|
||||
await registerChatSocket(fastify.io)
|
||||
})
|
||||
},
|
||||
{ name: 'socket-io', dependencies: ['mediasoup-worker', 'mediasoup-router'] },
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
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,14 +1,6 @@
|
||||
import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'
|
||||
import { env } from 'prisma/config'
|
||||
import { PrismaClient } from './generated/client/index.js'
|
||||
import 'dotenv/config'
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const client = new PrismaClient({
|
||||
adapter: new PrismaBetterSqlite3({
|
||||
url: env('DATABASE_URL'),
|
||||
}, {
|
||||
timestampFormat: 'unixepoch-ms',
|
||||
}),
|
||||
log: ['query', 'error', 'warn'],
|
||||
})
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
/* !!! 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
1
server/prisma/generated/client/client.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! This is code generated by Prisma. Do not edit directly. !!!
|
||||
/* eslint-disable */
|
||||
// biome-ignore-all lint: generated file
|
||||
module.exports = { ...require('.') }
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
/* !!! 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
|
||||
@@ -1,298 +0,0 @@
|
||||
|
||||
/* !!! 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
1
server/prisma/generated/client/default.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export * from "./index"
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! 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
1
server/prisma/generated/client/edge.d.ts
vendored
@@ -1 +0,0 @@
|
||||
export * from "./default"
|
||||
@@ -1,165 +0,0 @@
|
||||
|
||||
/* !!! 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)
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
/* !!! 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 {}
|
||||
@@ -1,196 +0,0 @@
|
||||
|
||||
/* !!! 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
6981
server/prisma/generated/client/index.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,165 +0,0 @@
|
||||
|
||||
/* !!! 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)
|
||||
@@ -1,220 +0,0 @@
|
||||
|
||||
/* !!! 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
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,128 +0,0 @@
|
||||
|
||||
/* !!! 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]
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
/* !!! 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'
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,144 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
"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});
|
||||
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
3317
server/prisma/generated/client/runtime/client.d.ts
vendored
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,87 +0,0 @@
|
||||
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 { }
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
/* !!! 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
|
||||
File diff suppressed because one or more lines are too long
@@ -1,45 +0,0 @@
|
||||
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)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! 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')
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
/* !!! 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')
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
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"
|
||||
output = "./generated/client"
|
||||
provider = "prisma-client-js"
|
||||
// output = "./generated/client"
|
||||
}
|
||||
|
||||
model User {
|
||||
@@ -15,15 +15,16 @@ 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])
|
||||
@@ -33,14 +34,7 @@ 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)
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
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)
|
||||
})
|
||||
33
server/routes/attachments.ts
Normal file
33
server/routes/attachments.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import bcrypt from 'bcrypt'
|
||||
import { z } from 'zod'
|
||||
|
||||
export default function (fastify: FastifyInstance) {
|
||||
fastify.post('/attachments/upload', async (req, reply) => {
|
||||
try {
|
||||
const schema = z.object({
|
||||
file: z.file(),
|
||||
})
|
||||
const input = schema.parse(req.body)
|
||||
|
||||
// const file = req.file({ limits: { } })
|
||||
|
||||
const id = await bcrypt.hash(input.file, 10)
|
||||
|
||||
return {
|
||||
id,
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import { z } from 'zod'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { channelPublicSelect } from '../dto/channel.dto.ts'
|
||||
|
||||
export default function (fastify: FastifyInstance) {
|
||||
// GET /chad/channels - List all channels with client counts
|
||||
fastify.get('/channels', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
const channels = await prisma.channel.findMany({
|
||||
select: channelPublicSelect,
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
|
||||
// Add client count to each channel using Socket.IO rooms
|
||||
const channelsWithCounts = await Promise.all(
|
||||
channels.map(async channel => {
|
||||
const socketsInChannel = await fastify.io.in(channel.id).fetchSockets()
|
||||
return {
|
||||
...channel,
|
||||
clientCount: socketsInChannel.length,
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
return channelsWithCounts
|
||||
})
|
||||
|
||||
// GET /chad/channels/:id - Get specific channel with client list
|
||||
fastify.get('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
const channel = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
if (!channel) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
// Get clients in this channel using Socket.IO rooms
|
||||
const sockets = await fastify.io.in(params.id).fetchSockets()
|
||||
const clients = sockets
|
||||
.filter(s => s.data.joined)
|
||||
.map(s => {
|
||||
const channelId = Array.from(s.rooms).find(room => room !== s.id) || 'default'
|
||||
return {
|
||||
socketId: s.id,
|
||||
userId: s.data.userId,
|
||||
username: s.data.username,
|
||||
displayName: s.data.displayName,
|
||||
inputMuted: s.data.inputMuted,
|
||||
outputMuted: s.data.outputMuted,
|
||||
currentChannelId: channelId,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...channel,
|
||||
clients,
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// POST /chad/channels - Create channel
|
||||
fastify.post('/channels', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const schema = z.object({
|
||||
name: z.string().min(1).max(50),
|
||||
maxClients: z.number().int().positive().optional().nullable(),
|
||||
persistent: z.boolean().default(false),
|
||||
})
|
||||
const input = schema.parse(req.body)
|
||||
|
||||
const channel = await prisma.channel.create({
|
||||
data: {
|
||||
name: input.name,
|
||||
maxClients: input.maxClients,
|
||||
persistent: input.persistent,
|
||||
owner_id: req.user.id,
|
||||
},
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
// Notify all connected clients about new channel
|
||||
fastify.io.emit('channelCreated', channel)
|
||||
|
||||
return channel
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// PATCH /chad/channels/:id - Update channel
|
||||
fastify.patch('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
const schema = z.object({
|
||||
name: z.string().min(1).max(50).optional(),
|
||||
maxClients: z.number().int().positive().optional().nullable(),
|
||||
})
|
||||
const input = schema.parse(req.body)
|
||||
|
||||
// Cannot update default channel
|
||||
if (params.id === 'default') {
|
||||
return reply.code(403).send({ error: 'Cannot modify default channel' })
|
||||
}
|
||||
|
||||
const existing = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
if (!existing) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
if (existing.owner_id !== req.user.id) {
|
||||
return reply.code(403).send({ error: 'Not channel owner' })
|
||||
}
|
||||
|
||||
const channel = await prisma.channel.update({
|
||||
where: { id: params.id },
|
||||
data: input,
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
fastify.io.emit('channelUpdated', channel)
|
||||
|
||||
return channel
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// DELETE /chad/channels/:id - Delete channel
|
||||
fastify.delete('/channels/:id', async (req, reply) => {
|
||||
if (!req.user) {
|
||||
return reply.code(401).send({ error: 'Unauthorized' })
|
||||
}
|
||||
|
||||
try {
|
||||
const paramsSchema = z.object({
|
||||
id: z.string(),
|
||||
})
|
||||
const params = paramsSchema.parse(req.params)
|
||||
|
||||
if (params.id === 'default') {
|
||||
return reply.code(403).send({ error: 'Cannot delete default channel' })
|
||||
}
|
||||
|
||||
const existing = await prisma.channel.findUnique({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
if (!existing) {
|
||||
return reply.code(404).send({ error: 'Channel not found' })
|
||||
}
|
||||
|
||||
if (existing.owner_id !== req.user.id) {
|
||||
return reply.code(403).send({ error: 'Not channel owner' })
|
||||
}
|
||||
|
||||
// Move all users in this channel back to default using Socket.IO rooms
|
||||
const sockets = await fastify.io.in(params.id).fetchSockets()
|
||||
|
||||
for (const socket of sockets) {
|
||||
// Close all their producers
|
||||
for (const producer of socket.data.producers.values()) {
|
||||
producer.close()
|
||||
}
|
||||
socket.data.producers.clear()
|
||||
|
||||
// Close all their consumers
|
||||
for (const consumer of socket.data.consumers.values()) {
|
||||
consumer.close()
|
||||
}
|
||||
socket.data.consumers.clear()
|
||||
|
||||
// Move to default room
|
||||
await socket.leave(params.id)
|
||||
await socket.join('default')
|
||||
|
||||
socket.emit('forcedChannelSwitch', { channelId: 'default' })
|
||||
}
|
||||
|
||||
await prisma.channel.delete({
|
||||
where: { id: params.id },
|
||||
})
|
||||
|
||||
fastify.io.emit('channelDeleted', { channelId: params.id })
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
catch (err) {
|
||||
fastify.log.error(err)
|
||||
reply.code(400)
|
||||
if (err instanceof z.ZodError) {
|
||||
reply.send({ error: z.prettifyError(err) })
|
||||
}
|
||||
else {
|
||||
reply.send({ error: err.message })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FastifyInstance } from 'fastify'
|
||||
import type { Namespace } from '../types/socket.ts'
|
||||
import type { Namespace } from '../types/webrtc.ts'
|
||||
import { z } from 'zod'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { socketToClient } from '../utils/socket-to-client.ts'
|
||||
@@ -77,7 +77,7 @@ export default function (fastify: FastifyInstance) {
|
||||
|
||||
if (found) {
|
||||
found.data.displayName = input.displayName
|
||||
namespace.emit('webrtc:client-changed', found.id, socketToClient(found))
|
||||
namespace.emit('clientChanged', found.id, socketToClient(found))
|
||||
}
|
||||
|
||||
return updatedUser
|
||||
|
||||
@@ -3,9 +3,12 @@ import { fileURLToPath } from 'node:url'
|
||||
import FastifyAutoLoad from '@fastify/autoload'
|
||||
import FastifyCookie from '@fastify/cookie'
|
||||
import FastifyCors from '@fastify/cors'
|
||||
import FastifyMultipart from '@fastify/multipart'
|
||||
import Fastify from 'fastify'
|
||||
import prisma from './prisma/client.ts'
|
||||
|
||||
console.log(process.env.DATABASE_URL)
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = dirname(__filename)
|
||||
|
||||
@@ -25,6 +28,7 @@ fastify.register(FastifyCors, {
|
||||
})
|
||||
|
||||
fastify.register(FastifyCookie)
|
||||
fastify.register(FastifyMultipart)
|
||||
|
||||
fastify.register(FastifyAutoLoad, {
|
||||
dir: join(__dirname, 'plugins'),
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
import type {
|
||||
ChadSocket,
|
||||
ClientToServerEvents,
|
||||
ExtractCallbackPayload,
|
||||
SocketServer,
|
||||
} from '../types/socket.ts'
|
||||
import { consola } from 'consola'
|
||||
import { channelPublicSelect } from '../dto/channel.dto.ts'
|
||||
import prisma from '../prisma/client.ts'
|
||||
import { socketToClient } from '../utils/socket-to-client.ts'
|
||||
|
||||
export default async function (io: SocketServer, socket: ChadSocket) {
|
||||
// io.on('channel:join', async (cb) => {
|
||||
// if (socket.data.joined) {
|
||||
// consola.error('[WebRtc]', 'Already joined')
|
||||
// cb({ error: 'Already joined' })
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// socket.data.joined = true
|
||||
// socket.data.rtpCapabilities = rtpCapabilities
|
||||
//
|
||||
// // Get current channel from Socket.IO rooms
|
||||
// const currentChannelId = Array.from(socket.rooms).find(room => room !== socket.id) || 'default'
|
||||
// const joinedSockets = await getJoinedSockets(socket.id, currentChannelId)
|
||||
//
|
||||
// cb(joinedSockets.map(socketToClient))
|
||||
//
|
||||
// for (const joinedSocket of joinedSockets) {
|
||||
// for (const producer of joinedSocket.data.producers.values()) {
|
||||
// createConsumer(
|
||||
// socket,
|
||||
// joinedSocket,
|
||||
// producer,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Broadcast only to same channel using Socket.IO room
|
||||
// socket.to(currentChannelId).emit('newPeer', socketToClient(socket))
|
||||
// })
|
||||
|
||||
socket.on('channel:join', async ({ channelId }, cb) => {
|
||||
try {
|
||||
cb(await handleJoin(channelId))
|
||||
}
|
||||
catch (e) {
|
||||
cb(e)
|
||||
}
|
||||
})
|
||||
|
||||
await handleJoin('default')
|
||||
|
||||
const channels = await prisma.channel.findMany({
|
||||
select: channelPublicSelect,
|
||||
orderBy: { name: 'asc' },
|
||||
})
|
||||
|
||||
type ChannelJoinCallback = ExtractCallbackPayload<ClientToServerEvents['channel:join']>
|
||||
async function handleJoin(channelId: string): Promise<ChannelJoinCallback> {
|
||||
try {
|
||||
const channel = await prisma.channel.findUnique({
|
||||
where: { id: channelId },
|
||||
select: channelPublicSelect,
|
||||
})
|
||||
|
||||
if (!channel) {
|
||||
return { error: 'Channel not found' }
|
||||
}
|
||||
|
||||
if (channel.maxClients) {
|
||||
const socketsInChannel = await io.in(channelId).fetchSockets()
|
||||
if (socketsInChannel.length >= channel.maxClients) {
|
||||
return { error: 'Channel is full' }
|
||||
}
|
||||
}
|
||||
|
||||
const oldChannelId = Array.from(socket.rooms).find(room => room !== socket.id)
|
||||
|
||||
// for (const producer of socket.data.producers.values()) {
|
||||
// producer.close()
|
||||
// }
|
||||
// socket.data.producers.clear()
|
||||
//
|
||||
// for (const consumer of socket.data.consumers.values()) {
|
||||
// consumer.close()
|
||||
// }
|
||||
// socket.data.consumers.clear()
|
||||
|
||||
if (oldChannelId) {
|
||||
await socket.leave(oldChannelId)
|
||||
io.emit('channel:user-left', { channelId: oldChannelId, clientId: socket.id })
|
||||
|
||||
// // Auto-delete non-persistent empty channels
|
||||
// if (isLeavingNonPersistentChannel) {
|
||||
// const oldChannelSockets = await io.in(oldChannelId).fetchSockets()
|
||||
//
|
||||
// if (oldChannelSockets.length === 0) {
|
||||
// const oldChannel = await prisma.channel.findUnique({
|
||||
// where: { id: oldChannelId },
|
||||
// select: { persistent: true, id: true },
|
||||
// })
|
||||
//
|
||||
// if (oldChannel && !oldChannel.persistent) {
|
||||
// await prisma.channel.delete({ where: { id: oldChannelId } })
|
||||
// io.emit('channelDeleted', { channelId: oldChannelId })
|
||||
// consola.info('[Channel]', `Auto-deleted empty non-persistent channel: ${oldChannelId}`)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
await socket.join(channelId)
|
||||
|
||||
// Get new channel members
|
||||
// const newChannelSockets = await getJoinedSockets(socket.id, channelId)
|
||||
//
|
||||
// // Create consumers for existing producers in new channel
|
||||
// for (const peer of newChannelSockets) {
|
||||
// for (const producer of peer.data.producers.values()) {
|
||||
// createConsumer(socket, peer, producer)
|
||||
// }
|
||||
// }
|
||||
|
||||
io.emit('channel:user-joined', {
|
||||
channelId,
|
||||
client: socketToClient(socket),
|
||||
})
|
||||
|
||||
return {
|
||||
channel,
|
||||
clients: newChannelSockets.map(socketToClient),
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
consola.error('[channel:join]', error)
|
||||
|
||||
if (error instanceof Error) {
|
||||
return { error: error.message }
|
||||
}
|
||||
else {
|
||||
return { error: 'Something went wrong' }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
channels,
|
||||
}
|
||||
}
|
||||
28
server/socket/chat.ts
Normal file
28
server/socket/chat.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { Server as SocketServer } from 'socket.io'
|
||||
import type { ChatClientMessage, ChatMessage } from '../types/chat.ts'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export default async function (io: SocketServer) {
|
||||
const messages: ChatMessage[] = []
|
||||
|
||||
io.on('connection', async (socket) => {
|
||||
socket.on('chat:message', async (clientMessage: ChatClientMessage, cb) => {
|
||||
const message: ChatMessage = {
|
||||
id: uuidv4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
sender: socket.data.username,
|
||||
text: clientMessage.text,
|
||||
}
|
||||
|
||||
console.log(message)
|
||||
|
||||
messages.push(message)
|
||||
|
||||
if (messages.length > 5000) {
|
||||
messages.shift()
|
||||
}
|
||||
|
||||
io.emit('chat:new-message', message)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,21 +1,49 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { ActiveSpeakerObserver, AudioLevelObserver } from 'mediasoup/types'
|
||||
import type { Server as SocketServer } from 'socket.io'
|
||||
import type {
|
||||
ChadSocket,
|
||||
ChadClient,
|
||||
SomeSocket,
|
||||
} from '../types/socket.ts'
|
||||
} from '../types/webrtc.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,
|
||||
socket: ChadSocket,
|
||||
router: types.Router,
|
||||
audioLevelObserver: AudioLevelObserver,
|
||||
activeSpeakerObserver: ActiveSpeakerObserver,
|
||||
) {
|
||||
export default async function (io: SocketServer, router: types.Router) {
|
||||
const audioLevelObserver = await router.createAudioLevelObserver({
|
||||
maxEntries: 10,
|
||||
threshold: -80,
|
||||
interval: 800,
|
||||
})
|
||||
|
||||
const activeSpeakerObserver = await router.createActiveSpeakerObserver()
|
||||
|
||||
audioLevelObserver.on('volumes', async (volumes: types.AudioLevelObserverVolume[]) => {
|
||||
io.emit('speakingPeers', volumes.map(({ producer, volume }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
return {
|
||||
clientId: socketId,
|
||||
volume,
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
audioLevelObserver.on('silence', () => {
|
||||
io.emit('speakingPeers', [])
|
||||
io.emit('activeSpeaker', undefined)
|
||||
})
|
||||
|
||||
activeSpeakerObserver.on('dominantspeaker', ({ producer }) => {
|
||||
const { socketId } = producer.appData as { socketId: ChadClient['socketId'] }
|
||||
|
||||
io.emit('activeSpeaker', socketId)
|
||||
})
|
||||
|
||||
io.on('connection', async (socket) => {
|
||||
consola.info('[WebRtc]', 'Client connected', socket.id)
|
||||
|
||||
socket.data.joined = false
|
||||
|
||||
socket.data.inputMuted = false
|
||||
socket.data.outputMuted = false
|
||||
|
||||
@@ -23,11 +51,54 @@ export default async function (
|
||||
socket.data.producers = new Map()
|
||||
socket.data.consumers = new Map()
|
||||
|
||||
socket.on('webrtc:get-rtp-capabilities', (cb) => {
|
||||
const { id, username, displayName } = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: socket.handshake.auth.userId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
displayName: true,
|
||||
},
|
||||
})
|
||||
|
||||
socket.data.userId = id
|
||||
socket.data.username = username
|
||||
socket.data.displayName = displayName
|
||||
|
||||
socket.emit('authenticated')
|
||||
|
||||
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('webrtc:create-transport', async ({ producing, consuming }, cb) => {
|
||||
socket.on('createTransport', async ({ producing, consuming }, cb) => {
|
||||
try {
|
||||
const transport = await router.createWebRtcTransport({
|
||||
listenInfos: [
|
||||
@@ -82,7 +153,7 @@ export default async function (
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('webrtc:connect-transport', async ({ transportId, dtlsParameters }, cb) => {
|
||||
socket.on('connectTransport', async ({ transportId, dtlsParameters }, cb) => {
|
||||
const transport = socket.data.transports.get(transportId)
|
||||
|
||||
if (!transport) {
|
||||
@@ -105,7 +176,7 @@ export default async function (
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('webrtc:produce', async ({ transportId, kind, rtpParameters, appData }, cb) => {
|
||||
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' })
|
||||
@@ -113,14 +184,6 @@ export default async function (
|
||||
return
|
||||
}
|
||||
|
||||
// Block production in default channel
|
||||
const currentChannelId = Array.from(socket.rooms).find(room => room !== socket.id) || 'default'
|
||||
if (currentChannelId === 'default') {
|
||||
consola.error('Cannot produce in default channel')
|
||||
cb({ error: 'Cannot produce media in default channel' })
|
||||
return
|
||||
}
|
||||
|
||||
const transport = socket.data.transports.get(transportId)
|
||||
|
||||
if (!transport) {
|
||||
@@ -137,8 +200,7 @@ export default async function (
|
||||
|
||||
cb({ id: producer.id })
|
||||
|
||||
// Filter by channel when creating consumers
|
||||
const otherSockets = await getJoinedSockets(socket.id, currentChannelId)
|
||||
const otherSockets = await getJoinedSockets(socket.id)
|
||||
|
||||
for (const otherSocket of otherSockets) {
|
||||
createConsumer(
|
||||
@@ -159,7 +221,7 @@ export default async function (
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('webrtc:close-producer', async ({ producerId }, cb) => {
|
||||
socket.on('closeProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
@@ -183,7 +245,7 @@ export default async function (
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('webrtc:pause-producer', async ({ producerId }, cb) => {
|
||||
socket.on('pauseProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
@@ -208,7 +270,7 @@ export default async function (
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('webrtc:resume-producer', async ({ producerId }, cb) => {
|
||||
socket.on('resumeProducer', async ({ producerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
@@ -230,7 +292,7 @@ export default async function (
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('webrtc:pause-consumer', async ({ consumerId }, cb) => {
|
||||
socket.on('pauseConsumer', async ({ consumerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
@@ -252,7 +314,7 @@ export default async function (
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('webrtc:resume-consumer', async ({ consumerId }, cb) => {
|
||||
socket.on('resumeConsumer', async ({ consumerId }, cb) => {
|
||||
if (!socket.data.joined) {
|
||||
consola.error('Peer not joined yet')
|
||||
cb({ error: 'Peer not joined yet' })
|
||||
@@ -274,7 +336,7 @@ export default async function (
|
||||
cb({ ok: true })
|
||||
})
|
||||
|
||||
socket.on('webrtc:update-client', async (updatedClient, cb) => {
|
||||
socket.on('updateClient', async (updatedClient, cb) => {
|
||||
if (typeof updatedClient.inputMuted === 'boolean') {
|
||||
socket.data.inputMuted = updatedClient.inputMuted
|
||||
}
|
||||
@@ -285,21 +347,14 @@ export default async function (
|
||||
|
||||
cb(socketToClient(socket))
|
||||
|
||||
io.emit('webrtc:client-changed', socket.id, socketToClient(socket))
|
||||
io.emit('clientChanged', socket.id, socketToClient(socket))
|
||||
})
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
consola.info('Client disconnected:', socket.id)
|
||||
|
||||
// Get current channel from Socket.IO rooms
|
||||
const channelId = Array.from(socket.rooms).find(room => room !== socket.id)
|
||||
|
||||
if (socket.data.joined) {
|
||||
// Notify only same channel using Socket.IO room
|
||||
if (channelId) {
|
||||
socket.to(channelId).emit('webrtc:peer-closed', socket.id)
|
||||
io.emit('channelUserLeft', { channelId, clientId: socket.id })
|
||||
}
|
||||
socket.broadcast.emit('peerClosed', socket.id)
|
||||
}
|
||||
|
||||
for (const transport of socket.data.transports.values()) {
|
||||
@@ -308,21 +363,10 @@ export default async function (
|
||||
})
|
||||
})
|
||||
|
||||
async function getJoinedSockets(excludeId?: string, channelId?: string) {
|
||||
let sockets = await io.fetchSockets()
|
||||
async function getJoinedSockets(excludeId?: string) {
|
||||
const sockets = await io.fetchSockets()
|
||||
|
||||
// Filter by channel using Socket.IO rooms
|
||||
if (channelId) {
|
||||
sockets = await io.in(channelId).fetchSockets()
|
||||
}
|
||||
|
||||
return sockets.filter((socket) => {
|
||||
if (!socket.data.joined)
|
||||
return false
|
||||
if (excludeId && socket.id === excludeId)
|
||||
return false
|
||||
return true
|
||||
})
|
||||
return sockets.filter(socket => socket.data.joined && (excludeId ? excludeId !== socket.id : true))
|
||||
}
|
||||
|
||||
async function createConsumer(
|
||||
@@ -380,24 +424,24 @@ export default async function (
|
||||
consumer.on('producerclose', () => {
|
||||
consumerSocket.data.consumers.delete(consumer.id)
|
||||
|
||||
consumerSocket.emit('webrtc:consumer-closed', { consumerId: consumer.id })
|
||||
consumerSocket.emit('consumerClosed', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('producerpause', () => {
|
||||
consumerSocket.emit('webrtc:consumer-paused', { consumerId: consumer.id })
|
||||
consumerSocket.emit('consumerPaused', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('producerresume', () => {
|
||||
consumerSocket.emit('webrtc:consumer-resumed', { consumerId: consumer.id })
|
||||
consumerSocket.emit('consumerResumed', { consumerId: consumer.id })
|
||||
})
|
||||
|
||||
consumer.on('score', (score: types.ConsumerScore) => {
|
||||
consumerSocket.emit('webrtc:consumer-score', { consumerId: consumer.id, score })
|
||||
consumerSocket.emit('consumerScore', { consumerId: consumer.id, score })
|
||||
})
|
||||
|
||||
try {
|
||||
await consumerSocket.emitWithAck(
|
||||
'webrtc:new-consumer',
|
||||
'newConsumer',
|
||||
{
|
||||
socketId: producerSocket.id,
|
||||
producerId: producer.id,
|
||||
@@ -413,7 +457,7 @@ export default async function (
|
||||
await consumer.resume()
|
||||
|
||||
consumerSocket.emit(
|
||||
'webrtc:consumer-score',
|
||||
'consumerScore',
|
||||
{
|
||||
consumerId: consumer.id,
|
||||
score: consumer.score,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"target": "es2016",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"target": "ES2023",
|
||||
"moduleResolution": "nodenext",
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"esModuleInterop": true
|
||||
"skipLibCheck": true,
|
||||
"allowImportingTsExtensions": true
|
||||
}
|
||||
}
|
||||
|
||||
18
server/types/chat.ts
Normal file
18
server/types/chat.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export interface ChatClientMessage {
|
||||
text: string
|
||||
replyTo?: {
|
||||
messageId: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string
|
||||
sender: string
|
||||
text: string
|
||||
createdAt: string
|
||||
replyTo?: {
|
||||
messageId: string
|
||||
sender: string
|
||||
text: string
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { RemoteSocket, Server, Socket } from 'socket.io'
|
||||
import type { ChannelPublicDTO } from '../dto/channel.dto.ts'
|
||||
import type { ChannelModel, UserModel } from '../prisma/generated/client/models.ts'
|
||||
|
||||
export interface ServerInfo {
|
||||
owner_id: UserModel['id']
|
||||
channels: ChannelPublicDTO[]
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
}
|
||||
|
||||
export interface ChadClient {
|
||||
socketId: string
|
||||
userId: UserModel['id']
|
||||
username: UserModel['username']
|
||||
displayName: UserModel['displayName']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
currentChannelId: ChannelModel['id']
|
||||
}
|
||||
|
||||
export interface ProducerShort {
|
||||
producerId: types.Producer['id']
|
||||
kind: types.MediaKind
|
||||
}
|
||||
|
||||
export interface ErrorCallbackResult {
|
||||
error: string
|
||||
}
|
||||
|
||||
export interface SuccessCallbackResult {
|
||||
ok: true
|
||||
}
|
||||
|
||||
export type EventCallback<T = SuccessCallbackResult> = (result: T | ErrorCallbackResult) => void
|
||||
export type EventCallbackResult<T = SuccessCallbackResult> = Parameters<EventCallback<T>>[0]
|
||||
|
||||
type LastArg<T extends any[]> = T extends [...any[], infer Last] ? Last : T extends [infer Only] ? Only : never
|
||||
|
||||
export type ExtractCallbackPayload<
|
||||
T,
|
||||
Fallback = EventCallbackResult,
|
||||
>
|
||||
= T extends (...args: infer Args) => any
|
||||
? LastArg<Args> extends (...inner: infer CallbackArgs) => any
|
||||
? CallbackArgs extends [infer First, ...any[]]
|
||||
? First
|
||||
: Fallback
|
||||
: Fallback
|
||||
: Fallback
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
'webrtc:get-rtp-capabilities': (
|
||||
cb: EventCallback<types.RtpCapabilities>
|
||||
) => void
|
||||
'webrtc:create-transport': (
|
||||
options: {
|
||||
producing: boolean
|
||||
consuming: boolean
|
||||
},
|
||||
cb: EventCallback<Pick<types.WebRtcTransport, 'id' | 'iceParameters' | 'iceCandidates' | 'dtlsParameters'>>
|
||||
) => void
|
||||
'webrtc:connect-transport': (
|
||||
options: {
|
||||
transportId: types.WebRtcTransport['id']
|
||||
dtlsParameters: types.WebRtcTransport['dtlsParameters']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:produce': (
|
||||
options: {
|
||||
transportId: types.WebRtcTransport['id']
|
||||
kind: types.MediaKind
|
||||
rtpParameters: types.RtpParameters
|
||||
appData: { source: 'share' | string }
|
||||
},
|
||||
cb: EventCallback<{ id: types.Producer['id'] }>
|
||||
) => void
|
||||
'webrtc:close-producer': (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:pause-producer': (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:resume-producer': (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:pause-consumer': (
|
||||
options: {
|
||||
consumerId: types.Consumer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:resume-consumer': (
|
||||
options: {
|
||||
consumerId: types.Consumer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:update-client': (
|
||||
options: Partial<Pick<ChadClient, 'inputMuted' | 'outputMuted'>>,
|
||||
cb: EventCallback<ChadClient>
|
||||
) => void
|
||||
|
||||
'channel:join': (
|
||||
options: { channelId: ChannelModel['id'] },
|
||||
cb: EventCallback<{ channel: ChannelPublicDTO, clients: ChadClient[] }>
|
||||
) => void
|
||||
}
|
||||
|
||||
export interface ServerToClientEvents {
|
||||
'webrtc:authenticated': (arg: ServerInfo) => void
|
||||
'webrtc:producers': (arg: ProducerShort[]) => void
|
||||
'webrtc:new-consumer': (
|
||||
arg: {
|
||||
socketId: string
|
||||
producerId: types.Producer['id']
|
||||
id: types.Consumer['id']
|
||||
kind: types.MediaKind
|
||||
rtpParameters: types.RtpParameters
|
||||
type: types.ConsumerType
|
||||
appData: types.Producer['appData']
|
||||
producerPaused: types.Consumer['producerPaused']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
'webrtc:peer-closed': (arg: string) => void
|
||||
'webrtc:consumer-closed': (arg: { consumerId: string }) => void
|
||||
'webrtc:consumer-paused': (arg: { consumerId: string }) => void
|
||||
'webrtc:consumer-resumed': (arg: { consumerId: string }) => void
|
||||
'webrtc:consumer-score': (arg: { consumerId: string, score: types.ConsumerScore }) => void
|
||||
'webrtc:client-changed': (clientId: ChadClient['socketId'], client: ChadClient) => void
|
||||
'webrtc:speaking-peers': (arg: { clientId: ChadClient['socketId'], volume: types.AudioLevelObserverVolume['volume'] }[]) => void
|
||||
'webrtc:active-speaker': (clientId?: ChadClient['socketId']) => void
|
||||
|
||||
'channel:user-joined': (arg: { channelId: string, client: ChadClient }) => void
|
||||
'channel:user-left': (arg: { channelId: string, clientId: string }) => void
|
||||
'channel:deleted': (arg: { channelId: string }) => void
|
||||
'channel:created': (arg: ChannelPublicDTO) => void
|
||||
'channel:updated': (arg: ChannelPublicDTO) => void
|
||||
'channel:force-switch': (arg: { channelId: string }) => void
|
||||
}
|
||||
|
||||
export interface InterServerEvent {}
|
||||
|
||||
export interface SocketData {
|
||||
joined: boolean
|
||||
userId: UserModel['id']
|
||||
username: UserModel['username']
|
||||
displayName: UserModel['displayName']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
transports: Map<types.WebRtcTransport['id'], types.WebRtcTransport>
|
||||
producers: Map<types.Producer['id'], types.Producer>
|
||||
consumers: Map<types.Consumer['id'], types.Consumer>
|
||||
}
|
||||
|
||||
export type ChadSocket = Socket<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
export type ChadRemoteSocket = RemoteSocket<ServerToClientEvents, SocketData>
|
||||
|
||||
export type SomeSocket = ChadSocket | ChadRemoteSocket
|
||||
|
||||
export type SocketServer = Server<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
@@ -1,4 +0,0 @@
|
||||
export type LastArgument<T>
|
||||
= T extends (...args: [...any[], infer Last]) => any
|
||||
? Last
|
||||
: never
|
||||
142
server/types/webrtc.ts
Normal file
142
server/types/webrtc.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import type { types } from 'mediasoup'
|
||||
import type { RemoteSocket, Socket, Namespace as SocketNamespace } from 'socket.io'
|
||||
import type { User } from '../prisma/client'
|
||||
|
||||
export interface ChadClient {
|
||||
socketId: string
|
||||
userId: User['id']
|
||||
username: User['username']
|
||||
displayName: User['displayName']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
}
|
||||
|
||||
export interface ProducerShort {
|
||||
producerId: types.Producer['id']
|
||||
kind: types.MediaKind
|
||||
}
|
||||
|
||||
export interface ErrorCallbackResult {
|
||||
error: string
|
||||
}
|
||||
|
||||
export interface SuccessCallbackResult {
|
||||
ok: true
|
||||
}
|
||||
|
||||
export type EventCallback<T = SuccessCallbackResult> = (result: T | ErrorCallbackResult) => void
|
||||
|
||||
export interface ClientToServerEvents {
|
||||
join: (
|
||||
options: {
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
},
|
||||
cb: EventCallback<ChadClient[]>
|
||||
) => void
|
||||
getRtpCapabilities: (
|
||||
cb: EventCallback<types.RtpCapabilities>
|
||||
) => void
|
||||
createTransport: (
|
||||
options: {
|
||||
producing: boolean
|
||||
consuming: boolean
|
||||
},
|
||||
cb: EventCallback<Pick<types.WebRtcTransport, 'id' | 'iceParameters' | 'iceCandidates' | 'dtlsParameters'>>
|
||||
) => void
|
||||
connectTransport: (
|
||||
options: {
|
||||
transportId: types.WebRtcTransport['id']
|
||||
dtlsParameters: types.WebRtcTransport['dtlsParameters']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
produce: (
|
||||
options: {
|
||||
transportId: types.WebRtcTransport['id']
|
||||
kind: types.MediaKind
|
||||
rtpParameters: types.RtpParameters
|
||||
appData: { source: 'share' | string }
|
||||
},
|
||||
cb: EventCallback<{ id: types.Producer['id'] }>
|
||||
) => void
|
||||
closeProducer: (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
pauseProducer: (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
resumeProducer: (
|
||||
options: {
|
||||
producerId: types.Producer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
pauseConsumer: (
|
||||
options: {
|
||||
consumerId: types.Consumer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
resumeConsumer: (
|
||||
options: {
|
||||
consumerId: types.Consumer['id']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
updateClient: (
|
||||
options: Partial<Pick<ChadClient, 'inputMuted' | 'outputMuted'>>,
|
||||
cb: EventCallback<ChadClient>
|
||||
) => void
|
||||
}
|
||||
|
||||
export interface ServerToClientEvents {
|
||||
authenticated: () => void
|
||||
newPeer: (arg: ChadClient) => void
|
||||
producers: (arg: ProducerShort[]) => void
|
||||
newConsumer: (
|
||||
arg: {
|
||||
socketId: string
|
||||
producerId: types.Producer['id']
|
||||
id: types.Consumer['id']
|
||||
kind: types.MediaKind
|
||||
rtpParameters: types.RtpParameters
|
||||
type: types.ConsumerType
|
||||
appData: types.Producer['appData']
|
||||
producerPaused: types.Consumer['producerPaused']
|
||||
},
|
||||
cb: EventCallback
|
||||
) => void
|
||||
peerClosed: (arg: string) => void
|
||||
consumerClosed: (arg: { consumerId: string }) => void
|
||||
consumerPaused: (arg: { consumerId: string }) => void
|
||||
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']
|
||||
inputMuted: boolean
|
||||
outputMuted: boolean
|
||||
rtpCapabilities: types.RtpCapabilities
|
||||
transports: Map<types.WebRtcTransport['id'], types.WebRtcTransport>
|
||||
producers: Map<types.Producer['id'], types.Producer>
|
||||
consumers: Map<types.Consumer['id'], types.Consumer>
|
||||
}
|
||||
|
||||
export type SomeSocket = Socket<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData> | RemoteSocket<ServerToClientEvents, SocketData>
|
||||
|
||||
export type Namespace = SocketNamespace<ClientToServerEvents, ServerToClientEvents, InterServerEvent, SocketData>
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { SocketServer } from '../types/socket.ts'
|
||||
|
||||
export async function fetchSockets(io: SocketServer, excludeId?: string, channelId?: string) {
|
||||
let sockets: Awaited<ReturnType<typeof io.fetchSockets>>
|
||||
|
||||
if (channelId) {
|
||||
sockets = await io.in(channelId).fetchSockets()
|
||||
}
|
||||
else {
|
||||
sockets = await io.fetchSockets()
|
||||
}
|
||||
|
||||
return sockets.filter((socket) => {
|
||||
return !(excludeId && socket.id === excludeId)
|
||||
})
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import type { ChadClient, SomeSocket } from '../types/socket.ts'
|
||||
import type { ChadClient, SomeSocket } from '../types/webrtc.ts'
|
||||
|
||||
export function socketToClient(socket: SomeSocket): ChadClient {
|
||||
// Socket.IO rooms: Extract channel room (filter out the socket's own room)
|
||||
const channelId = Array.from(socket.rooms).find(room => room !== socket.id) || 'default'
|
||||
|
||||
return {
|
||||
socketId: socket.id,
|
||||
userId: socket.data.userId,
|
||||
@@ -11,6 +8,5 @@ export function socketToClient(socket: SomeSocket): ChadClient {
|
||||
displayName: socket.data.displayName,
|
||||
inputMuted: socket.data.inputMuted,
|
||||
outputMuted: socket.data.outputMuted,
|
||||
currentChannelId: channelId,
|
||||
}
|
||||
}
|
||||
|
||||
863
server/yarn.lock
863
server/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user