| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- package service
- import (
- "time"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- "github.com/mhsanaei/3x-ui/v3/internal/util/common"
- "github.com/mhsanaei/3x-ui/v3/internal/xray"
- )
- type ClientRenewalPreviewRequest struct {
- ExpiryTime int64 `json:"expiryTime" example:"1893456000000"`
- Reset int `json:"reset" example:"0"`
- ResetDay int `json:"resetDay" example:"1"`
- ResetWeekday int `json:"resetWeekday" example:"0"`
- ResetMax int `json:"resetMax" example:"0"`
- ResetCount int `json:"resetCount" example:"0"`
- }
- type ClientRenewalPreview struct {
- TimeZone string `json:"timeZone" example:"UTC"`
- RenewAt string `json:"renewAt" example:"2030-01-01T00:00:00Z"`
- ValidThrough string `json:"validThrough" example:"2029-12-31T23:59:59Z"`
- NextExpiry string `json:"nextExpiry" example:"2030-02-01T00:00:00Z"`
- SuggestedExpiryTime int64 `json:"suggestedExpiryTime" example:"1893456000000"`
- SuggestedExpiry string `json:"suggestedExpiry" example:"2030-01-01T00:00:00Z"`
- Renewals int `json:"renewals" example:"1"`
- CanRenew bool `json:"canRenew" example:"true"`
- DelayedStart bool `json:"delayedStart" example:"false"`
- }
- func (s *ClientService) PreviewRenewal(request ClientRenewalPreviewRequest, settings *SettingService) (*ClientRenewalPreview, error) {
- client := model.Client{Reset: request.Reset, ResetDay: request.ResetDay, ResetWeekday: request.ResetWeekday}
- if err := validateClientRenewal(client); err != nil {
- return nil, err
- }
- if err := validateClientResetMax(request.ResetMax); err != nil {
- return nil, err
- }
- if request.ResetCount < 0 || request.Reset < 0 {
- return nil, common.NewError("renewal preview reset and resetCount must not be negative")
- }
- loc, err := settings.GetTimeLocation()
- if err != nil {
- return nil, err
- }
- return previewClientRenewal(request, time.Now().UnixMilli(), loc)
- }
- func previewClientRenewal(request ClientRenewalPreviewRequest, now int64, loc *time.Location) (*ClientRenewalPreview, error) {
- preview := &ClientRenewalPreview{TimeZone: loc.String(), DelayedStart: request.ExpiryTime < 0}
- if request.ResetDay > 0 || request.ResetWeekday > 0 {
- preview.SuggestedExpiryTime = nextClientRenewal(now, request.Reset, request.ResetDay, request.ResetWeekday, loc)
- if preview.SuggestedExpiryTime <= now {
- return nil, common.NewError("calendar renewal could not find a future expiry")
- }
- preview.SuggestedExpiry = time.UnixMilli(preview.SuggestedExpiryTime).In(loc).Format(time.RFC3339)
- }
- if request.ExpiryTime <= 0 || (request.Reset <= 0 && request.ResetDay <= 0 && request.ResetWeekday <= 0) {
- return preview, nil
- }
- at := canonicalRenewalExpiry(request.ExpiryTime, request.Reset, request.ResetDay, request.ResetWeekday, loc)
- preview.RenewAt = time.UnixMilli(at).In(loc).Format(time.RFC3339Nano)
- // Billing alignment does not rewrite the stored exclusive deadline; the
- // preview must not promise an extra second before the first renewal.
- preview.ValidThrough = time.UnixMilli(request.ExpiryTime - 1).In(loc).Format(time.RFC3339)
- traffic := &xray.ClientTraffic{
- ExpiryTime: request.ExpiryTime, Reset: request.Reset, ResetDay: request.ResetDay,
- ResetWeekday: request.ResetWeekday, ResetMax: request.ResetMax, ResetCount: request.ResetCount,
- }
- expiry, renewals := catchUpClientRenewal(traffic, max(now, at+1), loc)
- if renewals == 0 && request.ResetWeekday > 0 && (request.ResetMax == 0 || request.ResetCount < request.ResetMax) {
- return nil, common.NewError("calendar renewal could not find a future expiry")
- }
- preview.Renewals = renewals
- preview.CanRenew = renewals > 0 && expiry > max(now, at)
- if renewals > 0 {
- preview.NextExpiry = time.UnixMilli(expiry).In(loc).Format(time.RFC3339Nano)
- }
- return preview, nil
- }
|