Commit Graph
6 Commits
Author SHA1 Message Date
yuanyuanxianganddeepseek-flash f76cd79bb0 Fix: Renew expired licenses from today and report the actual expiry
Preset renewal stored its target date as the old expiry plus the entered
days, while the authorization actually issued to the client is always
today plus a day count. For a license that had already expired, the
preset date was therefore in the past: SendPendingRenewal clamped the
day count to one, the client received a one-day license starting today,
but the renewal tip and the logs still printed the stale preset date.
The result was a message such as "renewed until 2026-05-30" while the
license really ran until tomorrow.

Presetting a renewal now bases the new date on today when the license
has already expired, and on the original expiry date otherwise, which
matches the one-day-per-day extension admins expect for live licenses.
The renewal dialog and the success prompt state which base was used, so
the computed date is never ambiguous. Renewal tips and both logging
paths (heartbeat-driven SendPendingRenewal and client-requested
GenerateRenewalInfo) now print the date that was actually issued, which
also corrects legacy preset records whose stored date has already
passed.

Co-Authored-By: deepseek-flash
2026-10-19 15:15:49 +02:00
yuanyuanxiangandClaude Sonnet 4.6 837d89c8b5 Feat: Sub-license count limit - LicenseLimit field in licenses.ini + context menu
- Add LicenseLimit field to LicenseInfo struct (0 = not set, unlimited)
- Add GetLicenseLimit/SetLicenseLimit: read/write LicenseLimit key in licenses.ini
- Append |lic:N to reserved field in TOKEN_AUTH response only when
  LicenseLimit > 0; absent |lic: means no limit (client defaults to 9999),
  so super admin authenticating to its own server is never falsely terminated
- Add "Sub-license limit" item in CLicenseDlg right-click menu (1-9999,
  empty = clear limit); menu label shows current value in real time
- Limit change takes effect when sub-client re-authenticates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 13:01:35 +02:00
yuanyuanxiang 3f662f1ca7 Fix: macOS use quality profile FPS/bitrate, add HW resolution downscaling 2026-06-10 22:40:03 +02:00
yuanyuanxiang 740ec8baf3 Perf(license): mutex + write-suppression for licenses.ini hot path
licenses.ini was hit on every heartbeat -- 5s x clients x ~8 SetStr per
auth -- with no concurrency protection. Two consequences:
  1. 100 concurrent online would saturate the file (~160 writes/sec,
     full-file rewrite each via WritePrivateProfileString).
  2. Concurrent SetPendingRenewal / DecrementPendingQuota with no lock
     occasionally clobbered freshly-set renewal quotas (reported by
     user as "preset renewal silently disappears").

Add LicensesIniMutex() (Meyers singleton recursive_mutex, exposed in
CPasswordDlg.h so both CPasswordDlg.cpp and CLicenseDlg.cpp share it)
and wrap all 15 functions that touch licenses.ini.

Rewrite UpdateLicenseActivity around g_activityCache (in-memory state
keyed by "SN|IP|machine"): skip the entire write path when nothing
changed and the 30s LastActiveTime throttle window hasn't expired.
Passcode/HMAC are only flushed on actual change (renewal path); IP
list is only rewritten when the yyMMdd timestamp would roll a day.

Measured impact (local 2-client baseline):
  before: 0.60 writes/sec (4 writes per heartbeat cluster)
  after:  0.07 writes/sec (one write per client per 30s throttle)

Extrapolated to the 100-online target:
  before: ~160 writes/sec (saturation)
  after:  ~3.3 writes/sec  (100 clients / 30s throttle window)

Race elimination is the more important win: PendingQuota's
read-modify-write is now atomic, so the "preset renewal disappears"
race is closed.

Notes from audit (these landed during the same iteration):
- Cache key is (SN, IP, machine), not SN alone. A single SN can be
  shared by 100+ end machines in bulk-license deployments, so a
  per-SN cache flips on every heartbeat and defeats suppression.
  Per-(SN, IP, machine) throttling is what makes the 100/30 model
  actually hold; an SN-only key reproduced the original ~0.7 writes/s.
- DeleteLicense invalidates the per-SN activity cache via
  InvalidateLicenseActivityCache() (prefix scan since one SN maps to
  many cache entries). Without this, cache hits after delete would
  skip the auto-recreate path and leave the section permanently
  missing.
- OnLicenseViewIPs: m_ListLicense.SetItemText moved outside the lock
  so the critical section only covers disk I/O.
2026-05-22 00:31:54 +02:00
yuanyuanxiang 5b7d3903b5 Feature: Automatically start frp client for subordinate 2026-05-21 23:33:06 +02:00
yuanyuanxiang 5a325a202b Init: Migrate SimpleRemoter (Since v1.3.1) to Gitea 2026-04-19 22:55:21 +02:00