Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ This readme and the [docs/](docs/) directory are **versioned** to match the prog

![Mobile Web UI](readme/webui-mobile.png)

- JSON API endpoint at `/json` for programmatic access to DNS record information

- Send notifications with [**Shoutrrr**](https://containrrr.dev/shoutrrr/v0.8/services/overview/) using `SHOUTRRR_ADDRESSES`
- Container (Docker/K8s) specific features:
- Lightweight 12MB Docker image based on the Scratch Docker image
Expand Down Expand Up @@ -266,6 +268,7 @@ Check the documentation for your DNS provider:
- [Vultr](docs/vultr.md)
- [Zoneedit](docs/zoneedit.md)
- [Custom](docs/custom.md)
- [JSON API](docs/json-api.md)

Note that:

Expand Down
99 changes: 99 additions & 0 deletions docs/json-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# JSON API Endpoint

The DDNS Updater provides a JSON API endpoint at `/json` that returns the same information as the web interface but in JSON format.

## Endpoint

- **URL**: `/json`
- **Method**: `GET`
- **Content-Type**: `application/json`
- **Cache-Control**: `no-cache`

## Response Format

The response is a JSON object with the following structure:

```json
{
"records": [
{
"domain": "example.com",
"owner": "www",
"provider": "cloudflare",
"ip_version": "ipv4",
"status": "success",
"message": "no IP change for 2h",
"current_ip": "192.168.1.1",
"previous_ips": ["192.168.1.2", "192.168.1.3"],
"total_ips_in_history": 3,
"last_update": "2023-01-01T12:00:00Z",
"success_time": "2023-01-01T12:00:00Z",
"duration_since_success": "2h"
}
],
"time": "2023-01-01T12:00:00Z",
"last_success_time": "2023-01-01T12:00:00Z",
"last_success_ip": "192.168.1.1"
}
```

## Field Descriptions

### Root Object
- `records`: Array of DNS record objects
- `time`: Current server time when the response was generated
- `last_success_time`: Time of the most recent successful update across all records (zero time if no successful updates)
- `last_success_ip`: IP address from the record with the most recent successful update (empty string if no successful updates)

### Record Object
- `domain`: The domain name (e.g., "example.com")
- `owner`: The subdomain owner (e.g., "www")
- `provider`: The DNS provider name (e.g., "cloudflare", "duckdns")
- `ip_version`: IP version supported ("ipv4", "ipv6", or "ipv4 or ipv6")
- `status`: Current status ("success", "failure", "updating", "up to date", "unset")
- `message`: Additional status information or error message
- `current_ip`: Current IP address (or "N/A" if not available)
- `previous_ips`: Array of previous IP addresses (limited to last 10, empty if none)
- `total_ips_in_history`: Total number of IP addresses available in the history
- `last_update`: Timestamp of the last update attempt
- `success_time`: Timestamp of the last successful update
- `duration_since_success`: Human-readable duration since last success (e.g., "2h", "30m", "1d")

## Example Usage

### Using curl
```bash
curl http://localhost:8080/json
```

### Using JavaScript
```javascript
fetch('/json')
.then(response => response.json())
.then(data => {
console.log('Current time:', data.time);
data.records.forEach(record => {
console.log(`${record.domain} (${record.provider}): ${record.status}`);
});
});
```

### Using Python
```python
import requests

response = requests.get('http://localhost:8080/json')
data = response.json()

print(f"Current time: {data['time']}")
for record in data['records']:
print(f"{record['domain']} ({record['provider']}): {record['status']}")
```

## Status Values

- `success`: Last update was successful
- `failure`: Last update failed
- `updating`: Update is currently in progress
- `up to date`: No IP change needed
- `unset`: Status not yet determined
27 changes: 27 additions & 0 deletions internal/models/json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package models

import "time"

// JSONData is the root structure for the JSON API response
type JSONData struct {
Records []JSONRecord `json:"records"`
Time time.Time `json:"time"`
LastSuccessTime time.Time `json:"last_success_time"`
LastSuccessIP string `json:"last_success_ip"`
}

// JSONRecord contains all the information for a DNS record in JSON format
type JSONRecord struct {
Domain string `json:"domain"`
Owner string `json:"owner"`
Provider string `json:"provider"`
IPVersion string `json:"ip_version"`
Status string `json:"status"`
Message string `json:"message"`
CurrentIP string `json:"current_ip"`
PreviousIPs []string `json:"previous_ips"`
TotalIPsInHistory int `json:"total_ips_in_history"`
LastUpdate time.Time `json:"last_update"`
SuccessTime time.Time `json:"success_time"`
Duration string `json:"duration_since_success"`
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import (

type Provider interface {
String() string
Name() models.Provider
Domain() string
Owner() string
BuildDomainName() string
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/aliyun/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ func validateSettings(domain, accessKeyID, accessSecret string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Aliyun
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Aliyun, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/allinkl/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,12 @@ func validateSettings(domain, owner, username, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.AllInkl
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.AllInkl, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/changeip/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ func validateSettings(domain, username, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Changeip
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Changeip, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/cloudflare/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,12 @@ func validateSettings(domain, email, key, userServiceKey, zoneIdentifier string,
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Cloudflare
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Cloudflare, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/custom/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,12 @@ func validateSettings(domain string, url *url.URL,
}
}

func (p *Provider) Name() models.Provider {
return constants.Custom
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Custom, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dd24/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ func validateSettings(domain, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Dd24
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Dd24, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/ddnss/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ func validateSettings(domain, owner, username, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DdnssDe
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DdnssDe, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/desec/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,12 @@ func validateSettings(domain, token string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DeSEC
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DeSEC, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/digitalocean/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ func validateSettings(domain, token string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DigitalOcean
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DigitalOcean, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dnsomatic/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ func validateSettings(domain, username, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DNSOMatic
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DNSOMatic, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dnspod/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ func validateSettings(domain, token string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DNSPod
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DNSPod, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/domeneshop/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,12 @@ func validateSettings(domain, owner, token, secret string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Domeneshop
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Domeneshop, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dondominio/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,12 @@ func validateSettings(domain, username, key string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DonDominio
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DonDominio, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dreamhost/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ func validateSettings(domain, key string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Dreamhost
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Dreamhost, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/duckdns/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,12 @@ func validateSettings(domain, owner, token string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.DuckDNS
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.DuckDNS, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dyn/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ func validateSettings(domain, username, clientKey string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Dyn
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Dyn, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
6 changes: 5 additions & 1 deletion internal/provider/providers/dynu/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,12 @@ func validateSettings(domain, owner, username, password string) (err error) {
return nil
}

func (p *Provider) Name() models.Provider {
return constants.Dynu
}

func (p *Provider) String() string {
return utils.ToString(p.domain, p.owner, constants.Dynu, p.ipVersion)
return utils.ToString(p.domain, p.owner, p.Name(), p.ipVersion)
}

func (p *Provider) Domain() string {
Expand Down
Loading