2024-07-28 13:57:08 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"github.com/pocketbase/dbx"
|
|
|
|
"github.com/pocketbase/pocketbase/models"
|
|
|
|
"github.com/pocketbase/pocketbase/tools/cron"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
"traintimes/trains"
|
|
|
|
|
|
|
|
"github.com/labstack/echo/v5"
|
|
|
|
"github.com/pocketbase/pocketbase"
|
|
|
|
"github.com/pocketbase/pocketbase/apis"
|
|
|
|
"github.com/pocketbase/pocketbase/core"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DummyRecord struct {
|
|
|
|
Ts string `json:"ts"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type GetTrainsRec struct {
|
|
|
|
From string `db:"from" json:"from"`
|
|
|
|
To string `db:"to" json:"to"`
|
|
|
|
Body string `db:"body" json:"body"`
|
|
|
|
Ts int64 `db:"ts" json:"ts"`
|
|
|
|
Hash string `db:"hash" json:"hash"`
|
|
|
|
}
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
type GetTrainTimesRec struct {
|
|
|
|
Hash string `db:"hash" json:"hash"`
|
|
|
|
Body string `db:"body" json:"body"`
|
|
|
|
Ts int64 `db:"ts" json:"ts"`
|
|
|
|
}
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
const host = "https://huxley2.azurewebsites.net"
|
|
|
|
|
|
|
|
var (
|
|
|
|
Version string
|
|
|
|
Build string
|
2024-10-28 17:26:42 +00:00
|
|
|
tr = &http.Transport{
|
|
|
|
MaxIdleConns: 10,
|
|
|
|
IdleConnTimeout: 30 * time.Second,
|
|
|
|
DisableCompression: true,
|
|
|
|
}
|
|
|
|
client = &http.Client{Transport: tr}
|
|
|
|
gmtTimeLoc = time.FixedZone("GMT", 0)
|
|
|
|
timeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
|
2024-07-28 13:57:08 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
log.Printf("GO-TRAINTIMES v%+v build %+v\n\n", Version, Build)
|
|
|
|
app := pocketbase.New()
|
|
|
|
|
|
|
|
// serves static files from the provided public dir (if exists)
|
|
|
|
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
|
|
|
scheduler := cron.New()
|
|
|
|
|
|
|
|
e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), false))
|
|
|
|
|
|
|
|
e.Router.GET("/gettrains", func(c echo.Context) error {
|
|
|
|
|
|
|
|
apis.ActivityLogger(app)
|
|
|
|
return getTrains(c, app)
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
e.Router.GET("/getnexttraintimes", func(c echo.Context) error {
|
|
|
|
apis.ActivityLogger(app)
|
2024-10-28 17:26:42 +00:00
|
|
|
return GetNextTrainTimes(c, app)
|
2024-07-28 13:57:08 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
scheduler.MustAdd("cleanup", "*/10 * * * *", func() {
|
|
|
|
go CleanupDB(app)
|
|
|
|
})
|
|
|
|
|
|
|
|
scheduler.Start()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err := app.Start(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NotImplemented(c echo.Context, from string) error {
|
|
|
|
|
|
|
|
params := c.QueryParams()
|
|
|
|
|
|
|
|
fmt.Printf("NotImplemented from: %s\n", from)
|
|
|
|
fmt.Printf("-- %+v\n", params)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func getTrains(c echo.Context, app *pocketbase.PocketBase) error {
|
|
|
|
params := c.QueryParams()
|
|
|
|
|
|
|
|
fmt.Printf("$$:getTrains")
|
|
|
|
fmt.Printf("-- %+v\n", params)
|
|
|
|
|
|
|
|
From := c.QueryParamDefault("from", "")
|
|
|
|
To := c.QueryParamDefault("to", "")
|
|
|
|
|
|
|
|
ts := time.Now().Unix()
|
|
|
|
|
|
|
|
log.Printf("-- %+v\n", ts)
|
|
|
|
|
|
|
|
log.Printf("-- From: %s\n", From)
|
|
|
|
log.Printf("-- To: %s\n", To)
|
|
|
|
|
|
|
|
dummy := DummyRecord{Ts: strconv.FormatInt(ts, 10)}
|
|
|
|
|
|
|
|
if From != "" && To != "" {
|
|
|
|
log.Println("-- Got something to search for")
|
|
|
|
|
|
|
|
// check db first..
|
|
|
|
|
|
|
|
recentTrain := GetTrainsRec{}
|
|
|
|
|
|
|
|
hash := fmt.Sprintf("%s%s", From, To)
|
|
|
|
|
|
|
|
dberr := app.Dao().DB().NewQuery("SELECT * FROM trains WHERE hash = {:hash} and ts >= {:ts}").Bind(dbx.Params{
|
|
|
|
"ts": ts - 120,
|
|
|
|
"hash": hash,
|
|
|
|
}).One(&recentTrain)
|
|
|
|
|
|
|
|
if dberr == nil {
|
|
|
|
// handle error
|
2024-10-28 17:26:42 +00:00
|
|
|
log.Printf("-- Cache hit train %+v\n", recentTrain.Hash)
|
2024-07-28 13:57:08 +00:00
|
|
|
return c.String(200, recentTrain.Body)
|
|
|
|
}
|
|
|
|
|
|
|
|
// `/all/${ req.query.from }/to/${ req.query.to }/10?accessToken=215b99fe-b237-4a01-aadc-cf315d6756d8`;
|
|
|
|
url := "/all/" + From + "/to/" + To + "/10?accessToken=215b99fe-b237-4a01-aadc-cf315d6756d8"
|
|
|
|
|
|
|
|
log.Printf("-- url: %+v\n", url)
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
/*tr := &http.Transport{
|
2024-07-28 13:57:08 +00:00
|
|
|
MaxIdleConns: 10,
|
|
|
|
IdleConnTimeout: 30 * time.Second,
|
|
|
|
DisableCompression: true,
|
2024-10-28 17:26:42 +00:00
|
|
|
}*/
|
|
|
|
// client := &http.Client{Transport: tr}
|
2024-07-28 13:57:08 +00:00
|
|
|
|
|
|
|
log.Printf("-- full url: %+v\n", host+url)
|
|
|
|
resp, err := client.Get(host + url)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
// handle error
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var nTrainRec GetTrainsRec
|
|
|
|
|
|
|
|
nTrainRec.To = To
|
|
|
|
nTrainRec.From = From
|
|
|
|
nTrainRec.Ts = ts
|
|
|
|
nTrainRec.Body = string(body)
|
|
|
|
nTrainRec.Hash = hash
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := saveTrainRec(app, nTrainRec)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
// log.Printf("-- body: %+v\n", string(body))
|
|
|
|
|
|
|
|
// return c.SendString(string(body))
|
|
|
|
return c.String(200, string(body))
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.JSON(http.StatusAccepted, dummy)
|
|
|
|
}
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
func GetNextTrainTimes(c echo.Context, app *pocketbase.PocketBase) error {
|
2024-07-28 13:57:08 +00:00
|
|
|
|
|
|
|
params := c.QueryParams()
|
|
|
|
|
|
|
|
log.Println("$$:GetNextTrainTimes")
|
|
|
|
log.Printf("-- %+v\n", params)
|
|
|
|
|
|
|
|
From := c.QueryParamDefault("from", "")
|
|
|
|
To := c.QueryParamDefault("to", "")
|
|
|
|
|
|
|
|
ts := time.Now().String()
|
2024-10-28 17:26:42 +00:00
|
|
|
tsUnix := time.Now().Unix()
|
|
|
|
now := time.Now()
|
|
|
|
then := now.Add(-2 * time.Minute)
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
log.Printf("-- %+v\n", ts)
|
|
|
|
|
|
|
|
log.Printf("-- From: %+v\n", From)
|
|
|
|
log.Printf("-- To: %+v\n", To)
|
|
|
|
|
|
|
|
dummy := DummyRecord{Ts: ts}
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
recentTraintimes := GetTrainTimesRec{}
|
|
|
|
|
|
|
|
hash := fmt.Sprintf("%s%s", From, To)
|
|
|
|
|
|
|
|
dberr := app.Dao().DB().NewQuery("SELECT * FROM traintimes WHERE hash = {:hash} and ts >= {:ts}").Bind(dbx.Params{
|
|
|
|
"ts": tsUnix - 120,
|
|
|
|
"hash": hash,
|
|
|
|
}).One(&recentTraintimes)
|
|
|
|
|
|
|
|
if dberr == nil {
|
|
|
|
// handle error
|
|
|
|
log.Printf("-- Cache hit traintimes %+v\n", recentTraintimes.Hash)
|
|
|
|
return c.String(200, recentTraintimes.Body)
|
|
|
|
}
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
if From != "" && To != "" {
|
|
|
|
log.Println("-- Got something to search for")
|
|
|
|
|
|
|
|
// `/all/${ req.query.from }/to/${ req.query.to }/10?accessToken=215b99fe-b237-4a01-aadc-cf315d6756d8`;
|
|
|
|
url := "/next/" + From + "/to/" + To + "?accessToken=215b99fe-b237-4a01-aadc-cf315d6756d8"
|
|
|
|
|
|
|
|
log.Printf("-- url: %+v\n", url)
|
|
|
|
|
|
|
|
log.Printf("-- full url: %+v\n", host+url)
|
2024-10-28 17:26:42 +00:00
|
|
|
|
|
|
|
req, err := http.NewRequest("GET", host+url, nil)
|
2024-07-28 13:57:08 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
// handle error
|
|
|
|
}
|
2024-10-28 17:26:42 +00:00
|
|
|
|
|
|
|
req.Header.Add("If-Modified-Since", then.In(gmtTimeLoc).Format(timeFormat))
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var services trains.AllDepartures
|
|
|
|
unerr := json.Unmarshal(body, &services)
|
|
|
|
|
|
|
|
if unerr != nil {
|
|
|
|
|
|
|
|
log.Println("Failed to Unmarshal json")
|
|
|
|
panic(unerr)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
departure := reduceNextTrainTimes(services)
|
|
|
|
|
|
|
|
departJson, derr := json.Marshal(departure)
|
|
|
|
|
|
|
|
if derr != nil {
|
|
|
|
|
|
|
|
log.Println("Failed to marshal json")
|
|
|
|
panic(derr)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
var nTrainTimesRec GetTrainTimesRec
|
|
|
|
|
|
|
|
nTrainTimesRec.Hash = hash
|
|
|
|
nTrainTimesRec.Ts = tsUnix
|
|
|
|
nTrainTimesRec.Body = string(departJson)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := saveTraintimesRec(app, nTrainTimesRec)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
// log.Println("-- body: %+v\n", string(body))
|
|
|
|
// c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSON)
|
|
|
|
|
|
|
|
// return c.SendString(string(departJson))
|
|
|
|
// return c.Send(body)
|
|
|
|
return c.String(200, string(departJson))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.JSON(http.StatusAccepted, dummy)
|
|
|
|
}
|
|
|
|
|
|
|
|
func reduceNextTrainTimes(departData trains.AllDepartures) trains.NextTrain {
|
|
|
|
|
|
|
|
// log.Printf("-- obj?: %+v\n", departData)
|
|
|
|
|
|
|
|
depart := departData.Departures[0].Service
|
|
|
|
// log.Printf("-- depart: %+v\n", depart)
|
|
|
|
var output trains.NextTrain
|
|
|
|
|
|
|
|
if depart.Origin != nil {
|
|
|
|
if depart.Sta != "" {
|
|
|
|
output.Sta = depart.Sta
|
|
|
|
} else {
|
|
|
|
output.Sta = depart.Std
|
|
|
|
}
|
|
|
|
|
|
|
|
if depart.Eta != "" {
|
|
|
|
output.Eta = depart.Eta
|
|
|
|
} else {
|
|
|
|
output.Eta = depart.Etd
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
output.Eta = "No Service"
|
|
|
|
output.Sta = "No Service"
|
|
|
|
}
|
|
|
|
|
|
|
|
// log.Printf("-- output: %+v\n", depart)
|
|
|
|
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
|
|
|
func saveTrainRec(app *pocketbase.PocketBase, trainRecord GetTrainsRec) error {
|
|
|
|
|
|
|
|
log.Println("$$:saveTrainRec")
|
|
|
|
|
|
|
|
// foundTrain := GetTrainsRec{}
|
|
|
|
|
|
|
|
collection, err := app.Dao().FindCollectionByNameOrId("trains")
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
record, recErr := app.Dao().FindRecordsByExpr("trains", dbx.NewExp("hash = {:hash}", dbx.Params{
|
|
|
|
"hash": trainRecord.Hash,
|
|
|
|
}))
|
|
|
|
|
|
|
|
if recErr != nil {
|
|
|
|
log.Println(recErr)
|
|
|
|
return recErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(record) == 0 {
|
2024-10-28 17:26:42 +00:00
|
|
|
log.Printf("-- Insert new train\n")
|
2024-07-28 13:57:08 +00:00
|
|
|
|
|
|
|
record := models.NewRecord(collection)
|
|
|
|
|
|
|
|
record.Set("from", trainRecord.From)
|
|
|
|
record.Set("to", trainRecord.To)
|
|
|
|
record.Set("body", trainRecord.Body)
|
|
|
|
record.Set("ts", trainRecord.Ts)
|
|
|
|
record.Set("hash", trainRecord.Hash)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(record); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
2024-10-28 17:26:42 +00:00
|
|
|
log.Printf("-- Update train\n")
|
2024-07-28 13:57:08 +00:00
|
|
|
rec := record[0]
|
|
|
|
|
|
|
|
rec.Set("body", trainRecord.Body)
|
|
|
|
rec.Set("ts", trainRecord.Ts)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(rec); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*dberr := app.Dao().DB().NewQuery("SELECT * FROM trains WHERE hash = {:hash}").Bind(dbx.Params{
|
|
|
|
"hash": trainRecord.Hash,
|
|
|
|
}).One(&foundTrain)
|
|
|
|
|
|
|
|
if dberr != nil {
|
|
|
|
// handle error
|
|
|
|
log.Printf("-- Insert New Record %+v,%+v\n", trainRecord.Hash)
|
|
|
|
record := models.NewRecord(collection)
|
|
|
|
|
|
|
|
record.Set("from", trainRecord.From)
|
|
|
|
record.Set("to", trainRecord.To)
|
|
|
|
record.Set("body", trainRecord.Body)
|
|
|
|
record.Set("ts", trainRecord.Ts)
|
|
|
|
record.Set("hash", trainRecord.Hash)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(record); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
foundTrain.Ts = trainRecord.Ts
|
|
|
|
foundTrain.Body = trainRecord.Body
|
|
|
|
|
|
|
|
record := models.NewRecord(foundTrain)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(record); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-10-28 17:26:42 +00:00
|
|
|
func saveTraintimesRec(app *pocketbase.PocketBase, traintimesRecord GetTrainTimesRec) error {
|
|
|
|
|
|
|
|
log.Println("$$:saveTraintimesRec")
|
|
|
|
|
|
|
|
// foundTrain := GetTrainsRec{}
|
|
|
|
|
|
|
|
collection, err := app.Dao().FindCollectionByNameOrId("traintimes")
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
record, recErr := app.Dao().FindRecordsByExpr("traintimes", dbx.NewExp("hash = {:hash}", dbx.Params{
|
|
|
|
"hash": traintimesRecord.Hash,
|
|
|
|
}))
|
|
|
|
|
|
|
|
if recErr != nil {
|
|
|
|
log.Println(recErr)
|
|
|
|
return recErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(record) == 0 {
|
|
|
|
log.Printf("-- Insert new traintimes\n")
|
|
|
|
|
|
|
|
record := models.NewRecord(collection)
|
|
|
|
|
|
|
|
record.Set("body", traintimesRecord.Body)
|
|
|
|
record.Set("ts", traintimesRecord.Ts)
|
|
|
|
record.Set("hash", traintimesRecord.Hash)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(record); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log.Printf("-- Update traintimes\n")
|
|
|
|
rec := record[0]
|
|
|
|
|
|
|
|
rec.Set("body", traintimesRecord.Body)
|
|
|
|
rec.Set("ts", traintimesRecord.Ts)
|
|
|
|
|
|
|
|
if err := app.Dao().SaveRecord(rec); err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-07-28 13:57:08 +00:00
|
|
|
func CleanupDB(app *pocketbase.PocketBase) {
|
|
|
|
|
|
|
|
ts := time.Now().Unix() - 600
|
|
|
|
|
|
|
|
log.Println("$$:CleanupDB", ts)
|
|
|
|
|
|
|
|
/*raw := "Delete from trains where ts <=" + strconv.FormatInt(ts, 10)
|
|
|
|
|
|
|
|
if _, err := app.Dao().DB().NewQuery(raw).Execute(); err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
}
|