diff --git a/pb.zip b/pb.zip
deleted file mode 100644
index 5211787..0000000
Binary files a/pb.zip and /dev/null differ
diff --git a/pb/.env b/pb/.env
deleted file mode 100644
index 2c4392e..0000000
--- a/pb/.env
+++ /dev/null
@@ -1,3 +0,0 @@
-APP_SIGNATURE = fcsc.gov.ae.X7pL9qZm2A
-
-BASE_URL = https://pocket.fcsc.gov.ae
diff --git a/pb/main.go b/pb/main.go
deleted file mode 100755
index d75f467..0000000
--- a/pb/main.go
+++ /dev/null
@@ -1,2992 +0,0 @@
-package main
-
-import (
- "context"
- "crypto/rand"
- "encoding/hex"
- "encoding/json"
- "encoding/xml"
- "fmt"
- "io/ioutil"
- "log"
- "math/big"
- "net/http"
- "net/mail"
- "os"
- "path/filepath"
- "pocketbase/utils"
- "strings"
- "time"
-
- firebase "firebase.google.com/go"
- "firebase.google.com/go/messaging"
- "github.com/joho/godotenv"
- "github.com/labstack/echo/v5"
- "github.com/pocketbase/dbx"
- "github.com/pocketbase/pocketbase"
- "github.com/pocketbase/pocketbase/apis"
- "github.com/pocketbase/pocketbase/core"
- "github.com/pocketbase/pocketbase/models"
-
- // "github.com/pocketbase/pocketbase/tools/hook"
-
- "github.com/pocketbase/pocketbase/tools/mailer"
- "github.com/pocketbase/pocketbase/tools/types"
- "google.golang.org/api/option"
-)
-
-var baseUrl = os.Getenv("BASE_URL")
-
-type EmailConfigurationResponse struct {
- Page int `json:"page"`
- PerPage int `json:"perPage"`
- TotalItems int `json:"totalItems"`
- TotalPages int `json:"totalPages"`
- Items []struct {
- Type string `json:"type"`
- Email string `json:"email"`
- } `json:"items"`
-}
-
-type GenericData struct {
- XMLName xml.Name `xml:"GenericData"`
- DataSet []Obs `xml:"DataSet>Obs"`
-}
-
-type Obs struct {
- ObsKey []ObsKey `xml:"ObsKey>Value"` // Dynamic key-value pairs
- ObsValue ObsValue `xml:"ObsValue"`
-}
-
-type ObsKey struct {
- ID string `xml:"id,attr"` // Attribute for 'id'
- Value string `xml:"value,attr"` // Attribute for 'value'
-}
-
-type ObsValue struct {
- Value string `xml:"value,attr"` // Attribute for 'value'
-}
-
-// Helper function to map ObsKey into a dictionary
-func mapObsKey(keys []ObsKey) map[string]string {
- result := map[string]string{}
- for _, key := range keys {
- result[key.ID] = key.Value
- }
- return result
-}
-
-// Helper function to clean XML namespace prefixes
-func removeNamespace(xmlBytes []byte) string {
- return strings.ReplaceAll(string(xmlBytes), "generic:", "")
-}
-
-// Function to fetch XML from an API and convert it to JSON
-func fetchAndConvertXMLToJSON(apiURL string, outputDir string, fileName string, tableName string, id string) ([]map[string]interface{}, error) {
- // Call the API and get the XML response
- resp, err := http.Get(apiURL)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- // Read the response body
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
-
- // Clean the XML by removing namespace prefixes
- cleanXML := []byte(removeNamespace(body))
-
- // Parse the cleaned XML data
- var data GenericData
- err = xml.Unmarshal(cleanXML, &data)
- if err != nil {
- return nil, err
- }
-
- // Transform the data for JSON output
- response := []map[string]interface{}{}
- for _, obs := range data.DataSet {
- obsMap := map[string]interface{}{
- "ObsKey": mapObsKey(obs.ObsKey),
- "ObsValue": obs.ObsValue,
- }
- response = append(response, obsMap)
- }
-
- // Convert the response to JSON
- jsonData, err := json.MarshalIndent(response, "", " ")
- if err != nil {
- return nil, err
- }
-
- // Save the JSON data to a file if required
- // Ensure the folder exists
- if err := os.MkdirAll(outputDir, os.ModePerm); err != nil {
- return nil, err
- }
-
- // Save the file
- file := fmt.Sprintf("%s/%s.json", outputDir, fileName)
- if err := ioutil.WriteFile(file, jsonData, 0644); err != nil {
- return nil, err
- }
-
- // Find the charts record
- record, err := app.Dao().FindRecordById(tableName, id)
- if err != nil {
- // return c.JSON(500, map[string]interface{}{"code": 500,"message": "Error finding user.",})
- }
- record.Set("file_status", true)
- if err := app.Dao().SaveRecord(record); err != nil {
- // return c.JSON(500, map[string]interface{}{"code": 500,"message": "Failed to verify user.",})
- }
-
- return response, nil
-}
-
-// Function to read JSON data from a file
-func readJSONFromFile(folderName, fileName string) ([]map[string]interface{}, error) {
- // Construct the file path dynamically using the folder name
- filePath := fmt.Sprintf("%s/%s.json", folderName, fileName)
-
- // Read and return the JSON content from the file
- fileContent, err := ioutil.ReadFile(filePath)
- if err != nil {
- return nil, fmt.Errorf("failed to read file '%s': %w", filePath, err)
- }
-
- // Parse the JSON content from the file
- var jsonResponse []map[string]interface{}
- if err := json.Unmarshal(fileContent, &jsonResponse); err != nil {
- return nil, fmt.Errorf("failed to parse JSON from file '%s': %w", filePath, err)
- }
-
- return jsonResponse, nil
-}
-
-type ResponseItem struct {
- ObsKey map[string]string `json:"ObsKey"`
- ObsValue map[string]string `json:"ObsValue"`
-}
-
-type FilterJsonData struct {
- Order int `json:"order"`
- En string `json:"en"`
- Ar string `json:"ar"`
-}
-
-type FilterGroup struct {
- FilterKey string `json:"filter_key"`
- FilterData []interface{} `json:"filter_data"`
- FilterTextAndOrder FilterJsonData `json:"filter_text_and_order"`
-}
-
-func generateFilters(responseRaw []map[string]interface{}, filterKeys []string, filterJsonData map[string]FilterJsonData) ([]FilterGroup, error) {
- // Transform responseRaw to []ResponseItem
- var response []ResponseItem
- responseBytes, err := json.Marshal(responseRaw) // Marshal raw data to JSON
- if err != nil {
- log.Printf("Failed to marshal raw response: %v", err)
- return nil, err
- }
-
- if err := json.Unmarshal(responseBytes, &response); err != nil { // Unmarshal to []ResponseItem
- log.Printf("Failed to unmarshal response: %v", err)
- return nil, err
- }
-
- // Create a map to hold unique filter data
- filterMap := make(map[string]map[string]struct{})
- for _, key := range filterKeys {
- filterMap[key] = make(map[string]struct{})
- }
-
- // Populate filter data dynamically
- for _, item := range response {
- for _, key := range filterKeys {
- if value, exists := item.ObsKey[key]; exists {
- filterMap[key][value] = struct{}{}
- }
- }
- }
-
- // Convert filter map to the desired structure
- var filters []FilterGroup
- for key, values := range filterMap {
- filterData := make([]interface{}, 0, len(values))
- for value := range values {
- filterData = append(filterData, value)
- }
-
- if key == "REF_AREA" {
- filterData = []interface{}{"AE-AZ", "AE-DU", "AE-SH", "AE-AJ", "AE-UQ", "AE-RK", "AE-FJ"}
- }
- filters = append(filters, FilterGroup{
- FilterKey: key,
- FilterData: filterData,
- FilterTextAndOrder: filterJsonData[key],
- })
- }
-
- return filters, nil
-}
-
-// Function to fetch filter data of dataset
-func fetchfilterJSON(kpi string) ([]FilterGroup, error) {
-
- // Define struct for the result
- var dataSetFilter struct {
- Id string `db:"id" json:"id"`
- FullDataUrl string `db:"full_data_url" json:"full_data_url"`
- FilterKeys string `db:"filter_keys" json:"filter_keys"`
- FileStatus bool `db:"file_status" json:"file_status"`
- DataSet string `db:"dataset" json:"dataset"`
- Kpi string `db:"kpi" json:"kpi"`
- ExclusionFiltersJSON string `db:"exclusion_filters_array" json:"exclusion_filters_array"`
- FilterJson string `db:"filter_json" json:"filter_json"`
- }
-
- // Query to get one row from the data_set_filter table
- err := app.DB().
- Select("id", "full_data_url", "filter_keys", "file_status", "dataset", "kpi", "filter_json", "exclusion_filters_array").
- From("data_set_filter").
- Where(dbx.HashExp{"kpi": kpi}).
- One(&dataSetFilter)
-
- if err != nil {
- log.Printf("Failed to fetch data_set_filter data: %v", err)
- return nil, err
- }
-
- // Use a map to store the dynamic JSON keys
- var filterJson map[string]FilterJsonData
-
- // Parse the JSON
- err = json.Unmarshal([]byte(dataSetFilter.FilterJson), &filterJson)
- if err != nil {
- log.Fatalf("Error parsing JSON: %v", err)
- }
-
- // Fetch and convert XML to JSON if file is not saved
- if !dataSetFilter.FileStatus { // Check if FileStatus is false
- _, err := fetchAndConvertXMLToJSON(dataSetFilter.FullDataUrl, "filter_files", dataSetFilter.Kpi, "data_set_filter", dataSetFilter.Id)
- if err != nil {
- log.Printf("Failed to process URL '%s': %v", dataSetFilter.FullDataUrl, err)
- return nil, err
- }
- }
-
- // Read the JSON data from the file
- response, err := readJSONFromFile("filter_files", dataSetFilter.Kpi)
- if err != nil {
- log.Printf("Failed to read JSON for Kpi filter '%s': %v", dataSetFilter.Kpi, err)
- return nil, err
- }
-
- // Parse the filter keys
- filterKeys := strings.Split(dataSetFilter.FilterKeys, ",")
-
- // Generate filters
- filters, err := generateFilters(response, filterKeys, filterJson)
- if err != nil {
- log.Printf("Failed to generate filters: %v", err)
- return nil, err
- }
-
- // Parse exclusion filters JSON if it's not empty
- var exclusionFilters []utils.FilterGroup
- if dataSetFilter.ExclusionFiltersJSON != "" {
- err = json.Unmarshal([]byte(dataSetFilter.ExclusionFiltersJSON), &exclusionFilters)
- if err != nil {
- log.Printf("Failed to parse exclusion filters JSON: %v", err)
- return nil, err
- }
- // log.Printf("Parsed Exclusion Filters: %+v", exclusionFilters)
- }
-
- // Convert filters from []FilterGroup to []utils.FilterGroup manually
- var utilsFilters []utils.FilterGroup
- for _, f := range filters {
- utilsFilters = append(utilsFilters, convertToUtilsFilterGroup(f))
- }
-
- // Apply the filter removal logic only if exclusionFilters has values
- if len(exclusionFilters) > 0 {
- filteredResult := utils.RemoveExcludedFilters(utilsFilters, exclusionFilters)
-
- // Convert back to []FilterGroup before returning
- var finalFilters []FilterGroup
- for _, f := range filteredResult {
- finalFilters = append(finalFilters, convertToFilterGroup(f))
- }
-
- return finalFilters, nil
- }
-
- // If no exclusion filters, return the original filters
- return filters, nil
-
-}
-
-// Convert FilterGroup to utils.FilterGroup
-func convertToUtilsFilterGroup(f FilterGroup) utils.FilterGroup {
- return utils.FilterGroup{
- FilterKey: f.FilterKey,
- FilterData: f.FilterData,
- FilterTextAndOrder: utils.FilterJsonData{
- Order: f.FilterTextAndOrder.Order,
- En: f.FilterTextAndOrder.En,
- Ar: f.FilterTextAndOrder.Ar,
- },
- }
-}
-
-// Convert utils.FilterGroup to FilterGroup
-func convertToFilterGroup(f utils.FilterGroup) FilterGroup {
- return FilterGroup{
- FilterKey: f.FilterKey,
- FilterData: f.FilterData,
- FilterTextAndOrder: FilterJsonData{
- Order: f.FilterTextAndOrder.Order,
- En: f.FilterTextAndOrder.En,
- Ar: f.FilterTextAndOrder.Ar,
- },
- }
-}
-
-// LanguageSource represents the source language data structure
-type LanguageSource struct {
- Dataset string `json:"dataset"`
- Key string `json:"key"`
- Value string `json:"value"`
- ValueEn string `json:"value_en"`
- ValueAr string `json:"value_ar"`
-}
-
-func TranslateFilters(app *pocketbase.PocketBase, dataset string, filterGroups []FilterGroup, langKey string) ([]FilterGroup, error) {
- // Fetch source data from the PocketBase collection
- param := dbx.NewExp("dataset = {:dataset}", dbx.Params{"dataset": dataset})
- records, err := app.Dao().FindRecordsByExpr("charts_variables", param)
- if err != nil {
- return nil, fmt.Errorf("failed to fetch source data: %w", err)
- }
-
- // Parse the source data into a slice of LanguageSource
- var sourceData []LanguageSource
- for _, record := range records {
- source := LanguageSource{
- Dataset: record.GetString("dataset"),
- Key: record.GetString("key"),
- Value: record.GetString("value"),
- ValueEn: record.GetString("value_en"),
- ValueAr: record.GetString("value_ar"),
- }
- sourceData = append(sourceData, source)
- }
-
- // Translate the filter data
- for i, filter := range filterGroups {
-
- // Create a map for quick lookup of translations
- translationMap := make(map[string]string)
- for _, source := range sourceData {
- var value string
- switch langKey {
- case "value_en":
- value = source.ValueEn
- case "value_ar":
- value = source.ValueAr
- default:
- continue
- }
- if source.Key == filter.FilterKey {
- translationMap[source.Value] = value
- }
-
- }
-
- if filter.FilterKey == "TIME_PERIOD" {
- // Skip translation for TIME_PERIOD
- continue
- }
- for j, v := range filter.FilterData {
- // Type assertion for interface{} to string
- value, ok := v.(string)
- if !ok {
- return nil, fmt.Errorf("unexpected type for filter data: %v", v)
- }
-
- // Translate the value if it exists in the map
- if translatedValue, exists := translationMap[value]; exists {
- filterGroups[i].FilterData[j] = translatedValue
- }
- }
- }
-
- return filterGroups, nil
-}
-
-// Generic function to filter JSON data dynamically based on key-value pairs
-func filterDynamicChartData(chartData []map[string]interface{}, keyToFilter, valueToExclude string) []map[string]interface{} {
- var filteredData []map[string]interface{}
-
- for _, item := range chartData {
- obsKey, ok := item["ObsKey"].(map[string]interface{})
- if !ok {
- // Skip items without "ObsKey" or malformed data
- continue
- }
-
- // Check if the key exists and matches the value to exclude
- if obsKey[keyToFilter] == valueToExclude {
- continue
- }
-
- // Add item to filtered data
- filteredData = append(filteredData, item)
- }
-
- return filteredData
-}
-
-func generateToken() string {
- bytes := make([]byte, 32)
- _, err := rand.Read(bytes)
- if err != nil {
- log.Fatal(err)
- }
- return hex.EncodeToString(bytes)
-}
-
-var app *pocketbase.PocketBase
-
-func main() {
-
- // load .env file
- err := godotenv.Load()
- if err != nil {
- log.Println("No .env file found")
- }
-
- var baseUrl = os.Getenv("BASE_URL")
-
- app = pocketbase.New()
-
- app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
- // Serve privacy policy static page
- e.Router.GET("/privacy/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public/privacy"), false))
-
- // API endpoint: returns JSON policy
- e.Router.GET("/api/privacy", func(c echo.Context) error {
- policy := map[string]string{
- "title": "Privacy Policy",
- "content": "We respect your privacy. Data is collected only for authentication and secure service delivery.",
- }
- return c.JSON(http.StatusOK, policy)
- })
-
- return nil
- })
-
- // Hook to send notifications when a new record is created
- app.OnRecordAfterCreateRequest("notification").Add(func(e *core.RecordCreateEvent) error {
- title_en := e.Record.GetString("title_en")
- message_en := e.Record.GetString("message_en")
- title_ar := e.Record.GetString("title_ar")
- message_ar := e.Record.GetString("message_ar")
- notificationID := e.Record.GetString("id") // Get the new notification ID
-
- // Initialize Firebase
- opt := option.WithCredentialsFile("firebase-adminsdk.json")
- firebaseApp, err := firebase.NewApp(context.Background(), nil, opt)
- if err != nil {
- log.Fatalf("Error initializing Firebase: %v", err)
- }
-
- fcmClient, err := firebaseApp.Messaging(context.Background())
- if err != nil {
- log.Fatalf("Error getting FCM client: %v", err)
- }
-
- // Fetch users with valid device tokens
- users, err := app.Dao().FindRecordsByExpr("users", nil) // Adjust collection name if different
- if err != nil {
- return err
- }
-
- for _, user := range users {
- token := user.GetString("device_token")
- language := user.GetString("language")
- if token != "" {
- // tokens = append(tokens, token)
- var message *messaging.Message
- if language == "en" {
- message = &messaging.Message{
- Token: token,
- Notification: &messaging.Notification{
- Title: title_en,
- Body: message_en,
- },
- }
- } else {
- message = &messaging.Message{
- Token: token,
- Notification: &messaging.Notification{
- Title: title_ar,
- Body: message_ar,
- },
- }
-
- }
-
- response, err := fcmClient.Send(context.Background(), message)
- if err != nil {
-
- if err.Error() == "messaging/registration-token-not-registered" {
- log.Println("⚠️ Device token is invalid. Remove it from the database.")
- // Here you should remove the token from your database
- } else {
- log.Printf("Error sending message: %v", err)
- }
- } else {
- fmt.Printf("✅ Successfully sent message: %s\n", response)
-
- // Update the pushed_notification field
- var pushedNotifications []string
-
- // Get existing pushed_notification JSON field
- pushedNotificationsJSON := user.GetString("pushed_notification")
- if pushedNotificationsJSON != "" {
- err := json.Unmarshal([]byte(pushedNotificationsJSON), &pushedNotifications)
- if err != nil {
- log.Printf("⚠️ Error parsing pushed_notification for user %s: %v", user.GetString("id"), err)
- continue
- }
- }
-
- // Append the new notification ID if not already present
- alreadyExists := false
- for _, existingID := range pushedNotifications {
- if existingID == notificationID {
- alreadyExists = true
- break
- }
- }
- if !alreadyExists {
- pushedNotifications = append(pushedNotifications, notificationID)
- }
-
- // Convert back to JSON
- updatedPushedNotifications, err := json.Marshal(pushedNotifications)
- if err != nil {
- log.Printf("⚠️ Error marshalling pushed_notification JSON: %v", err)
- continue
- }
-
- // Update the user record in the database
- user.Set("pushed_notification", string(updatedPushedNotifications))
- if err := app.Dao().SaveRecord(user); err != nil {
- log.Printf("❌ Error updating pushed_notification for user %s: %v", user.GetString("id"), err)
- } else {
- fmt.Printf("✅ Updated pushed_notification for user %s\n", user.GetString("id"))
- }
- }
-
- }
- }
-
- return nil
- })
-
- app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
-
- e.Router.GET("/api/getImagePath", func(c echo.Context) error {
-
- fileName := c.QueryParam("file")
- if fileName == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{
- "error": "missing file parameter",
- })
- }
-
- // Static files folder
- staticPath := "./pb_public"
- fullPath := filepath.Join(staticPath, fileName)
-
- // Check if file exists
- if _, err := os.Stat(fullPath); os.IsNotExist(err) {
- return c.JSON(http.StatusNotFound, map[string]string{
- "error": "file not found",
- })
- }
-
- // Serve the file directly (browser will show the image)
- return c.File(fullPath)
- })
-
- // e.Router.Static("/static", "./pb_public")
-
- e.Router.GET("/pw/auth/confirm-password-reset/:token", func(c echo.Context) error {
- return c.File("pb_public/confirm_password_reset.html")
- })
-
- e.Router.GET("/api/auth/request-password-reset", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- email := c.QueryParam("email")
- if email == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "Email is required"})
- }
-
- // Find user by email
- user, err := app.Dao().FindFirstRecordByData("users", "email", email)
- if err != nil {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
- }
-
- //check the user type
- isOAuthLogin := user.GetString("is_oauth_login")
- if isOAuthLogin != "0" {
- return c.JSON(http.StatusOK, map[string]string{"status": "failed", "message": "Password reset is not available for accounts signed in with Apple or Google."})
- }
-
- name := user.GetString("name")
- // Generate token
- token := generateToken()
- expiration := time.Now().Add(30 * time.Minute).UTC()
-
- // Store token in the database
- user.Set("pw_reset_token", token)
- user.Set("pw_reset_token_expiry", expiration)
-
- if err := app.Dao().SaveRecord(user); err != nil {
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to update user record"})
- }
-
- // Send email
- subject := "Reset your FCSC password"
- // resetURL := fmt.Sprintf("%s/api/auth/reset-password?token=%s", baseUrl, token)
-
- resetURL := fmt.Sprintf("https://fcscapp.onelink.me/forgot-password/%s", email)
-
- body := fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
- إعادة تعيين كلمة المرور - تطبيق إحصاءات الإمارات العربية المتحدة
- Reset your FCSC password
-
- |
-
-
-
-
-
-
- Hello,
- Click on the link below to reset your password.
-
- Reset Password
-
- If you didn't ask to reset your password, you can ignore this email.
- |
-
- مرحبًا،
- يُرجى الضغط على الرابط أدناه لإعادة تعيين كلمة المرور الخاصة بك:
- إعادة تعيين كلمة المرور
-
- إذا لم تطلب إعادة تعيين كلمة المرور، يمكنك تجاهل هذا البريد الإلكتروني.
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
- `, resetURL, resetURL)
-
- // Create the email message
- message := &mailer.Message{
- From: mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress,
- },
- To: []mail.Address{
- {
- Name: name,
- Address: email,
- },
- },
- Subject: subject,
- HTML: body,
- }
-
- // Send the email
- err = app.NewMailClient().Send(message)
- if err != nil {
- log.Printf("Failed to send verification email to user: %v", err)
- return err
- }
-
- return c.JSON(http.StatusOK, map[string]string{"status": "success", "message": "Reset link sent to your email"})
- })
-
- e.Router.GET("/api/auth/reset-password", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- token := c.QueryParam("token")
- if token == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "Token is required"})
- }
-
- // Find user with the token
- user, err := app.Dao().FindFirstRecordByData("users", "pw_reset_token", token)
- if err != nil {
- return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Invalid token"})
- }
-
- // Convert PocketBase `types.DateTime` to `time.Time`
- expiryTime := user.GetDateTime("pw_reset_token_expiry").Time()
- log.Printf("expiryTime : '%v'", expiryTime)
- log.Printf("time.Now : '%v'", time.Now().UTC())
- if expiryTime.Before(time.Now().UTC()) {
- return c.File("pb_public/token_expired.html")
- // return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Token expired"})
- }
-
- // return c.JSON(http.StatusOK, map[string]string{"message": "Token is valid"})
-
- // Serve the reset page
- return c.File("pb_public/confirm_password_reset.html")
- })
-
- e.Router.GET("/api/login_success", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- userID := c.QueryParam("id")
- if userID == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "User ID is required"})
- }
-
- // Find user by ID
- record, err := app.Dao().FindRecordById("users", userID)
- if err != nil {
- log.Printf("User not found: %v", err)
- return c.JSON(http.StatusNotFound, map[string]string{"error": "User not found"})
- }
-
- // Get current login count and convert it properly
- var loginCount int
- if count, ok := record.Get("login_count").(float64); ok {
- loginCount = int(count) // Convert float64 to int
- } else {
- loginCount = 0 // Default if not set
- }
-
- // Increment login count
- loginCount++
- record.Set("login_count", loginCount)
-
- // Save the updated record
- if err := app.Dao().SaveRecord(record); err != nil {
- log.Printf("Failed to update login count: %v", err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to update login count"})
- }
-
- log.Printf("User %s login count updated to %d", userID, loginCount)
- return c.JSON(http.StatusOK, map[string]interface{}{
- "message": "Success",
- "login_count": loginCount,
- })
-
- })
-
- e.Router.GET("/api/getNotification", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- language := c.QueryParam("language")
- if language == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "language is required"})
- }
-
- categoryColumn := "category_" + language
- titleColumn := "title_" + language
- messageColumn := "message_" + language
-
- id := c.QueryParam("id")
-
- if id != "" {
- // Define a single notification struct, not a slice
- var notification struct {
- Id string `db:"id" json:"id"`
- Category string `db:"category" json:"category"`
- Title string `db:"title" json:"title"`
- Message string `db:"message" json:"message"`
- Created string `db:"created" json:"created"`
- }
-
- query := `SELECT id, created, ` + categoryColumn + ` AS category, ` + titleColumn + ` AS title, ` + messageColumn + ` AS message FROM notification WHERE id = {:id}`
- err := app.DB().NewQuery(query).Bind(dbx.Params{"id": id}).One(¬ification)
-
- if err != nil {
- log.Printf("notification not found: %v", err)
- return c.JSON(http.StatusNotFound, map[string]string{"error": "notification not found"})
- }
-
- return c.JSON(http.StatusOK, map[string]interface{}{
- "message": "Success",
- "data": notification,
- })
- }
-
- // Fetch all notifications if no ID is provided
- var notifications []struct {
- Id string `db:"id" json:"id"`
- Category string `db:"category" json:"category"`
- Title string `db:"title" json:"title"`
- Message string `db:"message" json:"message"`
- Created string `db:"created" json:"created"`
- }
-
- query := `SELECT id, created, ` + categoryColumn + ` AS category, ` + titleColumn + ` AS title, ` + messageColumn + ` AS message FROM notification`
- err := app.DB().NewQuery(query).All(¬ifications)
-
- if err != nil {
- log.Printf("notifications not found: %v", err)
- return c.JSON(http.StatusNotFound, map[string]string{"error": "notifications not found"})
- }
-
- return c.JSON(http.StatusOK, map[string]interface{}{
- "message": "Success",
- "data": notifications,
- })
- })
-
- e.Router.GET("/api/removeNotification", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- userID := c.QueryParam("user_id")
- notificationID := c.QueryParam("notification_id")
-
- if userID == "" || notificationID == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "user_id and notification_id are required"})
- }
-
- // Fetch the user by ID
- user, err := app.Dao().FindRecordById("users", userID)
- if err != nil {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "user not found"})
- }
-
- var pushedNotifications []string
-
- // Get existing pushed_notification JSON field
- pushedNotificationsJSON := user.GetString("pushed_notification")
- if pushedNotificationsJSON != "" {
- err := json.Unmarshal([]byte(pushedNotificationsJSON), &pushedNotifications)
- if err != nil {
- log.Printf("⚠️ Error parsing pushed_notification for user %s: %v", userID, err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to parse pushed_notification"})
- }
- }
-
- // Remove the notification ID if it exists
- updatedNotifications := []string{}
- for _, existingID := range pushedNotifications {
- if existingID != notificationID {
- updatedNotifications = append(updatedNotifications, existingID)
- }
- }
-
- // Convert updated array back to JSON
- updatedJSON, err := json.Marshal(updatedNotifications)
- if err != nil {
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to update pushed_notification"})
- }
-
- // Update the user record with the new pushed_notification field
- user.Set("pushed_notification", string(updatedJSON))
- if err := app.Dao().SaveRecord(user); err != nil {
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "failed to save updated notifications"})
- }
-
- return c.JSON(http.StatusOK, map[string]interface{}{
- "message": "success",
- "pushed_notification": updatedNotifications,
- })
- })
-
- e.Router.GET("/api/pushNotification", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- // type Request struct {
- // DeviceToken string `json:"device_token"`
- // }
-
- // var requestData Request
- // log.Printf("🔥 requestData: '%v'", requestData)
- // // Validate required fields
- // if requestData.DeviceToken == "" {
- // return c.JSON(http.StatusBadRequest, map[string]string{"error": "DeviceToken is required"})
- // }
-
- // DeviceToken := requestData.DeviceToken
-
- DeviceToken := c.QueryParam("device_token")
-
- // Load Firebase credentials JSON file
- opt := option.WithCredentialsFile("firebase-adminsdk.json")
- app, err := firebase.NewApp(context.Background(), nil, opt)
- if err != nil {
- log.Printf("🔥 Error initializing Firebase App: '%v'", err)
- }
-
- client, err := app.Messaging(context.Background())
- if err != nil {
- log.Printf("Error getting Messaging client: %v", err)
- }
-
- message := &messaging.Message{
- Token: DeviceToken,
- Notification: &messaging.Notification{
- Title: "Hello!",
- Body: "This is a test push notification By Gowtham.",
- },
- }
-
- response, err := client.Send(context.Background(), message)
- if err != nil {
-
- if err.Error() == "messaging/registration-token-not-registered" {
- log.Println("⚠️ Device token is invalid. Remove it from the database.")
- // Here you should remove the token from your database
- } else {
- log.Printf("Error sending message: %v", err)
- }
- } else {
- fmt.Printf("✅ Successfully sent message: %s\n", response)
- }
-
- return c.JSON(http.StatusOK, map[string]string{"status": "success"})
- })
-
- e.Router.POST("/api/getDataSet", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- type RequestData struct {
- Dataset string `json:"dataset"`
- KPI string `json:"kpi"`
- Language string `json:"language"`
- ColorMode string `json:"color_mode"`
- FilterData []utils.Filter `json:"filter_data"`
- }
-
- var requestData RequestData
-
- // Bind JSON request to struct
- if err := c.Bind(&requestData); err != nil {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request format"})
- }
-
- // Validate required fields
- if requestData.Dataset == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{"error": "dataset is required"})
- }
-
- dataset := requestData.Dataset
- language := requestData.Language
- langKey := "value_" + language
- colorMode := requestData.ColorMode
-
- // Fetch all matching records from charts table
- param := dbx.NewExp("dataset = {:dataset}", dbx.Params{"dataset": dataset})
- records, err := app.Dao().FindRecordsByExpr("charts", param)
- if err != nil {
- log.Printf("Failed to fetch records for dataset '%s': %v", dataset, err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch records"})
- }
- if len(records) == 0 {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "No records found for the given dataset"})
- }
-
- // Fetch all matching records from charts variables table
- languageSource, err := app.Dao().FindRecordsByExpr("charts_variables", param)
- if err != nil {
- log.Printf("Failed to fetch language source: %v", err)
- }
- // Convert languageSource to []map[string]interface{}
- languageSourceConverted := []map[string]interface{}{}
- for _, record := range languageSource {
- languageSourceConverted = append(languageSourceConverted, record.SchemaData())
- }
-
- //construct chart and card data based of configuration/calculation/default
- var aggregatedResponse []map[string]interface{}
- for _, record := range records {
-
- // Extract the URL for each record
- apiURL := record.GetString("url")
- file_name := record.GetString("kpi_file_name")
- if apiURL == "" {
- log.Printf("No URL for record '%v'", record)
- continue // Skip records with no URL
- }
-
- // Fetch and convert XML to JSON
- saveToFile := record.GetBool("file_status")
- chartId := record.GetString("id")
- if !saveToFile { // Check if saveToFile is false
- _, err := fetchAndConvertXMLToJSON(apiURL, "kpi_files", file_name, "charts", chartId)
- if err != nil {
- log.Printf("Failed to process URL '%s': %v", apiURL, err)
- continue // Skip this record and proceed with others
- }
- }
-
- // Read the JSON data from the file
- chartData, err := readJSONFromFile("kpi_files", file_name)
- if err != nil {
- log.Printf("Failed to read JSON for KPI '%s': %v", file_name, err)
- }
-
- // unset key and value if value to be excluded
- keyToFilter := record.GetString("unset_key")
- valueToExclude := record.GetString("unset_value")
- if keyToFilter == "" || valueToExclude == "" {
- // fmt.Println("unset_key or unset_value is empty. Skipping processing.")
- } else {
- // Filter the data dynamically and update chartData
- chartData = filterDynamicChartData(chartData, keyToFilter, valueToExclude)
- }
-
- //logo url generating
- var logoURL string
- cardLogo := record.GetString("card_logo")
- if cardLogo == "" {
- logoURL = ""
- } else {
- logoURL = fmt.Sprintf("%s/api/files/charts/%s/%s",
- baseUrl,
- record.GetString("id"),
- cardLogo,
- )
- }
-
- cResRaw := utils.TranslateChartData(chartData, languageSourceConverted, langKey)
-
- //responce data structure
- chartHeadingKey := "chart_heading_" + language
- chartSubHeadingKey := "chart_sub_heading_" + language
- tabHeadingKey := "tab_heading_" + language
- // Correct conditional assignment for "response"
- recordResult := map[string]interface{}{
- "url": apiURL,
- "dataset": record.GetString("dataset"),
- "main_id": record.GetString("main_id"),
- "sub_id": record.GetString("sub_id"),
- "kpi": record.GetString("kpi"),
- "is_chart": record.GetString("is_chart"),
- "chart_type": record.GetString("chart_type"),
- "group_by": record.GetString("group_by"),
- "chart_heading": record.GetString(chartHeadingKey),
- "chart_sub_heading": record.GetString(chartSubHeadingKey),
- "tab_heading": record.GetString(tabHeadingKey),
- "unset_value": record.Get("unset_value"),
- "card_logo": logoURL,
- "order_id": record.Get("order_id"),
- "card_type_json": record.Get("card_type_json"),
- "tab_order": record.GetString("tab_order"),
- "chart_type_json": record.Get("chart_type_json"),
- "card_full_length": record.Get("card_full_length"),
- "chart_bar_color": record.Get("chart_bar_color"),
- "chart_height": record.Get("chart_height"),
- "chart_label_width": record.Get("chart_label_width"),
- }
-
- var cRes []utils.Observation
- cResJSON, err := json.Marshal(cResRaw)
- if err != nil {
- fmt.Println("Error encoding chart data:", err)
- }
- err = json.Unmarshal(cResJSON, &cRes)
- if err != nil {
- fmt.Println("Error decoding chart data:", err)
- }
-
- if requestData.KPI == record.GetString("kpi") && len(requestData.FilterData) > 0 {
-
- filteredData, err := utils.FilterByMultipleKeys(cRes, requestData.FilterData)
- if err != nil {
- log.Fatal(err)
- }
- if 0 < len(filteredData) {
- cRes = filteredData
- } else {
- defaultYearsFilteredData, err := utils.DefaultYearFilter(cRes, record.GetString("default_latest_year_count"))
- if err != nil {
- fmt.Println("Error processing defaultYearsFilteredData:", err)
- }
- cRes = defaultYearsFilteredData
-
- }
-
- } else {
- defaultYearsFilteredData, err := utils.DefaultYearFilter(cRes, record.GetString("default_latest_year_count"))
- if err != nil {
- fmt.Println("Error processing defaultYearsFilteredData:", err)
- }
- // fmt.Println("defaultYearsFilteredData:", defaultYearsFilteredData)
- cRes = defaultYearsFilteredData
- }
-
- var cardResults []map[string]string
- if record.GetString("is_chart") == "false" { // card data calculations
-
- // Extract `card_type` JSON from `record.Get("card_type")`
- requestData := record.Get("card_type_json")
- requestJSON, err := json.Marshal(requestData) // Convert interface{} to JSON string
- if err != nil {
- fmt.Println("Error encoding request data:", err)
- }
-
- // Decode JSON into an array of `utils.Request`
- var requests []utils.CardCalculationRequest
- err = json.Unmarshal(requestJSON, &requests)
- if err != nil {
- fmt.Println("Error decoding request data:", err)
- }
-
- if record.GetString("custom_calculation") == "true" {
-
- customCalculationReq := record.Get("chart_custom_calculation_json")
-
- var chartCustomCalculationRequest utils.ChartCalculationRequest
-
- jsonData, err := json.Marshal(customCalculationReq)
- if err != nil {
- log.Println("Error marshaling to JSON:", err)
- }
-
- err = json.Unmarshal(jsonData, &chartCustomCalculationRequest)
- if err != nil {
- log.Println("Error unmarshalling JSON:", err)
- }
- customResult, err := utils.CalculateGroupedSum(cRes, chartCustomCalculationRequest)
- if err != nil {
- fmt.Println("Error:", err)
- }
-
- cRes = customResult
-
- }
-
- // Process each request and store results
- for _, req := range requests {
- result, err := utils.CardCalculation(cRes, req)
- if err != nil {
- fmt.Println("Error processing data:", err)
- continue // Skip this request and move to the next
- }
- cardResults = append(cardResults, result)
- }
-
- recordResult["response"] = cardResults
-
- } else {
-
- if record.GetString("custom_calculation") == "false" {
- recordResult["response"] = cRes
- } else {
-
- // fmt.Println("cRes-------", cRes)
-
- customCalculationReq := record.Get("chart_custom_calculation_json")
-
- var chartCustomCalculationRequest utils.ChartCalculationRequest
-
- jsonData, err := json.Marshal(customCalculationReq)
- if err != nil {
- log.Println("Error marshaling to JSON:", err)
- }
-
- err = json.Unmarshal(jsonData, &chartCustomCalculationRequest)
- if err != nil {
- log.Println("Error unmarshalling JSON:", err)
- }
- customResult, err := utils.CalculateGroupedSum(cRes, chartCustomCalculationRequest)
- if err != nil {
- fmt.Println("Error:", err)
- }
-
- recordResult["response"] = customResult
-
- }
-
- }
-
- // Add the result to the aggregated response
- aggregatedResponse = append(aggregatedResponse, recordResult)
-
- }
- //--------------------
-
- // get all filter based on kpi
- var dataSetKpis []struct {
- Kpi string `db:"kpi" json:"kpi"`
- }
- err = app.DB().Select("kpi").From("charts").Where(dbx.HashExp{"dataset": dataset}).GroupBy("kpi").All(&dataSetKpis)
- if err != nil {
- log.Fatalf("Failed to get kpi groupby data: %v", err)
- }
-
- aggregatedFilterResponse := make(map[string]interface{})
- for _, dataSetKpi := range dataSetKpis {
-
- kpi := dataSetKpi.Kpi
- filterData, err := fetchfilterJSON(kpi)
-
- // Translate filter data using the desired dataset and language key
- fRes, err := TranslateFilters(app, dataset, filterData, langKey)
- if err != nil {
- log.Fatalf("Error translating filters: %v", err)
- }
-
- aggregatedFilterResponse[kpi] = fRes
-
- }
- //--------------------
-
- //mainTopic kpi heading and screen color
- headingAndColor := struct {
- DataSet string `db:"data_set" json:"data_set"`
- MainTopic string `db:"main_topic" json:"main_topic"`
- SubTopic string `db:"sub_topic" json:"sub_topic"`
- DataSetTileHeading string `db:"data_set_tile_heading" json:"data_set_tile_heading"`
- HeaderColor string `db:"header_color" json:"header_color"`
- BodyColor string `db:"body_color" json:"body_color"`
- BorderColor string `db:"border_color" json:"border_color"`
- }{}
-
- header_color := "header_color_" + colorMode
- body_color := "body_color_" + colorMode
- border_color := "border_color_" + colorMode
- data_set_tile_heading := "data_set_tile_heading_" + language
- main_topic := "main_topic_" + language
- sub_topic := "sub_topic_" + language
- query := ` SELECT id,data_set,` + data_set_tile_heading + ` AS data_set_tile_heading,` + main_topic + ` AS main_topic,` + sub_topic + ` AS sub_topic, ` + header_color + ` AS header_color,` + body_color + ` AS body_color, ` + border_color + ` AS border_color FROM uae_numbers_screen WHERE data_set = {:data_set} `
- err = app.DB().NewQuery(query).Bind(dbx.Params{"data_set": dataset}).One(&headingAndColor)
- if err != nil {
- log.Printf("Failed to fetch data-set for sub topic %s: %v", dataset, err)
- }
- //---------------------
-
- // Create the response structure
- response := map[string]interface{}{
- "new_filter_data": aggregatedFilterResponse,
- "data": aggregatedResponse,
- "screen_heading_and_color": headingAndColor,
- "kpi": requestData.KPI,
- "filter_data": requestData.FilterData,
- }
-
- if len(aggregatedResponse) == 0 {
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "No data could be fetched from the provided URLs"})
- }
-
- return c.JSON(http.StatusOK, response)
- })
-
- e.Router.GET("/api/getHomePageData", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- language := c.QueryParam("language")
- colorMode := c.QueryParam("color_mode")
-
- // Determine the column name dynamically
- colorPatternColumn := "color_pattern_" + colorMode
-
- // Define the struct for holding the query results
- mainTopics := []struct {
- MainTopicEn string `db:"main_topic_en" json:"main_topic_en"`
- MainTopicAr string `db:"main_topic_ar" json:"main_topic_ar"`
- MainTopicListOrder string `db:"main_topic_list_order" json:"main_topic_list_order"`
- ColorPattern string `db:"color_pattern" json:"color_pattern"`
- }{}
-
- // Execute the query
- query := fmt.Sprintf(`
- SELECT
- main_topic_en,
- main_topic_ar,
- main_topic_list_order,
- %s AS color_pattern
- FROM home_screen
- GROUP BY main_topic_en, main_topic_ar, main_topic_list_order, %s
- ORDER BY main_topic_list_order ASC
- `, colorPatternColumn, colorPatternColumn)
-
- err := app.DB().NewQuery(query).All(&mainTopics)
-
- // err := app.DB().
- // Select("main_topic_en", "main_topic_ar", "main_topic_list_order", "color_pattern").
- // From("home_screen").
- // GroupBy("main_topic_en", "main_topic_ar", "color_pattern").
- // OrderBy("main_topic_list_order ASC").
- // All(&mainTopics)
-
- if err != nil {
- log.Printf("Failed to fetch home_screen data: %v", err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch records"})
- }
-
- // If no records are found
- if len(mainTopics) == 0 {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "No records found for home screen"})
- }
-
- // Final result structure
- result := []map[string]interface{}{}
-
- // Loop through main topics and fetch sub-topic data for each
- for _, mainTopic := range mainTopics {
-
- dataSets := []struct {
- DataSet string `db:"data_set" json:"data_set"`
- DataSetTileHeading string `db:"data_set_tile_heading" json:"data_set_tile_heading"`
- ValueSource string `db:"value_source" json:"value_source"`
- Value string `db:"value" json:"value"`
- DataSetListOrder string `db:"data_set_list_order" json:"data_set_list_order"`
- }{}
-
- languageSpecificColumn := "data_set_tile_heading_" + language
- query := ` SELECT data_set,` + languageSpecificColumn + ` AS data_set_tile_heading,value_source,value, data_set_list_order FROM home_screen WHERE main_topic_en = {:topic}`
- err := app.DB().
- NewQuery(query).
- Bind(dbx.Params{
- "topic": mainTopic.MainTopicEn,
- }).
- All(&dataSets)
-
- if err != nil {
- log.Printf("Error fetching data sets: %v", err)
- continue
- }
-
- var MainTopic string
- if language == "en" {
- MainTopic = mainTopic.MainTopicEn
- } else {
- MainTopic = mainTopic.MainTopicAr
- }
-
- // Add the main topic and its sub-topics to the result
- result = append(result, map[string]interface{}{
- "main_topic": MainTopic,
- "main_topic_list_order": mainTopic.MainTopicListOrder,
- "color_pattern": mainTopic.ColorPattern,
- "tile_data": dataSets,
- })
- }
-
- // Send the combined result as JSON
- return c.JSON(http.StatusOK, result)
-
- })
-
- // e.Router.GET("/api/getHomePageData", func(c echo.Context) error {
-
- // language := c.QueryParam("language")
- // colorMode := c.QueryParam("color_mode")
-
- // var colorPatternColumn string
- // if colorMode == "dark" {
- // colorPatternColumn = "color_pattern_dark"
- // } else {
- // colorPatternColumn = "color_pattern_light"
- // }
-
- // var languageSpecificColumn string
- // if language == "en" {
- // languageSpecificColumn = "data_set_tile_heading_en"
- // } else {
- // languageSpecificColumn = "data_set_tile_heading_ar"
- // }
-
- // // Define the struct for holding the query results
- // mainTopics := []struct {
- // MainTopicEn string `db:"main_topic_en" json:"main_topic_en"`
- // MainTopicAr string `db:"main_topic_ar" json:"main_topic_ar"`
- // MainTopicListOrder string `db:"main_topic_list_order" json:"main_topic_list_order"`
- // ColorPattern string `db:"color_pattern" json:"color_pattern"`
- // }{}
-
- // // Execute the query
- // query := fmt.Sprintf(`
- // SELECT
- // main_topic_en,
- // main_topic_ar,
- // main_topic_list_order,
- // %s AS color_pattern
- // FROM home_screen
- // GROUP BY main_topic_en, main_topic_ar, main_topic_list_order, %s
- // ORDER BY main_topic_list_order ASC
- // `, colorPatternColumn, colorPatternColumn)
-
- // err := app.DB().NewQuery(query).All(&mainTopics)
-
- // // err := app.DB().
- // // Select("main_topic_en", "main_topic_ar", "main_topic_list_order", "color_pattern").
- // // From("home_screen").
- // // GroupBy("main_topic_en", "main_topic_ar", "color_pattern").
- // // OrderBy("main_topic_list_order ASC").
- // // All(&mainTopics)
-
- // if err != nil {
- // log.Printf("Failed to fetch home_screen data: %v", err)
- // return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch records"})
- // }
-
- // // If no records are found
- // if len(mainTopics) == 0 {
- // return c.JSON(http.StatusNotFound, map[string]string{"error": "No records found for home screen"})
- // }
-
- // // Final result structure
- // result := []map[string]interface{}{}
-
- // // Loop through main topics and fetch sub-topic data for each
- // for _, mainTopic := range mainTopics {
-
- // dataSets := []struct {
- // DataSet string `db:"data_set" json:"data_set"`
- // DataSetTileHeading string `db:"data_set_tile_heading" json:"data_set_tile_heading"`
- // ValueSource string `db:"value_source" json:"value_source"`
- // Value string `db:"value" json:"value"`
- // DataSetListOrder string `db:"data_set_list_order" json:"data_set_list_order"`
- // }{}
-
- // query := ` SELECT data_set,` + languageSpecificColumn + ` AS data_set_tile_heading,value_source,value, data_set_list_order FROM home_screen WHERE main_topic_en = {:topic}`
- // err := app.DB().
- // NewQuery(query).
- // Bind(dbx.Params{
- // "topic": mainTopic.MainTopicEn,
- // }).
- // All(&dataSets)
-
- // if err != nil {
- // log.Printf("Error fetching data sets: %v", err)
- // continue
- // }
-
- // var MainTopic string
- // if language == "en" {
- // MainTopic = mainTopic.MainTopicEn
- // } else {
- // MainTopic = mainTopic.MainTopicAr
- // }
-
- // // Add the main topic and its sub-topics to the result
- // result = append(result, map[string]interface{}{
- // "main_topic": MainTopic,
- // "main_topic_list_order": mainTopic.MainTopicListOrder,
- // "color_pattern": mainTopic.ColorPattern,
- // "tile_data": dataSets,
- // })
- // }
-
- // // Send the combined result as JSON
- // return c.JSON(http.StatusOK, result)
-
- // })
-
- e.Router.GET("/api/getUAENumbersData", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- language := c.QueryParam("language")
- colorMode := c.QueryParam("color_mode")
-
- // Determine the column name dynamically
- colorPatternColumn := "color_pattern_" + colorMode
-
- // Define the struct for holding the query results
- mainTopics := []struct {
- MainTopicEn string `db:"main_topic_en" json:"main_topic_en"`
- MainTopicAr string `db:"main_topic_ar" json:"main_topic_ar"`
- MainTopicListOrder string `db:"main_topic_list_order" json:"main_topic_list_order"`
- ColorPattern string `db:"color_pattern" json:"color_pattern"`
- }{}
-
- // Execute the query
-
- // Execute the query
- query := fmt.Sprintf(`
- SELECT
- main_topic_en,
- main_topic_ar,
- main_topic_list_order,
- %s AS color_pattern
- FROM uae_numbers_screen
- GROUP BY main_topic_en, main_topic_ar, %s
- ORDER BY main_topic_list_order ASC
- `, colorPatternColumn, colorPatternColumn)
-
- err := app.DB().NewQuery(query).All(&mainTopics)
-
- // err := app.DB().
- // Select("main_topic_en", "main_topic_ar", "main_topic_list_order", "color_pattern").
- // From("uae_numbers_screen").
- // GroupBy("main_topic_en", "main_topic_ar", "color_pattern").
- // OrderBy("main_topic_list_order ASC").
- // All(&mainTopics)
-
- if err != nil {
- log.Printf("Failed to fetch uae_numbers_screen data: %v", err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch records"})
- }
-
- // If no records are found
- if len(mainTopics) == 0 {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "No records found for home screen"})
- }
-
- // Final result structure
- result := []map[string]interface{}{}
-
- // Loop through main topics and fetch sub-topic data for each
- for _, mainTopic := range mainTopics {
-
- subTopics := []struct {
- SubTopicEn string `db:"sub_topic_en" json:"sub_topic_en"`
- SubTopicAr string `db:"sub_topic_ar" json:"sub_topic_ar"`
- SubTopicListOrder string `db:"sub_topic_list_order" json:"sub_topic_list_order"`
- }{}
-
- // Query to get sub-topics for the current main topic
- err := app.DB().
- Select("sub_topic_en", "sub_topic_ar", "sub_topic_list_order").
- From("uae_numbers_screen").
- Where(dbx.HashExp{"main_topic_en": mainTopic.MainTopicEn}).
- GroupBy("sub_topic_en", "sub_topic_ar").
- OrderBy("sub_topic_list_order ASC").
- All(&subTopics)
-
- if err != nil {
- log.Printf("Failed to fetch sub-topics for main topic %s: %v", mainTopic.MainTopicEn, err)
- continue
- }
-
- result2 := []map[string]interface{}{}
- // Loop through sub Topics topics and fetch dataSet data for each
- for _, subTopic := range subTopics {
-
- dataSets := []struct {
- DataSet string `db:"data_set" json:"data_set"`
- DataSetTileHeading string `db:"data_set_tile_heading" json:"data_set_tile_heading"`
- ValueSource string `db:"value_source" json:"value_source"`
- Value string `db:"value" json:"value"`
- DataSetListOrder string `db:"data_set_list_order" json:"data_set_list_order"`
- }{}
-
- languageSpecificColumn := "data_set_tile_heading_" + language
- query := ` SELECT data_set,` + languageSpecificColumn + ` AS data_set_tile_heading,value_source,value, data_set_list_order FROM uae_numbers_screen WHERE sub_topic_en = {:topic}`
- err := app.DB().
- NewQuery(query).
- Bind(dbx.Params{
- "topic": subTopic.SubTopicEn,
- }).
- All(&dataSets)
-
- // languageSpecificColumn := "data_set_tile_heading_" + language
- // // Query to get data-set for the current sub topic
- // err := app.DB().
- // Select("data_set", languageSpecificColumn, "value_source", "value", "data_set_list_order").
- // From("uae_numbers_screen").
- // Where(dbx.HashExp{"sub_topic_en": subTopic.SubTopicEn}).
- // OrderBy("data_set_list_order ASC").
- // All(&dataSets)
-
- if err != nil {
- log.Printf("Failed to fetch data-set for sub topic %s: %v", subTopic.SubTopicEn, err)
- continue
- }
-
- var SubTopic string
- if language == "en" {
- SubTopic = subTopic.SubTopicEn
- } else {
- SubTopic = subTopic.SubTopicAr
- }
-
- result2 = append(result2, map[string]interface{}{
- "sub_topic": SubTopic,
- "sub_topic_list_order": subTopic.SubTopicListOrder,
- "tile_data": dataSets,
- })
- }
-
- var MainTopic string
- if language == "en" {
- MainTopic = mainTopic.MainTopicEn
- } else {
- MainTopic = mainTopic.MainTopicAr
- }
-
- // Add the main topic and its sub-topics to the result
- result = append(result, map[string]interface{}{
- "main_topic": MainTopic,
- "main_topic_list_order": mainTopic.MainTopicListOrder,
- "color_pattern": mainTopic.ColorPattern,
- "sub_topics": result2,
- })
- }
-
- // Send the combined result as JSON
- return c.JSON(http.StatusOK, result)
-
- })
-
- e.Router.GET("/api/getUserBookmark", func(c echo.Context) error {
-
- apiKey := c.Request().Header.Get("APP_SIGNATURE")
- if apiKey != os.Getenv("APP_SIGNATURE") {
- return c.JSON(http.StatusForbidden, map[string]string{"error": "Invalid API Key"})
- }
-
- language := c.QueryParam("language")
- user_id := c.QueryParam("user_id")
- colorMode := c.QueryParam("color_mode")
-
- // Define the struct for holding the query results
- bookmarks := []struct {
- Id string `db:"id" json:"id"`
- UserId string `db:"user_id" json:"user_id"`
- Dataset string `db:"dataset" json:"dataset"`
- }{}
-
- // Execute the query
- err := app.DB().
- Select("id", "user_id", "dataset").
- From("bookmark").
- Where(dbx.HashExp{"user_id": user_id}).
- All(&bookmarks)
- // return c.JSON(http.StatusOK, bookmarks)
- if err != nil {
- log.Printf("Failed to fetch bookmark data: %v", err)
- return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to fetch records"})
- }
-
- // If no records are found
- if len(bookmarks) == 0 {
- return c.JSON(http.StatusNotFound, map[string]string{"error": "No records found for Bookmark"})
- }
-
- // Final result structure
- result := []map[string]interface{}{}
-
- // Loop through main topics and fetch sub-topic data for each
- for _, bookmark := range bookmarks {
- dataSets := struct {
- DataSet string `db:"data_set" json:"data_set"`
- MainTopic string `db:"main_topic" json:"main_topic"`
- SubTopic string `db:"sub_topic" json:"sub_topic"`
- DataSetTileHeading string `db:"data_set_tile_heading" json:"data_set_tile_heading"`
- ValueSource string `db:"value_source" json:"value_source"`
- Value string `db:"value" json:"value"`
- ColorPattern string `db:"color_pattern" json:"color_pattern"`
- }{}
-
- data_set_tile_heading := "data_set_tile_heading_" + language
- main_topic := "main_topic_" + language
- sub_topic := "sub_topic_" + language
- color_pattern := "color_pattern_" + colorMode
- query := ` SELECT id,data_set,` + data_set_tile_heading + ` AS data_set_tile_heading,` + main_topic + ` AS main_topic,` + sub_topic + ` AS sub_topic,` + color_pattern + ` AS color_pattern,value_source,value FROM uae_numbers_screen WHERE data_set = {:bookmark} `
- err := app.DB().
- NewQuery(query).
- Bind(dbx.Params{
- "bookmark": bookmark.Dataset,
- }).
- One(&dataSets)
-
- log.Printf("query %s: %v", bookmark.Dataset, query)
- if err != nil {
- log.Printf("Failed to fetch data-set for sub topic %s: %v", bookmark.Dataset, err)
- continue
- }
-
- // Add the main topic and its sub-topics to the result
- result = append(result, map[string]interface{}{
- "id": bookmark.Id,
- "data_set": dataSets.DataSet,
- "data_set_tile_heading": dataSets.DataSetTileHeading,
- "value_source": dataSets.ValueSource,
- "value": dataSets.Value,
- "main_topic": dataSets.MainTopic,
- "sub_topic": dataSets.SubTopic,
- "color_pattern": dataSets.ColorPattern,
- })
-
- }
-
- // Send the combined result as JSON
- return c.JSON(http.StatusOK, result)
-
- })
-
- return nil
- })
-
- //send mail while status changed from app admin
- app.OnRecordBeforeUpdateRequest().Add(func(e *core.RecordUpdateEvent) error {
- if e.Collection.Name == "users" {
- originalRecord, err := app.Dao().FindRecordById("users", e.Record.GetString("id"))
- if err != nil {
- return fmt.Errorf("failed to fetch original record: %v", err)
- }
-
- oldStatus := originalRecord.GetString("status")
- newStatus := e.Record.GetString("status")
-
- // Check if the status has changed
- if oldStatus != newStatus {
- err := sendStatusChangeEmail(app, e.Record, newStatus)
- if err != nil {
- return err
- }
- }
- }
- return nil
- })
-
- //Hook to send a verification link to register user
- app.OnRecordAfterCreateRequest().Add(func(e *core.RecordCreateEvent) error {
- if e.Record.Collection().Name == "users" {
- // Retrieve user details
- name := e.Record.GetString("uname")
- email := e.Record.GetString("email")
- userID := e.Record.Id // User ID
-
- // imageRecord, err := app.Dao().FindFirstRecordByData("images", "file_name", "app_logo")
- // if err != nil {
- // log.Printf("Failed to fetch logo from images collection: %v", err)
- // return nil
- // }
-
- // Construct the logo URL
- // logoURL := fmt.Sprintf("%s/api/files/images/%s/%s",
- // baseUrl,
- // imageRecord.Id,
- // imageRecord.GetString("file"),
- // )
-
- // Email subject
- subject := "FCSC Verify Your Email to Complete Registration"
-
- // Generate a verification URL (example: using user ID or token)
- verificationURL := fmt.Sprintf("%s/verify-email?userId=%s", baseUrl, userID)
-
- // Email body
- body := fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
- تطبيق إحصاءات الإمارات العربية المتحدة - يرجى التحقق من عنوان بريدك الإلكتروني
- Please Verify Your Email Address – UAE Stats App
-
- |
-
-
-
-
-
-
- Dear %s,
- Thank you for registering. Please click the link below to verify your email address:
-
- Verify Email
-
- If you did not register for this account, please ignore this email.
- |
-
- عزيزي %s،
- شكرًا لتسجيلك. يُرجى النقر على الرابط أدناه للتحقق من عنوان بريدك الإلكتروني:
-
- تحقق من البريد الإلكتروني
-
- إذا لم تكن قد قمت بتسجيل هذا الحساب، يُرجى تجاهل هذا البريد الإلكتروني.
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
-
- `, name, verificationURL, name, verificationURL)
-
- // Create the email message
- message := &mailer.Message{
- From: mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress,
- },
- To: []mail.Address{
- {
- Name: name,
- Address: email,
- },
- },
- Subject: subject,
- HTML: body,
- }
-
- // Send the email
- err := app.NewMailClient().Send(message)
- if err != nil {
- log.Printf("Failed to send verification email to user: %v", err)
- return err
- }
-
- log.Println("Verification email sent successfully to the user.")
- }
- return nil
- })
-
- // Hook to send an email after a user is created
- app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
-
- e.Router.GET("/api/custom/approve", func(c echo.Context) error {
- userId := c.QueryParam("userId")
- if userId == "" {
- return c.JSON(400, map[string]interface{}{
- "code": 400,
- "message": "Missing userId query parameter.",
- })
- }
-
- // Find the user record
- record, err := app.Dao().FindRecordById("users", userId)
- if err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Error finding user.",
- })
- }
-
- // Check if the email is already verified
- if record.GetBool("reviewed") {
- return c.HTML(http.StatusOK, `
-
-
-
-
-
-
-
✖
-
Your Already Reviewed
-
You have already been reviewed. No further action is required.
-
-
-
- `)
- }
-
- // Update the status
- newStatus := "Approved"
- record.Set("verified", true)
- record.Set("status", newStatus)
- record.Set("reviewed", true)
- if err := app.Dao().SaveRecord(record); err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Failed to verify user.",
- })
- }
-
- // Send the status change email
- err = sendStatusChangeEmail(app, record, newStatus)
- if err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Failed to send approval email.",
- })
- }
-
- return c.HTML(http.StatusOK, `
-
-
-
-
-
-
-
✔
-
User Approved Successfully
-
The user has been approved, and the approval email has been sent.
-
-
-
- `)
- })
-
- return nil
- })
-
- app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
-
- // Approve Endpoint
- e.Router.GET("/api/custom/reject", func(c echo.Context) error {
- userId := c.QueryParam("userId")
- if userId == "" {
- return c.JSON(400, map[string]interface{}{
- "code": 400,
- "message": "Missing userId query parameter.",
- })
- }
-
- // Find the user record in PocketBase by userId
- record, err := app.Dao().FindRecordById("users", userId)
- if err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Error finding user.",
- })
- }
-
- // Check if the email is already verified
- if record.GetBool("reviewed") {
- return c.HTML(http.StatusOK, `
-
-
-
-
-
-
-
✖
-
Your Already Reviewed
-
You have already been reviewed. No further action is required.
-
-
-
- `)
- }
-
- // Check if the status is already "Denied" to avoid duplicate email
- if record.GetString("status") == "Denied" {
- return c.JSON(400, map[string]interface{}{
- "code": 400,
- "message": "User is already rejected.",
- })
- }
-
- // Update the status
- newStatus := "Denied"
- record.Set("verified", false)
- record.Set("status", newStatus)
- record.Set("reviewed", true)
- if err := app.Dao().SaveRecord(record); err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Failed to verify user.",
- })
- }
-
- // Send the status change email
- err = sendStatusChangeEmail(app, record, newStatus)
- if err != nil {
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Failed to send rejection email.",
- })
- }
-
- // Render a success message as an HTML response
- return c.HTML(http.StatusOK, `
-
-
-
-
-
- User Rejected
-
-
-
-
-
❌
-
User Rejected
-
Rejection email sent successfully.
-
-
-
- `)
- })
-
- return nil
- })
-
- // Serve static files from the provided public directory (if exists)
- app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
- // e.Router.GET("/verify-email", verifyEmailHandler)
-
- e.Router.GET("/verify-email", func(c echo.Context) error {
- return verifyEmailHandler(c, baseUrl)
- })
- e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), false))
- return nil
- })
-
- //Feedback Mail Trigger
- app.OnRecordAfterCreateRequest().Add(func(e *core.RecordCreateEvent) error {
- if e.Record.Collection().Name == "feedback" {
- // Retrieve feedback details
- feedback := e.Record
- userId := feedback.GetString("userId")
- emojiRating := feedback.GetString("emoji_rating")
- easeOfUse := feedback.GetString("ease_of_use")
- quality := feedback.GetString("quality")
- design := feedback.GetString("design")
- redundancy := feedback.GetString("redundancy")
- additionalFeedback := feedback.GetString("feedback")
-
- if additionalFeedback == "" {
- additionalFeedback = "No additional feedback provided."
- }
-
- // Fetch user details using userId
- user, err := app.Dao().FindRecordById("users", userId)
- if err != nil {
- log.Printf("Error fetching user details: %v\n", err)
- return err
- }
-
- userName := user.GetString("uname")
- userEmail := user.GetString("email")
-
- // Extract and format the creation time
- createdTime := e.Record.Get("created").(types.DateTime) // Extract created time
- feedbackDateTime := createdTime.Time().UTC() // Convert to time.Time in UTC
-
- // Format the date and time
- formattedDate := feedbackDateTime.Format("02-01-2006") // DD-MM-YYYY
- // formattedDateTime := feedbackDateTime.Format("02.01.2006 15:04 UTC") // DD.MM.YYYY HH:mm UTC
-
- // Admin details
- adminName := "FCSC"
-
- // Email subject
- subject := "UAE Stats Feedback"
-
- // imageRecord, err := app.Dao().FindFirstRecordByData("images", "file_name", "app_logo")
- // if err != nil {
- // log.Printf("Failed to fetch logo from images collection: %v", err)
- // return nil
- // }
-
- // // Construct the logo URL
- // logoURL := fmt.Sprintf("%s/api/files/images/%s/%s",
- // baseUrl,
- // imageRecord.Id,
- // imageRecord.GetString("file"),
- // )
-
- // Email body in HTML format
- body := fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
- ملاحظات حول التطبيق الهاتفي للإحصاءات الإماراتي UAE
- UAE Stats Mobile Application Feedback
-
- |
-
-
-
-
-
-
- Hi Team,
- We have received new feedback from %s. Below are the details:
- Feedback Details:
-
- - User Name: %s
- - Email: %s
- - Date: %s
-
- Feedback:
-
- 1. How was your experience with us today : %s
- 2. How good did we do in this aspect?
- * Ease of Use: %s
- * Quality: %s
- * Design: %s
- * Redundancy: %s
- 3. Tell us how we can improve
- %s
-
- |
-
- مرحبا فريق العمل،
- لقد تلقينا تعليقات جديدة من %s. فيما يلي التفاصيل:
-
- تفاصيل الملاحظات:
-
- - اسم المستخدم: %s
- - البريد الإلكتروني: %s
- - التاريخ : %s
-
- تعليق :
-
- 1. كيف كانت تجربتك معنا اليوم؟ : %s
- 2. ما مدى نجاحنا في هذا الجانب؟
- * سهولة الاستخدام: %s
- * الجودة: %s
- * التصميم: %s
- * التكرار: %s
- 3. أخبرنا كيف يمكننا تحسين خدماتنا.؟
- %s
-
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
-`, userName, userName, userEmail, formattedDate, emojiRating, easeOfUse, quality, design, redundancy, additionalFeedback, userName, userName, userEmail, formattedDate, emojiRating, easeOfUse, quality, design, redundancy, additionalFeedback)
-
- // Define the target type (e.g., "smtp")
- targetType := "feedback_receive_mail"
-
- // Fetch the email configuration record where type matches targetType
- emailConfig, err := app.Dao().FindFirstRecordByData("email_configuration", "type", targetType)
- if err != nil {
- log.Printf("Failed to fetch email configuration for type '%s': %v", targetType, err)
- return fmt.Errorf("email configuration not found for type '%s'", targetType)
- }
-
- // Get the email address
- feedbackEmail := emailConfig.GetString("email")
- if feedbackEmail == "" {
- log.Printf("No email address configured for type '%s'", targetType)
- return fmt.Errorf("no email address configured for type '%s'", targetType)
- }
- // Create the email message
- message := &mailer.Message{
- From: mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress,
- },
- To: []mail.Address{
- {
- Name: adminName,
- Address: feedbackEmail,
- },
- },
- Subject: subject,
- HTML: body,
- }
-
- // Send the email
- err = app.NewMailClient().Send(message)
- if err != nil {
- log.Printf("Failed to send feedback email to admin: %v", err)
- return err
- }
-
- log.Println("Feedback email sent successfully to the admin.")
- }
- return nil
- })
-
- app.OnRecordAfterCreateRequest("otp_requests").Add(func(e *core.RecordCreateEvent) error {
- // Ensure the record has an email field
- email, ok := e.Record.Get("email").(string)
- if !ok || email == "" {
- log.Println("Invalid or missing email field.")
- return nil // or handle this case appropriately
- }
-
- // Generate the OTP
- otp, err := generateOTP()
- if err != nil {
- log.Printf("Failed to generate OTP: %v\n", err)
- return err
- }
-
- // Calculate the expiration time (5 minutes from now)
- expiresAt := time.Now().Add(5 * time.Minute)
-
- // Create a mail.Address for the sender
- sender := mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress, // Ensure this is a valid email
- }
-
- // Create the email message
- message := &mailer.Message{
- From: sender,
- To: []mail.Address{{Address: email}}, // Wrap the recipient email
- Subject: "YOUR VERIFICATION CODE",
- HTML: fmt.Sprintf(
- `Thanks for verifying your %s account!
- Your code is: %d
- Sincerely,
Support team.
`,
- email, otp,
- ),
- }
-
- // Send the email
- if err := app.NewMailClient().Send(message); err != nil {
- log.Printf("Failed to send email: %v\n", err)
- return err
- }
-
- // Update the record with the OTP
- e.Record.Set("otp", fmt.Sprintf("%04d", otp))
- e.Record.Set("expires_at", expiresAt) // Set the expiration time
-
- if err := app.Dao().SaveRecord(e.Record); err != nil {
- log.Printf("Failed to update record with OTP and expiration time: %v\n", err)
- return err
- }
-
- log.Printf("OTP sent to %s expires at and saved successfully.\n", email)
- return nil
- })
-
- // Start the PocketBase server
- go func() {
- if err := app.Start(); err != nil {
- log.Fatal(err)
- }
- }()
-
- // Start your custom HTTP server on port 8091
- log.Println("Starting custom HTTP server on :8091")
- if err := http.ListenAndServe(":8091", nil); err != nil {
- log.Fatal("Failed to start server: ", err)
- }
-}
-
-// generateOTP generates a random 4-digit OTP
-func generateOTP() (int, error) {
- max := big.NewInt(10000) // 4-digit numbers range: 0000 to 9999
- n, err := rand.Int(rand.Reader, max)
- if err != nil {
- return 0, err
- }
- return int(n.Int64()), nil
-}
-
-// Email verification handler
-func verifyEmailHandler(c echo.Context, baseUrl string) error {
- log.Println("Starting verifyEmailHandler")
-
- // Get the userId from the query parameter
- userID := c.QueryParam("userId")
-
- if userID == "" {
- return c.JSON(http.StatusBadRequest, map[string]string{
- "message": "User ID not provided",
- })
- }
-
- // Check if app is properly initialized
- if app == nil {
- log.Fatal("PocketBase app is not initialized")
- return c.JSON(http.StatusInternalServerError, map[string]string{
- "message": "Internal server error",
- })
- }
-
- // Find the user record in PocketBase by userId
- record, err := app.Dao().FindRecordById("users", userID)
- if err != nil {
- log.Printf("Error finding user: %v", err)
- return c.JSON(500, map[string]interface{}{
- "code": 500,
- "message": "Error finding user.",
- })
- }
-
- // Check if the email is already verified
- if record.GetBool("user_mail_verify") {
- return c.HTML(http.StatusOK, `
-
-
-
-
-
-
-
✖
-
Email Already Verified
-
Your email has already been verified. No further action is required.
-
-
-
- `)
- }
-
- // Set the verified status to true
- record.Set("user_mail_verify", true)
- // log.Printf("Failed to verify user: %v", err)
- if err := app.Dao().SaveRecord(record); err != nil {
- log.Printf("Failed to update email verification status: %v", err)
- return c.JSON(http.StatusInternalServerError, map[string]string{
- "error": "Failed to update email verification status",
- })
- }
-
- response := c.HTML(http.StatusOK, `
-
-
-
-
-
-
-
✔
-
Your registration is pending for Admin Approval
-
Access will be granted once your account is approved.
-
-
-
- `)
-
- // Fetch user details for email notification
- username := record.GetString("uname")
- userEmail := record.GetString("email")
-
- // Send admin email asynchronously
- go func() {
- err = sendAdminEmail(app, userID, username, userEmail, baseUrl)
- if err != nil {
- log.Printf("Failed to send admin email: %v", err)
- }
- }()
-
- return response
-
-}
-
-func sendAdminEmail(app *pocketbase.PocketBase, userID, username, userEmail string, baseUrl string) error {
-
- adminNameEn := "Admin"
- adminNameAr := "المدير"
- subject := "New User Registration Pending Review"
-
- // Fetch the email configuration from PocketBase
- emailConfig, err := app.Dao().FindFirstRecordByData("email_configuration", "type", "admin_receive_mail")
- if err != nil {
- log.Printf("Failed to fetch email configuration: %v", err)
- return err
- }
- adminEmail := emailConfig.GetString("email")
-
- // Construct email body
- body := fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
- تسجيل مستخدم جديد - تطبيق إحصاءات الإمارات العربية المتحدة - قيد المراجعة
-
- New User Registration - UAE Stats App - Pending Review
-
- |
-
-
-
-
-
-
- Dear %s,
- A new user has registered on UAE Stats Mobile app and is awaiting your review.
-
- Details
- Name: %s
- Email: %s
-
- Please review the registration and take appropriate action
-
-
- |
-
- عزيزي %s،
- تم تسجيل مستخدم جديد في تطبيق إحصاءات الإمارات
- للهواتف المحمولة، وهو بانتظار مراجعتك.
-
- التفاصيل
- الاسم: %s
- البريد الإلكتروني: %s
-
- يرجى مراجعة التسجيل واتخاذ الإجراء المناسب
- الموافقة أو الرفض.
-
-
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
-
- `,
- adminNameEn, username, userEmail, userEmail, baseUrl, userID, baseUrl, baseUrl, userID, baseUrl,
- adminNameAr, username, userEmail, userEmail, baseUrl, userID, baseUrl, baseUrl, userID, baseUrl)
-
- // Send email
- message := &mailer.Message{
- From: mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress,
- },
- To: []mail.Address{
- {Name: adminNameEn, Address: adminEmail},
- },
- Subject: subject,
- HTML: body,
- }
-
- if err := app.NewMailClient().Send(message); err != nil {
- log.Printf("Failed to send admin email: %v", err)
- return err
- }
-
- log.Println("Admin email sent successfully.")
- return nil
-}
-
-func sendStatusChangeEmail(app *pocketbase.PocketBase, record *models.Record, newStatus string) error {
- userEmail := record.GetString("email")
- userName := record.GetString("uname")
- log.Printf("userName: %s", userName)
-
- if userEmail == "" {
- return fmt.Errorf("user email is empty")
- }
-
- // imageRecord, err := app.Dao().FindFirstRecordByData("images", "file_name", "app_logo")
- // if err != nil {
- // log.Printf("Failed to fetch logo from images collection: %v", err)
- // return nil
- // }
-
- // // Construct the logo URL
- // logoURL := fmt.Sprintf("%s/api/files/images/%s/%s",
- // baseUrl,
- // imageRecord.Id,
- // imageRecord.GetString("file"),
- // )
-
- var subject, body string
-
- if newStatus == "Approved" {
- subject = "Registration Approved – FCSC"
- body = fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
-
-
- تمت الموافقة على التسجيل - إحصاءات الإمارات العربية المتحدة
- Registration Approved – UAE Statistics App
-
- |
-
-
-
-
-
-
- Dear %s,
- We are pleased to inform you that your registration with UAE Stats app has been approved. You can now log in using the credentials that you had created.
-
- Please access your account using the following link: Login.
-
- If you have any questions or believe this decision was made in error, please feel free to contact us for further clarification.
- |
-
- عزيزي %s،
- يسرنا إبلاغك بأنه قد تمت الموافقة على تسجيلك في تطبيق إحصاءات الإمارات. يمكنك الآن تسجيل الدخول باستخدام بيانات الاعتماد التي أنشأتها.
-
-
- يرجى الوصول إلى حسابك عبر الرابط التالي تسجيل الدخول.
-
- إذا كانت لديك أي استفسارات أو كنت تعتقد أن هذا القرار خاطئ، فلا تتردد في التواصل معنا لمزيد من التوضيح.
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
-
- `, userName, userName)
- } else if newStatus == "Denied" {
- subject = "Registration Update – FCSC"
- body = fmt.Sprintf(`
-
-
-
-
-
- Registration Update – UAE Stats App
-
-
-
-
-
-
-
-
-
- تحديث التسجيل – تطبيق إحصاءات الإمارات العربية المتحدة
- Registration Update – UAE Stats App
-
- |
-
-
-
-
-
-
- Dear %s,
- Thank you for registering with UAE Stats Mobile App. After careful review, we regret to inform you that your registration has not been approved at this time.
-
- If you have any questions or believe this decision was made in error, please feel free to contact us for further clarification.
-
- We appreciate your understanding and thank you for your interest.
- |
-
- عزيزي %s،
- نشكرك على تسجيلك في تطبيق إحصاءات الإمارات للهواتف المحمولة. بعد مراجعة دقيقة، يؤسفنا إبلاغك بأنه لم تتم الموافقة على تسجيلك حتى الآن.
-
- إذا كانت لديك أي أسئلة أو كنت تعتقد أن هذا القرار خاطئ، فلا تتردد في التواصل معنا لمزيد من التوضيح.
-
- نقدّر تفهمك ونشكرك على اهتمامك.
- |
-
-
- |
-
-
-
-
- |
-
-
-
- تنبيه: هذه الرسالة الإلكترونية و أيٌ من مرفقاتها قد تحتوي على معلومات سرية وهامة موجهه للشخص/الأشخاص المعنيين وعليه يرجى من المتلقي في حال تلقي الرسالة الإلكترونية عن طريق الخطأ ولم يكن المعني بها، إخطار المرسل وحذفها من بريده الإلكتروني و كذلك إتلاف أي نسخ مطبوعة عنها حيث أنه يحظر عليه قراءة ونسخ ونشر أو توزيع أو استخدام هذه الرسالة إلكترونية و أيٌ من مرفقاتها بأي شكل من الأشكال
- |
-
-
-
- Disclaimer: The information contained in and transmitted with this e-mail message is PRIVILEGED AND/OR CONFIDENTIAL.If you are not the intended recipient, or have received the message by error, please notify the sender via E-Mail or over the telephone and delete this e-mail. You are not authorized to read, copy, disseminate, distribute or use this E-Mail or any of its attachments in any way.
- |
-
-
- |
-
-
-
-
- `, userName, userName)
- } else {
- // If the status is neither "Approved" nor "Denied", skip sending an email
- return nil
- }
-
- // Create and send the email
- message := &mailer.Message{
- From: mail.Address{
- Name: "FCSC",
- Address: app.Settings().Meta.SenderAddress,
- },
- To: []mail.Address{
- {
- Name: userName,
- Address: userEmail,
- },
- },
- Subject: subject,
- HTML: body,
- }
-
- err := app.NewMailClient().Send(message)
- if err != nil {
- return fmt.Errorf("failed to send email: %v", err)
- }
-
- log.Printf("Status change email sent successfully to: %s", userEmail)
- return nil
-}
diff --git a/pb/pb_public/privacy/index.html b/pb/pb_public/privacy/index.html
deleted file mode 100644
index 5a6491c..0000000
--- a/pb/pb_public/privacy/index.html
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
- Privacy Policy - FCSC
-
-
-
-
-
- Privacy Policy
-
-
-
- Welcome to the Federal Competitiveness and Statistics Centre (FCSC) Mobile Application.
- We value your privacy and data protection and are committed to safeguarding your personal
- information. This Privacy Policy explains how we collect, use, share, and protect your information
- when using this App.
-
-
-
- By using the App, you consent to the practices described in this Privacy Policy.
-
-
-
1. Information We Collect
-
1.1 Personal Information
-
- - Name, email address, phone number (when registering an account).
- - User credentials (such as username and password).
- - Demographic data (if provided by the user).
-
-
-
1.2 Usage Data
-
- - Device information (IP address, operating system, device type).
- - App interactions (pages viewed, features used, actions performed).
- - Log data (error reports, login timestamps).
-
-
-
1.3 Location Data
-
If enabled, we may collect location-based data (to enhance certain features).
-
-
1.4 Cookies and Tracking Technologies
-
We may use cookies, analytics tools, and tracking technologies to improve App functionality and user experience.
-
-
2. How We Use Your Information
-
- - Provide and improve services.
- - Deliver insights, statistics, and reports.
- - Personalize user experience.
- - Ensure security and prevent unauthorized access.
- - Comply with UAE laws and regulations.
- - Communicate with users (inquiries, updates, support).
-
-
-
3. How We Share Your Information
-
FCSC does not sell or trade your personal information. However, we may share data in the following cases:
-
- - Government and Legal Compliance (UAE authorities, legal obligations).
- - Third-Party Service Providers (hosting, analytics, technical support).
- - Security and Fraud Prevention.
-
-
-
4. Data Storage and Security
-
- - AES-256 encryption to protect user data.
- - Stored on government-approved secure servers.
- - Access only by authorized personnel.
- - Data retained only as long as legally necessary.
-
-
-
5. User Rights and Choices
-
- - Access & Review your stored personal data.
- - Correction & Deletion of personal data.
- - Opt-Out of marketing communications.
- - Disable location services via device settings.
-
-
To exercise these rights, contact us at [Insert Official FCSC Email].
-
-
6. Third-Party Links and Services
-
The App may contain links to external websites or services (e.g., UAE government portals).
- FCSC is not responsible for third-party privacy practices. Users should review their respective Privacy Policies.
-
-
7. Children's Privacy
-
The App is not intended for children under 13 years. We do not knowingly collect data from minors.
- Parents or guardians may request removal of a child’s data by contacting FCSC.
-
-
8. Changes to This Privacy Policy
-
We may update this Privacy Policy periodically to reflect changes in laws, technology, or user feedback.
- The latest version will always be available in the App.
-
-
9. Contact Information
-
For privacy-related inquiries, you may contact: info@fcsc.gov.ae
-
-
-
diff --git a/pb_data/data.db b/pb_data/data.db
index adb4c5f..c1fdcfd 100755
Binary files a/pb_data/data.db and b/pb_data/data.db differ
diff --git a/pb_data/logs.db b/pb_data/logs.db
index daa8a7c..872e8fc 100644
Binary files a/pb_data/logs.db and b/pb_data/logs.db differ
diff --git a/utils/calculation.go b/utils/calculation.go
index b0c2b74..b52680b 100755
--- a/utils/calculation.go
+++ b/utils/calculation.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"math"
+ "regexp"
"sort"
"strconv"
"strings"
@@ -467,14 +468,19 @@ func CalculateGroupedSum(observations []Observation, request ChartCalculationReq
// Sort by value in descending order or given key
sortByKey := request.SortBy
+ var nonDigit = regexp.MustCompile(`\D`)
if sortByKey != "" {
// Sort by given key
sort.Slice(valueList, func(i, j int) bool {
val1Str := valueList[i].ObsKey[sortByKey]
val2Str := valueList[j].ObsKey[sortByKey]
- val1, _ := strconv.ParseFloat(val1Str, 64)
- val2, _ := strconv.ParseFloat(val2Str, 64)
+ // remove non-digits
+ val1Digits := nonDigit.ReplaceAllString(val1Str, "")
+ val2Digits := nonDigit.ReplaceAllString(val2Str, "")
+
+ val1, _ := strconv.ParseInt(val1Digits, 10, 64)
+ val2, _ := strconv.ParseInt(val2Digits, 10, 64)
sortActionKey := request.SortAction
if sortActionKey == "DESC" {
@@ -550,14 +556,19 @@ func CalculateGroupedSum(observations []Observation, request ChartCalculationReq
// Sort by value in descending order or given key
sortByKey := request.SortBy
+ var nonDigit = regexp.MustCompile(`\D`)
if sortByKey != "" {
// Sort by given key
sort.Slice(result, func(i, j int) bool {
val1Str := result[i].ObsKey[sortByKey]
val2Str := result[j].ObsKey[sortByKey]
- val1, _ := strconv.ParseFloat(val1Str, 64)
- val2, _ := strconv.ParseFloat(val2Str, 64)
+ // remove non-digits
+ val1Digits := nonDigit.ReplaceAllString(val1Str, "")
+ val2Digits := nonDigit.ReplaceAllString(val2Str, "")
+
+ val1, _ := strconv.ParseInt(val1Digits, 10, 64)
+ val2, _ := strconv.ParseInt(val2Digits, 10, 64)
sortActionKey := request.SortAction
if sortActionKey == "DESC" {