feat: Rewrite to use the Diyanet API

This commit is contained in:
2026-02-21 01:50:59 +03:00
parent 43d4560fbb
commit 73316d4e62
17 changed files with 637 additions and 9198 deletions
+71 -19
View File
@@ -1,9 +1,12 @@
package api
import (
"context"
"errors"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gofiber/fiber/v2"
@@ -22,8 +25,13 @@ type httpError struct {
}
type Services struct {
TimesProvider prayer.TimesProvider
LocationTimesProvider prayer.LocationTimesProvider
Provider DiyanetProvider
}
type DiyanetProvider interface {
Get(ctx context.Context, locationID string) ([]prayer.Times, error)
GetByCoords(ctx context.Context, coords prayer.Coordinates) ([]prayer.Times, error)
SearchLocations(ctx context.Context, query string) ([]prayer.Location, error)
}
func New(services Services) *fiber.App {
@@ -55,33 +63,77 @@ func New(services Services) *fiber.App {
})
app.Get("/api/v1/diyanet/prayertimes", func(ctx *fiber.Ctx) error {
var times []prayer.Times
var err error
locationID := ctx.Query("location_id")
var loc struct {
Latitude float64 `query:"latitude"`
Longitude float64 `query:"longitude"`
var query struct {
LocationID string `query:"location_id"`
Latitude string `query:"latitude"`
Longitude string `query:"longitude"`
}
if err := ctx.QueryParser(&query); err != nil {
return fmt.Errorf("failed to bind prayer times query parameters: %w", errors.Join(fiber.ErrBadRequest, err))
}
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,
locationID := strings.TrimSpace(query.LocationID)
latitude := strings.TrimSpace(query.Latitude)
longitude := strings.TrimSpace(query.Longitude)
var (
times []prayer.Times
err error
)
switch {
case locationID != "":
times, err = services.Provider.Get(ctx.Context(), locationID)
case latitude != "" && longitude != "":
lat, latErr := strconv.ParseFloat(latitude, 64)
if latErr != nil {
return fmt.Errorf("failed to parse latitude query parameter: %w", errors.Join(fiber.ErrBadRequest, latErr))
}
lng, lngErr := strconv.ParseFloat(longitude, 64)
if lngErr != nil {
return fmt.Errorf("failed to parse longitude query parameter: %w", errors.Join(fiber.ErrBadRequest, lngErr))
}
times, err = services.Provider.GetByCoords(ctx.Context(), prayer.Coordinates{
Latitude: lat,
Longitude: lng,
})
} else {
return fmt.Errorf("%w: missing location id or coordinates", fiber.ErrBadRequest)
default:
return fmt.Errorf("failed to validate prayer times query parameters: %w", fiber.ErrBadRequest)
}
if err != nil {
return err
return fmt.Errorf("failed to fetch prayer times: %w", err)
}
ctx.Response().Header.Add(fiber.HeaderCacheControl, "max-age=86400")
return ctx.JSON(fiber.Map{
"prayertimes": times,
})
})
return ctx.JSON(times)
app.Get("/api/v1/diyanet/location", func(ctx *fiber.Ctx) error {
var query struct {
Text string `query:"query"`
}
if err := ctx.QueryParser(&query); err != nil {
return fmt.Errorf("failed to bind location query parameters: %w", errors.Join(fiber.ErrBadRequest, err))
}
query.Text = strings.TrimSpace(query.Text)
if query.Text == "" {
return fmt.Errorf("failed to validate location query parameters: %w", fiber.ErrBadRequest)
}
locations, err := services.Provider.SearchLocations(ctx.Context(), query.Text)
if err != nil {
return fmt.Errorf("failed to search locations: %w", err)
}
ctx.Response().Header.Add(fiber.HeaderCacheControl, "max-age=86400")
return ctx.JSON(fiber.Map{
"locations": locations,
})
})
app.Use("/", filesystem.New(filesystem.Config{
-38
View File
@@ -1,38 +0,0 @@
package database
import (
"database/sql"
"fmt"
"net/url"
"github.com/doug-martin/goqu/v9"
goqusqlite3 "github.com/doug-martin/goqu/v9/dialect/sqlite3"
_ "modernc.org/sqlite"
)
func init() {
goqu.RegisterDialect("sqlite3", func() *goqu.SQLDialectOptions {
do := goqusqlite3.DialectOptions()
do.SupportsReturn = true
return do
}())
}
func NewSqliteDB(filename string) (*goqu.Database, error) {
pragmas := url.Values{
"_pragma": {
"journal_mode(WAL)",
"foreign_keys(1)",
"synchronous(NORMAL)",
"busy_timeout(5000)",
},
}.Encode()
dsn := fmt.Sprintf("%s?%s", filename, pragmas)
conn, err := sql.Open("sqlite", dsn)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
}
return goqu.New("sqlite3", conn), nil
}
-32
View File
@@ -1,11 +1,6 @@
package net
import (
"context"
"fmt"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/imroc/req/v3"
)
@@ -15,30 +10,3 @@ var ReqClient = req.NewClient().
DisableAutoReadResponse().
EnableInsecureSkipVerify().
SetUserAgent(userAgent)
func GetHTML(ctx context.Context, url string) (string, error) {
res, err := ReqClient.R().SetContext(ctx).SetRetryCount(3).Get(url)
if err != nil {
return "", fmt.Errorf("failed to get url: %w", err)
}
html, err := res.ToString()
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
}
return html, nil
}
func GetParsed(ctx context.Context, url string) (*goquery.Document, error) {
html, err := GetHTML(ctx, url)
if err != nil {
return nil, fmt.Errorf("failed to get html: %w", err)
}
doc, err := goquery.NewDocumentFromReader(strings.NewReader(html))
if err != nil {
return nil, fmt.Errorf("failed to parse html: %w", err)
}
return doc, nil
}
type Fetcher struct {
}