Integrations
Vue
Initialize MarzbanSDK in Vue 3 with a provide/inject composable, and learn when client-side usage is safe versus proxying through a backend.
Use MarzbanSDK in browser Vue apps only in controlled network environments (admin dashboards on private networks, Electron apps). In public-facing apps, proxy API calls through a backend.
Plugin / composable pattern
// plugins/marzban.ts
import { ref, provide, inject, type InjectionKey, type Ref } from 'vue'
import { createMarzbanSDK, type MarzbanSDK } from 'marzban-sdk'
const MARZBAN_KEY: InjectionKey<Ref<MarzbanSDK | null>> = Symbol('marzban')
export function provideMarzban() {
const sdk = ref<MarzbanSDK | null>(null)
createMarzbanSDK({
baseUrl: import.meta.env.VITE_MARZBAN_URL,
username: import.meta.env.VITE_MARZBAN_USER,
password: import.meta.env.VITE_MARZBAN_PASS,
}).then(s => { sdk.value = s })
provide(MARZBAN_KEY, sdk)
// Cleanup on app unmount
return () => sdk.value?.destroy()
}
export function useMarzban(): MarzbanSDK {
const sdk = inject(MARZBAN_KEY)
if (!sdk?.value) throw new Error('Marzban SDK not initialized')
return sdk.value
}// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { provideMarzban } from './plugins/marzban'
const app = createApp(App)
// Provide at root level
app.mount('#app')
const cleanup = provideMarzban()
// cleanup() on app unmount if neededUsing in components
<!-- components/UserList.vue -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useMarzban } from '../plugins/marzban'
import { formatBytes, humanRemaining } from 'marzban-sdk'
import type { UserResponse } from 'marzban-sdk'
const sdk = useMarzban()
const users = ref<UserResponse[]>([])
const loading = ref(true)
onMounted(async () => {
const result = await sdk.user.getUsers({ limit: 50, status: 'active' })
users.value = result.users
loading.value = false
})
</script>
<template>
<div v-if="loading">Loading...</div>
<table v-else>
<thead>
<tr>
<th>Username</th>
<th>Data used</th>
<th>Expires in</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" :key="user.username">
<td>{{ user.username }}</td>
<td>{{ formatBytes(user.used_traffic) }}</td>
<td>{{ user.expire ? humanRemaining(user.expire * 1000) : '∞' }}</td>
</tr>
</tbody>
</table>
</template>Composable with data fetching
// composables/useUsers.ts
import { ref, onMounted } from 'vue'
import { useMarzban } from '../plugins/marzban'
import type { UserResponse } from 'marzban-sdk'
export function useUsers() {
const sdk = useMarzban()
const users = ref<UserResponse[]>([])
const total = ref(0)
const loading = ref(false)
const error = ref<Error | null>(null)
async function fetchUsers(page = 0, limit = 50) {
loading.value = true
error.value = null
try {
const result = await sdk.user.getUsers({ offset: page * limit, limit })
users.value = result.users
total.value = result.total
} catch (e) {
error.value = e as Error
} finally {
loading.value = false
}
}
onMounted(() => fetchUsers())
return { users, total, loading, error, fetchUsers }
}WebSocket logs composable
// composables/useCoreLogs.ts
import { ref, onUnmounted } from 'vue'
import { useMarzban } from '../plugins/marzban'
export function useCoreLogs() {
const sdk = useMarzban()
const logs = ref<string[]>([])
let close: (() => void) | undefined
async function start() {
close = await sdk.logs.connectByCore({
onMessage: (data) => { logs.value.push(String(data)) },
})
}
function stop() {
close?.()
}
onUnmounted(stop)
return { logs, start, stop }
}