feat(config): add configurable max accounts limit (#156)

Adds `maxAccounts` configuration parameter to control the maximum number of Google accounts.

**Changes:**
- New config field `maxAccounts` (default: 10, range: 1-100)
- Settings page: slider control for adjusting limit
- Accounts page: counter badge (e.g., "8/10") with visual feedback
- Add button disabled when limit reached
- Server-side validation on account creation

**Breaking Changes:** None
This commit is contained in:
jgor20
2026-01-20 20:34:57 +00:00
committed by GitHub
parent 11f135ef32
commit e51e3ff56a
10 changed files with 76 additions and 4 deletions

View File

@@ -250,6 +250,12 @@ window.Components.serverConfig = () => ({
(v) => window.Validators.validateTimeout(v, MAX_WAIT_MIN, MAX_WAIT_MAX));
},
toggleMaxAccounts(value) {
const { MAX_ACCOUNTS_MIN, MAX_ACCOUNTS_MAX } = window.AppConstants.VALIDATION;
this.saveConfigField('maxAccounts', value, 'Max Accounts',
(v) => window.Validators.validateRange(v, MAX_ACCOUNTS_MIN, MAX_ACCOUNTS_MAX, 'Max Accounts'));
},
toggleRateLimitDedupWindowMs(value) {
const { RATE_LIMIT_DEDUP_MIN, RATE_LIMIT_DEDUP_MAX } = window.AppConstants.VALIDATION;
this.saveConfigField('rateLimitDedupWindowMs', value, 'Rate Limit Dedup Window',

View File

@@ -69,6 +69,10 @@ window.AppConstants.VALIDATION = {
MAX_WAIT_MIN: 60000,
MAX_WAIT_MAX: 1800000,
// Max accounts range (1 - 100)
MAX_ACCOUNTS_MIN: 1,
MAX_ACCOUNTS_MAX: 100,
// Rate limit dedup window (1 - 30 seconds)
RATE_LIMIT_DEDUP_MIN: 1000,
RATE_LIMIT_DEDUP_MAX: 30000,

View File

@@ -13,6 +13,7 @@ document.addEventListener('alpine:init', () => {
modelConfig: {}, // Model metadata (hidden, pinned, alias)
quotaRows: [], // Filtered view
usageHistory: {}, // Usage statistics history (from /account-limits?includeHistory=true)
maxAccounts: 10, // Maximum number of accounts allowed (from config)
loading: false,
initialLoad: true, // Track first load for skeleton screen
connectionStatus: 'connecting',
@@ -135,6 +136,24 @@ document.addEventListener('alpine:init', () => {
}
},
async fetchVersion(password) {
try {
const { response } = await window.utils.request('/api/config', {}, password);
if (response.ok) {
const data = await response.json();
if (data.version) {
Alpine.store('global').version = data.version;
}
// Store maxAccounts from config
if (data.config && typeof data.config.maxAccounts === 'number') {
this.maxAccounts = data.config.maxAccounts;
}
}
} catch (error) {
console.warn('Failed to fetch version:', error);
}
},
async performHealthCheck() {
try {
// Get password from global store