add uptime robot
This commit is contained in:
@@ -19,10 +19,11 @@ Passport is a simple, fast, and lightweight web dashboard/new tab replacement.
|
||||
|
||||
1. Clone the repository
|
||||
2. Configure the `.env` file, an example is provided in the `.env example` file
|
||||
- The `OPENWEATHER_API_KEY` is required for the weather data to be displayed
|
||||
- The `OPENWEATHER_API_KEY` is required for the weather data to be displayed, if you want to disable the weather data, set `PASSPORT_ENABLE_WEATHER` to `false`
|
||||
- The `OPENWEATHER_LAT` and `OPENWEATHER_LON` are required for the weather data to be displayed
|
||||
- The `PASSPORT_ADMIN_USERNAME` and `PASSPORT_ADMIN_PASSWORD` are required for the admin dashboard
|
||||
- The `PASSPORT_SEARCH_PROVIDER` is the search provider used for the search bar, %s is replaced with the search query
|
||||
- The `UPTIMEROBOT_API_KEY` is required for the uptime data to be displayed, if you want to disable the uptime data, set `PASSPORT_ENABLE_UPTIME` to `false`
|
||||
3. Run `zqdgr build` to build a standalone binary
|
||||
4. Deploy `passport` to your web server
|
||||
5. profit
|
||||
|
||||
139
main.go
139
main.go
@@ -70,6 +70,8 @@ var (
|
||||
type App struct {
|
||||
*WeatherCache
|
||||
*CategoryManager
|
||||
*UptimeManager
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func NewApp(dbPath string) (*App, error) {
|
||||
@@ -93,12 +95,115 @@ func NewApp(dbPath string) (*App, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var weatherCache *WeatherCache
|
||||
if os.Getenv("PASSPORT_ENABLE_WEATHER") != "false" {
|
||||
weatherCache = NewWeatherCache()
|
||||
}
|
||||
|
||||
var uptimeManager *UptimeManager
|
||||
if os.Getenv("PASSPORT_ENABLE_UPTIME") != "false" {
|
||||
uptimeManager = NewUptimeManager()
|
||||
}
|
||||
|
||||
return &App{
|
||||
WeatherCache: NewWeatherCache(),
|
||||
WeatherCache: weatherCache,
|
||||
CategoryManager: categoryManager,
|
||||
UptimeManager: uptimeManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type UptimeRobotSite struct {
|
||||
FriendlyName string `json:"friendly_name"`
|
||||
Url string `json:"url"`
|
||||
Status int `json:"status"`
|
||||
}
|
||||
|
||||
type UptimeManager struct {
|
||||
sites []UptimeRobotSite
|
||||
lastUpdate time.Time
|
||||
mutex sync.RWMutex
|
||||
updateChan chan struct{}
|
||||
updateInterval int
|
||||
apiKey string
|
||||
}
|
||||
|
||||
func NewUptimeManager() *UptimeManager {
|
||||
if os.Getenv("UPTIMEROBOT_API_KEY") == "" {
|
||||
log.Fatalln("UptimeRobot API Key is required!")
|
||||
return nil
|
||||
}
|
||||
|
||||
updateInterval, err := strconv.Atoi(os.Getenv("UPTIMEROBOT_UPDATE_INTERVAL"))
|
||||
if err != nil || updateInterval < 1 {
|
||||
updateInterval = 5
|
||||
}
|
||||
|
||||
uptimeManager := &UptimeManager{
|
||||
updateChan: make(chan struct{}),
|
||||
updateInterval: updateInterval,
|
||||
apiKey: os.Getenv("UPTIMEROBOT_API_KEY"),
|
||||
sites: []UptimeRobotSite{},
|
||||
}
|
||||
|
||||
go uptimeManager.updateWorker()
|
||||
|
||||
uptimeManager.updateChan <- struct{}{}
|
||||
|
||||
return uptimeManager
|
||||
}
|
||||
|
||||
func (u *UptimeManager) getUptime() []UptimeRobotSite {
|
||||
u.mutex.RLock()
|
||||
defer u.mutex.RUnlock()
|
||||
return u.sites
|
||||
}
|
||||
|
||||
func (u *UptimeManager) updateWorker() {
|
||||
ticker := time.NewTicker(time.Duration(u.updateInterval) * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-u.updateChan:
|
||||
u.update()
|
||||
case <-ticker.C:
|
||||
u.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type UptimeRobotResponse struct {
|
||||
Monitors []UptimeRobotSite `json:"monitors"`
|
||||
}
|
||||
|
||||
func (u *UptimeManager) update() {
|
||||
resp, err := http.Post("https://api.uptimerobot.com/v2/getMonitors?api_key="+u.apiKey, "application/json", nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Error fetching uptime data: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("Error reading response: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var monitors UptimeRobotResponse
|
||||
if err := json.Unmarshal(body, &monitors); err != nil {
|
||||
fmt.Printf("Error parsing uptime data: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("%+v", monitors.Monitors)
|
||||
|
||||
u.mutex.Lock()
|
||||
u.sites = monitors.Monitors
|
||||
u.lastUpdate = time.Now()
|
||||
u.mutex.Unlock()
|
||||
}
|
||||
|
||||
type OpenWeatherResponse struct {
|
||||
Weather []struct {
|
||||
Name string `json:"main"`
|
||||
@@ -533,6 +638,10 @@ func main() {
|
||||
return ""
|
||||
})
|
||||
|
||||
engine.AddFunc("eq", func(a, b any) bool {
|
||||
return a == b
|
||||
})
|
||||
|
||||
router := fiber.New(fiber.Config{
|
||||
Views: engine,
|
||||
})
|
||||
@@ -550,17 +659,27 @@ func main() {
|
||||
}))
|
||||
|
||||
router.Get("/", func(c fiber.Ctx) error {
|
||||
weather := app.WeatherCache.GetWeather()
|
||||
|
||||
return c.Render("views/index", fiber.Map{
|
||||
renderData := fiber.Map{
|
||||
"SearchProvider": os.Getenv("PASSPORT_SEARCH_PROVIDER"),
|
||||
"WeatherData": fiber.Map{
|
||||
"Categories": app.CategoryManager.Categories,
|
||||
}
|
||||
|
||||
if os.Getenv("PASSPORT_ENABLE_WEATHER") != "false" {
|
||||
weather := app.WeatherCache.GetWeather()
|
||||
|
||||
renderData["WeatherData"] = fiber.Map{
|
||||
"Temp": weather.Temperature,
|
||||
"Desc": weather.WeatherText,
|
||||
"Icon": getWeatherIcon(weather.Icon),
|
||||
},
|
||||
"Categories": app.CategoryManager.Categories,
|
||||
}, "layouts/main")
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("PASSPORT_ENABLE_UPTIME") != "false" {
|
||||
fmt.Printf("%+v", app.UptimeManager.getUptime())
|
||||
renderData["UptimeData"] = app.UptimeManager.getUptime()
|
||||
}
|
||||
|
||||
return c.Render("views/index", renderData, "layouts/main")
|
||||
})
|
||||
|
||||
router.Use(middleware.AdminMiddleware(app.db))
|
||||
@@ -825,5 +944,7 @@ func main() {
|
||||
})
|
||||
}
|
||||
|
||||
router.Listen(":3000")
|
||||
router.Listen(":3000", fiber.ListenConfig{
|
||||
EnablePrefork: os.Getenv("PASSPORT_ENABLE_PREFORK") == "true",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<main class="flex justify-center items-center h-screen relative bg-[#0E0A0E]">
|
||||
{{#if WeatherData}}
|
||||
<div class="absolute top-2.5 left-2.5">
|
||||
<div class="text-[#BABABA] flex items-center">
|
||||
<span class="mr-2 flex items-center">
|
||||
@@ -10,6 +11,30 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if UptimeData}}
|
||||
<div class="absolute top-2.5 right-2.5">
|
||||
<div class="text-[#BABABA] flex items-end flex-col">
|
||||
{{!-- loop over UptimeData --}}
|
||||
{{#each UptimeData}}
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 flex items-center">
|
||||
{{{this.FriendlyName}}}
|
||||
</span>
|
||||
<div class="relative my-auto">
|
||||
{{#if (eq this.Status 2)}}
|
||||
<span class="absolute w-2 h-2 rounded-full bg-emerald-400 animate-ping block"></span>
|
||||
<span class="relative w-2 h-2 rounded-full bg-emerald-500 block"></span>
|
||||
{{else}}
|
||||
<span class="absolute w-2 h-2 rounded-full bg-rose-400 animate-ping block"></span>
|
||||
<span class="relative w-2 h-2 rounded-full bg-rose-500 block"></span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="flex flex-col items-center w-full mx-6">
|
||||
<div class="flex items-center pb-2.5">
|
||||
<svg class="mr-3 aspect-square w-[clamp(48px,10vw,60px)]" viewBox="0 0 100 100" fill="none"
|
||||
|
||||
Reference in New Issue
Block a user