diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index 44b684f..324bcc5 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -9,10 +9,7 @@ when: - renovate/* variables: - - &golang_image 'golang:1.24' - -depends_on: - - test + - &golang_image 'golang:1.22' steps: "Add vendor": @@ -25,15 +22,6 @@ steps: commands: - make - "Test packaging": - image: deblan/fpm-packager - commands: - - VERSION=test - - ./bin/build-debs.sh "$VERSION" - - ./bin/rename-builds.sh "$VERSION" - when: - event: [push, pull_request] - "Create packages": image: deblan/fpm-packager commands: diff --git a/.woodpecker/test.yml b/.woodpecker/test.yml deleted file mode 100644 index 81b482f..0000000 --- a/.woodpecker/test.yml +++ /dev/null @@ -1,15 +0,0 @@ -when: - - event: [pull_request, tag] - - event: push - branch: - - ${CI_REPO_DEFAULT_BRANCH} - - develop - - feature/* - - release/* - - renovate/* - -steps: - "Check shell scripts": - image: pipelinecomponents/shellcheck - commands: - - shellcheck ./bin/*.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index e3fb5b1..cc71a94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,5 @@ [Unreleased] -## v2.0.0 -### Changed -- new project archirecture - -## v1.4.0 -### Added -- add logger and option `-v` -### Fixed -- fix: render days and date when the value is later than danger value - -## v1.3.0 -### Added -- add manpage for Debian - ## v1.2.1 ### Fixed - fix helper descriptions diff --git a/Makefile b/Makefile index 576acde..9d7e66d 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,6 @@ BIN_DARWIN_AMD64 = $(DIR)/$(EXECUTABLE)-darwin-$(GO_ARCH_AMD) BIN_DARWIN_ARM64 = $(DIR)/$(EXECUTABLE)-darwin-$(GO_ARCH_ARM) BIN_LINUX_AMD64 = $(DIR)/$(EXECUTABLE)-linux-$(GO_ARCH_AMD) BIN_LINUX_ARM64 = $(DIR)/$(EXECUTABLE)-linux-$(GO_ARCH_ARM) -MAN_OUTPUT = $(DIR)/expiration-check.1 CC = go build CFLAGS = -trimpath @@ -25,7 +24,7 @@ GCFLAGS = all= ASMFLAGS = all= .PHONY: all -all: linux windows darwin man +all: linux windows darwin .PHONY: linux linux: $(BIN_LINUX_AMD64) $(BIN_LINUX_ARM64) @@ -40,9 +39,6 @@ darwin: $(BIN_DARWIN_AMD64) $(BIN_DARWIN_ARM64) .PHONY: windows windows: $(BIN_WIN_AMD64) $(BIN_WIN_ARM64) -.PHONY: man -man: $(BIN_LINUX_AMD64) $(MAN_OUTPUT) - .PHONY: $(BIN_LINUX_AMD64) $(BIN_LINUX_AMD64): GO111MODULE=$(GOMOD) \ @@ -50,7 +46,7 @@ $(BIN_LINUX_AMD64): GOOS=$(GO_OS_LINUX) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_LINUX_AMD64) cmd/main.go + -o $(BIN_LINUX_AMD64) . .PHONY: $(BIN_LINUX_ARM64) $(BIN_LINUX_ARM64): @@ -59,7 +55,7 @@ $(BIN_LINUX_ARM64): GOOS=$(GO_OS_LINUX) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_LINUX_ARM64) cmd/main.go + -o $(BIN_LINUX_ARM64) . .PHONY: $(BIN_WIN_AMD64) $(BIN_WIN_AMD64): @@ -68,7 +64,7 @@ $(BIN_WIN_AMD64): GOOS=$(GO_OS_WIN) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_WIN_AMD64) cmd/main.go + -o $(BIN_WIN_AMD64) . .PHONY: $(BIN_WIN_ARM64) $(BIN_WIN_ARM64): @@ -77,7 +73,7 @@ $(BIN_WIN_ARM64): GOOS=$(GO_OS_WIN) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_WIN_ARM64) cmd/main.go + -o $(BIN_WIN_ARM64) . .PHONY: $(BIN_DARWIN_AMD64) $(BIN_DARWIN_AMD64): @@ -86,7 +82,7 @@ $(BIN_DARWIN_AMD64): GOOS=$(GO_OS_DARWIN) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_DARWIN_AMD64) cmd/main.go + -o $(BIN_DARWIN_AMD64) . .PHONY: $(BIN_DARWIN_ARM64) $(BIN_DARWIN_ARM64): @@ -95,11 +91,7 @@ $(BIN_DARWIN_ARM64): GOOS=$(GO_OS_DARWIN) \ CGO_ENABLED=$(CGO_ENABLED) \ $(CC) $(CFLAGS) -ldflags="$(LDFLAGS)" -gcflags="$(GCFLAGS)" -asmflags="$(ASMFLAGS)" \ - -o $(BIN_DARWIN_ARM64) cmd/main.go - -.PHONY: $(MAN_OUTPUT) -$(MAN_OUTPUT): - ./bin/create-manpage.sh + -o $(BIN_DARWIN_ARM64) . .PHONY: clean clean: diff --git a/README.md b/README.md index 691d2d9..b84f40f 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,38 @@ -# ๐Ÿ—“๏ธ Expiration check +Expiration check +================ -**Expiration-check** is a command-line tool designed to verify the expiration dates of domains and TLS certificates. +Checks the expiration dates of domains and certificates. -For domain verification, it implements the [RDAP protocol](https://about.rdap.org/) whenever possible. If RDAP is unavailable, it falls back on the WHOIS protocol. +Domain expiration check uses [`RDAP`](https://about.rdap.org/) and fallback with a `whois` request. -## ๐Ÿ“— How to install the project +## Usage -Pre-compiled versions are available in the [Releases](https://gitnet.fr/deblan/expiration-check/releases). Multiple operating systems are supported, including Linux, Windows, and macOS. For Debian users, a package is also provided. +Go to [releases](https://gitnet.fr/deblan/expiration-check/releases) and download the latest version. -If you want to compile the project from source, you will need at least the GO compiler version 1.22. Clone the project and run the make command. The compiled output will be located in the `build` directory. +```text +$ expiration-check domains -d example.com -d other-example.com ++-------------------+------+---------------------+ +| DOMAIN | DAYS | DATE | ++-------------------+------+---------------------+ +| example.com | XX | YYYY-MM-DD HH:MM:SS | +| other-example.com | XXX | YYYY-MM-DD HH:MM:SS | ++-------------------+------+---------------------+ -``` -$ git clone https://gitnet.fr/deblan/expiration-check -$ make +$ expiration-check certificates -d example.com -d other-example.com -d mail.example.com:993 ++-------------------+------+---------------------+ +| DOMAIN | DAYS | DATE | ++-------------------+------+---------------------+ +| example.com | XX | YYYY-MM-DD HH:MM:SS | +| other-example.com | XXX | YYYY-MM-DD HH:MM:SS | +| mail.example.com | XXX | YYYY-MM-DD HH:MM:SS | ++-------------------+------+---------------------+ ``` -## ๐Ÿงช How to use the project +You can specify an ouput format using `--format` or `-f`: -### Commands - -- `certificates`, `certificate`, `cert`, `certs`, `c`: Checks the expiration dates of TLS certificates. -- `domains`, `domain`, `d`: Checks the expiration dates of domain names. -- `help`, `h`: Displays a list of all commands or detailed help for a specific command. - -### Global Options - -- `--help`, `-h`: Shows the help message, providing information about the usage and available commands. - -Use the `--format` or `-f` option to specify the output format. Available formats are table, csv, tsv, html, json, and markdown. The default format is table. - -### Examples - -Check certificate expirations: - -``` -expiration-check certificate --domain example.com -``` - -Check domain expirations: - -``` -expiration-check domain --domain example.com -``` - -Check certificates for multiple domains with default table format: - -``` -expiration-check certificate --domain example.com --domain example.org -``` - -Check certificates and output results in JSON format: - -``` -expiration-check certificate --domain example.com --format json -``` - -Get help for a specific command: - -``` -expiration-check help certificate -``` +- `table` (default) +- `json` +- `csv` +- `tsv` +- `html` +- `markdown` diff --git a/internal/cmd/app.go b/app.go similarity index 80% rename from internal/cmd/app.go rename to app.go index f0704f7..a39982f 100644 --- a/internal/cmd/app.go +++ b/app.go @@ -1,10 +1,9 @@ -package cmd +package main import ( "github.com/urfave/cli/v2" - "gitnet.fr/deblan/expiration-check/pkg/checker" - "gitnet.fr/deblan/expiration-check/pkg/logger" - "gitnet.fr/deblan/expiration-check/pkg/render" + "gitnet.fr/deblan/expiration-check/checker" + "gitnet.fr/deblan/expiration-check/render" ) func NormalizeFormat(format string) string { @@ -34,11 +33,6 @@ func App() *cli.App { Value: "table", Usage: "output format: table, csv, tsv, html, json, markdown", }, - &cli.BoolFlag{ - Name: "verbose", - Aliases: []string{"v"}, - Required: false, - }, } return &cli.App{ @@ -51,8 +45,6 @@ func App() *cli.App { Usage: "Checks certificate", Flags: flags, Action: func(c *cli.Context) error { - logger.Get().SetVerbose(c.Bool("verbose")) - render.Render( checker.CheckCertificates(c.StringSlice("domain")), 30, 14, @@ -68,8 +60,6 @@ func App() *cli.App { Aliases: []string{"d", "domains"}, Flags: flags, Action: func(c *cli.Context) error { - logger.Get().SetVerbose(c.Bool("verbose")) - render.Render( checker.CheckDomains(c.StringSlice("domain")), 30, 14, diff --git a/bin/build-debs.sh b/bin/build-debs.sh index 2e4fb3f..ed69cbb 100755 --- a/bin/build-debs.sh +++ b/bin/build-debs.sh @@ -1,12 +1,9 @@ #!/bin/sh -set -e - VERSION="$1" for ARCH in amd64 arm64; do - fpm -t deb -p "build/expiration-check-$VERSION-$ARCH.deb" \ - --architecture $ARCH --version "$VERSION" \ - "build/expiration-check-linux-$ARCH=/usr/bin/expiration-check" \ - "build/expiration-check.1.gz=/usr/share/man/man1/expiration-check.1.gz" + fpm -t deb -p "build/expiration-check-$VERSION-$ARCH.deb" \ + --architecture $ARCH --version "$VERSION" \ + "build/expiration-check-linux-$ARCH"=/usr/bin/expiration-check done diff --git a/bin/create-manpage.sh b/bin/create-manpage.sh deleted file mode 100755 index 78608de..0000000 --- a/bin/create-manpage.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -e - -( - printf ".TH expiration-check 1 \"%s\" \"Manual of expiration-check\"\n" "$(date +'%Y-%m-%d')" - printf ".LO 1\n" - - ./build/expiration-check-linux-amd64 -h \ - | sed -E 's/^([A-Z ]+):$/.SH \1/g' \ - | sed -E 's/^ (.+) (.+)/.TP\n.B \1\n\2\n\n/g' \ - | sed -E 's/^ (\w+)/\1/g' \ - | sed -E 's/\\{0}-([a-z]+)/\\-\1/g' \ - | sed -E 's/-\\-/\\-\\-/g' \ - | sed -E 's/\s+$//g' -) | gzip -9 > ./build/expiration-check.1.gz diff --git a/bin/rename-builds.sh b/bin/rename-builds.sh index 75fbada..ef1276c 100755 --- a/bin/rename-builds.sh +++ b/bin/rename-builds.sh @@ -1,19 +1,17 @@ #!/bin/sh -set -e - VERSION="$1" for OS in linux windows darwin; do - if [ "$OS" = "windows" ]; then - EXTENSION=".exe" - else - EXTENSION= - fi + if [ "$OS" = "windows" ]; then + EXTENSION=".exe" + else + EXTENSION= + fi - for ARCH in amd64 arm64; do - mv -v \ - "build/expiration-check-${OS}-${ARCH}${EXTENSION}" \ - "build/expiration-check-${VERSION}-${OS}-${ARCH}${EXTENSION}" - done + for ARCH in amd64 arm64; do + mv -v \ + "build/expiration-check-${OS}-${ARCH}${EXTENSION}" \ + "build/expiration-check-${VERSION}-${OS}-${ARCH}${EXTENSION}" + done done diff --git a/checker/certificates.go b/checker/certificates.go new file mode 100644 index 0000000..d0721c6 --- /dev/null +++ b/checker/certificates.go @@ -0,0 +1,47 @@ +package checker + +import ( + "crypto/tls" + "fmt" + "math" + "strings" + "time" +) + +func FormatDomain(domain string) string { + elements := strings.Split(domain, ":") + + if len(elements) == 1 { + return fmt.Sprintf("%s:443", elements[0]) + } + + return domain +} + +func CheckCertificate(domain string) Domain { + now := time.Now() + conn, err := tls.Dial("tcp", FormatDomain(domain), nil) + + if err == nil { + date := conn.ConnectionState().PeerCertificates[0].NotAfter + daysLeft := date.Sub(now).Hours() / 24 + + return Domain{ + Name: domain, + DaysLeft: math.Floor(daysLeft), + Date: date.Format(time.DateTime), + } + } + + return Domain{Name: domain, Failed: true} +} + +func CheckCertificates(domains []string) []Domain { + values := []Domain{} + + for _, domain := range domains { + values = append(values, CheckCertificate(domain)) + } + + return values +} diff --git a/checker/domains.go b/checker/domains.go new file mode 100644 index 0000000..119d229 --- /dev/null +++ b/checker/domains.go @@ -0,0 +1,140 @@ +package checker + +import ( + "encoding/json" + "fmt" + "math" + "net/http" + "regexp" + "strings" + "time" + + "github.com/likexian/whois" +) + +type RdapResponseData struct { + Events []struct { + EventAction string `json:"eventAction"` + EventDate string `json:"eventDate"` + } `json:"events"` +} + +type RdapServices struct { + Services [][][]string `json:"services"` +} + +func GetRdapServices() map[string]string { + response, _ := http.Get("https://data.iana.org/rdap/dns.json") + values := make(map[string]string) + + defer response.Body.Close() + var data RdapServices + json.NewDecoder(response.Body).Decode(&data) + + for _, value := range data.Services { + for _, tld := range value[0] { + values[tld] = value[1][0] + } + } + + return values +} + +func ExtractTld(domain string) string { + elements := strings.Split(domain, ".") + + if len(elements) == 1 { + return elements[0] + } + + return strings.Join(elements[1:], ".") +} + +func RdapCheck(domain, service string) Domain { + url := fmt.Sprintf("%sdomain/%s?jscard=1", service, domain) + response, _ := http.Get(url) + now := time.Now() + + defer response.Body.Close() + var data RdapResponseData + json.NewDecoder(response.Body).Decode(&data) + + for _, event := range data.Events { + if event.EventAction == "expiration" { + date, _ := time.Parse(time.RFC3339, event.EventDate) + daysLeft := date.Sub(now).Hours() / 24 + + return Domain{ + Name: domain, + DaysLeft: math.Floor(daysLeft), + Date: date.Format(time.DateTime), + } + } + } + + return Domain{Name: domain, Failed: true} +} + +func WhoisCheck(domain string) Domain { + domainFailed := Domain{Name: domain, Failed: true} + result, err := whois.Whois(domain) + if err != nil { + return domainFailed + } + + now := time.Now() + formats := []string{ + "expiration date", + "expiry date", + "expires on", + "paid-till", + "renewal", + "expires", + "domain_datebilleduntil", + "expiration", + "registry expiry", + "registrar registration expiration", + } + + result = strings.ToLower(result) + + for _, format := range formats { + r, _ := regexp.Compile(fmt.Sprintf(`%s\s*:?\s*([^\s]+)`, format)) + + for i, match := range r.FindStringSubmatch(result) { + if i%2 == 1 { + date, err := time.Parse(time.RFC3339, strings.ToUpper(match)) + + if err == nil { + daysLeft := date.Sub(now).Hours() / 24 + + return Domain{ + Name: domain, + DaysLeft: math.Floor(daysLeft), + Date: date.Format(time.DateTime), + } + } + } + } + } + + return domainFailed +} + +func CheckDomains(domains []string) []Domain { + values := []Domain{} + services := GetRdapServices() + + for _, domain := range domains { + tld := ExtractTld(domain) + service := services[tld] + + if service != "" { + values = append(values, RdapCheck(domain, service)) + } else { + values = append(values, WhoisCheck(domain)) + } + } + + return values +} diff --git a/checker/struct.go b/checker/struct.go new file mode 100644 index 0000000..ffdcfde --- /dev/null +++ b/checker/struct.go @@ -0,0 +1,8 @@ +package checker + +type Domain struct { + Name string `json:"name"` + DaysLeft float64 `json:"days"` + Date string `json:"date"` + Failed bool `json:"failed"` +} diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index f436970..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,14 +0,0 @@ -package main - -import ( - "log" - "os" - - "gitnet.fr/deblan/expiration-check/internal/cmd" -) - -func main() { - if err := cmd.App().Run(os.Args); err != nil { - log.Fatal(err) - } -} diff --git a/main.go b/main.go new file mode 100644 index 0000000..b2bb647 --- /dev/null +++ b/main.go @@ -0,0 +1,12 @@ +package main + +import ( + "log" + "os" +) + +func main() { + if err := App().Run(os.Args); err != nil { + log.Fatal(err) + } +} diff --git a/pkg/checker/certificate.go b/pkg/checker/certificate.go deleted file mode 100644 index ccc1f67..0000000 --- a/pkg/checker/certificate.go +++ /dev/null @@ -1,38 +0,0 @@ -package checker - -import ( - "crypto/tls" - "math" - "time" - - "gitnet.fr/deblan/expiration-check/pkg/formatter" - "gitnet.fr/deblan/expiration-check/pkg/model" -) - -func CheckCertificate(domain string) *model.Result { - conn, err := tls.Dial("tcp", formatter.ToDomainAndHttpPort(domain), nil) - - if err == nil { - date := conn.ConnectionState().PeerCertificates[0].NotAfter - - result := model.NewResult( - domain, - math.Floor(date.Sub(time.Now()).Hours()/24), - date.Format(time.DateTime), - ) - - return result - } - - return model.NewResultFailed(domain) -} - -func CheckCertificates(domains []string) []*model.Result { - var values []*model.Result - - for _, domain := range domains { - values = append(values, CheckCertificate(domain)) - } - - return values -} diff --git a/pkg/checker/domain.go b/pkg/checker/domain.go deleted file mode 100644 index 3b078bf..0000000 --- a/pkg/checker/domain.go +++ /dev/null @@ -1,53 +0,0 @@ -package checker - -import ( - "math" - "time" - - "gitnet.fr/deblan/expiration-check/pkg/extractor" - "gitnet.fr/deblan/expiration-check/pkg/logger" - "gitnet.fr/deblan/expiration-check/pkg/model" - "gitnet.fr/deblan/expiration-check/pkg/rdap" - "gitnet.fr/deblan/expiration-check/pkg/whois" -) - -func CheckDomains(domains []string) []*model.Result { - var err error - var date *time.Time - var values []*model.Result - - services, err := rdap.GetRdapServices() - - if err != nil { - logger.Get().Logf("GetRdapServices failed: error=%v", err) - - return values - } - - for _, domain := range domains { - tld := extractor.ExtractTld(domain) - service, ok := services[tld] - - if ok { - date, err = rdap.GetExpiration(domain, service) - } else { - date, err = whois.GetExpiration(domain) - } - - if err != nil { - logger.Get().Logf("CheckDomain: domain=%s error=%s", domain, err) - - values = append(values, model.NewResultFailed(domain)) - } else { - daysLeft := math.Floor(date.Sub(time.Now()).Hours() / 24) - - values = append(values, model.NewResult( - domain, - daysLeft, - date.Format(time.DateTime), - )) - } - } - - return values -} diff --git a/pkg/extractor/tld.go b/pkg/extractor/tld.go deleted file mode 100644 index 4031eeb..0000000 --- a/pkg/extractor/tld.go +++ /dev/null @@ -1,13 +0,0 @@ -package extractor - -import "strings" - -func ExtractTld(domain string) string { - elements := strings.Split(domain, ".") - - if len(elements) == 1 { - return elements[0] - } - - return strings.Join(elements[1:], ".") -} diff --git a/pkg/formatter/http_domain.go b/pkg/formatter/http_domain.go deleted file mode 100644 index 42dd1e3..0000000 --- a/pkg/formatter/http_domain.go +++ /dev/null @@ -1,16 +0,0 @@ -package formatter - -import ( - "fmt" - "strings" -) - -func ToDomainAndHttpPort(domain string) string { - elements := strings.Split(domain, ":") - - if len(elements) == 1 { - return fmt.Sprintf("%s:443", elements[0]) - } - - return domain -} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go deleted file mode 100644 index 1461150..0000000 --- a/pkg/logger/logger.go +++ /dev/null @@ -1,27 +0,0 @@ -package logger - -import "log" - -type Logger struct { - Verbose bool -} - -var logger *Logger - -func Get() *Logger { - if logger == nil { - logger = new(Logger) - } - - return logger -} - -func (l *Logger) SetVerbose(value bool) { - l.Verbose = value -} - -func (l *Logger) Logf(format string, v ...any) { - if l.Verbose { - log.Printf(format, v...) - } -} diff --git a/pkg/model/result.go b/pkg/model/result.go deleted file mode 100644 index 4384f08..0000000 --- a/pkg/model/result.go +++ /dev/null @@ -1,24 +0,0 @@ -package model - -type Result struct { - Name string `json:"name"` - DaysLeft *float64 `json:"days"` - Date *string `json:"date"` - Failed bool `json:"failed"` -} - -func NewResult(name string, daysLeft float64, date string) *Result { - return &Result{ - Name: name, - DaysLeft: &daysLeft, - Date: &date, - Failed: false, - } -} - -func NewResultFailed(name string) *Result { - return &Result{ - Name: name, - Failed: true, - } -} diff --git a/pkg/rdap/rdap.go b/pkg/rdap/rdap.go deleted file mode 100644 index 1335d61..0000000 --- a/pkg/rdap/rdap.go +++ /dev/null @@ -1,75 +0,0 @@ -package rdap - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "time" -) - -type RdapResponseData struct { - Events []struct { - EventAction string `json:"eventAction"` - EventDate string `json:"eventDate"` - } `json:"events"` -} - -type RdapServices struct { - Services [][][]string `json:"services"` -} - -func GetRdapServices() (map[string]string, error) { - response, err := http.Get("https://data.iana.org/rdap/dns.json") - - if err != nil { - return nil, err - } - - values := make(map[string]string) - - defer response.Body.Close() - var data RdapServices - - json.NewDecoder(response.Body).Decode(&data) - - for _, value := range data.Services { - for _, tld := range value[0] { - values[tld] = value[1][0] - } - } - - return values, nil -} - -func GetExpiration(domain, service string) (*time.Time, error) { - url := fmt.Sprintf("%sdomain/%s?jscard=1", service, domain) - response, err := http.Get(url) - - if err != nil { - return nil, err - } - - defer response.Body.Close() - var data RdapResponseData - - json.NewDecoder(response.Body).Decode(&data) - - actions := []string{"expiration", "Record expires"} - - for _, event := range data.Events { - for _, action := range actions { - if action == event.EventAction { - date, err := time.Parse(time.RFC3339, event.EventDate) - - if err != nil { - return nil, err - } - - return &date, nil - } - } - } - - return nil, errors.New("Expiration date not found") -} diff --git a/pkg/whois/whois.go b/pkg/whois/whois.go deleted file mode 100644 index afc0617..0000000 --- a/pkg/whois/whois.go +++ /dev/null @@ -1,47 +0,0 @@ -package whois - -import ( - "errors" - "fmt" - "regexp" - "strings" - "time" - - w "github.com/likexian/whois" -) - -func GetExpiration(domain string) (*time.Time, error) { - result, err := w.Whois(domain) - - if err != nil { - return nil, err - } - - result = strings.ToLower(result) - formats := []string{ - "expiration date", - "expiry date", - "expires on", - "paid-till", - "renewal", - "expires", - "domain_datebilleduntil", - "expiration", - "registry expiry", - "registrar registration expiration", - } - - for _, format := range formats { - r, _ := regexp.Compile(fmt.Sprintf(`%s\s*:?\s*([^\s]+)`, format)) - - for i, match := range r.FindStringSubmatch(result) { - if i%2 == 1 { - if date, err := time.Parse(time.RFC3339, strings.ToUpper(match)); err == nil { - return &date, nil - } - } - } - } - - return nil, errors.New("Expiration date not found") -} diff --git a/pkg/render/render.go b/render/render.go similarity index 67% rename from pkg/render/render.go rename to render/render.go index fded2b0..d3cd037 100644 --- a/pkg/render/render.go +++ b/render/render.go @@ -8,7 +8,7 @@ import ( "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" - "gitnet.fr/deblan/expiration-check/pkg/model" + "gitnet.fr/deblan/expiration-check/checker" ) func RenderColor(value string, c text.Colors, format string) string { @@ -19,7 +19,7 @@ func RenderColor(value string, c text.Colors, format string) string { return c.Sprint(value) } -func Render(values []*model.Result, warning, danger float64, format string) { +func Render(values []checker.Domain, warning, danger float64, format string) { sort.SliceStable(values, func(i, j int) bool { if values[i].Failed && values[j].Failed { return values[i].Name < values[j].Name @@ -33,7 +33,7 @@ func Render(values []*model.Result, warning, danger float64, format string) { return true } - return *values[i].DaysLeft < *values[j].DaysLeft + return values[i].DaysLeft < values[j].DaysLeft }) if format == "json" { @@ -64,15 +64,15 @@ func Render(values []*model.Result, warning, danger float64, format string) { var days string var date string - if *value.DaysLeft <= danger { - days = RenderColor(fmt.Sprintf("%.0f", *value.DaysLeft), text.Colors{0, text.FgRed}, format) - date = RenderColor(*value.Date, text.Colors{0, text.FgRed}, format) - } else if *value.DaysLeft <= warning { - days = RenderColor(fmt.Sprintf("%.0f", *value.DaysLeft), text.Colors{0, text.FgYellow}, format) - date = RenderColor(*value.Date, text.Colors{0, text.FgYellow}, format) + if value.DaysLeft <= danger { + days = failed + date = failed + } else if value.DaysLeft <= warning { + days = RenderColor(fmt.Sprintf("%.0f", value.DaysLeft), text.Colors{0, text.FgYellow}, format) + date = RenderColor(value.Date, text.Colors{0, text.FgYellow}, format) } else { - days = RenderColor(fmt.Sprintf("%.0f", *value.DaysLeft), text.Colors{0, text.FgGreen}, format) - date = RenderColor(*value.Date, text.Colors{0, text.FgGreen}, format) + days = RenderColor(fmt.Sprintf("%.0f", value.DaysLeft), text.Colors{0, text.FgGreen}, format) + date = RenderColor(value.Date, text.Colors{0, text.FgGreen}, format) } t.AppendRow(table.Row{