Skip to content

Commit

Permalink
feat: download the data functionality done
Browse files Browse the repository at this point in the history
  • Loading branch information
puni9869 committed Nov 10, 2024
1 parent 7daa0db commit 25d4531
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 50 deletions.
3 changes: 2 additions & 1 deletion frontend/app.js
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -9,5 +9,6 @@ document.addEventListener('DOMContentLoaded', function () {
AddNewWebLinkInit();
WebLinkActionsInit();
ShareLinkInit();
DownloadMyData();
console.info('App is loaded');
}, false);
37 changes: 37 additions & 0 deletions frontend/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
90 changes: 59 additions & 31 deletions frontend/setting/index.js
Original file line number Diff line number Diff line change
@@ -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);
}
});
});
}
36 changes: 22 additions & 14 deletions models/urls.go
Original file line number Diff line number Diff line change
@@ -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"`
}
1 change: 1 addition & 0 deletions server/routers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
27 changes: 27 additions & 0 deletions server/setting/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
8 changes: 4 additions & 4 deletions templates/setting/container.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@
<p class="mb-3 text-md">
<span>You can download your data in the formats we have listed below.</span>
</p>
<p><span class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .csv</span></p>
<p><span class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .json</span></p>
<p><span class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .html</span></p>
<p><span id="download" data-format="csv" class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .csv</span></p>
<p><span id="download" data-format="json" class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .json</span></p>
<p><span id="download" data-format="csv" class="ml-1 text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none">Download .html</span></p>
<br />
<p><span class="ml-1 select-none {{if eq .ShareDataOverMail "true"}}text-indigo-400 hover:underline hover:underline-offset-4 cursor-pointer {{else}}text-gray-400 cursor-not-allowed{{end}}">Send me my data over email</span></p>
</div>
Expand All @@ -77,7 +77,7 @@
<span class="font-bold italic">I authorize the removal of my account, along with all associated data.</span>
<span class="h-5 w-5">&#128542;</span>
</p>
<p><span class="text-indogo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none" id="disable-my-account">Deactivate my account until I login back.</span></p>
<p><span class="text-indigo-400 cursor-pointer hover:underline hover:underline-offset-4 select-none" id="disable-my-account">Deactivate my account until I login back.</span></p>
<div class="mb-2"></div>
<p><span class="text-red-400 cursor-pointer hover:underline hover:underline-offset-4 select-none" id="delete-my-account">Delete My Account</span></p>
</div>
Expand Down

0 comments on commit 25d4531

Please sign in to comment.