05920dce67
- Use TXT records in order to determine the latest available version. - This addresses a valid privacy issue, as with HTTP requests the server can keep track(estimated) of how many instances are using Forgejo, with DNS that's basically not possible as the server will never receive any data, as the only ones receiving data are DNS resolvers. (cherry picked from commit0baefb546a
) (cherry picked from commite8ee41880b
) (cherry picked from commit7eca4f3bf1
) (cherry picked from commit6dde3992dc
) (cherry picked from commitfb3a37fbfc
) (cherry picked from commit8304af1e9d
) (cherry picked from commit0543a7d12a
) (cherry picked from commitc3a22933b7
) (cherry picked from commite243707694
) (cherry picked from commit7eb6d1bcf7
) (cherry picked from commit1d7b9535cd
)
142 lines
3.6 KiB
Go
142 lines
3.6 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package updatechecker
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/json"
|
|
"code.gitea.io/gitea/modules/proxy"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/system"
|
|
|
|
"github.com/hashicorp/go-version"
|
|
)
|
|
|
|
// CheckerState stores the remote version from the JSON endpoint
|
|
type CheckerState struct {
|
|
LatestVersion string
|
|
}
|
|
|
|
// Name returns the name of the state item for update checker
|
|
func (r *CheckerState) Name() string {
|
|
return "update-checker"
|
|
}
|
|
|
|
// GiteaUpdateChecker returns error when new version of Gitea is available
|
|
func GiteaUpdateChecker(httpEndpoint, domainEndpoint string) error {
|
|
var version string
|
|
var err error
|
|
if domainEndpoint != "" {
|
|
version, err = getVersionDNS(domainEndpoint)
|
|
} else {
|
|
version, err = getVersionHTTP(httpEndpoint)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return UpdateRemoteVersion(version)
|
|
}
|
|
|
|
// getVersionDNS will request the TXT records for the domain. If a record starts
|
|
// with "forgejo_versions=" everything after that will be used as the latest
|
|
// version available.
|
|
func getVersionDNS(domainEndpoint string) (version string, err error) {
|
|
records, err := net.LookupTXT(domainEndpoint)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if len(records) == 0 {
|
|
return "", errors.New("no TXT records were found")
|
|
}
|
|
|
|
for _, record := range records {
|
|
if strings.HasPrefix(record, "forgejo_versions=") {
|
|
// Get all supported versions, separated by a comma.
|
|
supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",")
|
|
// For now always return the latest supported version.
|
|
return supportedVersions[len(supportedVersions)-1], nil
|
|
}
|
|
}
|
|
|
|
return "", errors.New("there is no TXT record with a valid value")
|
|
}
|
|
|
|
// getVersionHTTP will make an HTTP request to the endpoint, and the returned
|
|
// content is JSON. The "latest.version" path's value will be used as the latest
|
|
// version available.
|
|
func getVersionHTTP(httpEndpoint string) (version string, err error) {
|
|
httpClient := &http.Client{
|
|
Transport: &http.Transport{
|
|
Proxy: proxy.Proxy(),
|
|
},
|
|
}
|
|
|
|
req, err := http.NewRequest("GET", httpEndpoint, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resp, err := httpClient.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
type respType struct {
|
|
Latest struct {
|
|
Version string `json:"version"`
|
|
} `json:"latest"`
|
|
}
|
|
respData := respType{}
|
|
err = json.Unmarshal(body, &respData)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return respData.Latest.Version, nil
|
|
}
|
|
|
|
// UpdateRemoteVersion updates the latest available version of Gitea
|
|
func UpdateRemoteVersion(version string) (err error) {
|
|
return system.AppState.Set(&CheckerState{LatestVersion: version})
|
|
}
|
|
|
|
// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
|
|
func GetRemoteVersion() string {
|
|
item := new(CheckerState)
|
|
if err := system.AppState.Get(item); err != nil {
|
|
return ""
|
|
}
|
|
return item.LatestVersion
|
|
}
|
|
|
|
// GetNeedUpdate returns true whether a newer version of Gitea is available
|
|
func GetNeedUpdate() bool {
|
|
curVer, err := version.NewVersion(setting.AppVer)
|
|
if err != nil {
|
|
// return false to fail silently
|
|
return false
|
|
}
|
|
remoteVerStr := GetRemoteVersion()
|
|
if remoteVerStr == "" {
|
|
// no remote version is known
|
|
return false
|
|
}
|
|
remoteVer, err := version.NewVersion(remoteVerStr)
|
|
if err != nil {
|
|
// return false to fail silently
|
|
return false
|
|
}
|
|
return curVer.LessThan(remoteVer)
|
|
}
|