Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 58caaae37a | |||
| abd0cb26f5 |
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cloudsearch-backend",
|
"name": "cloudsearch-backend",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx watch src/main.ts",
|
"dev": "tsx watch src/main.ts",
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ async function doSaveFromShare(shareUrl: string, cloudType: string, sourceTitle?
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── Unified credential validation ──
|
// ── Unified credential validation ──
|
||||||
const credential = await getAndValidateCredential(cloudType);
|
const credential = await getAndValidateCredential(cloudType, ipAddress);
|
||||||
if (!credential.valid || !credential.config) {
|
if (!credential.valid || !credential.config) {
|
||||||
return { success: false, message: credential.message };
|
return { success: false, message: credential.message };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export interface CloudConfig {
|
|||||||
is_active: number;
|
is_active: number;
|
||||||
promotion_account?: string;
|
promotion_account?: string;
|
||||||
is_transfer_enabled: number;
|
is_transfer_enabled: number;
|
||||||
|
is_primary: number;
|
||||||
storage_used?: string;
|
storage_used?: string;
|
||||||
storage_total?: string;
|
storage_total?: string;
|
||||||
checkin_status: string; // 'none'|'success'|'failed'|'pending'|'skipped'
|
checkin_status: string; // 'none'|'success'|'failed'|'pending'|'skipped'
|
||||||
@@ -70,7 +71,7 @@ function extractQuarkUid(cookie: string): string | null {
|
|||||||
export function getCloudConfigs(): CloudConfig[] {
|
export function getCloudConfigs(): CloudConfig[] {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at, verification_status
|
last_used_at, total_saves, created_at, updated_at, verification_status
|
||||||
@@ -81,7 +82,7 @@ export function getCloudConfigs(): CloudConfig[] {
|
|||||||
export function getAvailableClouds(): CloudConfig[] {
|
export function getAvailableClouds(): CloudConfig[] {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at
|
last_used_at, total_saves, created_at, updated_at
|
||||||
@@ -93,7 +94,7 @@ export function getAvailableClouds(): CloudConfig[] {
|
|||||||
export function getCloudConfigByType(cloudType: string): CloudConfig | undefined {
|
export function getCloudConfigByType(cloudType: string): CloudConfig | undefined {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at, verification_status
|
last_used_at, total_saves, created_at, updated_at, verification_status
|
||||||
@@ -105,7 +106,7 @@ export function getCloudConfigByType(cloudType: string): CloudConfig | undefined
|
|||||||
export function getCloudConfigById(id: number): CloudConfig | undefined {
|
export function getCloudConfigById(id: number): CloudConfig | undefined {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at, verification_status
|
last_used_at, total_saves, created_at, updated_at, verification_status
|
||||||
@@ -117,7 +118,7 @@ export function getCloudConfigById(id: number): CloudConfig | undefined {
|
|||||||
export function getActiveCloudConfigs(): CloudConfig[] {
|
export function getActiveCloudConfigs(): CloudConfig[] {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at
|
last_used_at, total_saves, created_at, updated_at
|
||||||
@@ -126,6 +127,31 @@ export function getActiveCloudConfigs(): CloudConfig[] {
|
|||||||
).all() as CloudConfig[];
|
).all() as CloudConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the is_primary flag for a cloud config.
|
||||||
|
* Enforces max 2 primary accounts per cloud type.
|
||||||
|
*/
|
||||||
|
export function togglePrimary(id: number, setPrimary: boolean): CloudConfig {
|
||||||
|
const db = getDb();
|
||||||
|
const config = getCloudConfigById(id);
|
||||||
|
if (!config) throw new Error(`Cloud config ${id} not found`);
|
||||||
|
|
||||||
|
if (setPrimary) {
|
||||||
|
// Check how many primary accounts already exist for this cloud type
|
||||||
|
const primaryCount = db.prepare(
|
||||||
|
`SELECT COUNT(*) as c FROM cloud_configs WHERE cloud_type = ? AND is_primary = 1 AND id != ?`
|
||||||
|
).get(config.cloud_type, id) as { c: number };
|
||||||
|
if (primaryCount.c >= 2) {
|
||||||
|
throw new Error(`同类型网盘最多只能设置 2 个默认账号(已存在 ${primaryCount.c} 个)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.prepare(`UPDATE cloud_configs SET is_primary = ?, updated_at = datetime('now', 'localtime') WHERE id = ?`)
|
||||||
|
.run(setPrimary ? 1 : 0, id);
|
||||||
|
|
||||||
|
return getCloudConfigById(id)!;
|
||||||
|
}
|
||||||
|
|
||||||
export function saveCloudConfig(data: {
|
export function saveCloudConfig(data: {
|
||||||
id?: number;
|
id?: number;
|
||||||
cloud_type: string;
|
cloud_type: string;
|
||||||
@@ -199,7 +225,7 @@ export function saveCloudConfig(data: {
|
|||||||
// Re-read savedId for return
|
// Re-read savedId for return
|
||||||
const savedId = existing.id;
|
const savedId = existing.id;
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at
|
last_used_at, total_saves, created_at, updated_at
|
||||||
@@ -215,7 +241,7 @@ export function saveCloudConfig(data: {
|
|||||||
|
|
||||||
const savedId = data.id || (db.prepare('SELECT last_insert_rowid() as id').get() as any).id;
|
const savedId = data.id || (db.prepare('SELECT last_insert_rowid() as id').get() as any).id;
|
||||||
return db.prepare(
|
return db.prepare(
|
||||||
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, storage_used, storage_total,
|
`SELECT id, cloud_type, cookie, nickname, is_active, promotion_account, is_transfer_enabled, is_primary, storage_used, storage_total,
|
||||||
cloud_type_uid,
|
cloud_type_uid,
|
||||||
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
checkin_status, last_checkin_at, checkin_message, consecutive_failures,
|
||||||
last_used_at, total_saves, created_at, updated_at
|
last_used_at, total_saves, created_at, updated_at
|
||||||
@@ -397,16 +423,79 @@ export interface CredentialValidationResult {
|
|||||||
*
|
*
|
||||||
* Reference: search-ucmao get_and_validate_credential() pattern.
|
* Reference: search-ucmao get_and_validate_credential() pattern.
|
||||||
*/
|
*/
|
||||||
export async function getAndValidateCredential(cloudType: string): Promise<CredentialValidationResult> {
|
export async function getAndValidateCredential(cloudType: string, ipAddress?: string): Promise<CredentialValidationResult> {
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
|
|
||||||
const config = db.prepare(
|
let config: CloudConfig | undefined;
|
||||||
|
|
||||||
|
if (!ipAddress) {
|
||||||
|
// No IP info — fallback to simple LUR
|
||||||
|
config = db.prepare(
|
||||||
`SELECT * FROM cloud_configs
|
`SELECT * FROM cloud_configs
|
||||||
WHERE cloud_type = ? AND is_active = 1
|
WHERE cloud_type = ? AND is_active = 1
|
||||||
AND consecutive_failures < 5
|
AND consecutive_failures < 5
|
||||||
ORDER BY last_used_at ASC NULLS FIRST
|
ORDER BY is_primary DESC, last_used_at ASC NULLS FIRST
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
).get(cloudType) as CloudConfig | undefined;
|
).get(cloudType) as CloudConfig | undefined;
|
||||||
|
} else {
|
||||||
|
// Get today's date in Shanghai time
|
||||||
|
const today = (() => {
|
||||||
|
const now = new Date();
|
||||||
|
const shanghai = new Date(now.toLocaleString('en-US', { timeZone: 'Asia/Shanghai' }));
|
||||||
|
return shanghai.toISOString().slice(0, 10);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Count how many times this IP has saved today for this cloud type
|
||||||
|
const ipCountRow = db.prepare(
|
||||||
|
`SELECT COALESCE(SUM(save_count), 0) as total
|
||||||
|
FROM ip_daily_save_counts
|
||||||
|
WHERE ip_address = ? AND date = ? AND cloud_type = ?`
|
||||||
|
).get(ipAddress, today, cloudType) as { total: number };
|
||||||
|
|
||||||
|
const ipTodayCount = ipCountRow?.total || 0;
|
||||||
|
|
||||||
|
if (ipTodayCount < 3) {
|
||||||
|
// First 2 saves — use a primary account (is_primary=1), fallback to any healthy
|
||||||
|
config = db.prepare(
|
||||||
|
`SELECT * FROM cloud_configs
|
||||||
|
WHERE cloud_type = ? AND is_active = 1
|
||||||
|
AND consecutive_failures < 5
|
||||||
|
ORDER BY is_primary DESC, last_used_at ASC NULLS FIRST
|
||||||
|
LIMIT 1`
|
||||||
|
).get(cloudType) as CloudConfig | undefined;
|
||||||
|
} else {
|
||||||
|
// 3rd+ save — exclude accounts this IP has already used today,
|
||||||
|
// fall back to other available accounts round-robin
|
||||||
|
const usedConfigIds = db.prepare(
|
||||||
|
`SELECT DISTINCT config_id FROM ip_daily_save_counts
|
||||||
|
WHERE ip_address = ? AND date = ? AND cloud_type = ?
|
||||||
|
ORDER BY config_id`
|
||||||
|
).all(ipAddress, today, cloudType) as { config_id: number }[];
|
||||||
|
|
||||||
|
const usedIds = usedConfigIds.map(r => r.config_id);
|
||||||
|
const placeholders = usedIds.length > 0 ? usedIds.map(() => '?').join(',') : '-1';
|
||||||
|
|
||||||
|
config = db.prepare(
|
||||||
|
`SELECT * FROM cloud_configs
|
||||||
|
WHERE cloud_type = ? AND is_active = 1
|
||||||
|
AND consecutive_failures < 5
|
||||||
|
AND id NOT IN (${placeholders})
|
||||||
|
ORDER BY last_used_at ASC NULLS FIRST
|
||||||
|
LIMIT 1`
|
||||||
|
).get(cloudType, ...usedIds) as CloudConfig | undefined;
|
||||||
|
|
||||||
|
// If all accounts have been used by this IP, fall back to primary
|
||||||
|
if (!config) {
|
||||||
|
config = db.prepare(
|
||||||
|
`SELECT * FROM cloud_configs
|
||||||
|
WHERE cloud_type = ? AND is_active = 1
|
||||||
|
AND consecutive_failures < 5
|
||||||
|
ORDER BY is_primary DESC, last_used_at ASC NULLS FIRST
|
||||||
|
LIMIT 1`
|
||||||
|
).get(cloudType) as CloudConfig | undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return {
|
return {
|
||||||
@@ -457,6 +546,21 @@ export async function getAndValidateCredential(cloudType: string): Promise<Crede
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Track IP daily usage count (if ipAddress provided)
|
||||||
|
if (ipAddress && config) {
|
||||||
|
const today = (() => {
|
||||||
|
const now = new Date();
|
||||||
|
const shanghai = new Date(now.toLocaleString('en-US', { timeZone: 'Asia/Shanghai' }));
|
||||||
|
return shanghai.toISOString().slice(0, 10);
|
||||||
|
})();
|
||||||
|
db.prepare(
|
||||||
|
`INSERT INTO ip_daily_save_counts (ip_address, date, cloud_type, config_id, save_count)
|
||||||
|
VALUES (?, ?, ?, ?, 1)
|
||||||
|
ON CONFLICT(ip_address, date, cloud_type, config_id)
|
||||||
|
DO UPDATE SET save_count = save_count + 1`
|
||||||
|
).run(ipAddress, today, cloudType, config.id);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: true,
|
valid: true,
|
||||||
config: { ...config, cookie: decryptedCookie },
|
config: { ...config, cookie: decryptedCookie },
|
||||||
|
|||||||
@@ -119,6 +119,16 @@ function runMigrations(db: Database.Database): void {
|
|||||||
source TEXT,
|
source TEXT,
|
||||||
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime'))
|
updated_at TEXT NOT NULL DEFAULT (datetime('now', 'localtime'))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ip_daily_save_counts (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
ip_address TEXT NOT NULL,
|
||||||
|
date TEXT NOT NULL,
|
||||||
|
cloud_type TEXT NOT NULL,
|
||||||
|
config_id INTEGER NOT NULL,
|
||||||
|
save_count INTEGER NOT NULL DEFAULT 0,
|
||||||
|
UNIQUE(ip_address, date, cloud_type, config_id)
|
||||||
|
);
|
||||||
`);
|
`);
|
||||||
seedSystemConfigs(db);
|
seedSystemConfigs(db);
|
||||||
migrateSaveRecords(db);
|
migrateSaveRecords(db);
|
||||||
@@ -244,6 +254,13 @@ function migrateCloudConfigs(db: Database.Database): void {
|
|||||||
db.exec("ALTER TABLE cloud_configs ADD COLUMN is_transfer_enabled INTEGER DEFAULT 1");
|
db.exec("ALTER TABLE cloud_configs ADD COLUMN is_transfer_enabled INTEGER DEFAULT 1");
|
||||||
console.log('[DB] cloud_configs migration: is_transfer_enabled column added');
|
console.log('[DB] cloud_configs migration: is_transfer_enabled column added');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migration 6: Add is_primary column (default account, max 2 per cloud type)
|
||||||
|
const row6 = db.prepare("SELECT sql FROM sqlite_master WHERE name='cloud_configs' AND sql LIKE '%is_primary%'").get();
|
||||||
|
if (!row6) {
|
||||||
|
db.exec("ALTER TABLE cloud_configs ADD COLUMN is_primary INTEGER DEFAULT 0");
|
||||||
|
console.log('[DB] cloud_configs migration: is_primary column added');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function seedAdmin(db: Database.Database): void {
|
function seedAdmin(db: Database.Database): void {
|
||||||
@@ -257,6 +274,12 @@ function seedAdmin(db: Database.Database): void {
|
|||||||
'INSERT INTO admins (username, password_hash) VALUES (?, ?)'
|
'INSERT INTO admins (username, password_hash) VALUES (?, ?)'
|
||||||
).run(config.adminUsername, hash);
|
).run(config.adminUsername, hash);
|
||||||
|
|
||||||
|
// Migration 6: Add is_primary column (default account, max 2 per cloud type)
|
||||||
|
const row6 = db.prepare("SELECT sql FROM sqlite_master WHERE name='cloud_configs' AND sql LIKE %'is_primary%'").get();
|
||||||
|
if (!row6) {
|
||||||
|
db.exec("ALTER TABLE cloud_configs ADD COLUMN is_primary INTEGER DEFAULT 0");
|
||||||
|
console.log("[DB] cloud_configs migration: is_primary column added");
|
||||||
|
}
|
||||||
console.log(`[DB] Admin user "${config.adminUsername}" created`);
|
console.log(`[DB] Admin user "${config.adminUsername}" created`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import fs from "fs";
|
|||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import { adminLimiter, loginLimiter } from '../middleware/rate-limit';
|
import { adminLimiter, loginLimiter } from '../middleware/rate-limit';
|
||||||
import { getSaveRecords } from '../cloud/cloud.service';
|
import { getSaveRecords } from '../cloud/cloud.service';
|
||||||
import { getCloudConfigs, getCloudConfigById, saveCloudConfig, deleteCloudConfig, getCloudConfigByType, testCloudConnection, testCloudConnectionWithCookie } from '../cloud/credential.service';
|
import { getCloudConfigs, getCloudConfigById, saveCloudConfig, deleteCloudConfig, getCloudConfigByType, testCloudConnection, testCloudConnectionWithCookie, togglePrimary } from '../cloud/credential.service';
|
||||||
// Note: check-in routes were removed (sign-in feature removed)
|
// Note: check-in routes were removed (sign-in feature removed)
|
||||||
import { getAllCloudTypes } from '../cloud/cloud-types.service';
|
import { getAllCloudTypes } from '../cloud/cloud-types.service';
|
||||||
import { login, authMiddleware, verifyToken, changePassword } from '../admin/auth.service';
|
import { login, authMiddleware, verifyToken, changePassword } from '../admin/auth.service';
|
||||||
@@ -199,6 +199,20 @@ router.delete('/admin/cloud-configs/:id', (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /api/admin/cloud-configs/:id/primary — toggle primary status (max 2 per type)
|
||||||
|
*/
|
||||||
|
router.put('/admin/cloud-configs/:id/primary', (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const id = parseInt(req.params.id as string);
|
||||||
|
const { primary } = req.body;
|
||||||
|
const config = togglePrimary(id, !!primary);
|
||||||
|
res.json(config);
|
||||||
|
} catch (err: any) {
|
||||||
|
res.status(400).json({ error: err.message || 'Failed to toggle primary status' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/** POST /api/admin/cloud-configs/:type/test — test cloud connection (by type or id) */
|
/** POST /api/admin/cloud-configs/:type/test — test cloud connection (by type or id) */
|
||||||
router.post('/admin/cloud-configs/:type/test', async (req: Request, res: Response) => {
|
router.post('/admin/cloud-configs/:type/test', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -267,6 +267,14 @@ export async function deleteCloudConfig(
|
|||||||
await api.delete(`/admin/cloud-configs/${id}`)
|
await api.delete(`/admin/cloud-configs/${id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function setPrimary(
|
||||||
|
id: number,
|
||||||
|
primary: boolean
|
||||||
|
): Promise<any> {
|
||||||
|
const { data } = await api.put(`/admin/cloud-configs/${id}/primary`, { primary })
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
export async function getStats(days?: number): Promise<StatsData> {
|
export async function getStats(days?: number): Promise<StatsData> {
|
||||||
const params: Record<string, number> = {}
|
const params: Record<string, number> = {}
|
||||||
if (days) params.days = days
|
if (days) params.days = days
|
||||||
|
|||||||
@@ -99,6 +99,16 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="默认账号" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-switch
|
||||||
|
:model-value="row.is_primary === 1"
|
||||||
|
:disabled="!row.is_transfer_enabled"
|
||||||
|
size="small"
|
||||||
|
@change="(val: boolean) => handleTogglePrimary(row, val)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="390" align="center">
|
<el-table-column label="操作" width="390" align="center">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button text type="primary" @click="openDialog(row)">编辑</el-button>
|
<el-button text type="primary" @click="openDialog(row)">编辑</el-button>
|
||||||
@@ -171,7 +181,7 @@ import { Loading } from '@element-plus/icons-vue'
|
|||||||
import { CLOUD_LABELS } from '../../types'
|
import { CLOUD_LABELS } from '../../types'
|
||||||
import type { CloudType, CloudConfig } from '../../types'
|
import type { CloudType, CloudConfig } from '../../types'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { getCloudConfigs, saveCloudConfig, updateCloudConfig, deleteCloudConfig, testCloudConnection, getCloudTypes, toggleCloudType } from '../../api'
|
import { getCloudConfigs, saveCloudConfig, updateCloudConfig, deleteCloudConfig, testCloudConnection, getCloudTypes, toggleCloudType, setPrimary } from '../../api'
|
||||||
import CloudBadge from '../../components/CloudBadge.vue'
|
import CloudBadge from '../../components/CloudBadge.vue'
|
||||||
import type { ElForm } from 'element-plus'
|
import type { ElForm } from 'element-plus'
|
||||||
|
|
||||||
@@ -351,6 +361,16 @@ async function handleToggleTransfer(row: CloudConfig, enabled: boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleTogglePrimary(row: CloudConfig, enabled: boolean) {
|
||||||
|
try {
|
||||||
|
await setPrimary(row.id!, enabled)
|
||||||
|
row.is_primary = enabled ? 1 : 0
|
||||||
|
ElMessage.success(enabled ? `已将「${row.nickname || row.cloud_type}」设为默认账号` : '已取消默认账号')
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.response?.data?.error || e.message || '操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function autoVerifyAll() {
|
async function autoVerifyAll() {
|
||||||
for (const cfg of configs.value) {
|
for (const cfg of configs.value) {
|
||||||
if (cfg.cookie_preview || cfg.nickname) {
|
if (cfg.cookie_preview || cfg.nickname) {
|
||||||
|
|||||||
Reference in New Issue
Block a user