From a82bea567a23a3190d4414dcac59388e5ba70650 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 24 Mar 2026 19:45:36 +0100 Subject: [PATCH] feat(db): add license_status table migration Add database migration for the license_status table that stores instance ID, cached license validation response, and validation timestamps. --- pkg/license/license.go | 26 ++++++----------- pkg/migration/20260324120000.go | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 pkg/migration/20260324120000.go diff --git a/pkg/license/license.go b/pkg/license/license.go index d8be43cbc..b649b591e 100644 --- a/pkg/license/license.go +++ b/pkg/license/license.go @@ -108,10 +108,9 @@ func (f *Feature) UnmarshalJSON(data []byte) error { // Status represents the license_status table. type Status struct { - ID int64 `xorm:"bigint autoincr not null unique pk" json:"id"` - InstanceID string `xorm:"varchar(36) not null" json:"instance_id"` - LicenseKey string `xorm:"text not null" json:"-"` - Response string `xorm:"text not null" json:"response"` + ID int64 `xorm:"bigint autoincr not null unique pk" json:"id"` + InstanceID string `xorm:"varchar(36) not null" json:"instance_id"` + Response string `xorm:"text not null" json:"response"` ValidatedAt time.Time `xorm:"datetime null" json:"validated_at"` Created time.Time `xorm:"created not null" json:"created"` Updated time.Time `xorm:"updated not null" json:"updated"` @@ -163,12 +162,6 @@ func Init() { log.Errorf("Error loading cached license status: %s", err) } - // If cache exists but key changed, invalidate it - if cached != nil && cached.LicenseKey != key { - log.Infof("License key changed, invalidating cache.") - cached = nil - } - log.Debugf("Performing initial license check...") // Perform initial license check @@ -188,7 +181,7 @@ func Init() { log.Warningf("License key is invalid: %s. Pro features will not be available.", resp.Message) default: applyResponse(resp) - if err := cacheResponse(key, resp); err != nil { + if err := cacheResponse(resp); err != nil { log.Errorf("Error caching license response: %s", err) } log.Infof("License valid. Pro features enabled.") @@ -254,7 +247,6 @@ func loadOrCreateInstanceID() (string, error) { id := uuid.New().String() _, err = s.Insert(&Status{ InstanceID: id, - LicenseKey: "", Response: "{}", }) if err != nil { @@ -321,7 +313,7 @@ func degradeToFree(reason string) { log.Warningf("%s Pro features have been disabled.", reason) } -func cacheResponse(key string, resp *Response) error { +func cacheResponse(resp *Response) error { raw, err := serializeResponse(resp) if err != nil { return err @@ -331,8 +323,7 @@ func cacheResponse(key string, resp *Response) error { defer s.Close() // Update the existing row - _, err = s.Where("1=1").Update(&Status{ - LicenseKey: key, + _, err = s.Where("instance_id = ?", instanceID).Update(&Status{ Response: raw, ValidatedAt: time.Now(), }) @@ -394,7 +385,7 @@ func backgroundLoop(key string) { currentState.mu.RUnlock() applyResponse(resp) - if err := cacheResponse(key, resp); err != nil { + if err := cacheResponse(resp); err != nil { log.Errorf("Error caching license response: %s", err) } @@ -408,8 +399,7 @@ func clearCache() error { s := db.NewSession() defer s.Close() - _, err := s.Where("1=1").Update(&Status{ - LicenseKey: "", + _, err := s.Where("instance_id = ?", instanceID).Update(&Status{ Response: "{}", ValidatedAt: time.Time{}, }) diff --git a/pkg/migration/20260324120000.go b/pkg/migration/20260324120000.go new file mode 100644 index 000000000..8fee4b8ab --- /dev/null +++ b/pkg/migration/20260324120000.go @@ -0,0 +1,50 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package migration + +import ( + "time" + + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" +) + +type LicenseStatus20260324120000 struct { + ID int64 `xorm:"bigint autoincr not null unique pk"` + InstanceID string `xorm:"varchar(36) not null"` + Response string `xorm:"text not null"` + ValidatedAt time.Time `xorm:"datetime null"` + Created time.Time `xorm:"created not null"` + Updated time.Time `xorm:"updated not null"` +} + +func (LicenseStatus20260324120000) TableName() string { + return "license_status" +} + +func init() { + migrations = append(migrations, &xormigrate.Migration{ + ID: "20260324120000", + Description: "Add license_status table", + Migrate: func(tx *xorm.Engine) error { + return tx.Sync(LicenseStatus20260324120000{}) + }, + Rollback: func(tx *xorm.Engine) error { + return tx.DropTables(LicenseStatus20260324120000{}) + }, + }) +}