mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
chore(): rename website/src -> website/server and website/public -> website/client (#7199)
This commit is contained in:
239
website/server/controllers/top-level/dataexport.js
Normal file
239
website/server/controllers/top-level/dataexport.js
Normal file
@@ -0,0 +1,239 @@
|
||||
import { authWithSession } from '../../middlewares/api-v3/auth';
|
||||
import { model as User } from '../../models/user';
|
||||
import * as Tasks from '../../models/task';
|
||||
import {
|
||||
NotFound,
|
||||
} from '../../libs/api-v3/errors';
|
||||
import _ from 'lodash';
|
||||
import csvStringify from '../../libs/api-v3/csvStringify';
|
||||
import moment from 'moment';
|
||||
import js2xml from 'js2xmlparser';
|
||||
import Pageres from 'pageres';
|
||||
import AWS from 'aws-sdk';
|
||||
import nconf from 'nconf';
|
||||
import got from 'got';
|
||||
import Bluebird from 'bluebird';
|
||||
import locals from '../../middlewares/api-v3/locals';
|
||||
|
||||
let S3 = new AWS.S3({
|
||||
accessKeyId: nconf.get('S3:accessKeyId'),
|
||||
secretAccessKey: nconf.get('S3:secretAccessKey'),
|
||||
});
|
||||
const S3_BUCKET = nconf.get('S3:bucket');
|
||||
|
||||
const BASE_URL = nconf.get('BASE_URL');
|
||||
|
||||
let api = {};
|
||||
|
||||
/**
|
||||
* @api {get} /export/history.csv Export user tasks history in CSV format.
|
||||
* @apiDescription History is only available for habits and dailys so todos and rewards won't be included NOTE: Part of the private API that may change at any time.
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ExportUserHistory
|
||||
* @apiGroup DataExport
|
||||
*
|
||||
* @apiSuccess {string} A cvs file
|
||||
*/
|
||||
api.exportUserHistory = {
|
||||
method: 'GET',
|
||||
url: '/export/history.csv',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
|
||||
let tasks = await Tasks.Task.find({
|
||||
userId: user._id,
|
||||
type: {$in: ['habit', 'daily']},
|
||||
}).exec();
|
||||
|
||||
let output = [
|
||||
['Task Name', 'Task ID', 'Task Type', 'Date', 'Value'],
|
||||
];
|
||||
|
||||
tasks.forEach(task => {
|
||||
task.history.forEach(history => {
|
||||
output.push([
|
||||
task.text,
|
||||
task._id,
|
||||
task.type,
|
||||
moment(history.date).format('YYYY-MM-DD HH:mm:ss'),
|
||||
history.value,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'text/csv',
|
||||
'Content-disposition': 'attachment; filename=habitica-tasks-history.csv',
|
||||
});
|
||||
|
||||
let csvRes = await csvStringify(output);
|
||||
res.status(200).send(csvRes);
|
||||
},
|
||||
};
|
||||
|
||||
// Convert user to json and attach tasks divided by type
|
||||
// at user.tasks[`${taskType}s`] (user.tasks.{dailys/habits/...})
|
||||
async function _getUserDataForExport (user) {
|
||||
let userData = user.toJSON();
|
||||
userData.tasks = {};
|
||||
|
||||
let tasks = await Tasks.Task.find({
|
||||
userId: user._id,
|
||||
}).exec();
|
||||
|
||||
tasks = _.chain(tasks)
|
||||
.map(task => task.toJSON())
|
||||
.groupBy(task => task.type)
|
||||
.each((tasksPerType, taskType) => {
|
||||
userData.tasks[`${taskType}s`] = tasksPerType;
|
||||
})
|
||||
.value();
|
||||
|
||||
return userData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} /export/userdata.json Export user data in JSON format.
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ExportUserDataJson
|
||||
* @apiGroup DataExport
|
||||
* @apiDescription NOTE: Part of the private API that may change at any time.
|
||||
*
|
||||
* @apiSuccess {string} A json file
|
||||
*/
|
||||
api.exportUserDataJson = {
|
||||
method: 'GET',
|
||||
url: '/export/userdata.json',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let userData = await _getUserDataForExport(res.locals.user);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'application/json',
|
||||
'Content-disposition': 'attachment; filename=habitica-user-data.json',
|
||||
});
|
||||
let jsonRes = JSON.stringify(userData);
|
||||
|
||||
res.status(200).send(jsonRes);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /export/userdata.xml Export user data in XML format
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ExportUserDataXml
|
||||
* @apiGroup DataExport
|
||||
* @apiDescription NOTE: Part of the private API that may change at any time.
|
||||
*
|
||||
* @apiSuccess {string} A xml file
|
||||
*/
|
||||
api.exportUserDataXml = {
|
||||
method: 'GET',
|
||||
url: '/export/userdata.xml',
|
||||
middlewares: [authWithSession],
|
||||
async handler (req, res) {
|
||||
let userData = await _getUserDataForExport(res.locals.user);
|
||||
|
||||
res.set({
|
||||
'Content-Type': 'text/xml',
|
||||
'Content-disposition': 'attachment; filename=habitica-user-data.xml',
|
||||
});
|
||||
res.status(200).send(js2xml('user', userData));
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /export/avatar-:uuid.html Render a user avatar as an HTML page
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ExportUserAvatarHtml
|
||||
* @apiGroup DataExport
|
||||
* @apiDescription NOTE: Part of the private API that may change at any time.
|
||||
*
|
||||
* @apiSuccess {string} An html page
|
||||
*/
|
||||
api.exportUserAvatarHtml = {
|
||||
method: 'GET',
|
||||
url: '/export/avatar-:memberId.html',
|
||||
middlewares: [locals],
|
||||
async handler (req, res) {
|
||||
req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID();
|
||||
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let memberId = req.params.memberId;
|
||||
let member = await User
|
||||
.findById(memberId)
|
||||
.select('stats profile items achievements preferences backer contributor')
|
||||
.exec();
|
||||
|
||||
if (!member) throw new NotFound(res.t('userWithIDNotFound', {userId: memberId}));
|
||||
res.render('avatar-static', {
|
||||
title: member.profile.name,
|
||||
env: _.defaults({member}, res.locals.habitrpg),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {get} /export/avatar-:uuid.html Export a user avatar as a PNG file
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName ExportUserAvatarPng
|
||||
* @apiGroup DataExport
|
||||
* @apiDescription NOTE: Part of the private API that may change at any time.
|
||||
*
|
||||
* @apiSuccess {string} A png file
|
||||
*/
|
||||
api.exportUserAvatarPng = {
|
||||
method: 'GET',
|
||||
url: '/export/avatar-:memberId.png',
|
||||
async handler (req, res) {
|
||||
req.checkParams('memberId', res.t('memberIdRequired')).notEmpty().isUUID();
|
||||
|
||||
let validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
let memberId = req.params.memberId;
|
||||
|
||||
let filename = `avatars/${memberId}/.png`;
|
||||
let s3url = `https://${S3_BUCKET}+'.s3.amazonaws.com/${filename}`;
|
||||
let response = await got.head(s3url);
|
||||
|
||||
// cache images for 10 minutes on aws, else upload a new one
|
||||
if (response.statusCode === 200 && moment().diff(response.headers['last-modified'], 'minutes') < 10) {
|
||||
return res.redirect(301, s3url);
|
||||
}
|
||||
|
||||
let [stream] = await new Pageres()
|
||||
.src(`${BASE_URL}/export/avatar-${memberId}.html`, ['140x147'], {
|
||||
crop: true,
|
||||
filename: filename.replace('.png', ''),
|
||||
})
|
||||
.run();
|
||||
|
||||
let s3upload = S3.upload({
|
||||
Bucket: S3_BUCKET,
|
||||
Key: filename,
|
||||
ACL: 'public-read',
|
||||
StorageClass: 'REDUCED_REDUNDANCY',
|
||||
ContentType: 'image/png',
|
||||
Expires: moment().add({minutes: 3}),
|
||||
Body: stream,
|
||||
});
|
||||
|
||||
let s3res = await new Bluebird((resolve, reject) => {
|
||||
s3upload.send((err, s3uploadRes) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(s3uploadRes);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
res.redirect(s3res.Location);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = api;
|
||||
Reference in New Issue
Block a user