Compare commits

..

No commits in common. '43d4560fbbf7c5e325dbf00a52f0352c44f0dd24' and 'd1f67fbdb4eae36718d995f2442b18d221cd62cf' have entirely different histories.

@ -1,2 +0,0 @@
/.idea
*.sqlite3*

@ -1,54 +1,58 @@
module prayertimes module prayertimes
go 1.25 go 1.20
require ( require (
github.com/PuerkitoBio/goquery v1.8.1 github.com/PuerkitoBio/goquery v1.8.1
github.com/doug-martin/goqu/v9 v9.18.0 github.com/imroc/req/v3 v3.32.3
github.com/gofiber/fiber/v2 v2.42.0 github.com/stretchr/testify v1.8.2
github.com/imroc/req/v3 v3.57.0
github.com/rs/zerolog v1.29.0
github.com/samber/lo v1.37.0
github.com/stretchr/testify v1.11.1
modernc.org/sqlite v1.21.0
) )
require ( require (
github.com/andybalholm/brotli v1.2.0 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/doug-martin/goqu/v9 v9.18.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/google/go-querystring v1.2.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gofiber/fiber/v2 v2.42.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/icholy/digest v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/compress v1.16.0 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/onsi/ginkgo/v2 v2.9.0 // indirect
github.com/philhofer/fwd v1.1.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.57.1 // indirect github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
github.com/refraction-networking/utls v1.8.2 // indirect github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
github.com/quic-go/quic-go v0.33.0 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/rs/zerolog v1.29.0 // indirect
github.com/samber/lo v1.37.0 // indirect
github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/tinylib/msgp v1.1.8 // indirect github.com/tinylib/msgp v1.1.8 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.44.0 // indirect github.com/valyala/fasthttp v1.44.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/crypto v0.48.0 // indirect golang.org/x/crypto v0.6.0 // indirect
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017 // indirect golang.org/x/exp v0.0.0-20230304125523-9ff063c70017 // indirect
golang.org/x/mod v0.32.0 // indirect golang.org/x/mod v0.9.0 // indirect
golang.org/x/net v0.50.0 // indirect golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.6.0 // indirect
golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.8.0 // indirect
golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.6.0 // indirect
golang.org/x/tools v0.41.0 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect modernc.org/cc/v3 v3.40.0 // indirect
@ -57,6 +61,7 @@ require (
modernc.org/mathutil v1.5.0 // indirect modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect modernc.org/memory v1.5.0 // indirect
modernc.org/opt v0.1.3 // indirect modernc.org/opt v0.1.3 // indirect
modernc.org/sqlite v1.21.0 // indirect
modernc.org/strutil v1.1.3 // indirect modernc.org/strutil v1.1.3 // indirect
modernc.org/token v1.1.0 // indirect modernc.org/token v1.1.0 // indirect
) )

131
go.sum

@ -1,10 +1,9 @@
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@ -17,34 +16,41 @@ github.com/doug-martin/goqu/v9 v9.18.0 h1:/6bcuEtAe6nsSMVK/M+fOiXUNfyFF3yYtE07DB
github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ= github.com/doug-martin/goqu/v9 v9.18.0/go.mod h1:nf0Wc2/hV3gYK9LiyqIrzBEVGlI8qW3GuDCEobC4wBQ=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8= github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8=
github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc= github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU=
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso= github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 h1:CqYfpuYIjnlNxM3msdyPRKabhXZWbKjf3Q8BWROFBso=
github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/icholy/digest v1.1.0 h1:HfGg9Irj7i+IX1o1QAmPfIBNu/Q5A5Tu3n/MED9k9H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/icholy/digest v1.1.0/go.mod h1:QNrsSGQ5v7v9cReDI0+eyjsXGUoRSUZQHeQ5C4XLa0Y= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/imroc/req/v3 v3.57.0 h1:LMTUjNRUybUkTPn8oJDq8Kg3JRBOBTcnDhKu7mzupKI= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/imroc/req/v3 v3.57.0/go.mod h1:JL62ey1nvSLq81HORNcosvlf7SxZStONNqOprg0Pz00= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/imroc/req/v3 v3.32.3 h1:FX3xCN0iWn1wJKHN0EB8l7EjXDTE4j/C8m7zeAb2UH0=
github.com/imroc/req/v3 v3.32.3/go.mod h1:cZ+7C3L/AYOr4tLGG16hZF90F1WzAdAdzt1xFSlizXY=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -56,28 +62,32 @@ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/onsi/ginkgo/v2 v2.9.0 h1:Tugw2BKlNHTMfG+CheOITkYvk4LAh6MFOvikhGVnhE8=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk=
github.com/onsi/gomega v1.27.1 h1:rfztXRbg6nv/5f+Raen9RcGoSecHIFgBBLQK3Wdj754=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo= github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
@ -89,11 +99,14 @@ github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJv
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
@ -103,12 +116,9 @@ github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR
github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -116,38 +126,40 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017 h1:3Ea9SZLCB0aRIhSEjM+iaGIlzzeDJdpi579El/YIhEE= golang.org/x/exp v0.0.0-20230304125523-9ff063c70017 h1:3Ea9SZLCB0aRIhSEjM+iaGIlzzeDJdpi579El/YIhEE=
golang.org/x/exp v0.0.0-20230304125523-9ff063c70017/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230304125523-9ff063c70017/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -157,8 +169,8 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
@ -169,38 +181,35 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY= modernc.org/libc v1.22.3 h1:D/g6O5ftAfavceqlLOFwaZuA5KYafKwmr30A6iSqoyY=
modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw= modernc.org/libc v1.22.3/go.mod h1:MQrloYP209xa2zHome2a8HLiLm6k0UT8CoHpV74tOFw=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
@ -213,9 +222,5 @@ modernc.org/sqlite v1.21.0 h1:4aP4MdUf15i3R3M2mx6Q90WHKz3nZLoz96zlB6tNdow=
modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI= modernc.org/sqlite v1.21.0/go.mod h1:XwQ0wZPIh1iKb5mkvCJ3szzbhk+tykC8ZWqTRTgYRwI=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.1 h1:mOQwiEK4p7HruMZcwKTZPw/aqtGM4aY00uzWhlKKYws=
modernc.org/tcl v1.15.1/go.mod h1:aEjeGJX2gz1oWKOLDVZ2tnEWLUrIn8H+GFu+akoDhqs=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.0 h1:xkDw/KepgEjeizO2sNco+hqYkU12taxQFqPEmgm1GWE=
modernc.org/z v1.7.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ=

@ -9,12 +9,10 @@ import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors" "github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/favicon" "github.com/gofiber/fiber/v2/middleware/favicon"
"github.com/gofiber/fiber/v2/middleware/filesystem"
"github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/fiber/v2/middleware/logger"
recover "github.com/gofiber/fiber/v2/middleware/recover" recover "github.com/gofiber/fiber/v2/middleware/recover"
"prayertimes/pkg/prayer" "prayertimes/pkg/prayer"
"prayertimes/templates"
) )
type httpError struct { type httpError struct {
@ -22,8 +20,7 @@ type httpError struct {
} }
type Services struct { type Services struct {
TimesProvider prayer.TimesProvider PrayerTimesProvider prayer.TimesProvider
LocationTimesProvider prayer.LocationTimesProvider
} }
func New(services Services) *fiber.App { func New(services Services) *fiber.App {
@ -45,48 +42,26 @@ func New(services Services) *fiber.App {
app.Use( app.Use(
favicon.New(), favicon.New(),
recover.New(recover.Config{EnableStackTrace: true}), recover.New(recover.Config{
EnableStackTrace: true,
}),
logger.New(), logger.New(),
cors.New(), cors.New(),
) )
app.Get("/healthz", func(ctx *fiber.Ctx) error { cacheHeader := fmt.Sprintf("max-age=%0f", (time.Hour * 24).Seconds())
return ctx.SendString("OK") app.Get("/diyanet/prayertimes", func(ctx *fiber.Ctx) error {
})
app.Get("/api/v1/diyanet/prayertimes", func(ctx *fiber.Ctx) error {
var times []prayer.Times
var err error
locationID := ctx.Query("location_id") locationID := ctx.Query("location_id")
var loc struct {
Latitude float64 `query:"latitude"`
Longitude float64 `query:"longitude"`
}
if locationID != "" {
times, err = services.TimesProvider.Get(ctx.Context(), locationID)
} else if err := ctx.QueryParser(&loc); err == nil {
times, err = services.LocationTimesProvider.GetByCoords(ctx.Context(), prayer.Coordinates{
Latitude: loc.Latitude,
Longitude: loc.Longitude,
})
} else {
return fmt.Errorf("%w: missing location id or coordinates", fiber.ErrBadRequest)
}
times, err := services.PrayerTimesProvider.Get(ctx.Context(), locationID)
if err != nil { if err != nil {
return err return err
} }
ctx.Response().Header.Add(fiber.HeaderCacheControl, "max-age=86400") ctx.Response().Header.Add(fiber.HeaderCacheControl, cacheHeader)
return ctx.JSON(times) return ctx.JSON(times)
}) })
app.Use("/", filesystem.New(filesystem.Config{
Root: templates.HttpFS(),
}))
return app return app
} }

@ -1,4 +1,4 @@
package net package scrapeutils
import ( import (
"context" "context"

@ -9,10 +9,9 @@ import (
"prayertimes/internal/api" "prayertimes/internal/api"
"prayertimes/internal/database" "prayertimes/internal/database"
"prayertimes/internal/net" "prayertimes/internal/scrapeutils"
"prayertimes/pkg/dbtimesprovider" "prayertimes/pkg/dbtimesprovider"
"prayertimes/pkg/diyanet" "prayertimes/pkg/diyanet"
"prayertimes/pkg/diyanetapi"
) )
func main() { func main() {
@ -32,21 +31,22 @@ func main() {
} }
func newServices() (api.Services, func(), error) { func newServices() (api.Services, func(), error) {
diyanetAPIProvider := diyanetapi.New(net.ReqClient) diyanetProvider := diyanet.Diyanet{
diyanetProvider := diyanet.New(net.GetParsed) FetcherFunc: scrapeutils.GetParsed,
}
db, err := database.NewSqliteDB(getDefaultEnv("DATABASE_URL", "app.sqlite3")) db, err := database.NewSqliteDB(getDefaultEnv("DATABASE_URL", "app.sqlite3"))
if err != nil { if err != nil {
return api.Services{}, nil, fmt.Errorf("failed to init db: %w", err) return api.Services{}, nil, fmt.Errorf("failed to init db: %w", err)
} }
dbProvider := dbtimesprovider.New(db, diyanetProvider)
if err := dbtimesprovider.Migrate(db.Db.(*sql.DB)); err != nil { if err := dbtimesprovider.Migrate(db.Db.(*sql.DB)); err != nil {
return api.Services{}, nil, fmt.Errorf("failed to migrate database: %w", err) return api.Services{}, nil, fmt.Errorf("failed to migrate database: %w", err)
} }
return api.Services{ return api.Services{
TimesProvider: dbtimesprovider.New(db, diyanetProvider), PrayerTimesProvider: dbProvider,
LocationTimesProvider: diyanetAPIProvider,
}, func() { }, func() {
defer db.Db.(*sql.DB).Close() defer db.Db.(*sql.DB).Close()
}, nil }, nil

@ -42,11 +42,10 @@ func (p Provider) Name() string {
func (p Provider) Get(ctx context.Context, location string) ([]prayer.Times, error) { func (p Provider) Get(ctx context.Context, location string) ([]prayer.Times, error) {
times, err := p.loadTimes(ctx, location) times, err := p.loadTimes(ctx, location)
if err != nil { if err == nil && len(times) != 0 {
return nil, fmt.Errorf("failed to load prayer times from db: %w", err)
}
if len(times) > 0 {
return times, nil return times, nil
} else if err != nil {
return nil, fmt.Errorf("failed to load prayer times from db: %w", err)
} }
times, err = p.provider.Get(ctx, location) times, err = p.provider.Get(ctx, location)
@ -54,7 +53,7 @@ func (p Provider) Get(ctx context.Context, location string) ([]prayer.Times, err
return nil, fmt.Errorf("failed to get prayer times: %w", err) return nil, fmt.Errorf("failed to get prayer times: %w", err)
} }
if len(times) > 0 { if len(times) > 1 {
if err := p.saveTimes(ctx, location, times); err != nil { if err := p.saveTimes(ctx, location, times); err != nil {
return nil, fmt.Errorf("failed to save times to db: %w", err) return nil, fmt.Errorf("failed to save times to db: %w", err)
} }
@ -111,15 +110,15 @@ func Migrate(con *sql.DB) error {
} }
type prayerTimesRow struct { type prayerTimesRow struct {
ProviderID int64 `db:"provider_id"` ProviderID int64 `db:"provider_id"`
LocationID string `db:"location_id"` LocationID string `db:"location_id"`
Date string `db:"date"` Date time.Time `db:"date"`
Fajr string `db:"fajr"` Fajr string `db:"fajr"`
Sunrise string `db:"sunrise"` Sunrise string `db:"sunrise"`
Dhuhr string `db:"dhuhr"` Dhuhr string `db:"dhuhr"`
Asr string `db:"asr"` Asr string `db:"asr"`
Maghrib string `db:"maghrib"` Maghrib string `db:"maghrib"`
Isha string `db:"isha"` Isha string `db:"isha"`
} }
func (r prayerTimesRow) toDomain() prayer.Times { func (r prayerTimesRow) toDomain() prayer.Times {
@ -166,16 +165,13 @@ func (p Provider) saveTimes(ctx context.Context, locationID string, times []pray
} }
func (p Provider) loadTimes(ctx context.Context, locationID string) ([]prayer.Times, error) { func (p Provider) loadTimes(ctx context.Context, locationID string) ([]prayer.Times, error) {
now := p.clockFunc()
today := now.UTC().Truncate(time.Hour * 24)
q := p.db. q := p.db.
From(goqu.T("prayer_times").As("pt")). From(goqu.T("prayer_times").As("pt")).
Join(goqu.T("providers").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("pt.provider_id")))). Join(goqu.T("providers").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("pt.provider_id")))).
Where( Where(
goqu.I("p.name").Eq(p.provider.Name()), goqu.I("p.name").Eq(p.provider.Name()),
goqu.I("pt.location_id").Eq(locationID), goqu.I("pt.location_id").Eq(locationID),
goqu.I("pt.date").Gte(today.Format(time.DateOnly)), goqu.I("pt.date").Gte(p.clockFunc()),
). ).
Limit(100) Limit(100)

@ -54,8 +54,8 @@ func TestProvider_Get(t *testing.T) {
name: "provider succeeds, empty db", name: "provider succeeds, empty db",
provider: mockProvider(func() ([]prayer.Times, error) { provider: mockProvider(func() ([]prayer.Times, error) {
return []prayer.Times{ return []prayer.Times{
{Date: "2023-03-04"}, {Date: time.Date(2023, 3, 4, 0, 0, 0, 0, time.UTC)},
{Date: "2023-03-05"}, {Date: time.Date(2023, 3, 5, 0, 0, 0, 0, time.UTC)},
}, nil }, nil
}), }),
clock: then, clock: then,
@ -82,13 +82,10 @@ func TestProvider_Get(t *testing.T) {
{ {
name: "provider fails, populated db", name: "provider fails, populated db",
setupDB: func(t *testing.T, db *goqu.Database) { setupDB: func(t *testing.T, db *goqu.Database) {
_, err := db.Insert("providers").Rows(goqu.Record{"id": 1, "name": "mock"}).Executor().Exec() _, err := db.Insert("prayer_times").Rows(
require.NoError(t, err) prayerTimesRow{ProviderID: 1, LocationID: "1", Date: time.Date(2023, 3, 4, 0, 0, 0, 0, time.UTC), Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
prayerTimesRow{ProviderID: 1, LocationID: "1", Date: time.Date(2023, 3, 5, 0, 0, 0, 0, time.UTC), Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
_, err = db.Insert("prayer_times").Rows( prayerTimesRow{ProviderID: 1, LocationID: "1", Date: time.Date(2023, 3, 6, 0, 0, 0, 0, time.UTC), Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
prayerTimesRow{ProviderID: 1, LocationID: "1", Date: "2023-03-04", Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
prayerTimesRow{ProviderID: 1, LocationID: "1", Date: "2023-03-05", Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
prayerTimesRow{ProviderID: 1, LocationID: "1", Date: "2023-03-06", Fajr: "01:00", Sunrise: "02:00", Dhuhr: "03:00", Asr: "04:00", Maghrib: "05:00", Isha: "06:00"},
).Executor().Exec() ).Executor().Exec()
require.NoError(t, err) require.NoError(t, err)
}, },

@ -15,14 +15,10 @@ type Fetcher interface {
FetchParsed(ctx context.Context, url string) (*goquery.Document, error) FetchParsed(ctx context.Context, url string) (*goquery.Document, error)
} }
type Provider struct { type Diyanet struct {
FetcherFunc func(ctx context.Context, url string) (*goquery.Document, error) FetcherFunc func(ctx context.Context, url string) (*goquery.Document, error)
} }
func New(fetcherFunc func(ctx context.Context, url string) (*goquery.Document, error)) *Provider {
return &Provider{FetcherFunc: fetcherFunc}
}
var reNumeric = regexp.MustCompile(`\d+`) var reNumeric = regexp.MustCompile(`\d+`)
func validateLocation(location string) error { func validateLocation(location string) error {
@ -32,7 +28,7 @@ func validateLocation(location string) error {
return nil return nil
} }
func (d Provider) Get(ctx context.Context, location string) ([]prayer.Times, error) { func (d Diyanet) Get(ctx context.Context, location string) ([]prayer.Times, error) {
if err := validateLocation(location); err != nil { if err := validateLocation(location); err != nil {
return nil, fmt.Errorf("%w: %v", prayer.ErrInvalidLocation, err) return nil, fmt.Errorf("%w: %v", prayer.ErrInvalidLocation, err)
} }
@ -53,13 +49,13 @@ func (d Provider) Get(ctx context.Context, location string) ([]prayer.Times, err
} }
row := prayer.Times{ row := prayer.Times{
Date: parsedDate.Format(time.DateOnly), Date: parsedDate,
Fajr: el.Find("td:nth-of-type(3)").Text(), Fajr: el.Find("td:nth-of-type(2)").Text(),
Sunrise: el.Find("td:nth-of-type(4)").Text(), Sunrise: el.Find("td:nth-of-type(3)").Text(),
Dhuhr: el.Find("td:nth-of-type(5)").Text(), Dhuhr: el.Find("td:nth-of-type(4)").Text(),
Asr: el.Find("td:nth-of-type(6)").Text(), Asr: el.Find("td:nth-of-type(5)").Text(),
Maghrib: el.Find("td:nth-of-type(7)").Text(), Maghrib: el.Find("td:nth-of-type(6)").Text(),
Isha: el.Find("td:nth-of-type(8)").Text(), Isha: el.Find("td:nth-of-type(7)").Text(),
} }
times = append(times, row) times = append(times, row)
}) })
@ -67,6 +63,6 @@ func (d Provider) Get(ctx context.Context, location string) ([]prayer.Times, err
return times, err return times, err
} }
func (d Provider) Name() string { func (d Diyanet) Name() string {
return "diyanetweb" return "diyanet"
} }

@ -4,11 +4,12 @@ import (
"context" "context"
"strings" "strings"
"testing" "testing"
"time"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"prayertimes/internal/net" "prayertimes/internal/scrapeutils"
"prayertimes/pkg/prayer" "prayertimes/pkg/prayer"
) )
@ -26,7 +27,6 @@ const mockHtml = `
<thead> <thead>
<tr> <tr>
<th>Gregorian Calendar Date</th> <th>Gregorian Calendar Date</th>
<th>Hijri Date</th>
<th>Fajr</th> <th>Fajr</th>
<th>Sun</th> <th>Sun</th>
<th>Dhuhr</th> <th>Dhuhr</th>
@ -38,7 +38,6 @@ const mockHtml = `
<tbody> <tbody>
<tr> <tr>
<td>04.03.2023</td> <td>04.03.2023</td>
<td>...</td>
<td>05:48</td> <td>05:48</td>
<td>07:11</td> <td>07:11</td>
<td>13:05</td> <td>13:05</td>
@ -48,7 +47,6 @@ const mockHtml = `
</tr> </tr>
<tr> <tr>
<td>05.03.2023</td> <td>05.03.2023</td>
<td>...</td>
<td>05:46</td> <td>05:46</td>
<td>07:09</td> <td>07:09</td>
<td>13:04</td> <td>13:04</td>
@ -64,13 +62,13 @@ const mockHtml = `
func TestDiyanet_Get(t *testing.T) { func TestDiyanet_Get(t *testing.T) {
t.Run("validates location", func(t *testing.T) { t.Run("validates location", func(t *testing.T) {
d := Provider{} d := Diyanet{}
_, err := d.Get(context.Background(), " not numeric ") _, err := d.Get(context.Background(), " not numeric ")
assert.ErrorIs(t, err, prayer.ErrInvalidLocation) assert.ErrorIs(t, err, prayer.ErrInvalidLocation)
}) })
t.Run("extracts prayer times", func(t *testing.T) { t.Run("extracts prayer times", func(t *testing.T) {
d := Provider{ d := Diyanet{
FetcherFunc: mockFetcher(mockHtml).Fetch, FetcherFunc: mockFetcher(mockHtml).Fetch,
} }
actual, err := d.Get(context.Background(), "1234") actual, err := d.Get(context.Background(), "1234")
@ -78,7 +76,7 @@ func TestDiyanet_Get(t *testing.T) {
expected := []prayer.Times{ expected := []prayer.Times{
{ {
Date: "2023-03-04", Date: time.Date(2023, time.March, 4, 0, 0, 0, 0, time.UTC),
Fajr: "05:48", Fajr: "05:48",
Sunrise: "07:11", Sunrise: "07:11",
Dhuhr: "13:05", Dhuhr: "13:05",
@ -87,7 +85,7 @@ func TestDiyanet_Get(t *testing.T) {
Isha: "20:06", Isha: "20:06",
}, },
{ {
Date: "2023-03-05", Date: time.Date(2023, time.March, 5, 0, 0, 0, 0, time.UTC),
Fajr: "05:46", Fajr: "05:46",
Sunrise: "07:09", Sunrise: "07:09",
Dhuhr: "13:04", Dhuhr: "13:04",
@ -104,22 +102,12 @@ func TestDiyanet_Get(t *testing.T) {
t.Skip() t.Skip()
} }
d := Provider{ d := Diyanet{
FetcherFunc: net.GetParsed, FetcherFunc: scrapeutils.GetParsed,
} }
times, err := d.Get(context.Background(), "11104") times, err := d.Get(context.Background(), "9205")
if err != nil { assert.NoError(t, err)
t.Skipf("skipping live endpoint test due to upstream/network error: %v", err) assert.Greater(t, len(times), 1)
}
if len(times) == 0 {
t.Skip("skipping live endpoint test because upstream returned no times")
}
assert.Greater(t, len(times), 0)
assert.NotZero(t, times[0].Date) assert.NotZero(t, times[0].Date)
for _, it := range times {
t.Logf("%+v", it)
}
}) })
} }

@ -1,103 +0,0 @@
package diyanetapi
import (
"context"
"fmt"
"time"
"github.com/imroc/req/v3"
"prayertimes/pkg/prayer"
)
type Provider struct {
http *req.Client
}
func New(c *req.Client) Provider {
return Provider{
http: c.Clone().SetCommonBasicAuth("diyanet", "Q6Y3vYt5F3x2txPaaMF3uPgbK99EJhpM"),
}
}
func (d Provider) GetByCoords(ctx context.Context, coords prayer.Coordinates) ([]prayer.Times, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParams(map[string]string{
"latitude": fmt.Sprintf("%f", coords.Latitude),
"longitude": fmt.Sprintf("%f", coords.Longitude),
}).
Get("https://namazvakti.diyanet.gov.tr/api/NamazVakti/Aylik")
if err != nil {
return nil, fmt.Errorf("failed to get prayer times by coords: %w", err)
}
return d.parseResponse(res)
}
func (d Provider) Get(ctx context.Context, locationID string) ([]prayer.Times, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParam("ilceId", locationID).
Get("https://namazvakti.diyanet.gov.tr/api/NamazVakti/Aylik")
if err != nil {
return nil, fmt.Errorf("failed to get prayer times by location id: %w", err)
}
return d.parseResponse(res)
}
func (d Provider) parseResponse(res *req.Response) ([]prayer.Times, error) {
var response struct {
Success bool `json:"success"`
ResultObject struct {
Location struct {
ID int `json:"konum_Id"`
Timezone string `json:"timezone"`
} `json:"konum"`
PrayerTimes []struct {
Date time.Time `json:"miladi_tarih_uzun_Iso8601"`
Fajr time.Time `json:"imsak"`
Sunrise time.Time `json:"gunes"`
Dhuhr time.Time `json:"ogle"`
Asr time.Time `json:"ikindi"`
Maghrib time.Time `json:"aksam"`
Isha time.Time `json:"yatsi"`
} `json:"namazVakti"`
} `json:"resultObject"`
}
if err := res.Unmarshal(&response); err != nil {
return nil, fmt.Errorf("failed to unmarshal as json: %w", err)
}
if !response.Success {
return nil, fmt.Errorf("received error: %s", res.String())
}
if len(response.ResultObject.PrayerTimes) == 0 {
return nil, nil
}
var times []prayer.Times
today := time.Now().UTC().Truncate(time.Hour * 24)
for _, pt := range response.ResultObject.PrayerTimes {
then := prayer.Date(pt.Date).Time()
if then.Before(today) {
continue
}
times = append(times, prayer.Times{
Date: pt.Date.Format(time.DateOnly),
Fajr: pt.Fajr.Format(time.TimeOnly),
Sunrise: pt.Sunrise.Format(time.TimeOnly),
Dhuhr: pt.Dhuhr.Format(time.TimeOnly),
Asr: pt.Asr.Format(time.TimeOnly),
Maghrib: pt.Maghrib.Format(time.TimeOnly),
Isha: pt.Isha.Format(time.TimeOnly),
})
}
return times, nil
}
func (d Provider) Name() string {
return "diyanetapi"
}

@ -1,37 +0,0 @@
package diyanetapi
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"prayertimes/internal/net"
"prayertimes/pkg/prayer"
)
func TestDiyanetAPI_GetByCoords(t *testing.T) {
p := New(net.ReqClient)
t.Run("by coords", func(t *testing.T) {
t.Parallel()
times, err := p.GetByCoords(context.Background(), prayer.Coordinates{
Latitude: 52.5100846,
Longitude: 13.4518284,
})
assert.NoError(t, err)
assert.NotEmpty(t, times)
t.Logf("%#+v", times[0])
})
t.Run("by id", func(t *testing.T) {
t.Parallel()
times, err := p.Get(context.Background(), "11104")
assert.NoError(t, err)
assert.NotEmpty(t, times)
for _, time := range times {
t.Log(time)
}
})
}

@ -2,8 +2,6 @@ package prayer
import ( import (
"context" "context"
"database/sql/driver"
"encoding/json"
"errors" "errors"
"time" "time"
) )
@ -15,71 +13,12 @@ type TimesProvider interface {
Name() string Name() string
} }
type Coordinates struct {
Latitude float64
Longitude float64
}
type LocationTimesProvider interface {
GetByCoords(ctx context.Context, coords Coordinates) ([]Times, error)
Name() string
}
type Date time.Time
func (d Date) String() string {
return time.Time(d).Format(time.DateOnly)
}
func (d *Date) Scan(src any) error {
switch v := src.(type) {
case []byte:
return json.Unmarshal(v, d)
case string:
return json.Unmarshal([]byte(v), d)
case time.Time:
*d = Date(v)
return nil
}
return nil
}
func (d *Date) Value() (driver.Value, error) {
return json.Marshal(d)
}
func (d *Date) UnmarshalJSON(bytes []byte) error {
var t time.Time
if err := json.Unmarshal(bytes, &t); err != nil {
return err
}
_, offset := t.Zone()
t = t.Add(time.Duration(offset * int(time.Second)))
t = t.UTC()
*d = Date(t)
return nil
}
func (d Date) MarshalJSON() ([]byte, error) {
t := time.Time(d)
return json.Marshal(t.Format(time.DateOnly))
}
func (d Date) Time() time.Time {
t := time.Time(d)
_, offset := t.Zone()
t = t.Add(time.Duration(offset * int(time.Second)))
t = t.UTC()
return t
}
type Times struct { type Times struct {
Date string `json:"date"` Date time.Time `json:"date"`
Fajr string `json:"fajr"` Fajr string `json:"fajr"`
Sunrise string `json:"sunrise"` Sunrise string `json:"sunrise"`
Dhuhr string `json:"dhuhr"` Dhuhr string `json:"dhuhr"`
Asr string `json:"asr"` Asr string `json:"asr"`
Maghrib string `json:"maghrib"` Maghrib string `json:"maghrib"`
Isha string `json:"isha"` Isha string `json:"isha"`
} }

@ -1 +0,0 @@
(()=>{function d(t){let e=()=>{let n,l=localStorage;return t.interceptor((r,i,s,a,m)=>{let o=n||`_x_${a}`,u=f(o,l)?g(o,l):r;return s(u),t.effect(()=>{let c=i();p(o,c,l),s(c)}),u},r=>{r.as=i=>(n=i,r),r.using=i=>(l=i,r)})};Object.defineProperty(t,"$persist",{get:()=>e()}),t.magic("persist",e),t.persist=(n,{get:l,set:r},i=localStorage)=>{let s=f(n,i)?g(n,i):l();r(s),t.effect(()=>{let a=l();p(n,a,i),r(a)})}}function f(t,e){return e.getItem(t)!==null}function g(t,e){return JSON.parse(e.getItem(t,e))}function p(t,e,n){n.setItem(t,JSON.stringify(e))}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(d)});})();

File diff suppressed because one or more lines are too long

@ -1,98 +0,0 @@
<!doctype html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport'
content='width=device-width, initial-scale=1.0'>
<title>Document</title>
<script defer
src='main.js'></script>
<script defer
src='alpine.persist@3.x.x.min.js'></script>
<script defer
src='alpine@3.11.1.min.js'></script>
<link rel='preconnect'
href='https://fonts.googleapis.com'>
<link rel='preconnect'
href='https://fonts.gstatic.com'
crossorigin>
<link href='https://fonts.googleapis.com/css2?family=Inter:wght@400..900&family=Noto+Sans+Arabic:wght@400..700&display=swap'
rel='stylesheet'>
<link rel='stylesheet'
href='style.css'>
</head>
<body>
<div x-data='app()'
@hashchange.window='onHash'>
<template x-if='debug'>
<div>
<label>clock: <input type='range'
step='1'
min='0'
max='1439'
x-model='userMinutes'> <span x-text='userTime'></span></label>
</div>
</template>
<input type='text'
x-model='locationId'>
<template x-if='todayTimes'>
<div class='current-salath text--center'>
<p>
<time :datetime='userNow'
x-text='formatDate(userNow)'></time>
</p>
<p>
<span class='salath-name'
lang='tr'
x-text='todayTimes.currentSalath.name("tr")'></span>
<span class='salath-name'
lang='de'
x-text='todayTimes.currentSalath.name("de")'></span>
<span class='salath-name text--arabic'
lang='ar'
x-text='todayTimes.currentSalath.name("ar")'></span>
</p>
<p>
<time :datetime='todayTimes.nextSalath.startsAt'
class='text--numeric text--time'
x-text='todayTimes.nextSalath.untilHuman'></time>
</p>
</div>
</template>
<template x-if='todayTimes'>
<div class='table-wrapper'>
<table class='salath-table'>
<tbody>
<template x-for='it in todayTimes.times'>
<tr class='salath'
:class='{current: it.salath === todayTimes.currentSalath.salath}'>
<td class='cell-names'>
<span class='salath-name'
x-text='it.name("tr")'></span>
<span class='salath-name'
x-text='it.name("de")'></span>
<span class='salath-name text--arabic'
x-text='it.name("ar")'></span>
</td>
<td class='cell-times'>
<span class='salath-time text--time text--numeric'
x-text='it.timeLocal'></span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</template>
<div class='text--center text--numeric'>
<p><span class='clock'
x-text='formatTime(now)'></span></p>
</div>
</div>
</body>
</html>

@ -1,208 +0,0 @@
const app = () => ({
futureTimes: [],
locationId: Alpine.$persist('11104'),
lastUpdated: Alpine.$persist(null),
userMinutes: 0,
now: new Date(),
debug: location.hash === '#debug',
geolocation: null,
async init() {
await this.refreshIfStale();
setInterval(() => {
this.now = new Date();
}, 500);
getUserLocation()
.then(loc => {
this.geolocation = {latitude: loc.latitude, longitude: loc.longitude};
this.refreshIfStale();
})
.catch(() => this.geolocation = null)
},
onHash() {
this.debug = location.hash === '#debug'
},
get userNow() {
if (!this.debug) {
return this.now;
}
const d = new Date();
d.setHours(0, this.userMinutes, 0, 0);
return d;
},
get userTime() {
return formatTime(this.userNow);
},
async refreshIfStale() {
const updatedAt = new Date(this.lastUpdated);
const now = new Date();
const elapsedSeconds = (now - updatedAt) / 1000;
if (this.geolocation !== null) {
this.futureTimes = await fetchJSON(`/api/v1/diyanet/prayertimes?latitude=${this.geolocation.latitude}&longitude=${this.geolocation.longitude}`);
} else {
this.futureTimes = await fetchJSON(`/api/v1/diyanet/prayertimes?location_id=${this.locationId}`);
}
this.lastUpdated = now.toISOString();
},
get todayTimes() {
if (this.futureTimes.length === 0) {
return null;
}
return new PrayerTimes(this.futureTimes[0], () => this.userNow);
},
translate(key, lang) {
return translations[key][lang] ?? key
}
});
class PrayerTimes {
static salaths = ['fajr', 'sunrise', 'dhuhr', 'asr', 'maghrib', 'isha'];
static translations = {
fajr: {tr: 'İmsak', de: 'Frühgebet', ar: 'صلاة الفجر'},
sunrise: {tr: 'Güneş', de: 'Sonnenaufgang', ar: 'الشروق'},
dhuhr: {tr: 'Öğle', de: 'Mittagsgebet', ar: 'صلاة الظهر'},
asr: {tr: 'İkindi', de: 'Nachmittagsgebet', ar: 'صلاة العصر'},
maghrib: {tr: 'Akşam', de: 'Abendgebet', ar: 'صلاة المغرب'},
isha: {tr: 'Yatsı', de: 'Nachtgebet', ar: 'صلاة العشاء'},
}
constructor({date, ...rest}, clock = () => new Date()) {
this.date = date;
this.clock = clock
this.salathTimes = rest
}
get times() {
const now = this.clock()
return PrayerTimes.salaths.map(k => {
// "2023-03-05T00:00:00Z"
const startsAt = new Date(this.date.replace('T00:00', `T${this.salathTimes[k]}`).replace(/Z$/, ''));
return {
salath: k,
name: lang => PrayerTimes.translations[k][lang] ?? '??',
startsAt,
timeLocal: this.salathTimes[k],
get untilSeconds() {
let untilSeconds = (startsAt - now) / 1000;
return now > startsAt ? 0 : untilSeconds;
},
get untilHuman() {
return formatDuration(this.untilSeconds)
},
}
})
}
get currentSalath() {
let current = this.times.filter(it => it.untilSeconds === 0).at(-1);
if (current === undefined) {
// we're in isha -> today's fajr is almost the same as tomorrows
const prevDay = new Date(this.date);
prevDay.setDate(prevDay.getDate() - 1);
current = new PrayerTimes({date: prevDay.toISOString(), ...this.salathTimes}, this.clock).times.at(-1);
}
return current
}
get nextSalath() {
let next = this.times
.filter(it => it.untilSeconds > 0)[0]
if (next === undefined) {
// we're in isha -> today's fajr is almost the same as tomorrows
const nextDay = new Date(this.date);
nextDay.setDate(nextDay.getDate() + 1);
next = new PrayerTimes({date: nextDay.toISOString(), ...this.salathTimes}, this.clock).times[0];
}
return {
...next,
}
}
}
/**
* @param {number} seconds
* @return {string}
* */
function formatDuration(seconds) {
const d = new Date(0, 0, 0, 0, 0, seconds);
return formatTime(d);
}
/**
* @param {Date} then
* @return {string}
* */
function formatTime(then) {
return new Intl.DateTimeFormat(navigator.language, {
hour: "numeric",
minute: "numeric",
second: "numeric"
}).format(then);
}
/**
* @param {Date} then
* @return {string}
* */
function formatDate(then) {
return new Intl.DateTimeFormat(navigator.language, {
year: "numeric",
month: "2-digit",
day: "2-digit"
}).format(then);
}
/**
* @param {Date} then
* @return {string}
* */
function formatDateHijri(then) {
return new Intl.DateTimeFormat("en-u-ca-islamic-umalqura-nu-latn", {
year: "numeric",
month: "long",
day: "numeric",
}).format(then);
}
/**
* @param {string} url
* @param {RequestInit} req
* */
async function fetchJSON(url, req = {}) {
const res = await fetch(url, {
...req,
})
return res.json()
}
/**
* @return {Promise<GeolocationCoordinates>}
* */
function getUserLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject("Geolocation is not supported by this browser.");
return;
}
navigator.geolocation.getCurrentPosition(
(position) => resolve(position.coords),
(error) => reject(error.message)
);
});
}

@ -1,88 +0,0 @@
*, *:before, *:after {
box-sizing: border-box;
}
body {
line-height: 1.6;
font-family: 'Inter', sans-serif;
}
p {
margin: 0;
}
p + p {
margin-top: 1rem;
}
.text--numeric {
font-variant: tabular-nums;
letter-spacing: -0.03em;
}
.text--center {
text-align: center;
}
.text--arabic {
font-family: 'Noto Sans Arabic', Inter, sans-serif;
line-height: 1;
}
.text--time {
font-size: 1.5em;
}
.table-wrapper {
padding: 0 2rem;
max-width: 66%;
margin: auto;
}
.salath-table {
font-size: 2rem;
font-weight: 600;
width: 100%;
vertical-align: middle;
}
td {
padding: 0.25rem 0.5rem;
}
.cell-names {
white-space: nowrap;
}
.cell-times {
white-space: nowrap;
text-align: right;
}
.salath-name {
}
.current {
color: tomato;
}
.salath-name + .salath-name:before {
content: '/';
opacity: 0.25;
margin: 0 0.5em;
}
.salath-time {
font-weight: 700;
}
.clock {
font-size: 4rem;
font-weight: bold;
}
.current-salath {
font-weight: bold;
font-size: 1.5rem;
}

@ -1,24 +0,0 @@
package templates
import (
"embed"
"io/fs"
"net/http"
"os"
)
//go:embed static
var staticFS embed.FS
func FS() fs.FS {
if os.Getenv("DEBUG") == "1" {
return os.DirFS("./templates/static")
}
f, _ := fs.Sub(staticFS, "static")
return f
}
func HttpFS() http.FileSystem {
return http.FS(FS())
}
Loading…
Cancel
Save