mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-11-14 18:40:11 +01:00
WIP workspace pfp, CRUD functions complete
This commit is contained in:
parent
147426704c
commit
a087e1da6c
@ -238,6 +238,53 @@ const Workspace = {
|
||||
});
|
||||
},
|
||||
threads: WorkspaceThread,
|
||||
|
||||
uploadPfp: async function (formData, slug) {
|
||||
return await fetch(`${API_BASE}/workspace/${slug}/upload-pfp`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("Error uploading pfp.");
|
||||
return { success: true, error: null };
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
return { success: false, error: e.message };
|
||||
});
|
||||
},
|
||||
|
||||
fetchPfp: async function (slug) {
|
||||
return await fetch(`${API_BASE}/workspace/${slug}/pfp`, {
|
||||
method: "GET",
|
||||
cache: "no-cache",
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.ok && res.status !== 204) return res.blob();
|
||||
throw new Error("Failed to fetch pfp.");
|
||||
})
|
||||
.then((blob) => (blob ? URL.createObjectURL(blob) : null))
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
return null;
|
||||
});
|
||||
},
|
||||
removePfp: async function (slug) {
|
||||
return await fetch(`${API_BASE}/workspace/${slug}/remove-pfp`, {
|
||||
method: "DELETE",
|
||||
headers: baseHeaders(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.ok) return { success: true, error: null };
|
||||
throw new Error("Failed to remove pfp.");
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
return { success: false, error: e.message };
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default Workspace;
|
||||
|
@ -6,23 +6,57 @@ import VectorCount from "./VectorCount";
|
||||
import WorkspaceName from "./WorkspaceName";
|
||||
import SuggestedChatMessages from "./SuggestedChatMessages";
|
||||
import DeleteWorkspace from "./DeleteWorkspace";
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
|
||||
export default function GeneralInfo({ slug }) {
|
||||
const [workspace, setWorkspace] = useState(null);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [pfp, setPfp] = useState(null);
|
||||
const formEl = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchWorkspace() {
|
||||
const workspace = await Workspace.bySlug(slug);
|
||||
const pfpUrl = await Workspace.fetchPfp(workspace.slug);
|
||||
setPfp(pfpUrl);
|
||||
setWorkspace(workspace);
|
||||
setLoading(false);
|
||||
}
|
||||
fetchWorkspace();
|
||||
}, [slug]);
|
||||
|
||||
const handleFileUpload = async (event) => {
|
||||
const file = event.target.files[0];
|
||||
if (!file) return false;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const { success, error } = await Workspace.uploadPfp(
|
||||
formData,
|
||||
workspace.slug
|
||||
);
|
||||
if (!success) {
|
||||
showToast(`Failed to upload profile picture: ${error}`, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
const pfpUrl = await Workspace.fetchPfp(workspace.slug);
|
||||
setPfp(pfpUrl);
|
||||
showToast("Profile picture uploaded.", "success");
|
||||
};
|
||||
|
||||
const handleRemovePfp = async () => {
|
||||
const { success, error } = await Workspace.removePfp(workspace.slug);
|
||||
if (!success) {
|
||||
showToast(`Failed to remove profile picture: ${error}`, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
setPfp(null);
|
||||
};
|
||||
|
||||
const handleUpdate = async (e) => {
|
||||
setSaving(true);
|
||||
e.preventDefault();
|
||||
@ -70,6 +104,47 @@ export default function GeneralInfo({ slug }) {
|
||||
<SuggestedChatMessages slug={workspace.slug} />
|
||||
</div>
|
||||
<DeleteWorkspace workspace={workspace} />
|
||||
|
||||
{/* Image */}
|
||||
<div className="flex flex-col md:flex-row items-center gap-8">
|
||||
<div className="flex flex-col items-center">
|
||||
<label className="w-36 h-36 flex flex-col items-center justify-center bg-zinc-900/50 transition-all duration-300 rounded-full mt-8 border-2 border-dashed border-white border-opacity-60 cursor-pointer hover:opacity-60">
|
||||
<input
|
||||
id="workspace-pfp-upload"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
onChange={handleFileUpload}
|
||||
/>
|
||||
{pfp ? (
|
||||
<img
|
||||
src={pfp}
|
||||
alt="User profile picture"
|
||||
className="w-48 h-48 rounded-full object-cover bg-white"
|
||||
/>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center p-3">
|
||||
<Plus className="w-8 h-8 text-white/80 m-2" />
|
||||
<span className="text-white text-opacity-80 text-xs font-semibold">
|
||||
Workspace Image
|
||||
</span>
|
||||
<span className="text-white text-opacity-60 text-xs">
|
||||
800 x 800
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
{pfp && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRemovePfp}
|
||||
className="mt-3 text-white text-opacity-60 text-sm font-medium hover:underline"
|
||||
>
|
||||
Remove Profile Picture
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -548,8 +548,6 @@ function systemEndpoints(app) {
|
||||
|
||||
const userRecord = await User.get({ id: user.id });
|
||||
const oldPfpFilename = userRecord.pfpFilename;
|
||||
|
||||
console.log("oldPfpFilename", oldPfpFilename);
|
||||
if (oldPfpFilename) {
|
||||
const oldPfpPath = path.join(
|
||||
__dirname,
|
||||
|
@ -19,6 +19,15 @@ const { validWorkspaceSlug } = require("../utils/middleware/validWorkspace");
|
||||
const { convertToChatHistory } = require("../utils/helpers/chat/responses");
|
||||
const { CollectorApi } = require("../utils/collectorApi");
|
||||
const { handleUploads } = setupMulter();
|
||||
const { setupPfpUploads } = require("../utils/files/multer");
|
||||
const { normalizePath } = require("../utils/files");
|
||||
const { handlePfpUploads } = setupPfpUploads();
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const {
|
||||
determineWorkspacePfpFilepath,
|
||||
fetchPfp,
|
||||
} = require("../utils/files/pfp");
|
||||
|
||||
function workspaceEndpoints(app) {
|
||||
if (!app) return;
|
||||
@ -422,6 +431,127 @@ function workspaceEndpoints(app) {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.get(
|
||||
"/workspace/:slug/pfp",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const { slug } = request.params;
|
||||
const pfpPath = await determineWorkspacePfpFilepath(slug);
|
||||
|
||||
if (!pfpPath) {
|
||||
response.sendStatus(204).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const { found, buffer, size, mime } = fetchPfp(pfpPath);
|
||||
if (!found) {
|
||||
response.sendStatus(204).end();
|
||||
return;
|
||||
}
|
||||
|
||||
response.writeHead(200, {
|
||||
"Content-Type": mime || "image/png",
|
||||
"Content-Disposition": `attachment; filename=${path.basename(
|
||||
pfpPath
|
||||
)}`,
|
||||
"Content-Length": size,
|
||||
});
|
||||
response.end(Buffer.from(buffer, "base64"));
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error("Error processing the logo request:", error);
|
||||
response.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.post(
|
||||
"/workspace/:slug/upload-pfp",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||
handlePfpUploads.single("file"),
|
||||
async function (request, response) {
|
||||
try {
|
||||
const { slug } = request.params;
|
||||
const uploadedFileName = request.randomFileName;
|
||||
if (!uploadedFileName) {
|
||||
return response.status(400).json({ message: "File upload failed." });
|
||||
}
|
||||
|
||||
const workspaceRecord = await Workspace.get({
|
||||
slug,
|
||||
});
|
||||
|
||||
const oldPfpFilename = workspaceRecord.pfpFilename;
|
||||
if (oldPfpFilename) {
|
||||
const oldPfpPath = path.join(
|
||||
__dirname,
|
||||
`../storage/assets/pfp/${normalizePath(
|
||||
workspaceRecord.pfpFilename
|
||||
)}`
|
||||
);
|
||||
|
||||
if (fs.existsSync(oldPfpPath)) fs.unlinkSync(oldPfpPath);
|
||||
}
|
||||
|
||||
const { workspace, message } = await Workspace.update(
|
||||
workspaceRecord.id,
|
||||
{
|
||||
pfpFilename: uploadedFileName,
|
||||
}
|
||||
);
|
||||
|
||||
return response.status(workspace ? 200 : 500).json({
|
||||
message: workspace
|
||||
? "Profile picture uploaded successfully."
|
||||
: message,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error processing the profile picture upload:", error);
|
||||
response.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.delete(
|
||||
"/workspace/:slug/remove-pfp",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||
async function (request, response) {
|
||||
try {
|
||||
const { slug } = request.params;
|
||||
const workspaceRecord = await Workspace.get({
|
||||
slug,
|
||||
});
|
||||
const oldPfpFilename = workspaceRecord.pfpFilename;
|
||||
|
||||
if (oldPfpFilename) {
|
||||
const oldPfpPath = path.join(
|
||||
__dirname,
|
||||
`../storage/assets/pfp/${normalizePath(oldPfpFilename)}`
|
||||
);
|
||||
|
||||
if (fs.existsSync(oldPfpPath)) fs.unlinkSync(oldPfpPath);
|
||||
}
|
||||
|
||||
const { workspace, message } = await Workspace.update(
|
||||
workspaceRecord.id,
|
||||
{
|
||||
pfpFilename: null,
|
||||
}
|
||||
);
|
||||
|
||||
return response.status(workspace ? 200 : 500).json({
|
||||
message: workspace
|
||||
? "Profile picture removed successfully."
|
||||
: message,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error processing the profile picture removal:", error);
|
||||
response.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { workspaceEndpoints };
|
||||
|
@ -19,6 +19,7 @@ const Workspace = {
|
||||
"chatModel",
|
||||
"topN",
|
||||
"chatMode",
|
||||
"pfpFilename",
|
||||
],
|
||||
|
||||
new: async function (name = null, creatorId = null) {
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "workspaces" ADD COLUMN "pfpFilename" TEXT;
|
@ -100,6 +100,7 @@ model workspaces {
|
||||
chatModel String?
|
||||
topN Int? @default(4)
|
||||
chatMode String? @default("chat")
|
||||
pfpFilename String?
|
||||
workspace_users workspace_users[]
|
||||
documents workspace_documents[]
|
||||
workspace_suggested_messages workspace_suggested_messages[]
|
||||
|
@ -3,6 +3,7 @@ const fs = require("fs");
|
||||
const { getType } = require("mime");
|
||||
const { User } = require("../../models/user");
|
||||
const { normalizePath } = require(".");
|
||||
const { Workspace } = require("../../models/workspace");
|
||||
|
||||
function fetchPfp(pfpPath) {
|
||||
if (!fs.existsSync(pfpPath)) {
|
||||
@ -38,7 +39,21 @@ async function determinePfpFilepath(id) {
|
||||
return pfpFilepath;
|
||||
}
|
||||
|
||||
async function determineWorkspacePfpFilepath(slug) {
|
||||
const workspace = await Workspace.get({ slug });
|
||||
const pfpFilename = workspace?.pfpFilename || null;
|
||||
if (!pfpFilename) return null;
|
||||
|
||||
const basePath = process.env.STORAGE_DIR
|
||||
? path.join(process.env.STORAGE_DIR, "assets/pfp")
|
||||
: path.join(__dirname, "../../storage/assets/pfp");
|
||||
const pfpFilepath = path.join(basePath, normalizePath(pfpFilename));
|
||||
if (!fs.existsSync(pfpFilepath)) return null;
|
||||
return pfpFilepath;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchPfp,
|
||||
determinePfpFilepath,
|
||||
determineWorkspacePfpFilepath,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user