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