feat(frontend): rebrand Sub2API to FireflyAPI
- Replace all user-visible Sub2API brand names with FireflyAPI - Remove GitHub navigation link from AppHeader dropdown - Remove GitHub link from HomeView footer - Simplify VersionBadge to static version display (remove update check) - Update i18n zh/en locale strings - Update default site name fallbacks across all views Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/png" href="/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sub2API - AI API Gateway</title>
|
||||
<title>FireflyAPI - AI API Gateway</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -1,555 +1,11 @@
|
||||
<template>
|
||||
<div class="relative">
|
||||
<!-- Admin: Full version badge with dropdown -->
|
||||
<template v-if="isAdmin">
|
||||
<button
|
||||
@click="toggleDropdown"
|
||||
class="flex items-center gap-1.5 rounded-lg px-2 py-1 text-xs transition-colors"
|
||||
:class="[
|
||||
hasUpdate
|
||||
? 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400 dark:hover:bg-amber-900/50'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-dark-800 dark:text-dark-400 dark:hover:bg-dark-700'
|
||||
]"
|
||||
:title="hasUpdate ? t('version.updateAvailable') : t('version.upToDate')"
|
||||
>
|
||||
<span v-if="currentVersion" class="font-medium">v{{ currentVersion }}</span>
|
||||
<span
|
||||
v-else
|
||||
class="h-3 w-12 animate-pulse rounded bg-gray-200 font-medium dark:bg-dark-600"
|
||||
></span>
|
||||
<!-- Update indicator -->
|
||||
<span v-if="hasUpdate" class="relative flex h-2 w-2">
|
||||
<span
|
||||
class="absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75"
|
||||
></span>
|
||||
<span class="relative inline-flex h-2 w-2 rounded-full bg-amber-500"></span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown -->
|
||||
<transition name="dropdown">
|
||||
<div
|
||||
v-if="dropdownOpen"
|
||||
ref="dropdownRef"
|
||||
class="absolute left-0 z-50 mt-2 w-64 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg dark:border-dark-700 dark:bg-dark-800"
|
||||
>
|
||||
<!-- Header with refresh button -->
|
||||
<div
|
||||
class="flex items-center justify-between border-b border-gray-100 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
<span class="text-sm font-medium text-gray-700 dark:text-dark-300">{{
|
||||
t('version.currentVersion')
|
||||
}}</span>
|
||||
<button
|
||||
@click="refreshVersion(true)"
|
||||
class="rounded-lg p-1.5 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-dark-700 dark:hover:text-dark-200"
|
||||
:disabled="loading"
|
||||
:title="t('version.refresh')"
|
||||
>
|
||||
<Icon
|
||||
name="refresh"
|
||||
size="sm"
|
||||
:stroke-width="2"
|
||||
:class="{ 'animate-spin': loading }"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<!-- Loading state -->
|
||||
<div v-if="loading" class="flex items-center justify-center py-6">
|
||||
<svg class="h-6 w-6 animate-spin text-primary-500" fill="none" viewBox="0 0 24 24">
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<template v-else>
|
||||
<!-- Version display - centered and prominent -->
|
||||
<div class="mb-4 text-center">
|
||||
<div class="inline-flex items-center gap-2">
|
||||
<span
|
||||
v-if="currentVersion"
|
||||
class="text-2xl font-bold text-gray-900 dark:text-white"
|
||||
>v{{ currentVersion }}</span
|
||||
>
|
||||
<span v-else class="text-2xl font-bold text-gray-400 dark:text-dark-500">--</span>
|
||||
<!-- Show check mark when up to date -->
|
||||
<span
|
||||
v-if="!hasUpdate"
|
||||
class="flex h-5 w-5 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30"
|
||||
>
|
||||
<svg
|
||||
class="h-3 w-3 text-green-600 dark:text-green-400"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-dark-400">
|
||||
{{
|
||||
hasUpdate
|
||||
? t('version.latestVersion') + ': v' + latestVersion
|
||||
: t('version.upToDate')
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Priority 1: Update error (must check before hasUpdate) -->
|
||||
<div v-if="updateError" class="space-y-2">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3 dark:border-red-800/50 dark:bg-red-900/20"
|
||||
>
|
||||
<div
|
||||
class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-red-100 dark:bg-red-900/50"
|
||||
>
|
||||
<Icon
|
||||
name="x"
|
||||
size="sm"
|
||||
:stroke-width="2"
|
||||
class="text-red-600 dark:text-red-400"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-red-700 dark:text-red-300">
|
||||
{{ t('version.updateFailed') }}
|
||||
</p>
|
||||
<p class="truncate text-xs text-red-600/70 dark:text-red-400/70">
|
||||
{{ updateError }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Retry button -->
|
||||
<button
|
||||
@click="handleUpdate"
|
||||
:disabled="updating"
|
||||
class="flex w-full items-center justify-center gap-2 rounded-lg bg-red-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-red-600 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{{ t('version.retry') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Priority 2: Update success - need restart -->
|
||||
<div v-else-if="updateSuccess && needRestart" class="space-y-2">
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-800/50 dark:bg-green-900/20"
|
||||
>
|
||||
<div
|
||||
class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/50"
|
||||
>
|
||||
<svg
|
||||
class="h-4 w-4 text-green-600 dark:text-green-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-green-700 dark:text-green-300">
|
||||
{{ t('version.updateComplete') }}
|
||||
</p>
|
||||
<p class="text-xs text-green-600/70 dark:text-green-400/70">
|
||||
{{ t('version.restartRequired') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Restart button with countdown -->
|
||||
<button
|
||||
@click="handleRestart"
|
||||
:disabled="restarting"
|
||||
class="flex w-full items-center justify-center gap-2 rounded-lg bg-green-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-green-600 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<svg
|
||||
v-if="restarting"
|
||||
class="h-4 w-4 animate-spin"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
class="h-4 w-4"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
</svg>
|
||||
<template v-if="restarting">
|
||||
<span>{{ t('version.restarting') }}</span>
|
||||
<span v-if="restartCountdown > 0" class="tabular-nums"
|
||||
>({{ restartCountdown }}s)</span
|
||||
>
|
||||
</template>
|
||||
<span v-else>{{ t('version.restartNow') }}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Priority 3: Update available for source build - show git pull hint -->
|
||||
<div v-else-if="hasUpdate && !isReleaseBuild" class="space-y-2">
|
||||
<a
|
||||
v-if="releaseInfo?.html_url && releaseInfo.html_url !== '#'"
|
||||
:href="releaseInfo.html_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="group flex items-center gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3 transition-colors hover:bg-amber-100 dark:border-amber-800/50 dark:bg-amber-900/20 dark:hover:bg-amber-900/30"
|
||||
>
|
||||
<div
|
||||
class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-amber-100 dark:bg-amber-900/50"
|
||||
>
|
||||
<Icon
|
||||
name="download"
|
||||
size="sm"
|
||||
:stroke-width="2"
|
||||
class="text-amber-600 dark:text-amber-400"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-amber-700 dark:text-amber-300">
|
||||
{{ t('version.updateAvailable') }}
|
||||
</p>
|
||||
<p class="text-xs text-amber-600/70 dark:text-amber-400/70">
|
||||
v{{ latestVersion }}
|
||||
</p>
|
||||
</div>
|
||||
<svg
|
||||
class="h-4 w-4 text-amber-500 transition-transform group-hover:translate-x-0.5 dark:text-amber-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</a>
|
||||
<!-- Source build hint -->
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-lg border border-blue-200 bg-blue-50 p-2 dark:border-blue-800/50 dark:bg-blue-900/20"
|
||||
>
|
||||
<svg
|
||||
class="h-3.5 w-3.5 flex-shrink-0 text-blue-500 dark:text-blue-400"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<p class="text-xs text-blue-600 dark:text-blue-400">
|
||||
{{ t('version.sourceModeHint') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Priority 4: Update available for release build - show update button -->
|
||||
<div v-else-if="hasUpdate && isReleaseBuild" class="space-y-2">
|
||||
<!-- Update info card -->
|
||||
<div
|
||||
class="flex items-center gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-800/50 dark:bg-amber-900/20"
|
||||
>
|
||||
<div
|
||||
class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-amber-100 dark:bg-amber-900/50"
|
||||
>
|
||||
<Icon
|
||||
name="download"
|
||||
size="sm"
|
||||
:stroke-width="2"
|
||||
class="text-amber-600 dark:text-amber-400"
|
||||
/>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-amber-700 dark:text-amber-300">
|
||||
{{ t('version.updateAvailable') }}
|
||||
</p>
|
||||
<p class="text-xs text-amber-600/70 dark:text-amber-400/70">
|
||||
v{{ latestVersion }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Update button -->
|
||||
<button
|
||||
@click="handleUpdate"
|
||||
:disabled="updating"
|
||||
class="flex w-full items-center justify-center gap-2 rounded-lg bg-primary-500 px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-primary-600 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
<svg v-if="updating" class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
<Icon v-else name="download" size="sm" :stroke-width="2" />
|
||||
{{ updating ? t('version.updating') : t('version.updateNow') }}
|
||||
</button>
|
||||
|
||||
<!-- View release link -->
|
||||
<a
|
||||
v-if="releaseInfo?.html_url && releaseInfo.html_url !== '#'"
|
||||
:href="releaseInfo.html_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center justify-center gap-1 text-xs text-gray-500 transition-colors hover:text-gray-700 dark:text-dark-400 dark:hover:text-dark-200"
|
||||
>
|
||||
{{ t('version.viewChangelog') }}
|
||||
<Icon name="externalLink" size="xs" :stroke-width="2" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Priority 5: Up to date - show GitHub link -->
|
||||
<a
|
||||
v-else-if="releaseInfo?.html_url && releaseInfo.html_url !== '#'"
|
||||
:href="releaseInfo.html_url"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center justify-center gap-2 py-2 text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-dark-400 dark:hover:text-dark-200"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z"
|
||||
/>
|
||||
</svg>
|
||||
{{ t('version.viewRelease') }}
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<!-- Non-admin: Simple static version text -->
|
||||
<span v-else-if="version" class="text-xs text-gray-500 dark:text-dark-400">
|
||||
v{{ version }}
|
||||
</span>
|
||||
</div>
|
||||
<span v-if="version" class="text-xs text-gray-500 dark:text-dark-400">
|
||||
v{{ version }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAuthStore, useAppStore } from '@/stores'
|
||||
import { performUpdate, restartService } from '@/api/admin/system'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
version?: string
|
||||
}>()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
const isAdmin = computed(() => authStore.isAdmin)
|
||||
|
||||
const dropdownOpen = ref(false)
|
||||
const dropdownRef = ref<HTMLElement | null>(null)
|
||||
|
||||
// Use store's cached version state
|
||||
const loading = computed(() => appStore.versionLoading)
|
||||
const currentVersion = computed(() => appStore.currentVersion || props.version || '')
|
||||
const latestVersion = computed(() => appStore.latestVersion)
|
||||
const hasUpdate = computed(() => appStore.hasUpdate)
|
||||
const releaseInfo = computed(() => appStore.releaseInfo)
|
||||
const buildType = computed(() => appStore.buildType)
|
||||
|
||||
// Update process states (local to this component)
|
||||
const updating = ref(false)
|
||||
const restarting = ref(false)
|
||||
const needRestart = ref(false)
|
||||
const updateError = ref('')
|
||||
const updateSuccess = ref(false)
|
||||
const restartCountdown = ref(0)
|
||||
|
||||
// Only show update check for release builds (binary/docker deployment)
|
||||
const isReleaseBuild = computed(() => buildType.value === 'release')
|
||||
|
||||
function toggleDropdown() {
|
||||
dropdownOpen.value = !dropdownOpen.value
|
||||
}
|
||||
|
||||
function closeDropdown() {
|
||||
dropdownOpen.value = false
|
||||
}
|
||||
|
||||
async function refreshVersion(force = true) {
|
||||
if (!isAdmin.value) return
|
||||
|
||||
// Reset update states when refreshing
|
||||
updateError.value = ''
|
||||
updateSuccess.value = false
|
||||
needRestart.value = false
|
||||
|
||||
await appStore.fetchVersion(force)
|
||||
}
|
||||
|
||||
async function handleUpdate() {
|
||||
if (updating.value) return
|
||||
|
||||
updating.value = true
|
||||
updateError.value = ''
|
||||
updateSuccess.value = false
|
||||
|
||||
try {
|
||||
const result = await performUpdate()
|
||||
updateSuccess.value = true
|
||||
needRestart.value = result.need_restart
|
||||
// Clear version cache to reflect update completed
|
||||
appStore.clearVersionCache()
|
||||
} catch (error: unknown) {
|
||||
const err = error as { response?: { data?: { message?: string } }; message?: string }
|
||||
updateError.value = err.response?.data?.message || err.message || t('version.updateFailed')
|
||||
} finally {
|
||||
updating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRestart() {
|
||||
if (restarting.value) return
|
||||
|
||||
restarting.value = true
|
||||
restartCountdown.value = 8
|
||||
|
||||
try {
|
||||
await restartService()
|
||||
// Service will restart, page will reload automatically or show disconnected
|
||||
} catch (error) {
|
||||
// Expected - connection will be lost during restart
|
||||
console.log('Service restarting...')
|
||||
}
|
||||
|
||||
// Start countdown
|
||||
const countdownInterval = setInterval(() => {
|
||||
restartCountdown.value--
|
||||
if (restartCountdown.value <= 0) {
|
||||
clearInterval(countdownInterval)
|
||||
// Try to check if service is back before reload
|
||||
checkServiceAndReload()
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
async function checkServiceAndReload() {
|
||||
const maxRetries = 5
|
||||
const retryDelay = 1000
|
||||
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
const response = await fetch('/health', {
|
||||
method: 'GET',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
if (response.ok) {
|
||||
// Service is back, reload page
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
// Service not ready yet
|
||||
}
|
||||
|
||||
if (i < maxRetries - 1) {
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelay))
|
||||
}
|
||||
}
|
||||
|
||||
// After retries, reload anyway
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
function handleClickOutside(event: MouseEvent) {
|
||||
const target = event.target as Node
|
||||
const button = (event.target as Element).closest('button')
|
||||
if (dropdownRef.value && !dropdownRef.value.contains(target) && !button?.contains(target)) {
|
||||
closeDropdown()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (isAdmin.value) {
|
||||
// Use cached version if available, otherwise fetch
|
||||
appStore.fetchVersion(false)
|
||||
}
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.dropdown-enter-active,
|
||||
.dropdown-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-enter-from,
|
||||
.dropdown-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(-4px);
|
||||
}
|
||||
|
||||
.line-clamp-3 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -122,22 +122,6 @@
|
||||
{{ t('nav.apiKeys') }}
|
||||
</router-link>
|
||||
|
||||
<a
|
||||
href="https://github.com/Wei-Shaw/sub2api"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@click="closeDropdown"
|
||||
class="dropdown-item"
|
||||
>
|
||||
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.464-1.11-1.464-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.578.688.48C19.138 20.167 22 16.418 22 12c0-5.523-4.477-10-10-10z"
|
||||
/>
|
||||
</svg>
|
||||
{{ t('nav.github') }}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Contact Support (only show if configured) -->
|
||||
|
||||
@@ -65,7 +65,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { getPublicSettings } from '@/api/auth'
|
||||
import { sanitizeUrl } from '@/utils/url'
|
||||
|
||||
const siteName = ref('Sub2API')
|
||||
const siteName = ref('FireflyAPI')
|
||||
const siteLogo = ref('')
|
||||
const siteSubtitle = ref('Subscription to API Conversion Platform')
|
||||
|
||||
@@ -74,7 +74,7 @@ const currentYear = computed(() => new Date().getFullYear())
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const settings = await getPublicSettings()
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
siteName.value = settings.site_name || 'FireflyAPI'
|
||||
siteLogo.value = sanitizeUrl(settings.site_logo || '', { allowRelative: true })
|
||||
siteSubtitle.value = settings.site_subtitle || 'Subscription to API Conversion Platform'
|
||||
} catch (error) {
|
||||
|
||||
@@ -43,8 +43,8 @@ export default {
|
||||
|
||||
// Setup Wizard
|
||||
setup: {
|
||||
title: 'Sub2API Setup',
|
||||
description: 'Configure your Sub2API instance',
|
||||
title: 'FireflyAPI Setup',
|
||||
description: 'Configure your FireflyAPI instance',
|
||||
database: {
|
||||
title: 'Database Configuration',
|
||||
description: 'Connect to your PostgreSQL database',
|
||||
@@ -3066,7 +3066,7 @@ export default {
|
||||
secretKeyConfiguredHint: 'Secret key configured. Leave empty to keep the current value.' },
|
||||
linuxdo: {
|
||||
title: 'LinuxDo Connect Login',
|
||||
description: 'Configure LinuxDo Connect OAuth for Sub2API end-user login',
|
||||
description: 'Configure LinuxDo Connect OAuth for FireflyAPI end-user login',
|
||||
enable: 'Enable LinuxDo Login',
|
||||
enableHint: 'Show LinuxDo login on the login/register pages',
|
||||
clientId: 'Client ID',
|
||||
@@ -3096,7 +3096,7 @@ export default {
|
||||
title: 'Site Settings',
|
||||
description: 'Customize site branding',
|
||||
siteName: 'Site Name',
|
||||
siteNamePlaceholder: 'Sub2API',
|
||||
siteNamePlaceholder: 'FireflyAPI',
|
||||
siteNameHint: 'Displayed in emails and page titles',
|
||||
siteSubtitle: 'Site Subtitle',
|
||||
siteSubtitlePlaceholder: 'Subscription to API Conversion Platform',
|
||||
@@ -3155,7 +3155,7 @@ export default {
|
||||
fromEmail: 'From Email',
|
||||
fromEmailPlaceholder: "noreply{'@'}example.com",
|
||||
fromName: 'From Name',
|
||||
fromNamePlaceholder: 'Sub2API',
|
||||
fromNamePlaceholder: 'FireflyAPI',
|
||||
useTls: 'Use TLS',
|
||||
useTlsHint: 'Enable TLS encryption for SMTP connection'
|
||||
},
|
||||
@@ -3434,14 +3434,14 @@ export default {
|
||||
// Admin tour steps
|
||||
admin: {
|
||||
welcome: {
|
||||
title: '👋 Welcome to Sub2API',
|
||||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Sub2API is a powerful AI service gateway platform that helps you easily manage and distribute AI services.</p><p style="margin-bottom: 12px;"><b>🎯 Core Features:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>📦 <b>Group Management</b> - Create service tiers (VIP, Free Trial, etc.)</li><li>🔗 <b>Account Pool</b> - Connect multiple upstream AI service accounts</li><li>🔑 <b>Key Distribution</b> - Generate independent API Keys for users</li><li>💰 <b>Billing Control</b> - Flexible rate and quota management</li></ul><p style="color: #10b981; font-weight: 600;">Let\'s complete the initial setup in 3 minutes →</p></div>',
|
||||
title: '👋 Welcome to FireflyAPI',
|
||||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">FireflyAPI is a powerful AI service gateway platform that helps you easily manage and distribute AI services.</p><p style="margin-bottom: 12px;"><b>🎯 Core Features:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>📦 <b>Group Management</b> - Create service tiers (VIP, Free Trial, etc.)</li><li>🔗 <b>Account Pool</b> - Connect multiple upstream AI service accounts</li><li>🔑 <b>Key Distribution</b> - Generate independent API Keys for users</li><li>💰 <b>Billing Control</b> - Flexible rate and quota management</li></ul><p style="color: #10b981; font-weight: 600;">Let\'s complete the initial setup in 3 minutes →</p></div>',
|
||||
nextBtn: 'Start Setup 🚀',
|
||||
prevBtn: 'Skip'
|
||||
},
|
||||
groupManage: {
|
||||
title: '📦 Step 1: Group Management',
|
||||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>What is a Group?</b></p><p style="margin-bottom: 12px;">Groups are the core concept of Sub2API, like a "service package":</p><ul style="margin-left: 20px; margin-bottom: 12px; font-size: 13px;"><li>🎯 Each group can contain multiple upstream accounts</li><li>💰 Each group has independent billing multiplier</li><li>👥 Can be set as public or exclusive</li></ul><p style="margin-top: 12px; padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Example:</b> You can create "VIP Premium" (high rate) and "Free Trial" (low rate) groups</p><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 Click "Group Management" on the left sidebar</p></div>'
|
||||
description: '<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>What is a Group?</b></p><p style="margin-bottom: 12px;">Groups are the core concept of FireflyAPI, like a "service package":</p><ul style="margin-left: 20px; margin-bottom: 12px; font-size: 13px;"><li>🎯 Each group can contain multiple upstream accounts</li><li>💰 Each group has independent billing multiplier</li><li>👥 Can be set as public or exclusive</li></ul><p style="margin-top: 12px; padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 Example:</b> You can create "VIP Premium" (high rate) and "Free Trial" (low rate) groups</p><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 Click "Group Management" on the left sidebar</p></div>'
|
||||
},
|
||||
createGroup: {
|
||||
title: '➕ Create New Group',
|
||||
@@ -3534,8 +3534,8 @@ export default {
|
||||
// User tour steps
|
||||
user: {
|
||||
welcome: {
|
||||
title: '👋 Welcome to Sub2API',
|
||||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Hello! Welcome to the Sub2API AI service platform.</p><p style="margin-bottom: 12px;"><b>🎯 Quick Start:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>🔑 Create API Key</li><li>📋 Copy key to your application</li><li>🚀 Start using AI services</li></ul><p style="color: #10b981; font-weight: 600;">Just 1 minute, let\'s get started →</p></div>',
|
||||
title: '👋 Welcome to FireflyAPI',
|
||||
description: '<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Hello! Welcome to the FireflyAPI AI service platform.</p><p style="margin-bottom: 12px;"><b>🎯 Quick Start:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>🔑 Create API Key</li><li>📋 Copy key to your application</li><li>🚀 Start using AI services</li></ul><p style="color: #10b981; font-weight: 600;">Just 1 minute, let\'s get started →</p></div>',
|
||||
nextBtn: 'Start 🚀',
|
||||
prevBtn: 'Skip'
|
||||
},
|
||||
|
||||
@@ -40,8 +40,8 @@ export default {
|
||||
|
||||
// Setup Wizard
|
||||
setup: {
|
||||
title: 'Sub2API 安装向导',
|
||||
description: '配置您的 Sub2API 实例',
|
||||
title: 'FireflyAPI 安装向导',
|
||||
description: '配置您的 FireflyAPI 实例',
|
||||
database: {
|
||||
title: '数据库配置',
|
||||
description: '连接到您的 PostgreSQL 数据库',
|
||||
@@ -3237,7 +3237,7 @@ export default {
|
||||
},
|
||||
linuxdo: {
|
||||
title: 'LinuxDo Connect 登录',
|
||||
description: '配置 LinuxDo Connect OAuth,用于 Sub2API 用户登录',
|
||||
description: '配置 LinuxDo Connect OAuth,用于 FireflyAPI 用户登录',
|
||||
enable: '启用 LinuxDo 登录',
|
||||
enableHint: '在登录/注册页面显示 LinuxDo 登录入口',
|
||||
clientId: 'Client ID',
|
||||
@@ -3267,7 +3267,7 @@ export default {
|
||||
description: '自定义站点品牌',
|
||||
siteName: '站点名称',
|
||||
siteNameHint: '显示在邮件和页面标题中',
|
||||
siteNamePlaceholder: 'Sub2API',
|
||||
siteNamePlaceholder: 'FireflyAPI',
|
||||
siteSubtitle: '站点副标题',
|
||||
siteSubtitleHint: '显示在登录和注册页面',
|
||||
siteSubtitlePlaceholder: '订阅转 API 转换平台',
|
||||
@@ -3327,7 +3327,7 @@ export default {
|
||||
fromEmail: '发件人邮箱',
|
||||
fromEmailPlaceholder: "noreply{'@'}example.com",
|
||||
fromName: '发件人名称',
|
||||
fromNamePlaceholder: 'Sub2API',
|
||||
fromNamePlaceholder: 'FireflyAPI',
|
||||
useTls: '使用 TLS',
|
||||
useTlsHint: '为 SMTP 连接启用 TLS 加密'
|
||||
},
|
||||
@@ -3603,16 +3603,16 @@ export default {
|
||||
// Admin tour steps
|
||||
admin: {
|
||||
welcome: {
|
||||
title: '👋 欢迎使用 Sub2API',
|
||||
title: '👋 欢迎使用 FireflyAPI',
|
||||
description:
|
||||
'<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">Sub2API 是一个强大的 AI 服务中转平台,让您轻松管理和分发 AI 服务。</p><p style="margin-bottom: 12px;"><b>🎯 核心功能:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>📦 <b>分组管理</b> - 创建不同的服务套餐(VIP、免费试用等)</li><li>🔗 <b>账号池</b> - 连接多个上游 AI 服务商账号</li><li>🔑 <b>密钥分发</b> - 为用户生成独立的 API Key</li><li>💰 <b>计费管理</b> - 灵活的费率和配额控制</li></ul><p style="color: #10b981; font-weight: 600;">接下来,我们将用 3 分钟带您完成首次配置 →</p></div>',
|
||||
'<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">FireflyAPI 是一个强大的 AI 服务中转平台,让您轻松管理和分发 AI 服务。</p><p style="margin-bottom: 12px;"><b>🎯 核心功能:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>📦 <b>分组管理</b> - 创建不同的服务套餐(VIP、免费试用等)</li><li>🔗 <b>账号池</b> - 连接多个上游 AI 服务商账号</li><li>🔑 <b>密钥分发</b> - 为用户生成独立的 API Key</li><li>💰 <b>计费管理</b> - 灵活的费率和配额控制</li></ul><p style="color: #10b981; font-weight: 600;">接下来,我们将用 3 分钟带您完成首次配置 →</p></div>',
|
||||
nextBtn: '开始配置 🚀',
|
||||
prevBtn: '跳过'
|
||||
},
|
||||
groupManage: {
|
||||
title: '📦 第一步:分组管理',
|
||||
description:
|
||||
'<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>什么是分组?</b></p><p style="margin-bottom: 12px;">分组是 Sub2API 的核心概念,它就像一个"服务套餐":</p><ul style="margin-left: 20px; margin-bottom: 12px; font-size: 13px;"><li>🎯 每个分组可以包含多个上游账号</li><li>💰 每个分组有独立的计费倍率</li><li>👥 可以设置为公开或专属分组</li></ul><p style="margin-top: 12px; padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 示例:</b>您可以创建"VIP专线"(高倍率)和"免费试用"(低倍率)两个分组</p><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 点击左侧的"分组管理"开始</p></div>'
|
||||
'<div style="line-height: 1.7;"><p style="margin-bottom: 12px;"><b>什么是分组?</b></p><p style="margin-bottom: 12px;">分组是 FireflyAPI 的核心概念,它就像一个"服务套餐":</p><ul style="margin-left: 20px; margin-bottom: 12px; font-size: 13px;"><li>🎯 每个分组可以包含多个上游账号</li><li>💰 每个分组有独立的计费倍率</li><li>👥 可以设置为公开或专属分组</li></ul><p style="margin-top: 12px; padding: 8px 12px; background: #f0fdf4; border-left: 3px solid #10b981; border-radius: 4px; font-size: 13px;"><b>💡 示例:</b>您可以创建"VIP专线"(高倍率)和"免费试用"(低倍率)两个分组</p><p style="margin-top: 16px; color: #10b981; font-weight: 600;">👉 点击左侧的"分组管理"开始</p></div>'
|
||||
},
|
||||
createGroup: {
|
||||
title: '➕ 创建新分组',
|
||||
@@ -3724,9 +3724,9 @@ export default {
|
||||
// User tour steps
|
||||
user: {
|
||||
welcome: {
|
||||
title: '👋 欢迎使用 Sub2API',
|
||||
title: '👋 欢迎使用 FireflyAPI',
|
||||
description:
|
||||
'<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">您好!欢迎来到 Sub2API AI 服务平台。</p><p style="margin-bottom: 12px;"><b>🎯 快速开始:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>🔑 创建 API 密钥</li><li>📋 复制密钥到您的应用</li><li>🚀 开始使用 AI 服务</li></ul><p style="color: #10b981; font-weight: 600;">只需 1 分钟,让我们开始吧 →</p></div>',
|
||||
'<div style="line-height: 1.8;"><p style="margin-bottom: 16px;">您好!欢迎来到 FireflyAPI AI 服务平台。</p><p style="margin-bottom: 12px;"><b>🎯 快速开始:</b></p><ul style="margin-left: 20px; margin-bottom: 16px;"><li>🔑 创建 API 密钥</li><li>📋 复制密钥到您的应用</li><li>🚀 开始使用 AI 服务</li></ul><p style="color: #10b981; font-weight: 600;">只需 1 分钟,让我们开始吧 →</p></div>',
|
||||
nextBtn: '开始 🚀',
|
||||
prevBtn: '跳过'
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ const appStore = useAppStore()
|
||||
appStore.initFromInjectedConfig()
|
||||
|
||||
// Set document title immediately after config is loaded
|
||||
if (appStore.siteName && appStore.siteName !== 'Sub2API') {
|
||||
if (appStore.siteName && appStore.siteName !== 'FireflyAPI') {
|
||||
document.title = `${appStore.siteName} - AI API Gateway`
|
||||
}
|
||||
|
||||
|
||||
@@ -389,7 +389,7 @@ router.beforeEach((to, _from, next) => {
|
||||
|
||||
// Set page title
|
||||
const appStore = useAppStore()
|
||||
const siteName = appStore.siteName || 'Sub2API'
|
||||
const siteName = appStore.siteName || 'FireflyAPI'
|
||||
if (to.meta.title) {
|
||||
document.title = `${to.meta.title} - ${siteName}`
|
||||
} else {
|
||||
|
||||
@@ -24,7 +24,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
// Public settings cache state
|
||||
const publicSettingsLoaded = ref<boolean>(false)
|
||||
const publicSettingsLoading = ref<boolean>(false)
|
||||
const siteName = ref<string>('Sub2API')
|
||||
const siteName = ref<string>('FireflyAPI')
|
||||
const siteLogo = ref<string>('')
|
||||
const siteVersion = ref<string>('')
|
||||
const contactInfo = ref<string>('')
|
||||
@@ -284,7 +284,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
*/
|
||||
function applySettings(config: PublicSettings): void {
|
||||
cachedPublicSettings.value = config
|
||||
siteName.value = config.site_name || 'Sub2API'
|
||||
siteName.value = config.site_name || 'FireflyAPI'
|
||||
siteLogo.value = config.site_logo || ''
|
||||
siteVersion.value = config.version || ''
|
||||
contactInfo.value = config.contact_info || ''
|
||||
|
||||
@@ -390,14 +390,6 @@
|
||||
>
|
||||
{{ t('home.docs') }}
|
||||
</a>
|
||||
<a
|
||||
:href="githubUrl"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-sm text-gray-500 transition-colors hover:text-gray-700 dark:text-dark-400 dark:hover:text-white"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -417,7 +409,7 @@ const authStore = useAuthStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
// Site settings - directly from appStore (already initialized from injected config)
|
||||
const siteName = computed(() => appStore.cachedPublicSettings?.site_name || appStore.siteName || 'Sub2API')
|
||||
const siteName = computed(() => appStore.cachedPublicSettings?.site_name || appStore.siteName || 'FireflyAPI')
|
||||
const siteLogo = computed(() => appStore.cachedPublicSettings?.site_logo || appStore.siteLogo || '')
|
||||
const siteSubtitle = computed(() => appStore.cachedPublicSettings?.site_subtitle || 'AI API Gateway Platform')
|
||||
const docUrl = computed(() => appStore.cachedPublicSettings?.doc_url || appStore.docUrl || '')
|
||||
@@ -432,9 +424,6 @@ const isHomeContentUrl = computed(() => {
|
||||
// Theme
|
||||
const isDark = ref(document.documentElement.classList.contains('dark'))
|
||||
|
||||
// GitHub URL
|
||||
const githubUrl = 'https://github.com/Wei-Shaw/sub2api'
|
||||
|
||||
// Auth state
|
||||
const isAuthenticated = computed(() => authStore.isAuthenticated)
|
||||
const isAdmin = computed(() => authStore.isAdmin)
|
||||
|
||||
@@ -1135,7 +1135,7 @@ const form = reactive<SettingsForm>({
|
||||
totp_encryption_key_configured: false,
|
||||
default_balance: 0,
|
||||
default_concurrency: 1,
|
||||
site_name: 'Sub2API',
|
||||
site_name: 'FireflyAPI',
|
||||
site_logo: '',
|
||||
site_subtitle: 'Subscription to API Conversion Platform',
|
||||
api_base_url: '',
|
||||
|
||||
@@ -207,7 +207,7 @@ const hasRegisterData = ref<boolean>(false)
|
||||
// Public settings
|
||||
const turnstileEnabled = ref<boolean>(false)
|
||||
const turnstileSiteKey = ref<string>('')
|
||||
const siteName = ref<string>('Sub2API')
|
||||
const siteName = ref<string>('FireflyAPI')
|
||||
|
||||
// Turnstile for resend
|
||||
const turnstileRef = ref<InstanceType<typeof TurnstileWidget> | null>(null)
|
||||
@@ -243,7 +243,7 @@ onMounted(async () => {
|
||||
const settings = await getPublicSettings()
|
||||
turnstileEnabled.value = settings.turnstile_enabled
|
||||
turnstileSiteKey.value = settings.turnstile_site_key || ''
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
siteName.value = settings.site_name || 'FireflyAPI'
|
||||
} catch (error) {
|
||||
console.error('Failed to load public settings:', error)
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ const promoCodeEnabled = ref<boolean>(true)
|
||||
const invitationCodeEnabled = ref<boolean>(false)
|
||||
const turnstileEnabled = ref<boolean>(false)
|
||||
const turnstileSiteKey = ref<string>('')
|
||||
const siteName = ref<string>('Sub2API')
|
||||
const siteName = ref<string>('FireflyAPI')
|
||||
const linuxdoOAuthEnabled = ref<boolean>(false)
|
||||
|
||||
// Turnstile
|
||||
@@ -368,7 +368,7 @@ onMounted(async () => {
|
||||
invitationCodeEnabled.value = settings.invitation_code_enabled
|
||||
turnstileEnabled.value = settings.turnstile_enabled
|
||||
turnstileSiteKey.value = settings.turnstile_site_key || ''
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
siteName.value = settings.site_name || 'FireflyAPI'
|
||||
linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled
|
||||
|
||||
// Read promo code from URL parameter only if promo code is enabled
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
v-model="formData.database.dbname"
|
||||
type="text"
|
||||
class="input"
|
||||
placeholder="sub2api"
|
||||
placeholder="fireflyapi"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@@ -535,7 +535,7 @@ const formData = reactive<InstallRequest>({
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: '',
|
||||
dbname: 'sub2api',
|
||||
dbname: 'fireflyapi',
|
||||
sslmode: 'disable'
|
||||
},
|
||||
redis: {
|
||||
|
||||
Reference in New Issue
Block a user