Created profile pictures.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
feature/profile
Armored Dragon 2024-07-11 18:57:39 -05:00
parent f56cfc0e6b
commit 411d108813
Signed by: ArmoredDragon
GPG Key ID: C7207ACC3382AD8B
9 changed files with 87 additions and 21 deletions

View File

@ -101,7 +101,7 @@ async function getUser({ user_id, username, include_password = false }) {
}
// TODO: Rename patchUser
async function editUser({ requester_id, user_id, user_content }) {
const valid_settings = ['display_name', 'password', 'role']; // Valid settings that can be changed
const valid_settings = ["display_name", "password", "role", "profile_image"]; // Valid settings that can be changed
let user = await getUser({ user_id: user_id, include_password: true });
if (!user.success) return _r(false, "User not found");
@ -109,22 +109,35 @@ async function editUser({ requester_id, user_id, user_content }) {
// TODO:
// If there was a role change, see if the acting user can make these changes
const setting_name = user_content.setting_name
const setting_name = user_content.setting_name;
if (!valid_settings.includes(setting_name)) return _r(false, "Invalid setting.");
if (setting_name == 'password'){
if (setting_name == "password") {
// Check if current password value is correct
const password_match = await bcrypt.compare(user_content.original_password, user.password);
if (!password_match) return _r(false, "Incorrect password")
if (!password_match) return _r(false, "Incorrect password");
// If successful, compute new password hash
user_content.value = await bcrypt.hash(user_content.value, 10);
}
if (setting_name == "profile_image") {
const folder_params = { Bucket: process.env.S3_BUCKET_NAME, Prefix: `${process.env.ENVIRONMENT}/user/${user.id}` };
const listed_objects = await s3.send(new ListObjectsCommand(folder_params));
const all_media = listed_objects.Contents;
for (let i = 0; all_media.length > i; i++) {
if (all_media[i].Key.includes(user_content.value)) continue;
// Delete other profile pictures
deleteMedia({ parent_id: user.id, parent_type: "user", file_name: all_media[i].Key.split("/")[3] });
}
}
let formatted = {};
formatted[setting_name] = user_content.value;
await prisma.user.update({ where: { id: user.id }, data: formatted })
await prisma.user.update({ where: { id: user.id }, data: formatted });
return _r(true);
}
async function deleteUser({ user_id }) {
@ -683,4 +696,4 @@ const _r = (s, m) => {
return { success: s, message: m };
};
module.exports = { settings, newUser, getUser, editUser, getPost, newPost, editPost, deletePost, getBiography, updateBiography, uploadMedia, getTags, postSetting, getSetting, installTheme, deleteTheme };
module.exports = { settings, newUser, getUser, editUser, getPost, newPost, editPost, deletePost, getBiography, updateBiography, uploadMedia, getTags, postSetting, getSetting, installTheme, deleteTheme, getMedia };

View File

@ -46,7 +46,8 @@ async function postSetting(request, response) {
async function postImage(request, response) {
// TODO: Permissions for uploading images
// TODO: Verification for image uploading
return response.json(await core.uploadMedia({ parent_id: request.body.post_id, parent_type: request.body.parent_type, file_buffer: request.body.buffer, content_type: request.body.content_type }));
// FIXME: Naming
return response.json(await core.uploadMedia({ parent_id: request.body.post_id || request.body.parent_id, parent_type: request.body.parent_type, file_buffer: request.body.buffer, content_type: request.body.content_type }));
}
async function deleteImage(req, res) {
// TODO: Permissions for deleting image

View File

@ -45,14 +45,16 @@ async function login(request, response) {
response.render(_getThemePage("login"), await getDefaults(request));
}
async function author(req, res) {
const user = await core.getUser({ user_id: req.params.author_id });
let user = await core.getUser({ user_id: req.params.author_id });
// FIXME: Bandage fix for author get error
if (!user.success) return res.redirect("/");
const profile = await core.getBiography({ author_id: user.data.id });
user = user.data;
const profile = await core.getBiography({ author_id: user.id });
// TODO: Check for success
const posts = await core.getPost({ requester_id: user.data.id });
const posts = await core.getPost({ requester_id: user.id });
const profile_image = await core.getMedia({ parent_id: user.id, parent_type: "user", file_name: user.profile_image });
res.render(_getThemePage("author"), { ...(await getDefaults(req)), post: { ...profile.data, post_count: posts.data.length } });
res.render(_getThemePage("author"), { ...(await getDefaults(req)), post: { ...profile.data, post_count: posts.data.length, profile_image: profile_image } });
}
async function authorEdit(request, response) {
let author = await core.getBiography({ author_id: request.params.author_id });

View File

@ -1,4 +1,18 @@
import globals from "globals";
import pluginJs from "@eslint/js";
export default [{ files: ["**/*.js"], languageOptions: { sourceType: "commonjs" } }, { languageOptions: { globals: globals.browser } }, { rules: { "no-unused-vars": "error", "no-undef": "error", "indent": ["error", "tab", { tabWidth: 4 }], "semi-style": ["error", "last"], } }, pluginJs.configs.recommended];
export default [
{
files: ["**/*.js"],
languageOptions: { sourceType: "commonjs" },
},
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
{
rules: {
"no-unused-vars": "error",
"semi-style": ["error", "last"],
indent: ["error", "tab"],
},
},
];

View File

@ -42,8 +42,8 @@
}
.page .page-center .about .profile-picture img {
margin: auto;
max-height: 200px;
max-width: 200px;
height: 200px;
width: 200px;
}
.page .page-center .about .displayname {
font-size: 1.4rem;

View File

@ -43,8 +43,8 @@
img {
margin: auto;
max-height: 200px;
max-width: 200px;
height: 200px;
width: 200px;
}
}

View File

@ -25,7 +25,7 @@
<%- post.content %>
</div>
<div class="about">
<div class="profile-picture"><img src="<%= post.profile_picture %>" /></div>
<div class="profile-picture"><img src="<%= post.profile_image || "/img/hourglass.svg" %>" /></div>
<div class="displayname"><%= post.owner.display_name || post.owner.username %></div>
<!-- TODO: Format Date/time -->
<div class="stat">Registered <%= post.created_date.toLocaleString('en-US', { dateStyle:'medium' }) || "Null" %></div>

View File

@ -1,7 +1,7 @@
async function changeValue(setting_name, element) {
async function changeValue(setting_name, value) {
const form = {
setting_name: setting_name,
value: element.value,
value: value,
id: window.location.href.split("/")[4],
};
const response = await request(`/api/web/user`, "PATCH", form);
@ -31,7 +31,7 @@ function changePasswordInputUpdate() {
async function sendPasswordUpdate() {
const new_password_1 = qs("#cp-new-1");
const original_password_value = qs("#cp-current").value
const original_password_value = qs("#cp-current").value;
const form = {
setting_name: "password",
@ -44,4 +44,39 @@ async function sendPasswordUpdate() {
if (response.body.success) {
alert("Successfully changed password");
}
}
}
const fileInput = qs("#profile_picture");
fileInput.addEventListener("change", uploadProfileImage);
async function uploadProfileImage(event) {
const file = event.target.files[0];
const image_object = {
data_blob: new Blob([await file.arrayBuffer()]),
content_type: file.type,
};
let form_data = {
buffer: await _readFile(image_object.data_blob),
content_type: image_object.content_type,
parent_id: window.location.href.split("/")[4],
parent_type: "user",
};
const image_uploading_request = await request("/api/web/image", "POST", form_data);
if (image_uploading_request.status == 200) {
// Update profile picture link
changeValue("profile_image", image_uploading_request.body);
// alert(image_uploading_request.body);
}
}
function _readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}

View File

@ -15,6 +15,7 @@ model User {
username String @unique
password String
display_name String?
profile_image String?
role Role @default(USER)