Skip to content

Commit

Permalink
Refactor and add extra.time.now.utc.* placeholders
Browse files Browse the repository at this point in the history
  • Loading branch information
steffenbusch committed Oct 27, 2024
1 parent ff40b6d commit b66c4b0
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 59 deletions.
37 changes: 34 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ This plugin introduces new placeholders that can be used within Caddy configurat
| `{extra.loadavg.5}` | System load average over the last 5 minutes. |
| `{extra.loadavg.15}` | System load average over the last 15 minutes. |
| `{extra.hostinfo.uptime}` | System uptime in a human-readable format. |

### Current Server Local Time Placeholders

These placeholders reflect the **server's local timezone**:

| Placeholder | Description |
|--------------------------------------|-------------------------------------------------------|
| `{extra.time.now.month}` | Current month as an integer (e.g., 5 for May). |
| `{extra.time.now.month_padded}` | Current month as a zero-padded string (e.g., "05" for May). |
| `{extra.time.now.day}` | Current day of the month as an integer. |
Expand All @@ -33,7 +40,31 @@ This plugin introduces new placeholders that can be used within Caddy configurat
| `{extra.time.now.iso_week}` | The current ISO week number of the year. |
| `{extra.time.now.iso_year}` | The ISO year corresponding to the current ISO week. |
| `{extra.time.now.custom}` | Current time in a custom format, configurable via the `time_format_custom` directive. |
| | Note: All `extra.time.now.*` placeholders refer to the system's local timezone. |

### Current UTC Time Placeholders

These placeholders reflect **UTC time**:

| Placeholder | Description |
|--------------------------------------|-------------------------------------------------------|
| `{extra.time.now.utc.month}` | Current month in UTC as an integer (e.g., 5 for May). |
| `{extra.time.now.utc.month_padded}` | Current month in UTC as a zero-padded string (e.g., "05" for May). |
| `{extra.time.now.utc.day}` | Current day of the month in UTC as an integer. |
| `{extra.time.now.utc.day_padded}` | Current day of the month in UTC as a zero-padded string. |
| `{extra.time.now.utc.hour}` | Current hour in UTC in 24-hour format as an integer. |
| `{extra.time.now.utc.hour_padded}` | Current hour in UTC in 24-hour format as a zero-padded string. |
| `{extra.time.now.utc.minute}` | Current minute in UTC as an integer. |
| `{extra.time.now.utc.minute_padded}` | Current minute in UTC as a zero-padded string. |
| `{extra.time.now.utc.second}` | Current second in UTC as an integer. |
| `{extra.time.now.utc.second_padded}` | Current second in UTC as a zero-padded string. |
| `{extra.time.now.utc.timezone_offset}` | UTC timezone offset (always +0000). |
| `{extra.time.now.utc.timezone_name}` | UTC timezone abbreviation (always UTC). |
| `{extra.time.now.utc.iso_week}` | Current ISO week number of the year in UTC. |
| `{extra.time.now.utc.iso_year}` | ISO year corresponding to the current ISO week in UTC. |
| `{extra.time.now.utc.custom}` | Current UTC time in a custom format, configurable via the `time_format_custom` directive. |

> [!NOTE]
> All `extra.time.now.*` placeholders refer to the system's local timezone, while `extra.time.now.utc.*` placeholders represent the same values in UTC.
## Building

Expand Down Expand Up @@ -84,7 +115,7 @@ This means that `{extra.rand.int}` will default to generating a random integer b

### Custom Time Format

The `{extra.time.now.custom}` placeholder can be configured using the `time_format_custom` subdirective inside the `extra_placeholders` directive.
The `{extra.time.now.custom}` and `{extra.time.now.utc.custom}` placeholders can be configured using the `time_format_custom` subdirective inside the `extra_placeholders` directive.
This allows you to specify a custom date and time format using [Go's time format syntax](https://pkg.go.dev/time#pkg-constants).

```caddyfile
Expand All @@ -94,7 +125,7 @@ extra_placeholders {
}
```

If `time_format_custom` is not specified, it defaults to `"2006-01-02 15:04:05"`. This format will be used whenever `{extra.time.now.custom}` is referenced.
If `time_format_custom` is not specified, it defaults to `"2006-01-02 15:04:05"`. This format will be applied to both `{extra.time.now.custom}` (server’s local timezone) and `{extra.time.now.utc.custom}` (UTC time) placeholders.

### Example: Conditional Redirect Based on Random Value

Expand Down
88 changes: 32 additions & 56 deletions extra_placeholders.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ package extraplaceholders

import (
"fmt"
"math/rand"
"net/http"
"time"

"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/load"
"go.uber.org/zap"
)

Expand All @@ -39,6 +36,10 @@ import (
// `{extra.loadavg.5}` | System load average over the last 5 minutes.
// `{extra.loadavg.15}` | System load average over the last 15 minutes.
// `{extra.hostinfo.uptime}` | System uptime in a human-readable format.
//
// Current local time placeholders:
// Placeholder | Description
// ------------|-------------
// `{extra.time.now.month}` | Current month as an integer (e.g., 10 for October).
// `{extra.time.now.month_padded}` | Current month as a zero-padded string (e.g., "05" for May).
// `{extra.time.now.day}` | Current day of the month as an integer.
Expand All @@ -54,14 +55,33 @@ import (
// `{extra.time.now.iso_week}` | Current ISO week number of the year.
// `{extra.time.now.iso_year}` | ISO year corresponding to the current ISO week.
// `{extra.time.now.custom}` | Current time in a custom format, configurable via the `time_format_custom` directive.
//
// UTC equivalents of the current time placeholders (with `.utc` added):
// Placeholder | Description
// ------------|-------------
// `{extra.time.now.utc.month}` | Current month in UTC as an integer (e.g., 10 for October).
// `{extra.time.now.utc.month_padded}` | Current month in UTC as a zero-padded string (e.g., "05" for May).
// `{extra.time.now.utc.day}` | Current day of the month in UTC as an integer.
// `{extra.time.now.utc.day_padded}` | Current day of the month in UTC as a zero-padded string.
// `{extra.time.now.utc.hour}` | Current hour in UTC in 24-hour format as an integer.
// `{extra.time.now.utc.hour_padded}` | Current hour in UTC in 24-hour format as a zero-padded string.
// `{extra.time.now.utc.minute}` | Current minute in UTC as an integer.
// `{extra.time.now.utc.minute_padded}` | Current minute in UTC as a zero-padded string.
// `{extra.time.now.utc.second}` | Current second in UTC as an integer.
// `{extra.time.now.utc.second_padded}` | Current second in UTC as a zero-padded string.
// `{extra.time.now.utc.timezone_offset}` | UTC timezone offset (always +0000).
// `{extra.time.now.utc.timezone_name}` | UTC timezone abbreviation (always UTC).
// `{extra.time.now.utc.iso_week}` | Current ISO week number of the year in UTC.
// `{extra.time.now.utc.iso_year}` | ISO year corresponding to the current ISO week in UTC.
// `{extra.time.now.utc.custom}` | Current UTC time in a custom format, configurable via the `time_format_custom` directive.
type ExtraPlaceholders struct {
// RandIntMin defines the minimum value (inclusive) for the `{extra.rand.int}` placeholder.
RandIntMin int `json:"rand_int_min,omitempty"`

// RandIntMax defines the maximum value (inclusive) for the `{extra.rand.int}` placeholder.
RandIntMax int `json:"rand_int_max,omitempty"`

// TimeFormatCustom specifies a custom time format for the `{extra.time.now.custom}` placeholder.
// TimeFormatCustom specifies a custom time format for the `{extra.time.now.custom}` and `{extra.time.now.utc.custom}` placeholder.
// If left empty, a default format of "2006-01-02 15:04:05" is used.
TimeFormatCustom string `json:"time_format_custom,omitempty"`

Expand Down Expand Up @@ -116,60 +136,16 @@ func (e ExtraPlaceholders) ServeHTTP(w http.ResponseWriter, r *http.Request, nex
return caddyhttp.Error(http.StatusInternalServerError, nil)
}

// Set Caddy version placeholders.
simpleVersion, fullVersion := caddy.Version()
repl.Set("extra.caddy.version.simple", simpleVersion)
repl.Set("extra.caddy.version.full", fullVersion)

// Set placeholders for random float and integer values.
repl.Set("extra.rand.float", rand.Float64())
if e.RandIntMax > e.RandIntMin {
repl.Set("extra.rand.int", rand.Intn(e.RandIntMax-e.RandIntMin+1)+e.RandIntMin)
} else {
repl.Set("extra.rand.int", rand.Intn(101)) // Default range 0-100 if not properly configured
}
e.setCaddyPlaceholders(repl)
e.setRandPlaceholders(repl)
e.setLoadavgPlaceholders(repl)
e.setHostinfoPlaceholders(repl)

// Set placeholders for system load averages (1, 5, and 15 minutes).
loadAvg, err := load.Avg()
if err == nil {
repl.Set("extra.loadavg.1", loadAvg.Load1)
repl.Set("extra.loadavg.5", loadAvg.Load5)
repl.Set("extra.loadavg.15", loadAvg.Load15)
}

// Set placeholder for system uptime.
uptime, err := host.Uptime()
if err == nil {
uptimeDuration := time.Duration(uptime) * time.Second
repl.Set("extra.hostinfo.uptime", uptimeDuration.String())
} else {
repl.Set("extra.hostinfo.uptime", "error retrieving uptime")
}
// Set time placeholders for server's local time
e.setTimePlaceholders(repl, time.Now(), false)

// Set placeholders for current time (month, day, hour, minute, second).
now := time.Now() // System's local timezone
repl.Set("extra.time.now.month", int(now.Month()))
repl.Set("extra.time.now.month_padded", fmt.Sprintf("%02d", now.Month()))
repl.Set("extra.time.now.day", now.Day())
repl.Set("extra.time.now.day_padded", fmt.Sprintf("%02d", now.Day()))
repl.Set("extra.time.now.hour", now.Hour())
repl.Set("extra.time.now.hour_padded", fmt.Sprintf("%02d", now.Hour()))
repl.Set("extra.time.now.minute", now.Minute())
repl.Set("extra.time.now.minute_padded", fmt.Sprintf("%02d", now.Minute()))
repl.Set("extra.time.now.second", now.Second())
repl.Set("extra.time.now.second_padded", fmt.Sprintf("%02d", now.Second()))

// Set placeholders for timezone offset and name.
repl.Set("extra.time.now.timezone_offset", now.Format("-0700"))
repl.Set("extra.time.now.timezone_name", now.Format("MST"))

// Set placeholders for ISO week and ISO year.
isoYear, isoWeek := now.ISOWeek()
repl.Set("extra.time.now.iso_week", isoWeek)
repl.Set("extra.time.now.iso_year", isoYear)

// Set custom time format placeholder
repl.Set("extra.time.now.custom", now.Format(e.TimeFormatCustom))
// Set time placeholders for UTC time
e.setTimePlaceholders(repl, time.Now().UTC(), true)

// Call the next handler in the chain.
return next.ServeHTTP(w, r)
Expand Down
26 changes: 26 additions & 0 deletions placeholder_caddy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024 Steffen Busch

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package extraplaceholders

import (
"github.com/caddyserver/caddy/v2"
)

// setCaddyPlaceholders sets placeholders for the Caddy version.
func (e ExtraPlaceholders) setCaddyPlaceholders(repl *caddy.Replacer) {
simpleVersion, fullVersion := caddy.Version()
repl.Set("extra.caddy.version.simple", simpleVersion)
repl.Set("extra.caddy.version.full", fullVersion)
}
33 changes: 33 additions & 0 deletions placeholder_hostinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2024 Steffen Busch

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package extraplaceholders

import (
"time"

"github.com/caddyserver/caddy/v2"
"github.com/shirou/gopsutil/v4/host"
)

// setHostinfoPlaceholders sets placeholders for system uptime in a human-readable format.
func (e ExtraPlaceholders) setHostinfoPlaceholders(repl *caddy.Replacer) {
uptime, err := host.Uptime()
if err == nil {
uptimeDuration := time.Duration(uptime) * time.Second
repl.Set("extra.hostinfo.uptime", uptimeDuration.String())
} else {
repl.Set("extra.hostinfo.uptime", "error retrieving uptime")
}
}
30 changes: 30 additions & 0 deletions placeholder_loadavg.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2024 Steffen Busch

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package extraplaceholders

import (
"github.com/caddyserver/caddy/v2"
"github.com/shirou/gopsutil/v4/load"
)

// setLoadavgPlaceholders sets placeholders for system load averages (1, 5, and 15 minutes).
func (e ExtraPlaceholders) setLoadavgPlaceholders(repl *caddy.Replacer) {
loadAvg, err := load.Avg()
if err == nil {
repl.Set("extra.loadavg.1", loadAvg.Load1)
repl.Set("extra.loadavg.5", loadAvg.Load5)
repl.Set("extra.loadavg.15", loadAvg.Load15)
}
}
31 changes: 31 additions & 0 deletions placeholder_rand.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2024 Steffen Busch

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package extraplaceholders

import (
"math/rand"

"github.com/caddyserver/caddy/v2"
)

// setRandPlaceholders sets placeholders for random float and integer values.
func (e ExtraPlaceholders) setRandPlaceholders(repl *caddy.Replacer) {
repl.Set("extra.rand.float", rand.Float64())
if e.RandIntMax > e.RandIntMin {
repl.Set("extra.rand.int", rand.Intn(e.RandIntMax-e.RandIntMin+1)+e.RandIntMin)
} else {
repl.Set("extra.rand.int", rand.Intn(101)) // Default range 0-100 if not properly configured
}
}
42 changes: 42 additions & 0 deletions placeholder_time.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package extraplaceholders

import (
"fmt"
"time"

"github.com/caddyserver/caddy/v2"
)

// setTimePlaceholders sets placeholders for date, time, and custom format,
// using the provided time.Time. If isUTC is true, ".utc" is added in the placeholder path.
func (e ExtraPlaceholders) setTimePlaceholders(repl *caddy.Replacer, t time.Time, isUTC bool) {
// Determine the base path, with or without ".utc"
base := "extra.time.now"
if isUTC {
base += ".utc"
}

// Set date and time components with the specified base path
repl.Set(fmt.Sprintf("%s.month", base), int(t.Month()))
repl.Set(fmt.Sprintf("%s.month_padded", base), fmt.Sprintf("%02d", t.Month()))
repl.Set(fmt.Sprintf("%s.day", base), t.Day())
repl.Set(fmt.Sprintf("%s.day_padded", base), fmt.Sprintf("%02d", t.Day()))
repl.Set(fmt.Sprintf("%s.hour", base), t.Hour())
repl.Set(fmt.Sprintf("%s.hour_padded", base), fmt.Sprintf("%02d", t.Hour()))
repl.Set(fmt.Sprintf("%s.minute", base), t.Minute())
repl.Set(fmt.Sprintf("%s.minute_padded", base), fmt.Sprintf("%02d", t.Minute()))
repl.Set(fmt.Sprintf("%s.second", base), t.Second())
repl.Set(fmt.Sprintf("%s.second_padded", base), fmt.Sprintf("%02d", t.Second()))

// Set timezone offset and name
repl.Set(fmt.Sprintf("%s.timezone_offset", base), t.Format("-0700"))
repl.Set(fmt.Sprintf("%s.timezone_name", base), t.Format("MST"))

// Set ISO week and year components
isoYear, isoWeek := t.ISOWeek()
repl.Set(fmt.Sprintf("%s.iso_week", base), isoWeek)
repl.Set(fmt.Sprintf("%s.iso_year", base), isoYear)

// Set custom time format placeholder
repl.Set(fmt.Sprintf("%s.custom", base), t.Format(e.TimeFormatCustom))
}

0 comments on commit b66c4b0

Please sign in to comment.