···11+#!/usr/bin/env bash
22+set -e
33+if [[ ! -d "/home/kierank/Projects/ctfd-alerts" ]]; then
44+ echo "Cannot find source directory; Did you move it?"
55+ echo "(Looking for "/home/kierank/Projects/ctfd-alerts")"
66+ echo 'Cannot force reload with this script - use "direnv reload" manually and then try again'
77+ exit 1
88+fi
99+1010+# rebuild the cache forcefully
1111+_nix_direnv_force_reload=1 direnv exec "/home/kierank/Projects/ctfd-alerts" true
1212+1313+# Update the mtime for .envrc.
1414+# This will cause direnv to reload again - but without re-building.
1515+touch "/home/kierank/Projects/ctfd-alerts/.envrc"
1616+1717+# Also update the timestamp of whatever profile_rc we have.
1818+# This makes sure that we know we are up to date.
1919+touch -r "/home/kierank/Projects/ctfd-alerts/.envrc" "/home/kierank/Projects/ctfd-alerts/.direnv"/*.rc
···11+package status
22+33+import (
44+ "github.com/spf13/cobra"
55+)
66+77+// StatusCmd represents the status command
88+var StatusCmd = &cobra.Command{
99+ Use: "status",
1010+ Short: "Show CTFd status information",
1111+ Long: "Shows the current CTFd scoreboard and list of challenges in a tabular format",
1212+ Run: runDashboard,
1313+}
+70
config.go
···11+package main
22+33+import (
44+ "errors"
55+ "fmt"
66+ "os"
77+ "strings"
88+99+ "github.com/pelletier/go-toml/v2"
1010+)
1111+1212+type CTFdConfig struct {
1313+ ApiBase string `toml:"api_base"`
1414+ ApiKey string `toml:"api_key"`
1515+}
1616+1717+type NtfyConfig struct {
1818+ ApiBase string `toml:"api_base"`
1919+ AccessToken string `toml:"acess_token"`
2020+ Topic string `toml:"topic"`
2121+}
2222+2323+type Config struct {
2424+ Debug bool `toml:"debug"`
2525+ CTFdConfig CTFdConfig `toml:"ctfd"`
2626+ NtfyConfig NtfyConfig `toml:"ntfy"`
2727+ MonitorInterval int `toml:"interval"`
2828+}
2929+3030+var config *Config
3131+3232+func loadConfig(path string) (*Config, error) {
3333+ data, err := os.ReadFile(path)
3434+ if err != nil {
3535+ return nil, err
3636+ }
3737+3838+ var cfg Config
3939+ if err := toml.Unmarshal(data, &cfg); err != nil {
4040+ return nil, err
4141+ }
4242+4343+ if cfg.CTFdConfig.ApiBase == "" {
4444+ return nil, errors.New("ctfd api_base URL cannot be empty")
4545+ }
4646+4747+ if cfg.CTFdConfig.ApiKey == "" {
4848+ return nil, errors.New("ctfd api_key cannot be empty")
4949+ }
5050+5151+ // Check API key format (should start with ctfd_ followed by 64 hex characters)
5252+ if len(cfg.CTFdConfig.ApiKey) != 69 || !strings.HasPrefix(cfg.CTFdConfig.ApiKey, "ctfd_") {
5353+ return nil, errors.New("ctfd api_key must be in the format ctfd_<64 hex characters> not " + cfg.CTFdConfig.ApiKey)
5454+ }
5555+5656+ if cfg.NtfyConfig.ApiBase == "" {
5757+ return nil, errors.New("ntfy api_base URL cannot be empty")
5858+ }
5959+6060+ if cfg.NtfyConfig.Topic == "" {
6161+ return nil, errors.New("ntfy topic cannot be empty")
6262+ }
6363+6464+ if cfg.MonitorInterval == 0 {
6565+ cfg.MonitorInterval = 300
6666+ fmt.Println("you haven't set a monitor interval; setting to 300")
6767+ }
6868+6969+ return &cfg, nil
7070+}