Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
3d05e503
Commit
3d05e503
authored
Dec 18, 2025
by
shaw
Browse files
fix: frontend build error
parent
642842c2
Changes
33
Expand all
Show whitespace changes
Inline
Side-by-side
frontend/package-lock.json
View file @
3d05e503
This diff is collapsed.
Click to expand it.
frontend/package.json
View file @
3d05e503
...
...
@@ -5,9 +5,10 @@
"type"
:
"module"
,
"scripts"
:
{
"dev"
:
"vite"
,
"build"
:
"vue-tsc && vite build"
,
"build"
:
"vue-tsc
-b
&& vite build"
,
"preview"
:
"vite preview"
,
"lint"
:
"eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"
"lint"
:
"eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix"
,
"typecheck"
:
"vue-tsc --noEmit"
},
"dependencies"
:
{
"@vueuse/core"
:
"^10.7.0"
,
...
...
@@ -21,12 +22,13 @@
},
"devDependencies"
:
{
"@types/node"
:
"^20.10.5"
,
"@vitejs/plugin-vue"
:
"^
4.
5.2"
,
"@vitejs/plugin-vue"
:
"^5.2
.3
"
,
"autoprefixer"
:
"^10.4.16"
,
"postcss"
:
"^8.4.32"
,
"tailwindcss"
:
"^3.4.0"
,
"typescript"
:
"
^
5.
3.3
"
,
"typescript"
:
"
~
5.
6.0
"
,
"vite"
:
"^5.0.10"
,
"vue-tsc"
:
"^1.8.25"
"vite-plugin-checker"
:
"^0.9.1"
,
"vue-tsc"
:
"^2.2.0"
}
}
frontend/src/api/admin/redeem.ts
View file @
3d05e503
...
...
@@ -23,7 +23,7 @@ export async function list(
pageSize
:
number
=
20
,
filters
?:
{
type
?:
RedeemCodeType
;
status
?:
'
active
'
|
'
used
'
|
'
expired
'
;
status
?:
'
active
'
|
'
used
'
|
'
expired
'
|
'
unused
'
;
search
?:
string
;
}
):
Promise
<
PaginatedResponse
<
RedeemCode
>>
{
...
...
frontend/src/components/account/CreateAccountModal.vue
View file @
3d05e503
...
...
@@ -506,7 +506,7 @@
{{
t
(
'
common.back
'
)
}}
<
/button
>
<
button
v
-
if
=
"
oauthFlowRef?.inputMethod?.value === 'manual'
"
v
-
if
=
"
isManualInputMethod
"
type
=
"
button
"
:
disabled
=
"
!canExchangeCode
"
class
=
"
btn btn-primary
"
...
...
@@ -533,14 +533,22 @@ import { ref, reactive, computed, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
useAccountOAuth
,
type
AddMethod
}
from
'
@/composables/useAccountOAuth
'
import
{
useAccountOAuth
,
type
AddMethod
,
type
AuthInputMethod
}
from
'
@/composables/useAccountOAuth
'
import
type
{
Proxy
,
Group
,
AccountPlatform
,
AccountType
}
from
'
@/types
'
import
Modal
from
'
@/components/common/Modal.vue
'
import
Select
from
'
@/components/common/Select.vue
'
import
ProxySelector
from
'
@/components/common/ProxySelector.vue
'
import
GroupSelector
from
'
@/components/common/GroupSelector.vue
'
import
OAuthAuthorizationFlow
from
'
./OAuthAuthorizationFlow.vue
'
// Type for exposed OAuthAuthorizationFlow component
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
interface
OAuthFlowExposed
{
authCode
:
string
sessionKey
:
string
inputMethod
:
AuthInputMethod
reset
:
()
=>
void
}
const
{
t
}
=
useI18n
()
interface
Props
{
...
...
@@ -561,7 +569,7 @@ const appStore = useAppStore()
const
oauth
=
useAccountOAuth
()
// Refs
const
oauthFlowRef
=
ref
<
InstanceType
<
typeof
OAuthAuthorizationFlow
>
|
null
>
(
null
)
const
oauthFlowRef
=
ref
<
OAuthFlowExposed
|
null
>
(
null
)
// Model mapping type
interface
ModelMapping
{
...
...
@@ -630,8 +638,12 @@ const form = reactive({
// Helper to check if current type needs OAuth flow
const
isOAuthFlow
=
computed
(()
=>
accountCategory
.
value
===
'
oauth-based
'
)
const
isManualInputMethod
=
computed
(()
=>
{
return
oauthFlowRef
.
value
?.
inputMethod
===
'
manual
'
}
)
const
canExchangeCode
=
computed
(()
=>
{
const
authCode
=
oauthFlowRef
.
value
?.
authCode
?.
value
||
''
const
authCode
=
oauthFlowRef
.
value
?.
authCode
||
''
return
authCode
.
trim
()
&&
oauth
.
sessionId
.
value
&&
!
oauth
.
loading
.
value
}
)
...
...
@@ -815,7 +827,7 @@ const handleGenerateUrl = async () => {
}
const
handleExchangeCode
=
async
()
=>
{
const
authCode
=
oauthFlowRef
.
value
?.
authCode
?.
value
||
''
const
authCode
=
oauthFlowRef
.
value
?.
authCode
||
''
if
(
!
authCode
.
trim
()
||
!
oauth
.
sessionId
.
value
)
return
oauth
.
loading
.
value
=
true
...
...
frontend/src/components/account/ReAuthAccountModal.vue
View file @
3d05e503
...
...
@@ -71,7 +71,7 @@
{{
t
(
'
common.cancel
'
)
}}
</button>
<button
v-if=
"
oauthFlowRef?.inputMethod?.value === 'manual'
"
v-if=
"
isManualInputMethod
"
type=
"button"
:disabled=
"!canExchangeCode"
class=
"btn btn-primary"
...
...
@@ -98,11 +98,20 @@ import { ref, computed, watch } from 'vue'
import
{
useI18n
}
from
'
vue-i18n
'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
{
useAccountOAuth
,
type
AddMethod
}
from
'
@/composables/useAccountOAuth
'
import
{
useAccountOAuth
,
type
AddMethod
,
type
AuthInputMethod
}
from
'
@/composables/useAccountOAuth
'
import
type
{
Account
}
from
'
@/types
'
import
Modal
from
'
@/components/common/Modal.vue
'
import
OAuthAuthorizationFlow
from
'
./OAuthAuthorizationFlow.vue
'
// Type for exposed OAuthAuthorizationFlow component
// Note: defineExpose automatically unwraps refs, so we use the unwrapped types
interface
OAuthFlowExposed
{
authCode
:
string
sessionKey
:
string
inputMethod
:
AuthInputMethod
reset
:
()
=>
void
}
interface
Props
{
show
:
boolean
account
:
Account
|
null
...
...
@@ -121,14 +130,18 @@ const { t } = useI18n()
const
oauth
=
useAccountOAuth
()
// Refs
const
oauthFlowRef
=
ref
<
InstanceType
<
typeof
OAuthAuthorizationFlow
>
|
null
>
(
null
)
const
oauthFlowRef
=
ref
<
OAuthFlowExposed
|
null
>
(
null
)
// State
const
addMethod
=
ref
<
AddMethod
>
(
'
oauth
'
)
// Computed
const
isManualInputMethod
=
computed
(()
=>
{
return
oauthFlowRef
.
value
?.
inputMethod
===
'
manual
'
})
const
canExchangeCode
=
computed
(()
=>
{
const
authCode
=
oauthFlowRef
.
value
?.
authCode
?.
value
||
''
const
authCode
=
oauthFlowRef
.
value
?.
authCode
||
''
return
authCode
.
trim
()
&&
oauth
.
sessionId
.
value
&&
!
oauth
.
loading
.
value
})
...
...
@@ -163,7 +176,7 @@ const handleGenerateUrl = async () => {
const
handleExchangeCode
=
async
()
=>
{
if
(
!
props
.
account
)
return
const
authCode
=
oauthFlowRef
.
value
?.
authCode
?.
value
||
''
const
authCode
=
oauthFlowRef
.
value
?.
authCode
||
''
if
(
!
authCode
.
trim
()
||
!
oauth
.
sessionId
.
value
)
return
oauth
.
loading
.
value
=
true
...
...
frontend/src/components/common/ConfirmDialog.vue
View file @
3d05e503
...
...
@@ -47,7 +47,7 @@ interface Emits {
(
e
:
'
cancel
'
):
void
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{
withDefaults
(
defineProps
<
Props
>
(),
{
confirmText
:
'
Confirm
'
,
cancelText
:
'
Cancel
'
,
danger
:
false
...
...
frontend/src/components/common/DataTable.vue
View file @
3d05e503
...
...
@@ -86,16 +86,10 @@
<
script
setup
lang=
"ts"
>
import
{
computed
,
ref
}
from
'
vue
'
import
{
useI18n
}
from
'
vue-i18n
'
import
type
{
Column
}
from
'
./types
'
const
{
t
}
=
useI18n
()
export
interface
Column
{
key
:
string
label
:
string
sortable
?:
boolean
formatter
?:
(
value
:
any
,
row
:
any
)
=>
string
}
interface
Props
{
columns
:
Column
[]
data
:
any
[]
...
...
frontend/src/components/common/EmptyState.vue
View file @
3d05e503
...
...
@@ -69,7 +69,6 @@
<
script
setup
lang=
"ts"
>
import
type
{
Component
}
from
'
vue
'
import
{
RouterLink
}
from
'
vue-router
'
interface
Props
{
icon
?:
Component
|
string
...
...
@@ -81,7 +80,7 @@ interface Props {
message
?:
string
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{
withDefaults
(
defineProps
<
Props
>
(),
{
title
:
'
No data found
'
,
description
:
''
,
actionIcon
:
true
...
...
frontend/src/components/common/Select.vue
View file @
3d05e503
...
...
@@ -53,7 +53,7 @@
<div
class=
"select-options"
>
<div
v-for=
"option in filteredOptions"
:key=
"getOptionValue(option)"
:key=
"getOptionValue(option)
?? undefined
"
@
click=
"selectOption(option)"
:class=
"[
'select-option',
...
...
@@ -136,9 +136,9 @@ const searchQuery = ref('')
const
containerRef
=
ref
<
HTMLElement
|
null
>
(
null
)
const
searchInputRef
=
ref
<
HTMLInputElement
|
null
>
(
null
)
const
getOptionValue
=
(
option
:
SelectOption
|
Record
<
string
,
unknown
>
):
string
|
number
|
null
=>
{
const
getOptionValue
=
(
option
:
SelectOption
|
Record
<
string
,
unknown
>
):
string
|
number
|
null
|
undefined
=>
{
if
(
typeof
option
===
'
object
'
&&
option
!==
null
)
{
return
option
[
props
.
valueKey
]
as
string
|
number
|
null
return
option
[
props
.
valueKey
]
as
string
|
number
|
null
|
undefined
}
return
option
as
string
|
number
|
null
}
...
...
@@ -187,7 +187,7 @@ const toggle = () => {
}
const
selectOption
=
(
option
:
SelectOption
|
Record
<
string
,
unknown
>
)
=>
{
const
value
=
getOptionValue
(
option
)
const
value
=
getOptionValue
(
option
)
??
null
emit
(
'
update:modelValue
'
,
value
)
emit
(
'
change
'
,
value
,
option
as
SelectOption
)
isOpen
.
value
=
false
...
...
frontend/src/components/common/SubscriptionProgressMini.vue
View file @
3d05e503
...
...
@@ -121,7 +121,7 @@
<
/template
>
<
script
setup
lang
=
"
ts
"
>
import
{
ref
,
computed
,
onMounted
,
onBeforeUnmount
,
watch
}
from
'
vue
'
;
import
{
ref
,
computed
,
onMounted
,
onBeforeUnmount
}
from
'
vue
'
;
import
{
useI18n
}
from
'
vue-i18n
'
;
import
subscriptionsAPI
from
'
@/api/subscriptions
'
;
import
type
{
UserSubscription
}
from
'
@/types
'
;
...
...
frontend/src/components/common/index.ts
View file @
3d05e503
...
...
@@ -10,4 +10,4 @@ export { default as EmptyState } from './EmptyState.vue'
export
{
default
as
LocaleSwitcher
}
from
'
./LocaleSwitcher.vue
'
// Export types
export
type
{
Column
}
from
'
./
DataTable.vue
'
export
type
{
Column
}
from
'
./
types
'
frontend/src/components/common/types.ts
0 → 100644
View file @
3d05e503
/**
* Common component types
*/
export
interface
Column
{
key
:
string
label
:
string
sortable
?:
boolean
formatter
?:
(
value
:
any
,
row
:
any
)
=>
string
}
frontend/src/router/index.ts
View file @
3d05e503
...
...
@@ -268,7 +268,7 @@ const routes: RouteRecordRaw[] = [
const
router
=
createRouter
({
history
:
createWebHistory
(
import
.
meta
.
env
.
BASE_URL
),
routes
,
scrollBehavior
(
to
,
from
,
savedPosition
)
{
scrollBehavior
(
_
to
,
_
from
,
savedPosition
)
{
// Scroll to saved position when using browser back/forward
if
(
savedPosition
)
{
return
savedPosition
;
...
...
@@ -283,7 +283,7 @@ const router = createRouter({
*/
let
authInitialized
=
false
;
router
.
beforeEach
((
to
,
from
,
next
)
=>
{
router
.
beforeEach
((
to
,
_
from
,
next
)
=>
{
const
authStore
=
useAuthStore
();
// Restore auth state from localStorage on first navigation (page refresh)
...
...
frontend/src/types/index.ts
View file @
3d05e503
...
...
@@ -152,6 +152,12 @@ export interface UserStats {
// ==================== API Response Types ====================
export
interface
ApiResponse
<
T
=
unknown
>
{
code
:
number
;
message
:
string
;
data
:
T
;
}
export
interface
ApiError
{
detail
:
string
;
code
?:
string
;
...
...
@@ -357,6 +363,7 @@ export interface CreateAccountRequest {
export
interface
UpdateAccountRequest
{
name
?:
string
;
type
?:
AccountType
;
credentials
?:
Record
<
string
,
unknown
>
;
extra
?:
Record
<
string
,
string
>
;
proxy_id
?:
number
|
null
;
...
...
frontend/src/views/admin/AccountsView.vue
View file @
3d05e503
...
...
@@ -265,7 +265,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Account
,
Proxy
,
Group
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/
DataTable.vue
'
import
type
{
Column
}
from
'
@/components/common/
types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
...
...
frontend/src/views/admin/DashboardView.vue
View file @
3d05e503
...
...
@@ -398,7 +398,7 @@ const lineOptions = computed(() => ({
font
:
{
size
:
10
,
},
callback
:
(
value
:
number
)
=>
formatTokens
(
value
),
callback
:
(
value
:
string
|
number
)
=>
formatTokens
(
Number
(
value
)
)
,
},
},
},
...
...
frontend/src/views/admin/GroupsView.vue
View file @
3d05e503
...
...
@@ -450,7 +450,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Group
,
GroupPlatform
,
SubscriptionType
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/
DataTable.vue
'
import
type
{
Column
}
from
'
@/components/common/
types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
...
...
@@ -570,7 +570,7 @@ const loadGroups = async () => {
pagination
.
page
,
pagination
.
page_size
,
{
platform
:
filters
.
platform
||
undefined
,
platform
:
(
filters
.
platform
as
GroupPlatform
)
||
undefined
,
status
:
filters
.
status
as
any
,
is_exclusive
:
filters
.
is_exclusive
?
filters
.
is_exclusive
===
'
true
'
:
undefined
}
...
...
frontend/src/views/admin/ProxiesView.vue
View file @
3d05e503
...
...
@@ -465,7 +465,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
Proxy
,
ProxyProtocol
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/
DataTable.vue
'
import
type
{
Column
}
from
'
@/components/common/
types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
...
...
frontend/src/views/admin/RedeemView.vue
View file @
3d05e503
...
...
@@ -349,7 +349,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
RedeemCode
,
RedeemCodeType
,
Group
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/
DataTable.vue
'
import
type
{
Column
}
from
'
@/components/common/
types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
...
...
frontend/src/views/admin/SubscriptionsView.vue
View file @
3d05e503
...
...
@@ -303,7 +303,7 @@ import { useI18n } from 'vue-i18n'
import
{
useAppStore
}
from
'
@/stores/app
'
import
{
adminAPI
}
from
'
@/api/admin
'
import
type
{
UserSubscription
,
Group
,
User
}
from
'
@/types
'
import
type
{
Column
}
from
'
@/components/common/
DataTable.vue
'
import
type
{
Column
}
from
'
@/components/common/
types
'
import
AppLayout
from
'
@/components/layout/AppLayout.vue
'
import
DataTable
from
'
@/components/common/DataTable.vue
'
import
Pagination
from
'
@/components/common/Pagination.vue
'
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment