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 // TODO: Rename patchUser
async function editUser({ requester_id, user_id, user_content }) { 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 }); let user = await getUser({ user_id: user_id, include_password: true });
if (!user.success) return _r(false, "User not found"); if (!user.success) return _r(false, "User not found");
@ -109,22 +109,35 @@ async function editUser({ requester_id, user_id, user_content }) {
// TODO: // TODO:
// If there was a role change, see if the acting user can make these changes // 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 (!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 // Check if current password value is correct
const password_match = await bcrypt.compare(user_content.original_password, user.password); 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 // If successful, compute new password hash
user_content.value = await bcrypt.hash(user_content.value, 10); 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 = {}; let formatted = {};
formatted[setting_name] = user_content.value; 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); return _r(true);
} }
async function deleteUser({ user_id }) { async function deleteUser({ user_id }) {
@ -683,4 +696,4 @@ const _r = (s, m) => {
return { success: s, message: 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) { async function postImage(request, response) {
// TODO: Permissions for uploading images // TODO: Permissions for uploading images
// TODO: Verification for image uploading // 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) { async function deleteImage(req, res) {
// TODO: Permissions for deleting image // TODO: Permissions for deleting image

View File

@ -45,14 +45,16 @@ async function login(request, response) {
response.render(_getThemePage("login"), await getDefaults(request)); response.render(_getThemePage("login"), await getDefaults(request));
} }
async function author(req, res) { 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 // FIXME: Bandage fix for author get error
if (!user.success) return res.redirect("/"); 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 // 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) { async function authorEdit(request, response) {
let author = await core.getBiography({ author_id: request.params.author_id }); let author = await core.getBiography({ author_id: request.params.author_id });

View File

@ -1,4 +1,18 @@
import globals from "globals"; import globals from "globals";
import pluginJs from "@eslint/js"; 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 { .page .page-center .about .profile-picture img {
margin: auto; margin: auto;
max-height: 200px; height: 200px;
max-width: 200px; width: 200px;
} }
.page .page-center .about .displayname { .page .page-center .about .displayname {
font-size: 1.4rem; font-size: 1.4rem;

View File

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

View File

@ -25,7 +25,7 @@
<%- post.content %> <%- post.content %>
</div> </div>
<div class="about"> <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> <div class="displayname"><%= post.owner.display_name || post.owner.username %></div>
<!-- TODO: Format Date/time --> <!-- TODO: Format Date/time -->
<div class="stat">Registered <%= post.created_date.toLocaleString('en-US', { dateStyle:'medium' }) || "Null" %></div> <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 = { const form = {
setting_name: setting_name, setting_name: setting_name,
value: element.value, value: value,
id: window.location.href.split("/")[4], id: window.location.href.split("/")[4],
}; };
const response = await request(`/api/web/user`, "PATCH", form); const response = await request(`/api/web/user`, "PATCH", form);
@ -31,7 +31,7 @@ function changePasswordInputUpdate() {
async function sendPasswordUpdate() { async function sendPasswordUpdate() {
const new_password_1 = qs("#cp-new-1"); 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 = { const form = {
setting_name: "password", setting_name: "password",
@ -45,3 +45,38 @@ async function sendPasswordUpdate() {
alert("Successfully changed password"); 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 username String @unique
password String password String
display_name String? display_name String?
profile_image String?
role Role @default(USER) role Role @default(USER)