add cloudflare and documentation

This commit is contained in:
2025-12-22 23:25:06 +01:00
parent fbc8bcd089
commit 4590d46e17
17 changed files with 427 additions and 5 deletions

78
README.md Normal file
View File

@@ -0,0 +1,78 @@
SafelineAPI
=================
Small CLI to request and upsert TLS certificates (DNS-01) with multiple DNS provider backends. This repo now includes Cloudflare DNS provider support via the lego provider.
Prerequisites
- Go toolchain (only required to build from source)
- A SafeLine API token (set in `config.json`)
- Cloudflare API token (scoped) or Global API key + account email
Quick start
1. Copy `config.example.json` to `config.json` and fill in your values.
2. Build (optional):
```powershell
cd C:\Users\samge\coding\SafelineAPI-1
go build -o safelineApi.exe ./cmd/safelineApi
```
3. Run:
```powershell
# using built binary
.\safelineApi.exe
# or directly with go
go run ./cmd/safelineApi -- -t "<SafeLineApiToken>" -D "Cloudflare" -e "you@example.com"
```
Configuration notes
- The main configuration file is `config.json` in the project root.
- To use Cloudflare for DNS-01, set `ApplyCert.DNSProviderConfig.DNSProvider` to `Cloudflare` and set `ApplyCert.DNSProviderConfig.Cloudflare.APIToken` to a scoped API token with `Zone:DNS:Edit` permission.
- If you must use the global API key, set `Cloudflare.APIKey` and `Cloudflare.Email` instead (less secure).
Docs
- See `docs/CONFIGURATION.md` for detailed configuration and troubleshooting steps.
- See `docs/cloudflare.md` for a short Cloudflare-specific guide.
Security
- Prefer scoped API tokens over global keys.
- Keep `config.json` out of source control; use environment variables or secret management in production.
Need anything else?
- I can add a small PowerShell script to run the app with environment variable support or create a release artifact (Windows exe) if you'd like.
Linux usage (systemd)
Most users run this on a Linux host. Below are recommended steps to install and run SafelineAPI as a service.
1. Build on the target machine (or cross-compile):
```bash
cd /opt
git clone <your-repo-url> safelineapi
cd safelineapi
go build -o safelineApi ./cmd/safelineApi
```
2. Place your `config.json` in `/opt/safelineapi/config.json` (or edit accordingly). You can use `config.example.json` as a starting point.
3. Install systemd unit (example unit available at `contrib/safelineapi.service`):
```bash
sudo cp contrib/safelineapi.service /etc/systemd/system/
sudo useradd --system --no-create-home safeline || true
sudo chown -R safeline:safeline /opt/safelineapi
sudo systemctl daemon-reload
sudo systemctl enable --now safelineapi.service
sudo journalctl -u safelineapi.service -f
```
4. Alternatively run with the provided helper script (uses environment variables or builds if missing):
```bash
chmod +x scripts/run.sh
SAFELINE_API_TOKEN="..." DNS_PROVIDER=Cloudflare CONTACT_EMAIL="you@example.com" ./scripts/run.sh
```
Notes
- The `contrib/safelineapi.service` unit assumes files live in `/opt/safelineapi` and the binary is `/opt/safelineapi/safelineApi`. Adjust paths to fit your setup.
- For production, run the service as a dedicated unprivileged user and keep `config.json` permissions restricted.

View File

@@ -0,0 +1,11 @@
package main_test
import (
"testing"
"SafelineAPI/internal/app/config"
)
func TestDefaultConfig(t *testing.T) {
var c config.Config
c.Default()
}

View File

@@ -1,8 +1,14 @@
package main
//go:build test
// +build test
import "SafelineAPI/internal/app/config"
package main_test
func main() {
import (
"testing"
"SafelineAPI/internal/app/config"
)
func TestDefaultConfig(t *testing.T) {
var c config.Config
c.Default()
}

22
config.example.json Normal file
View File

@@ -0,0 +1,22 @@
{
"SafeLine": {
"Host": {
"HostName": "127.0.0.1",
"Port": "1443"
},
"ApiToken": "<YOUR_SAFELINE_API_TOKEN>"
},
"ApplyCert": {
"Days": 30,
"Email": "you@example.com",
"SavePath": "./ssl",
"DNSProviderConfig": {
"DNSProvider": "Cloudflare",
"Cloudflare": {
"APIToken": "<YOUR_CLOUDFLARE_API_TOKEN>",
"APIKey": "<GLOBAL_API_KEY_OPTIONAL>",
"Email": "you@cloudflare-account.com"
}
}
}
}

View File

@@ -34,6 +34,11 @@
"RainYun": {
"ApiKey": "xxx"
},
"Cloudflare": {
"APIToken": "xxx",
"APIKey": "xxx (optional)",
"Email": "your-email@example.com (optional)"
},
"Dode": {
"Token": "xxx"
}

12
contrib/safelineapi.env Normal file
View File

@@ -0,0 +1,12 @@
## Example environment file for systemd (placed at /etc/default/safelineapi)
## Values here are sourced by the systemd unit via EnvironmentFile
# Path to config.json
CONFIG_PATH=/opt/safelineapi/config.json
# Optional runtime overrides
#SAFELINE_API_TOKEN=
#DNS_PROVIDER=Cloudflare
#CONTACT_EMAIL=you@example.com
# Path to binary (if you installed somewhere else)
#BIN=/opt/safelineapi/safelineApi

View File

@@ -0,0 +1,16 @@
[Unit]
Description=SafelineAPI Service
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/safelineapi
ExecStart=/opt/safelineapi/safelineApi
Restart=on-failure
RestartSec=5
EnvironmentFile=/etc/default/safelineapi
User=safeline
Group=safeline
[Install]
WantedBy=multi-user.target

86
docs/CONFIGURATION.md Normal file
View File

@@ -0,0 +1,86 @@
**SafelineAPI Configuration and Cloudflare DNS Guide**
This guide explains how to configure SafelineAPI to use Cloudflare for DNS-01 challenges and how to run the program.
**Quick Start**
- **Prerequisites:** Go is only required for building from source. If you prefer, use the built binary produced by `go build`.
- **Minimal steps:** create a Cloudflare API token, update `config.json`, and run the program.
**Config File Location**
- The primary configuration file is `config.json` in the repository root. See `docs/cloudflare.md` for a short Cloudflare-specific note.
**Important fields**
- **SafeLine.ApiToken:** API token used to connect to the SafeLine API.
- **ApplyCert.Email:** Contact email used when requesting certificates.
- **ApplyCert.DNSProviderConfig.DNSProvider:** Set this to `Cloudflare` to use Cloudflare.
- **ApplyCert.DNSProviderConfig.Cloudflare.APIToken:** Recommended — a scoped Cloudflare API Token with `Zone:DNS:Edit` on your zone(s).
- **ApplyCert.DNSProviderConfig.Cloudflare.APIKey** and **Email:** Optional — use only if you must authenticate with the Global API key.
**Example `config.json` snippet**
```json
{
"SafeLine": {
"Host": { "HostName": "192.168.1.4", "Port": "1443" },
"ApiToken": "<your-safeline-api-token>"
},
"ApplyCert": {
"Days": 30,
"Email": "you@example.com",
"SavePath": "C:/path/to/ssl",
"DNSProviderConfig": {
"DNSProvider": "Cloudflare",
"Cloudflare": {
"APIToken": "<your-cloudflare-scoped-token>"
}
}
}
}
```
**Create a Cloudflare API Token**
1. Log into the Cloudflare dashboard and open **My Profile → API Tokens**.
2. Click **Create Token** and choose the **Edit zone DNS** template or set custom permissions:
- Zone:Zone:Read
- Zone:DNS:Edit
3. Scope the token to the specific zone(s) you need and create the token.
4. Put the token value in `ApplyCert.DNSProviderConfig.Cloudflare.APIToken`.
**Run commands**
- Build the binary (optional):
```powershell
cd C:\Users\samge\coding\SafelineAPI-1
go build -o safelineApi.exe ./cmd/safelineApi
```
- Run with the built binary:
```powershell
.\safelineApi.exe
```
- Or run directly with Go:
```powershell
go run ./cmd/safelineApi -- -t "<SafeLineApiToken>" -D "Cloudflare" -e "you@example.com"
```
Notes on flags: the project reads flags and `config.json`. If a flag is present it will be used for that run.
**Troubleshooting**
- Warning about missing values: If you see warnings like `未设置 DNS服务提供商`, set `ApplyCert.DNSProviderConfig.DNSProvider` or pass `-D` on the command line.
- Dependency/download issues: If `go build` stalls on module downloads, try setting a proxy:
```powershell
go env -w GOPROXY=https://goproxy.cn,direct
go clean -modcache
go mod tidy
go build -v ./...
```
- Cloudflare auth mismatch: Use `APIToken` (recommended). If using `APIKey` (global key), also provide the account `Email`.
**Security recommendations**
- Prefer scoped API tokens over the global API key.
- Store secrets outside source control. Use environment variables or an external secret store in production.
- Limit token scope to required zones.
**Files added/edited**
- Documentation: [docs/cloudflare.md](docs/cloudflare.md)
- Configuration example: `config.json` at project root
If you'd like, I can also add a short `README.md` or copy a minimal example `config.example.json` to the repo root for easy onboarding. Which would you prefer next?

42
docs/cloudflare.md Normal file
View File

@@ -0,0 +1,42 @@
# Cloudflare DNS provider
This project supports using Cloudflare for the DNS-01 challenge via the lego DNS provider.
Supported config fields (in `config.json` under `ApplyCert.DNSProviderConfig`):
- `DNSProvider`: set to `Cloudflare`
- `Cloudflare.APIToken`: Recommended — create a scoped API Token in Cloudflare (Zone.DNS edit).
- `Cloudflare.APIKey`: Optional — Global API Key (not recommended when token available).
- `Cloudflare.Email`: Optional — account email (used with Global API Key if needed).
Example `config.json` snippet:
{
"ApplyCert": {
"DNSProviderConfig": {
"DNSProvider": "Cloudflare",
"Cloudflare": {
"APIToken": "your-cloudflare-api-token",
"APIKey": "optional-global-api-key",
"Email": "you@example.com"
}
}
}
}
How to create a Cloudflare API token
1. Log into the Cloudflare dashboard.
2. Visit "My Profile" → "API Tokens" → "Create Token".
3. Use the "Edit zone DNS" template or create a custom token with the following permissions scoped to your zone(s):
- Zone:Zone:Read
- Zone:DNS:Edit
4. Save the token and put it into `Cloudflare.APIToken`.
Notes and links
- The integration uses the lego v4 Cloudflare provider.
- Cloudflare API docs: https://developers.cloudflare.com/api/
- Certbot cloudflare plugin docs (useful for end users): https://certbot-dns-cloudflare.readthedocs.io/en/stable/
If you want, I can run `go build` and fix any compile errors from these changes, or adjust field names to match the exact lego provider struct names on your machine. Would you like me to build and test now?

3
go.mod
View File

@@ -10,6 +10,8 @@ require (
require (
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
github.com/goccy/go-json v0.10.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -29,6 +31,7 @@ require (
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.8.0 // indirect
golang.org/x/tools v0.28.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

14
go.sum
View File

@@ -10,6 +10,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cloudflare-go v0.112.0 h1:caFwqXdGJCl3rjVMgbPEn8iCYAg9JsRYV3dIVQE5d7g=
github.com/cloudflare/cloudflare-go v0.112.0/go.mod h1:QB55kuJ5ZTeLNFcLJePfMuBilhu/LDKpLBmKFQIoSZ0=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -25,6 +27,8 @@ github.com/go-acme/lego/v4 v4.21.0/go.mod h1:HrSWzm3Ckj45Ie3i+p1zKVobbQoMOaGu9m4
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -60,6 +64,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -72,7 +78,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk=
github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc=
@@ -84,6 +89,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -197,6 +204,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -232,8 +241,9 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -7,6 +7,7 @@ type DNSProviderConfig struct {
HuaweiCloud `json:"HuaweiCloud,omitempty"`
WestCN `json:"WestCN,omitempty"`
RainYun `json:"RainYun,omitempty"`
Cloudflare `json:"Cloudflare,omitempty"`
Dode `json:"Dode,omitempty"`
}
@@ -40,3 +41,9 @@ type RainYun struct {
type Dode struct {
Token string `json:"Token,omitempty"`
}
type Cloudflare struct {
APIToken string `json:"APIToken,omitempty"`
APIKey string `json:"APIKey,omitempty"`
Email string `json:"Email,omitempty"`
}

View File

@@ -2,6 +2,7 @@ package config
import (
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/providers/dns/cloudflare"
"github.com/go-acme/lego/v4/providers/dns/alidns"
"github.com/go-acme/lego/v4/providers/dns/dode"
"github.com/go-acme/lego/v4/providers/dns/huaweicloud"
@@ -52,6 +53,24 @@ func (rain RainYun) Provider() (challenge.Provider, error) {
return p, err
}
func (cloud Cloudflare) Provider() (challenge.Provider, error) {
cfg := cloudflare.NewDefaultConfig()
// lego's cloudflare.Config uses AuthToken / AuthKey / AuthEmail / ZoneToken
if cloud.APIToken != "" {
cfg.AuthToken = cloud.APIToken
}
if cloud.APIKey != "" {
cfg.AuthKey = cloud.APIKey
}
if cloud.Email != "" {
cfg.AuthEmail = cloud.Email
}
// support ZoneToken if provided in future (keep empty if not set)
// if you add ZoneToken to the config struct, map it here: cfg.ZoneToken = cloud.ZoneToken
p, err := cloudflare.NewDNSProviderConfig(cfg)
return p, err
}
func (Dode Dode) Provider() (challenge.Provider, error) {
cfg := dode.NewDefaultConfig()
cfg.Token = Dode.Token

View File

@@ -17,6 +17,8 @@ func ChooseDNSProvider(config config.DNSProviderConfig) (challenge.Provider, err
return config.WestCN.Provider()
} else if config.DNSProvider == "RainYun" {
return config.RainYun.Provider()
} else if config.DNSProvider == "Cloudflare" {
return config.Cloudflare.Provider()
} else if config.DNSProvider == "Dode" {
return config.Dode.Provider()
}

BIN
safelineApi.exe Normal file

Binary file not shown.

73
scripts/install.sh Normal file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
# Install script for SafelineAPI (systemd)
# Usage: sudo ./scripts/install.sh [install_dir]
# Default install_dir: /opt/safelineapi
PREFIX=${1:-/opt/safelineapi}
SERVICE_NAME=safelineapi
SYSTEMD_UNIT=/etc/systemd/system/${SERVICE_NAME}.service
ENV_FILE=/etc/default/${SERVICE_NAME}
USER_NAME=safeline
echo "Install SafelineAPI to ${PREFIX}"
if [ "$(id -u)" -ne 0 ]; then
echo "This script must be run as root (or via sudo)" >&2
exit 2
fi
mkdir -p "$PREFIX"
# Build binary if not present in repo
if [ ! -f "$PREFIX/safelineApi" ]; then
echo "Building safelineApi..."
# build from repo root (assumes script run from repo root)
if [ -f ./cmd/safelineApi/main.go ]; then
GOOS=${GOOS:-linux} GOARCH=${GOARCH:-amd64} go build -o "$PREFIX/safelineApi" ./cmd/safelineApi
else
echo "Cannot find source to build. Place binary at ${PREFIX}/safelineApi or run from repo root." >&2
fi
fi
# Copy config.example.json to config.json if no config exists
if [ -f "$PREFIX/config.json" ]; then
echo "Found existing config.json in ${PREFIX}, leaving in place."
else
if [ -f ./config.example.json ]; then
cp ./config.example.json "$PREFIX/config.json"
echo "Copied example config to ${PREFIX}/config.json — edit and add secrets before starting service."
else
echo "No config.example.json found in repo root. Create ${PREFIX}/config.json before starting service." >&2
fi
fi
# copy service unit
cp contrib/safelineapi.service "$SYSTEMD_UNIT"
chmod 644 "$SYSTEMD_UNIT"
# copy example env file if not exists
if [ -f /etc/default/${SERVICE_NAME} ]; then
echo "/etc/default/${SERVICE_NAME} already exists — leaving in place."
else
cp contrib/safelineapi.env "$ENV_FILE"
chmod 644 "$ENV_FILE"
echo "Wrote ${ENV_FILE}. Edit it to set CONFIG_PATH and optional runtime vars."
fi
# create system user if missing
if id -u "$USER_NAME" >/dev/null 2>&1; then
echo "User $USER_NAME exists"
else
useradd --system --no-create-home --shell /usr/sbin/nologin "$USER_NAME"
echo "Created system user $USER_NAME"
fi
# chown files
chown -R ${USER_NAME}:${USER_NAME} "$PREFIX"
systemctl daemon-reload
systemctl enable --now ${SERVICE_NAME}.service
echo "Installation complete. Check status with: systemctl status ${SERVICE_NAME}.service"

30
scripts/run.sh Normal file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
# Simple runner: builds binary if missing then runs it.
# Environment variables supported:
# - BIN (path to binary, default ./safelineApi)
# - CONFIG (path to config.json, default ./config.json)
# - SAFELINE_API_TOKEN, DNS_PROVIDER, CONTACT_EMAIL (optional flags passed to binary)
BIN=${BIN:-./safelineApi}
CONFIG=${CONFIG:-./config.json}
if [ ! -f "$BIN" ]; then
echo "Binary not found, building..."
GOOS=${GOOS:-linux} GOARCH=${GOARCH:-amd64} go build -o "$BIN" ./cmd/safelineApi
fi
ARGS=()
if [ -n "${SAFELINE_API_TOKEN:-}" ]; then
ARGS+=("-t" "$SAFELINE_API_TOKEN")
fi
if [ -n "${DNS_PROVIDER:-}" ]; then
ARGS+=("-D" "$DNS_PROVIDER")
fi
if [ -n "${CONTACT_EMAIL:-}" ]; then
ARGS+=("-e" "$CONTACT_EMAIL")
fi
echo "Starting SafelineAPI ($BIN) with config: $CONFIG"
"$BIN" "${ARGS[@]}"