Files
jmiller bab29a83fa refactor: rename Go module path MokoConsulting/MokoGitea -> MokoConsulting/MokoGIT
Sweep all .go imports + go.mod, plus module-path refs in .golangci.yml,
Dockerfile, swagger templates, locale example, and repo/wiki URLs
(MokoGitea-Fork -> MokoGIT). Part of the MokoGIT rebrand.

Claude-Session: https://claude.ai/code/session_01Bqe7fAuHQeiLueYfeHFrHw
2026-07-14 15:31:19 -05:00

106 lines
3.2 KiB
Go

// Copyright 2026 Moko Consulting <hello@mokoconsulting.tech>
// SPDX-License-Identifier: GPL-3.0-or-later
package licensing
import (
"context"
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/models/db"
"code.mokoconsulting.tech/MokoConsulting/MokoGIT/modules/timeutil"
)
func init() {
db.RegisterModel(new(LicenseActivation))
}
// LicenseActivation tracks a domain that has activated a license.
type LicenseActivation struct {
ID int64 `xorm:"pk autoincr"`
LicenseID int64 `xorm:"INDEX NOT NULL"`
Domain string `xorm:"VARCHAR(255) NOT NULL"`
IPAddress string `xorm:"VARCHAR(64)"`
JoomlaVer string `xorm:"VARCHAR(20)"`
ActivatedAt timeutil.TimeStamp `xorm:"CREATED"`
LastSeenAt timeutil.TimeStamp
}
func (LicenseActivation) TableName() string {
return "license_activation"
}
// GetActivationsByLicense returns all domain activations for a license.
func GetActivationsByLicense(ctx context.Context, licenseID int64) ([]*LicenseActivation, error) {
var acts []*LicenseActivation
return acts, db.GetEngine(ctx).Where("license_id = ?", licenseID).Find(&acts)
}
// CountActivations returns the number of activated domains for a license.
func CountActivations(ctx context.Context, licenseID int64) (int64, error) {
return db.GetEngine(ctx).Where("license_id = ?", licenseID).Count(new(LicenseActivation))
}
// ActivateDomain registers a domain for a license. Returns the activation
// (existing or new) and whether it was newly created.
func ActivateDomain(ctx context.Context, licenseID int64, domain, ip, joomlaVer string, maxDomains int) (*LicenseActivation, bool, error) {
// Check if already activated
existing := new(LicenseActivation)
has, err := db.GetEngine(ctx).
Where("license_id = ? AND domain = ?", licenseID, domain).
Get(existing)
if err != nil {
return nil, false, err
}
if has {
// Update last seen
existing.LastSeenAt = timeutil.TimeStampNow()
existing.IPAddress = ip
if joomlaVer != "" {
existing.JoomlaVer = joomlaVer
}
_, _ = db.GetEngine(ctx).ID(existing.ID).Cols("last_seen_at", "ip_address", "joomla_ver").Update(existing)
return existing, false, nil
}
// Check domain limit (0 = unlimited)
if maxDomains > 0 {
count, err := CountActivations(ctx, licenseID)
if err != nil {
return nil, false, err
}
if count >= int64(maxDomains) {
return nil, false, ErrDomainLimitReached{LicenseID: licenseID, Max: maxDomains}
}
}
act := &LicenseActivation{
LicenseID: licenseID,
Domain: domain,
IPAddress: ip,
JoomlaVer: joomlaVer,
}
_, err = db.GetEngine(ctx).Insert(act)
if err != nil {
return nil, false, err
}
return act, true, nil
}
// DeactivateDomain removes a domain activation.
func DeactivateDomain(ctx context.Context, licenseID int64, domain string) error {
_, err := db.GetEngine(ctx).
Where("license_id = ? AND domain = ?", licenseID, domain).
Delete(new(LicenseActivation))
return err
}
// ErrDomainLimitReached is returned when a license has reached its max activated domains.
type ErrDomainLimitReached struct {
LicenseID int64
Max int
}
func (e ErrDomainLimitReached) Error() string {
return "license domain limit reached"
}