Files
Denozordec 2a8390e687
Publish telemt-api gateway Docker image / test (push) Successful in 27s
Publish telemt-api gateway Docker image / build-and-push (push) Successful in 1m27s
Enhance Dockerfile and documentation for GeoIP directory permissions
- Updated Dockerfile to create and set ownership for the /var/lib/telemt-gateway directory, ensuring the gateway user has the necessary permissions.
- Added documentation in GEOIP.md to clarify directory permissions required for GeoIP data downloads, including guidance on volume mounting and user ID consistency.
2026-03-30 01:54:29 +07:00

114 lines
3.0 KiB
Go

package geoip
import (
"compress/gzip"
"context"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)
// DownloadMMDB downloads an MMDB URL to destPath. If the URL ends with `.gz`, the body is gunzipped.
// Otherwise the response body is written as-is (raw `.mmdb`, e.g. GitHub raw).
func DownloadMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
if strings.HasSuffix(strings.ToLower(strings.TrimSpace(url)), ".gz") {
return downloadGzippedMMDB(ctx, client, url, destPath)
}
return downloadRawMMDB(ctx, client, url, destPath)
}
func downloadRawMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
if client == nil {
client = http.DefaultClient
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("geoip download: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("geoip download: http %s", resp.Status)
}
dir := filepath.Dir(destPath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("geoip mkdir %q: %w (directory must exist or be creatable by the process)", dir, err)
}
tmp, err := os.CreateTemp(dir, "geolite-*.mmdb.part")
if err != nil {
return fmt.Errorf("geoip temp file in %q: %w", dir, err)
}
tmpPath := tmp.Name()
_, copyErr := io.Copy(tmp, resp.Body)
closeErr := tmp.Close()
if copyErr != nil {
_ = os.Remove(tmpPath)
return copyErr
}
if closeErr != nil {
_ = os.Remove(tmpPath)
return closeErr
}
_ = os.Remove(destPath)
if err := os.Rename(tmpPath, destPath); err != nil {
_ = os.Remove(tmpPath)
return fmt.Errorf("geoip rename: %w", err)
}
return nil
}
func downloadGzippedMMDB(ctx context.Context, client *http.Client, url, destPath string) error {
if client == nil {
client = http.DefaultClient
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("geoip download: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("geoip download: http %s", resp.Status)
}
gzr, err := gzip.NewReader(resp.Body)
if err != nil {
return fmt.Errorf("geoip gzip: %w", err)
}
defer gzr.Close()
dir := filepath.Dir(destPath)
if err := os.MkdirAll(dir, 0o755); err != nil {
return fmt.Errorf("geoip mkdir %q: %w (directory must exist or be creatable by the process)", dir, err)
}
tmp, err := os.CreateTemp(dir, "geolite-*.mmdb.part")
if err != nil {
return fmt.Errorf("geoip temp file in %q: %w", dir, err)
}
tmpPath := tmp.Name()
_, copyErr := io.Copy(tmp, gzr)
closeErr := tmp.Close()
if copyErr != nil {
_ = os.Remove(tmpPath)
return copyErr
}
if closeErr != nil {
_ = os.Remove(tmpPath)
return closeErr
}
_ = os.Remove(destPath)
if err := os.Rename(tmpPath, destPath); err != nil {
_ = os.Remove(tmpPath)
return fmt.Errorf("geoip rename: %w", err)
}
return nil
}