From 25d453109b0b9b4e344a2a670198524703e2ba23 Mon Sep 17 00:00:00 2001 From: puni9869 Date: Sun, 10 Nov 2024 15:57:09 +0530 Subject: [PATCH] feat: download the data functionality done --- frontend/app.js | 3 +- frontend/common/index.js | 37 +++++++++++++ frontend/setting/index.js | 90 +++++++++++++++++++++----------- models/urls.go | 36 ++++++++----- server/routers.go | 1 + server/setting/setting.go | 27 ++++++++++ templates/setting/container.tmpl | 8 +-- 7 files changed, 152 insertions(+), 50 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index ee0c25b..a000378 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -1,5 +1,5 @@ import {NavItemSelected, SideNavCollapse, AddNewWebLinkInit, WebLinkActionsInit, ShareLinkInit} from './home/index.js'; -import {DeleteAccountModelInit, DisableMyAccountInit} from './setting/index.js'; +import {DeleteAccountModelInit, DisableMyAccountInit, DownloadMyData} from './setting/index.js'; document.addEventListener('DOMContentLoaded', function () { NavItemSelected(); @@ -9,5 +9,6 @@ document.addEventListener('DOMContentLoaded', function () { AddNewWebLinkInit(); WebLinkActionsInit(); ShareLinkInit(); + DownloadMyData(); console.info('App is loaded'); }, false); diff --git a/frontend/common/index.js b/frontend/common/index.js index de37555..9546621 100644 --- a/frontend/common/index.js +++ b/frontend/common/index.js @@ -12,3 +12,40 @@ export function CloseModal(modalId, toClose) { toClose.classList.toggle('hidden'); }); } + +export function JsonToCsv(jsonData) { + let csv = ''; + // Get the headers + let headers = Object.keys(jsonData[0]); + csv += headers.join(',') + '\n'; + + // Add the data + jsonData.forEach(function (row) { + let data = headers.map(header => row[header]).join(','); + csv += data + '\n'; + }); + return csv; +} + +export function SaveFile(filename, dataObjToWrite, format) { + const blob = new Blob([ + format === 'json' ? JSON.stringify(dataObjToWrite) : JsonToCsv(dataObjToWrite) + ], + { + type: `text/${format}` + }); + const link = document.createElement("a"); + + link.download = filename; + link.href = window.URL.createObjectURL(blob); + link.dataset.downloadurl = [`text/${format}`, link.download, link.href].join(':'); + + const evt = new MouseEvent("click", { + view: window, + bubbles: true, + cancelable: true, + }); + + link.dispatchEvent(evt); + link.remove() +} diff --git a/frontend/setting/index.js b/frontend/setting/index.js index 09236de..3275d38 100644 --- a/frontend/setting/index.js +++ b/frontend/setting/index.js @@ -1,40 +1,68 @@ -import { RedirectToLogin } from '../common/index.js'; +import {RedirectToLogin, SaveFile} from '../common/index.js'; export function DeleteAccountModelInit() { - const deleteBtn = document.querySelector('#delete-my-account'); - if (!deleteBtn) { - return; - } - const deleteModal = document.querySelector('#delete-account-modal'); - deleteBtn.addEventListener('click', (e) => { - deleteModal.classList.toggle('hidden'); - }); + const deleteBtn = document.querySelector('#delete-my-account'); + if (!deleteBtn) { + return; + } + const deleteModal = document.querySelector('#delete-account-modal'); + deleteBtn.addEventListener('click', (e) => { + deleteModal.classList.toggle('hidden'); + }); } async function DisableAccount() { - const url = '/setting/disablemyaccount'; - try { - const response = await fetch(url, { method: 'PUT' }); - if (!response.ok) { - throw new Error(`Response status: ${response.status}`); - } - return await response.json(); - } catch (error) { - console.error(error.message); - } + const url = '/setting/disablemyaccount'; + try { + const response = await fetch(url, {method: 'PUT'}); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + return await response.json(); + } catch (error) { + console.error(error.message); + } } export function DisableMyAccountInit() { - const disableBtn = document.querySelector('#disable-my-account'); - if (!disableBtn) { - return; - } - disableBtn.addEventListener('click', async (e) => { - console.info('Disable my account event'); - const res = await DisableAccount(); - if (res && res?.Status === 'OK') { - console.info('Account is disabled until you logged in back.'); - RedirectToLogin(); - } - }); + const disableBtn = document.querySelector('#disable-my-account'); + if (!disableBtn) { + return; + } + disableBtn.addEventListener('click', async (e) => { + console.info('Disable my account event'); + const res = await DisableAccount(); + if (res && res?.Status === 'OK') { + console.info('Account is disabled until you logged in back.'); + RedirectToLogin(); + } + }); +} + +export function DownloadMyData() { + const download = document.querySelectorAll('#download'); + if (!download.length) { + return; + } + download.forEach(d => { + d.addEventListener('click', async (e) => { + if (!e.target.dataset?.format.length) { + return + } + const url = `/setting/downloadmydata/${e.target.dataset?.format}`; + try { + const headers = new Headers(); + headers.append('Content-Type', 'application/json'); + + const response = await fetch(url, {method: 'GET', headers: headers}); + if (!response.ok) { + throw new Error(`Response status: ${response.status}`); + } + const jData = await response.json(); + SaveFile(`pinmyblogs.${e.target.dataset?.format}`, jData, e.target.dataset?.format); + } catch (error) { + console.error(error.message); + } + }); + }); } diff --git a/models/urls.go b/models/urls.go index db9e35e..5162242 100644 --- a/models/urls.go +++ b/models/urls.go @@ -1,19 +1,27 @@ package models -import "gorm.io/gorm" +import ( + "database/sql" + "gorm.io/gorm" + "time" +) +type DeletedAt sql.NullTime type Url struct { - gorm.Model - WebLink string - IsActive bool - IsDeleted bool - CreatedBy string `gorm:"index;size:255;not null"` - Comment string - Summary string - Title string - Tag string - IsFav bool - IsArchived bool - Category string - IsBookMarked bool + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + WebLink string `json:"webLink"` + IsActive bool `json:"-"` + IsDeleted bool `json:"isDeleted"` + CreatedBy string `gorm:"index;size:255;not null" json:"createdBy"` + Comment string `json:"-"` + Summary string `json:"summary"` + Title string `json:"title"` + Tag string `json:"tag"` + IsFav bool `json:"isFav"` + IsArchived bool `json:"isArchived"` + Category string `json:"-"` + IsBookMarked bool `json:"isBookMarked"` } diff --git a/server/routers.go b/server/routers.go index e06d390..a3f3c18 100644 --- a/server/routers.go +++ b/server/routers.go @@ -57,6 +57,7 @@ func RegisterRoutes(r *gin.Engine, sessionStore session.Store) { settingsRoute := authRouters.Group("/setting") { settingsRoute.GET("", setting.Setting) + settingsRoute.GET("downloadmydata/:format{json|csv|html}", setting.DownloadMyData) settingsRoute.DELETE("/deletemyaccount", setting.DeleteMyAccount) settingsRoute.PUT("/disablemyaccount", setting.DisableMyAccount) } diff --git a/server/setting/setting.go b/server/setting/setting.go index f005637..66bfcd2 100644 --- a/server/setting/setting.go +++ b/server/setting/setting.go @@ -7,6 +7,7 @@ import ( "github.com/puni9869/pinmyblogs/pkg/database" "github.com/puni9869/pinmyblogs/pkg/logger" "net/http" + "slices" "strings" "github.com/gin-contrib/sessions" @@ -67,3 +68,29 @@ func DisableMyAccount(c *gin.Context) { database.Db().Save(user) c.JSON(http.StatusOK, map[string]string{"Status": "OK"}) } + +func DownloadMyData(c *gin.Context) { + log := logger.NewLogger() + + format := strings.ToLower(c.Param("format")) + if !slices.Contains([]string{"csv", "json", "html"}, format) { + format = "json" + } + + session := sessions.Default(c) + currentlyLoggedIn := session.Get(middlewares.Userkey) + + var urls []models.Url + db := database.Db() + result := db.Where("created_by =? and is_active = ?", currentlyLoggedIn.(string), true).Find(&urls) + if result.Error != nil { + log.WithError(result.Error).Error("Error in fetching the data") + c.JSON(http.StatusInternalServerError, map[string]string{"Status": "NOT_OK", "Message": "Something went wrong."}) + return + } + if result.RowsAffected == 0 { + log.WithField("resultCount", result.RowsAffected).Info("Fetching the result") + c.JSON(http.StatusNotFound, map[string]string{"Status": "NOT_OK", "Message": "No Data."}) + } + c.JSON(http.StatusAccepted, urls) +} diff --git a/templates/setting/container.tmpl b/templates/setting/container.tmpl index daeb24d..2502120 100644 --- a/templates/setting/container.tmpl +++ b/templates/setting/container.tmpl @@ -59,9 +59,9 @@

You can download your data in the formats we have listed below.

-

Download .csv

-

Download .json

-

Download .html

+

Download .csv

+

Download .json

+

Download .html


Send me my data over email

@@ -77,7 +77,7 @@ I authorize the removal of my account, along with all associated data. 😞

-

Deactivate my account until I login back.

+

Deactivate my account until I login back.

Delete My Account