Country profile changes : GWM
This commit is contained in:
parent
661971f9d7
commit
89f8e1c515
494
main.go
494
main.go
@ -483,6 +483,13 @@ func main() {
|
|||||||
|
|
||||||
app = pocketbase.New()
|
app = pocketbase.New()
|
||||||
|
|
||||||
|
app.OnRecordAuthRequest("users").Add(func(e *core.RecordAuthEvent) error {
|
||||||
|
|
||||||
|
e.Record.Set("lastLogin", time.Now().UTC())
|
||||||
|
|
||||||
|
return app.Dao().SaveRecord(e.Record)
|
||||||
|
})
|
||||||
|
|
||||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||||
// Serve privacy policy static page
|
// Serve privacy policy static page
|
||||||
e.Router.GET("/privacy/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public/privacy"), false))
|
e.Router.GET("/privacy/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public/privacy"), false))
|
||||||
@ -1995,6 +2002,465 @@ func main() {
|
|||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// GET ALL COUNTRIES
|
||||||
|
// ============================================
|
||||||
|
e.Router.GET("/api/getCountries", func(c echo.Context) error {
|
||||||
|
|
||||||
|
language := c.QueryParam("language")
|
||||||
|
|
||||||
|
countries := []struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
CountryName string `db:"country_name" json:"country_name"`
|
||||||
|
CountryCode string `db:"country_code" json:"country_code"`
|
||||||
|
FlagUrl string `db:"flag_url" json:"flag_url"`
|
||||||
|
Logo string `db:"logo" json:"logo"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
countryNameColumn := "country_name_" + language
|
||||||
|
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries`
|
||||||
|
err := app.DB().
|
||||||
|
NewQuery(query).
|
||||||
|
All(&countries)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch countries: %v", err)
|
||||||
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch countries"})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(countries) == 0 {
|
||||||
|
return c.JSON(http.StatusNotFound, map[string]string{"error": "No countries found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build response with full logo URLs
|
||||||
|
result := []map[string]interface{}{}
|
||||||
|
for _, country := range countries {
|
||||||
|
var flagURL string
|
||||||
|
if country.Logo == "" {
|
||||||
|
flagURL = ""
|
||||||
|
} else {
|
||||||
|
flagURL = fmt.Sprintf("%s/api/files/countries/%s/%s",
|
||||||
|
baseUrl,
|
||||||
|
country.ID,
|
||||||
|
country.Logo,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, map[string]interface{}{
|
||||||
|
"id": country.ID,
|
||||||
|
"country_name": country.CountryName,
|
||||||
|
"country_code": country.CountryCode,
|
||||||
|
"flag_url": flagURL,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, result)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// GET COUNTRY DETAILS (Country + Leaders + All Statistics)
|
||||||
|
// ============================================
|
||||||
|
e.Router.GET("/api/getCountryDetails", func(c echo.Context) error {
|
||||||
|
|
||||||
|
countryCode := c.QueryParam("country_code")
|
||||||
|
language := c.QueryParam("language")
|
||||||
|
|
||||||
|
if countryCode == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "country_code is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get country
|
||||||
|
country := struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
CountryName string `db:"country_name" json:"country_name"`
|
||||||
|
CountryCode string `db:"country_code" json:"country_code"`
|
||||||
|
FlagUrl string `db:"flag_url" json:"flag_url"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
countryNameColumn := "country_name_" + language
|
||||||
|
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries WHERE country_code = {:country_code}`
|
||||||
|
err := app.DB().
|
||||||
|
NewQuery(query).
|
||||||
|
Bind(dbx.Params{
|
||||||
|
"country_code": countryCode,
|
||||||
|
}).
|
||||||
|
One(&country)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Country not found: %v", err)
|
||||||
|
return c.JSON(http.StatusNotFound, map[string]string{"error": "Country not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get leaders
|
||||||
|
leaders := []struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Position string `db:"position" json:"position"`
|
||||||
|
Photo string `db:"photo" json:"photo"`
|
||||||
|
IsCurrent bool `db:"is_current" json:"is_current"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
nameColumn := "name_" + language
|
||||||
|
positionColumn := "position_" + language
|
||||||
|
|
||||||
|
query2 := `SELECT id,` + nameColumn + ` AS name,` + positionColumn + ` AS position,photo,is_current FROM leaders WHERE country = {:country} AND is_current = true ORDER BY position ASC `
|
||||||
|
|
||||||
|
err = app.DB().
|
||||||
|
NewQuery(query2).
|
||||||
|
Bind(dbx.Params{
|
||||||
|
"country": country.ID,
|
||||||
|
}).
|
||||||
|
All(&leaders)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch leaders: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all statistics
|
||||||
|
allStatistics := []struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
Type string `db:"type" json:"type"`
|
||||||
|
Topic string `db:"topic" json:"topic"`
|
||||||
|
Value string `db:"value" json:"value"`
|
||||||
|
Rank float64 `db:"rank" json:"rank"`
|
||||||
|
IsRankPositive bool `db:"is_rank_positive" json:"is_rank_positive"`
|
||||||
|
RankHeading string `db:"rank_heading" json:"rank_heading"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
topicColumn := "topic_" + language
|
||||||
|
valueColumn := "value_" + language
|
||||||
|
rankHeadingColumn := "rank_heading_" + language
|
||||||
|
|
||||||
|
query3 := `SELECT id,type,` + topicColumn + ` AS topic,` + valueColumn + ` AS value,` + rankHeadingColumn + ` AS rank_heading,rank,is_rank_positive FROM country_statistics WHERE country = {:country} ORDER BY type ASC, rank ASC`
|
||||||
|
|
||||||
|
err = app.DB().
|
||||||
|
NewQuery(query3).
|
||||||
|
Bind(dbx.Params{
|
||||||
|
"country": country.ID,
|
||||||
|
}).
|
||||||
|
All(&allStatistics)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to fetch statistics: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group statistics by type
|
||||||
|
statisticsMap := map[string]interface{}{
|
||||||
|
"demography": []interface{}{},
|
||||||
|
"gdp": []interface{}{},
|
||||||
|
"fdi": []interface{}{},
|
||||||
|
"trade": []interface{}{},
|
||||||
|
"bilateral_trade": []interface{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stat := range allStatistics {
|
||||||
|
// Remove quotes from type field if present
|
||||||
|
cleanType := stat.Type
|
||||||
|
if len(cleanType) > 2 && cleanType[0] == '"' && cleanType[len(cleanType)-1] == '"' {
|
||||||
|
cleanType = cleanType[1 : len(cleanType)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := statisticsMap[cleanType]; exists {
|
||||||
|
statisticsMap[cleanType] = append(statisticsMap[cleanType].([]interface{}), stat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build final response
|
||||||
|
result := map[string]interface{}{
|
||||||
|
"country": country,
|
||||||
|
"leaders": leaders,
|
||||||
|
"statistics": statisticsMap,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// GET BILATERAL TRADE DATA
|
||||||
|
// ============================================
|
||||||
|
e.Router.GET("/api/getBilateralTradeData", func(c echo.Context) error {
|
||||||
|
|
||||||
|
countryCode := c.QueryParam("country_code")
|
||||||
|
statisticsId := c.QueryParam("statistics_id")
|
||||||
|
language := c.QueryParam("language")
|
||||||
|
|
||||||
|
if countryCode == "" {
|
||||||
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "country_code is required"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get country
|
||||||
|
country := struct {
|
||||||
|
ID string `db:"id" json:"id"`
|
||||||
|
CountryName string `db:"country_name" json:"country_name"`
|
||||||
|
CountryCode string `db:"country_code" json:"country_code"`
|
||||||
|
FlagUrl string `db:"flag_url" json:"flag_url"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
countryNameColumn := "country_name_" + language
|
||||||
|
query := ` SELECT id,country_code,flag_url,logo,` + countryNameColumn + ` AS country_name FROM countries WHERE country_code = {:country_code}`
|
||||||
|
err := app.DB().
|
||||||
|
NewQuery(query).
|
||||||
|
Bind(dbx.Params{
|
||||||
|
"country_code": countryCode,
|
||||||
|
}).
|
||||||
|
One(&country)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusNotFound, map[string]string{"error": "Country not found"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all bilateral trade statistics
|
||||||
|
allBilateralTradeData := []struct {
|
||||||
|
Topic string `db:"topic" json:"topic"`
|
||||||
|
Value string `db:"value" json:"value"`
|
||||||
|
Icon string `db:"icon" json:"icon"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
topicColumn := "topic_" + language
|
||||||
|
|
||||||
|
query2 := `SELECT ` + topicColumn + ` AS topic,value,icon FROM bilateral_trade_data WHERE country = {:country} AND statistics = {:statistics}`
|
||||||
|
|
||||||
|
err = app.DB().
|
||||||
|
NewQuery(query2).
|
||||||
|
Bind(dbx.Params{
|
||||||
|
"country": country.ID,
|
||||||
|
"statistics": statisticsId,
|
||||||
|
}).
|
||||||
|
All(&allBilateralTradeData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, map[string]string{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(allBilateralTradeData) == 0 {
|
||||||
|
return c.JSON(http.StatusNotFound, map[string]string{
|
||||||
|
"error": "Data not found",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, allBilateralTradeData)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// GET ALL COMPETITIVENESS REPORTS
|
||||||
|
// ============================================
|
||||||
|
e.Router.GET("/api/getCompetitivenessReports", func(c echo.Context) error {
|
||||||
|
|
||||||
|
type CountryMini struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
CountryName string `json:"country_name"`
|
||||||
|
Flag string `json:"flag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompetitivenessReport struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Year int `json:"year"`
|
||||||
|
CurrentYearRank int `json:"current_year_rank"`
|
||||||
|
PreviousYearRank int `json:"previous_year_rank"`
|
||||||
|
IsRankPositive bool `json:"is_rank_positive"`
|
||||||
|
|
||||||
|
Heading string `json:"heading"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Logo string `json:"logo"`
|
||||||
|
|
||||||
|
FirstGlobally []CountryMini `json:"first_in_globally"`
|
||||||
|
FirstGCC []CountryMini `json:"first_in_gcc"`
|
||||||
|
FirstArab []CountryMini `json:"first_in_arab_countries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
language := c.QueryParam("language")
|
||||||
|
if language == "" {
|
||||||
|
language = "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
headingColumn := "heading_" + language
|
||||||
|
contentColumn := "content_" + language
|
||||||
|
|
||||||
|
// Step 1: fetch competitiveness reports
|
||||||
|
rows := []struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
Year int `db:"year"`
|
||||||
|
CurrentYearRank int `db:"current_year_rank"`
|
||||||
|
PreviousYearRank int `db:"previous_year_rank"`
|
||||||
|
IsRankPositive bool `db:"is_rank_positive"`
|
||||||
|
Logo string `db:"logo"`
|
||||||
|
|
||||||
|
Heading string `db:"heading"`
|
||||||
|
Content string `db:"content"`
|
||||||
|
|
||||||
|
FirstGlobally string `db:"first_in_globally"`
|
||||||
|
FirstGCC string `db:"first_in_gcc"`
|
||||||
|
FirstArab string `db:"first_in_arab_countries"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
year,
|
||||||
|
current_year_rank,
|
||||||
|
previous_year_rank,
|
||||||
|
is_rank_positive,
|
||||||
|
logo,
|
||||||
|
` + headingColumn + ` AS heading,
|
||||||
|
` + contentColumn + ` AS content,
|
||||||
|
first_in_globally,
|
||||||
|
first_in_gcc,
|
||||||
|
first_in_arab_countries
|
||||||
|
FROM competitive_reports
|
||||||
|
`
|
||||||
|
|
||||||
|
err := app.DB().NewQuery(query).All(&rows)
|
||||||
|
if err != nil {
|
||||||
|
return c.JSON(http.StatusInternalServerError, echo.Map{
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rows) == 0 {
|
||||||
|
return c.JSON(http.StatusNotFound, echo.Map{
|
||||||
|
"error": "No competitiveness reports found",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// return c.JSON(http.StatusOK, rows)
|
||||||
|
|
||||||
|
// Step 2: loop & attach relation data
|
||||||
|
result := []CompetitivenessReport{}
|
||||||
|
|
||||||
|
for _, r := range rows {
|
||||||
|
|
||||||
|
report := CompetitivenessReport{
|
||||||
|
ID: r.ID,
|
||||||
|
Year: r.Year,
|
||||||
|
CurrentYearRank: r.CurrentYearRank,
|
||||||
|
PreviousYearRank: r.PreviousYearRank,
|
||||||
|
IsRankPositive: r.IsRankPositive,
|
||||||
|
Heading: r.Heading,
|
||||||
|
Content: r.Content,
|
||||||
|
Logo: baseUrl + "/api/files/competitive_reports/" + r.ID + "/" + r.Logo,
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCountries := func(ids []string) []CountryMini {
|
||||||
|
if len(ids) == 0 {
|
||||||
|
return []CountryMini{}
|
||||||
|
}
|
||||||
|
|
||||||
|
inClause := buildSafeInClause(ids)
|
||||||
|
|
||||||
|
query := `
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
country_name_` + language + ` AS country_name,
|
||||||
|
flag_url AS flag
|
||||||
|
FROM countries
|
||||||
|
WHERE id IN ` + inClause
|
||||||
|
|
||||||
|
countries := []CountryMini{}
|
||||||
|
|
||||||
|
err := app.DB().
|
||||||
|
NewQuery(query).
|
||||||
|
All(&countries)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Country fetch error:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return countries
|
||||||
|
}
|
||||||
|
|
||||||
|
report.FirstGlobally = loadCountries(parseRelationIDs(r.FirstGlobally))
|
||||||
|
report.FirstGCC = loadCountries(parseRelationIDs(r.FirstGCC))
|
||||||
|
report.FirstArab = loadCountries(parseRelationIDs(r.FirstArab))
|
||||||
|
|
||||||
|
result = append(result, report)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, result)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// SEED SAMPLE DATA (Run once to populate database)
|
||||||
|
// ============================================
|
||||||
|
e.Router.POST("/api/seedSampleData", func(c echo.Context) error {
|
||||||
|
|
||||||
|
// Create Argentina
|
||||||
|
countryID := ""
|
||||||
|
err := app.Dao().DB().
|
||||||
|
Select("id").
|
||||||
|
From("countries").
|
||||||
|
Where(dbx.HashExp{"country_code": "AR"}).
|
||||||
|
One(&struct{ ID string }{ID: countryID})
|
||||||
|
|
||||||
|
// If Argentina doesn't exist, create it
|
||||||
|
if err != nil {
|
||||||
|
_, err = app.Dao().DB().
|
||||||
|
Insert("countries", dbx.Params{
|
||||||
|
"country_name": "Argentina",
|
||||||
|
"country_code": "AR",
|
||||||
|
"flag_url": "https://flagcdn.com/ar.svg",
|
||||||
|
}).Execute()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to create Argentina: %v", err)
|
||||||
|
return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to create country"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the newly created country ID
|
||||||
|
err = app.Dao().DB().
|
||||||
|
Select("id").
|
||||||
|
From("countries").
|
||||||
|
Where(dbx.HashExp{"country_code": "AR"}).
|
||||||
|
One(&struct{ ID string }{ID: countryID})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert Leaders (simplified - you can add more)
|
||||||
|
leaders := []map[string]interface{}{
|
||||||
|
{"country": countryID, "name": "Javier Milei", "position": "President", "is_current": true},
|
||||||
|
{"country": countryID, "name": "Victoria Villarruel", "position": "Vice President", "is_current": true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, leader := range leaders {
|
||||||
|
_, err = app.Dao().DB().Insert("leaders", leader).Execute()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to insert leader: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert Statistics
|
||||||
|
statistics := []map[string]interface{}{
|
||||||
|
// Demography
|
||||||
|
{"country": countryID, "type": "\"demography\"", "topic": "Capital City", "value": "Buenos Aires"},
|
||||||
|
{"country": countryID, "type": "\"demography\"", "topic": "Area", "value": "2,780,400 sq. km"},
|
||||||
|
{"country": countryID, "type": "\"demography\"", "topic": "2021 Population", "value": "45.81 M", "rank": 0.9, "is_rank_positive": true},
|
||||||
|
|
||||||
|
// GDP
|
||||||
|
{"country": countryID, "type": "\"gdp\"", "topic": "2021 Nominal (AED)", "value": "1.8 T", "rank": 2.5, "is_rank_positive": true},
|
||||||
|
{"country": countryID, "type": "\"gdp\"", "topic": "2021 Real (AED)", "value": "2.08 T", "rank": 3.2, "is_rank_positive": true},
|
||||||
|
|
||||||
|
// FDI
|
||||||
|
{"country": countryID, "type": "\"fdi\"", "topic": "2020 FDI Inflow (AED)", "value": "4.75 B", "rank": -10.41, "is_rank_positive": false},
|
||||||
|
|
||||||
|
// Trade
|
||||||
|
{"country": countryID, "type": "\"trade\"", "topic": "2021 Total Trade (AED)", "value": "603 B", "rank": 35.36, "is_rank_positive": true},
|
||||||
|
|
||||||
|
// Bilateral Trade
|
||||||
|
{"country": countryID, "type": "\"bilateral_trade\"", "topic": "Total (2022)", "value": "$4.7M"},
|
||||||
|
{"country": countryID, "type": "\"bilateral_trade\"", "topic": "Top 5 Import Commodities", "value": "Jewellery, Diamonds, Gold", "rank": 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, stat := range statistics {
|
||||||
|
_, err = app.Dao().DB().Insert("country_statistics", stat).Execute()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to insert statistic: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(http.StatusOK, map[string]string{"message": "Sample data seeded successfully"})
|
||||||
|
})
|
||||||
|
|
||||||
type DownloadRequest struct {
|
type DownloadRequest struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
@ -3428,3 +3894,31 @@ func sendStatusChangeEmail(app *pocketbase.PocketBase, record *models.Record, ne
|
|||||||
log.Printf("Status change email sent successfully to: %s", userEmail)
|
log.Printf("Status change email sent successfully to: %s", userEmail)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseRelationIDs(value string) []string {
|
||||||
|
if value == "" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ids []string
|
||||||
|
_ = json.Unmarshal([]byte(value), &ids)
|
||||||
|
return ids
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSafeInClause(ids []string) string {
|
||||||
|
quoted := make([]string, 0, len(ids))
|
||||||
|
|
||||||
|
for _, id := range ids {
|
||||||
|
if id == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// PocketBase IDs are safe: lowercase letters + numbers
|
||||||
|
quoted = append(quoted, "'"+id+"'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(quoted) == 0 {
|
||||||
|
return "('')" // never matches
|
||||||
|
}
|
||||||
|
|
||||||
|
return "(" + strings.Join(quoted, ",") + ")"
|
||||||
|
}
|
||||||
|
|||||||
BIN
pb_data/data.db
BIN
pb_data/data.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
pb_data/logs.db
BIN
pb_data/logs.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user