Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ Note that responses may not always have the full schema of the object. Based on
- `role` - Role of the user. Can be either `standard` or `staff`. Staff can perform additional actions.
- `points` - Points earned by the user. Default is `0`.
- `created` - ISO datetime string of account creation date.
- `profilePicture` - Name of profile picture file. Nullable. Use this to interpolate into `${SERVERURL}/cdn/${profilePicture}` to get the full URL.
- `lastLogin` - ISO datetime string of last login date. Used to compute sesison expiry. Nullable.
- `authToken` - Authentication token for the user. Used to authenticate requests. Nullable.
- `activeGame` - ID of the game the user is currently playing. Nullable.
Expand All @@ -151,7 +152,7 @@ Note that responses may not always have the full schema of the object. Based on
- `scenarioID` - Primary key.
- `name` - Name of the scenario.
- `description` - Description of the scenario.
- `backgroundImage` - Name of the background image. Use this to interpolate into `${SERVERURL}/public/img/${backgroundImage}` to get the full URL.
- `backgroundImage` - Name of the background image. Use this to interpolate into `${SERVERURL}/cdn/${backgroundImage}` to get the full URL.
- `modelRole` - Role of the AI model in the scenario. Helps AI get a perspective when generating dialogues.
- `userRole` - Role of the user in the scenario. Helps AI get a perspective when responding to dialogues.
- `created` - ISO datetime string of scenario creation date.
Expand Down Expand Up @@ -275,6 +276,7 @@ Sample success response for user accessing their own data with aggregate perform
"email": "user@example.com",
"role": "standard",
"points": 117,
"profilePicture": null,
"created": "2024-09-17T11:51:42.837Z",
"lastLogin": "2024-09-17T11:51:42.837Z",
"activeGame": null,
Expand Down Expand Up @@ -433,6 +435,20 @@ Sample success response:
SUCCESS: Account updated successfully.
```

## POST `/identity/uploadProfilePicture`

Authorisation required: YES

Updates `profilePicture` attribute to be name of the uploaded file. Name of file submitted does not matter, as it will be renamed to a UUID.

Available request body (**Multi-part form data**) fields:
- `image` - File to be stored as profile picture.

Sample success response:
```
SUCCESS: Profile picture uploaded successfully. File: e24c0b66-7b7d-4315-b312-c5e243b54373.png
```

## POST `/identity/changePassword`

Authorisation required: YES
Expand Down Expand Up @@ -498,6 +514,7 @@ Sample success response:
"userID": "4005c2f4-f0d9-43e6-b0c8-4ef389c189ed",
"username": "someuser",
"email": "email@example.com",
"profilePicture": null,
"created": "2024-09-09T09:34:09.029Z",
"lastLogin": "2024-09-09T10:21:01.662Z",
"activeGame": null,
Expand Down Expand Up @@ -530,6 +547,7 @@ Sample success response:
"username": "someuser2",
"email": "email@example.com",
"points": 0,
"profilePicture": null,
"created": "2024-09-13T11:52:15.806Z",
"lastLogin": "2024-09-13T11:52:15.806Z",
"activeGame": null,
Expand Down
4 changes: 4 additions & 0 deletions models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: false
},
profilePicture: {
type: DataTypes.STRING,
allowNull: true
},
lastLogin: {
type: DataTypes.STRING,
allowNull: true
Expand Down
2 changes: 2 additions & 0 deletions routes/export.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ router.get("/export", async (req, res) => {
try {
fullSourceJSON = await Promise.all(fullSourceJSON.map(async user => {
user.banned = user.banned ? "Yes" : "No";
user.profilePicture = user.profilePicture ? "Yes" : "No";

user.playedGames.sort((a, b) => {
return new Date(a.startedTimestamp) - new Date(b.startedTimestamp)
Expand Down Expand Up @@ -264,6 +265,7 @@ Username: ${user.username}
Email: ${user.email}
Role: ${user.role}
Points: ${user.points}
Profile Picture: ${user.profilePicture}
Account Created: ${new Date(user.created).toString()}
Last Login: ${new Date(user.lastLogin).toString()}
Banned: ${user.banned}
Expand Down
59 changes: 58 additions & 1 deletion routes/identity.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ const express = require('express');
const yup = require('yup');
const { v4: uuidv4 } = require('uuid');
const { User, GameEvaluation } = require('../models');
const { Encryption, Logger, Universal, Extensions } = require('../services');
const { Encryption, Logger, Universal, Extensions, FileManager } = require('../services');
const { authorise, authoriseStaff } = require('../middleware/auth');
const { Model } = require('sequelize');
const { storeImage } = require('../middleware/storeImage');
const multer = require('multer');

const router = express.Router();

Expand Down Expand Up @@ -283,6 +285,61 @@ router.post('/update', authorise, async (req, res) => {
}
})

router.post('/uploadProfilePicture', authorise, async (req, res) => {
var user;
try {
user = await User.findByPk(req.userID);
} catch (err) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Failed to retrieve user; error: ${err}`);
return res.status(500).send(`ERROR: Failed to process request.`);
}

storeImage(req, res, async (err) => {
if (err instanceof multer.MulterError) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Image upload error; error: ${err}.`);
return res.status(400).send("ERROR: Image upload error.");
} else if (err) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Internal server error; error: ${err}.`);
return res.status(400).send(`ERROR: ${err}`);
} else if (req.file === undefined) {
res.status(400).send("UERROR: No file selected.");
return;
}

const fileName = req.file.filename;

// Delete existing picture, if any
if (user.profilePicture) {
try {
const fileDeletion = await FileManager.deleteFile(user.profilePicture);
if (fileDeletion !== true) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Failed to delete existing profile picture; error: ${fileDeletion}.`);
}
} catch (err) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Failed to delete existing profile picture; error: ${err}.`);
}
}

// Save new profile picture
try {
const fileSave = await FileManager.saveFile(fileName);
if (fileSave !== true) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Failed to save profile picture; error: ${fileSave}.`);
return res.status(500).send(`ERROR: Failed to process request.`);
}

user.profilePicture = fileName;
await user.save();
} catch (err) {
Logger.log(`IDENTITY UPLOADPROFILEPICTURE ERROR: Failed to save profile picture; error: ${err}.`);
return res.status(500).send(`ERROR: Failed to process request.`);
}

Logger.log(`IDENTITY UPLOADPROFILEPICTURE: Profile picture uploaded for user '${user.username}'.`);
return res.send(`SUCCESS: Profile picture uploaded successfully. File: ${fileName}`);
})
})

router.post('/changePassword', authorise, async (req, res) => {
var user;
try {
Expand Down
1 change: 1 addition & 0 deletions services/Extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class Extensions {
UserEmail: user.email,
UserRole: user.role,
UserPoints: user.points,
UserProfilePicture: user.profilePicture,
UserCreated: user.created,
UserLastLogin: user.lastLogin,
UserMindsListening: user.mindsListening,
Expand Down