|
|
|
|
@@ -397,16 +397,79 @@ export interface CredentialValidationResult {
|
|
|
|
|
*
|
|
|
|
|
* 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 config = db.prepare(
|
|
|
|
|
let config: CloudConfig | undefined;
|
|
|
|
|
|
|
|
|
|
if (!ipAddress) {
|
|
|
|
|
// No IP info — fallback to simple LUR
|
|
|
|
|
config = db.prepare(
|
|
|
|
|
`SELECT * FROM cloud_configs
|
|
|
|
|
WHERE cloud_type = ? AND is_active = 1
|
|
|
|
|
AND consecutive_failures < 5
|
|
|
|
|
ORDER BY last_used_at ASC NULLS FIRST
|
|
|
|
|
LIMIT 1`
|
|
|
|
|
).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 the primary account (least recently used, healthy)
|
|
|
|
|
config = db.prepare(
|
|
|
|
|
`SELECT * FROM cloud_configs
|
|
|
|
|
WHERE cloud_type = ? AND is_active = 1
|
|
|
|
|
AND consecutive_failures < 5
|
|
|
|
|
ORDER BY 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 last_used_at ASC NULLS FIRST
|
|
|
|
|
LIMIT 1`
|
|
|
|
|
).get(cloudType) as CloudConfig | undefined;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!config) {
|
|
|
|
|
return {
|
|
|
|
|
@@ -457,6 +520,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 {
|
|
|
|
|
valid: true,
|
|
|
|
|
config: { ...config, cookie: decryptedCookie },
|
|
|
|
|
|