refactor(storage-providers): streamline provider handling and remove unused types

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-11-02 20:46:33 +08:00
parent 6cf5fd7c51
commit 57c9642a98
7 changed files with 69 additions and 123 deletions

View File

@@ -125,11 +125,22 @@ export class SettingService {
const activeIdRaw = await this.get('builder.storage.activeProvider', options)
const activeId = typeof activeIdRaw === 'string' && activeIdRaw.trim().length > 0 ? activeIdRaw.trim() : null
if (!activeId) {
return null
// If activeId exists and matches, return it
if (activeId) {
const found = providers.find((provider) => provider.id === activeId) ?? null
if (found) return found
}
return providers.find((provider) => provider.id === activeId) ?? null
// Fallback: if there is exactly one provider, automatically set it active and persist the setting
if (providers.length === 1) {
const only = providers[0]
// Persist synchronously; ignore schema sensitivity (it's non-sensitive)
const setOptions = options ? { ...options, isSensitive: false } : { isSensitive: false }
await this.set('builder.storage.activeProvider', only.id, setOptions)
return only
}
return null
}
async set<K extends SettingKeyType>(key: K, value: SettingValueMap[K], options: SetSettingOptions): Promise<void>

View File

@@ -58,12 +58,7 @@ export const ProviderCard: FC<ProviderCardProps> = ({ provider, isActive, onEdit
case 'github': {
return cfg.repo || 'Not configured'
}
case 'local': {
return cfg.path || 'Not configured'
}
case 'eagle': {
return cfg.libraryPath || 'Not configured'
}
default: {
return 'Storage provider'
}

View File

@@ -70,8 +70,8 @@ export function StorageProvidersManager() {
}
// New provider
const result = [...prev, updatedProvider]
// Set as active if it's the first provider
if (!activeProviderId) {
// Set as active ONLY if it's the very first provider
if (prev.length === 0) {
setActiveProviderId(updatedProvider.id)
}
return result

View File

@@ -5,7 +5,7 @@ export const STORAGE_SETTING_KEYS = {
activeProvider: 'builder.storage.activeProvider',
} as const
export const STORAGE_PROVIDER_TYPES: readonly StorageProviderType[] = ['s3', 'github', 'local', 'eagle']
export const STORAGE_PROVIDER_TYPES: readonly StorageProviderType[] = ['s3', 'github']
export const STORAGE_PROVIDER_TYPE_OPTIONS: ReadonlyArray<{
value: StorageProviderType
@@ -13,8 +13,6 @@ export const STORAGE_PROVIDER_TYPE_OPTIONS: ReadonlyArray<{
}> = [
{ value: 's3', label: 'S3 / 兼容对象存储' },
{ value: 'github', label: 'GitHub 仓库' },
{ value: 'local', label: '本地文件系统' },
{ value: 'eagle', label: 'Eagle 素材库' },
]
export const STORAGE_PROVIDER_FIELD_DEFINITIONS: Record<
@@ -120,57 +118,4 @@ export const STORAGE_PROVIDER_FIELD_DEFINITIONS: Record<
helper: '使用自定义域名则可填写 false。',
},
],
local: [
{
key: 'basePath',
label: '基础路径',
placeholder: './apps/web/public/photos',
description: '本地素材所在的绝对或相对路径。',
},
{
key: 'baseUrl',
label: '访问 URL',
placeholder: '/photos',
description: '用于生成公开访问链接的基础 URL。',
},
{
key: 'distPath',
label: '输出目录',
placeholder: './dist/photos',
description: '可选,构建时复制素材到的目标目录。',
},
{
key: 'excludeRegex',
label: '排除规则 (正则)',
placeholder: '\\.(tmp|bak)$',
description: '可选,排除不需要复制的文件。',
multiline: true,
},
{
key: 'maxFileLimit',
label: '最大文件数量',
placeholder: '1000',
description: '可选,限制扫描时的最大文件数量。',
},
],
eagle: [
{
key: 'libraryPath',
label: 'Eagle Library 路径',
placeholder: '/Users/you/Library/Application Support/Eagle',
description: 'Eagle 应用素材库的安装路径。',
},
{
key: 'distPath',
label: '输出目录',
placeholder: './apps/web/public/originals',
description: '可选,原图导出的目标目录。',
},
{
key: 'baseUrl',
label: '访问 URL',
placeholder: '/originals',
description: '用于公开访问原图的基础 URL。',
},
],
}

View File

@@ -1,4 +1,4 @@
export type StorageProviderType = 's3' | 'github' | 'local' | 'eagle'
export type StorageProviderType = 's3' | 'github'
export interface StorageProvider {
id: string

View File

@@ -26,7 +26,7 @@ function coerceProvider(input: unknown): StorageProvider | null {
}
const record = input as Record<string, unknown>
const type = isStorageProviderType(record.type) ? record.type : 'local'
const type = isStorageProviderType(record.type) ? record.type : 's3'
const configInput =
record.config && typeof record.config === 'object' && !Array.isArray(record.config)
? (record.config as Record<string, unknown>)

View File

@@ -15,32 +15,32 @@ const rootIgnores = globalIgnores([
])
const hyobanConfig = await defineConfig(
{
formatting: false,
lessOpinionated: true,
preferESM: false,
react: true,
tailwindCSS: true,
{
formatting: false,
lessOpinionated: true,
preferESM: false,
react: true,
tailwindCSS: true,
},
{
languageOptions: {
parserOptions: {
emitDecoratorMetadata: true,
experimentalDecorators: true,
},
},
{
languageOptions: {
parserOptions: {
emitDecoratorMetadata: true,
experimentalDecorators: true,
},
// TailwindCSS v4 usually has no config file. Silence the plugin's
// config resolution warning by explicitly disabling auto-resolution.
settings: {
tailwindcss: {
// ESLint plugin will not attempt to resolve tailwind config
// which avoids repeated "Cannot resolve default tailwindcss config path" warnings.
config: false,
},
// TailwindCSS v4 usually has no config file. Silence the plugin's
// config resolution warning by explicitly disabling auto-resolution.
settings: {
tailwindcss: {
// ESLint plugin will not attempt to resolve tailwind config
// which avoids repeated "Cannot resolve default tailwindcss config path" warnings.
config: false,
},
},
rules: {
},
rules: {
'@typescript-eslint/triple-slash-reference': 0,
'unicorn/prefer-math-trunc': 'off',
'unicorn/no-static-only-class': 'off',
@@ -80,44 +80,39 @@ const hyobanConfig = await defineConfig(
'You can use `useLocaltion` or `getReadonlyRoute` to get the route info.',
},
],
},
},
},
// @ts-expect-error
{
files: ['locales/**/*.json'],
plugins: {
'recursive-sort': recursiveSort,
'check-i18n-json': checkI18nJson,
},
rules: {
'recursive-sort/recursive-sort': 'error',
'check-i18n-json/valid-i18n-keys': 'error',
'check-i18n-json/no-extra-keys': 'error',
},
// @ts-expect-error
{
files: ['locales/**/*.json'],
plugins: {
'recursive-sort': recursiveSort,
'check-i18n-json': checkI18nJson,
},
{
files: ['**/*.tsx'],
rules: {
'@stylistic/jsx-self-closing-comp': 'error',
},
rules: {
'recursive-sort/recursive-sort': 'error',
'check-i18n-json/valid-i18n-keys': 'error',
'check-i18n-json/no-extra-keys': 'error',
},
},
{
files: ['**/*.tsx'],
rules: {
'@stylistic/jsx-self-closing-comp': 'error',
},
},
// Backend framework isn't React — disable React-specific hooks rule there.
{
files: ['be/packages/framework/**/*.{ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
// Backend framework isn't React — disable React-specific hooks rule there.
{
files: ['be/packages/framework/**/*.{ts,tsx}'],
rules: {
'react-hooks/rules-of-hooks': 'off',
},
},
// Redundant but harmless: keep a local ignore in case this block is used standalone somewhere
globalIgnores([
'apps/ssr/src/index.html.ts',
'apps/ssr/public/**',
'apps/web/public/**',
'packages/docs/public/**',
]),
// Redundant but harmless: keep a local ignore in case this block is used standalone somewhere
globalIgnores(['apps/ssr/src/index.html.ts', 'apps/ssr/public/**', 'apps/web/public/**', 'packages/docs/public/**']),
)
export default [