diff --git a/app/panel/inspect/account/[id]/page.tsx b/app/panel/inspect/account/[id]/page.tsx
index 5c4e189..3bcde3a 100644
--- a/app/panel/inspect/account/[id]/page.tsx
+++ b/app/panel/inspect/account/[id]/page.tsx
@@ -24,6 +24,7 @@ import { User } from "revolt-api";
import { decodeTime } from "ulid";
import relativeTime from "dayjs/plugin/relativeTime";
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
dayjs.extend(relativeTime);
export default async function User({
@@ -43,6 +44,7 @@ export default async function User({
{user && }
+
diff --git a/app/panel/inspect/channel/[id]/page.tsx b/app/panel/inspect/channel/[id]/page.tsx
index 4229cd7..50a4ee0 100644
--- a/app/panel/inspect/channel/[id]/page.tsx
+++ b/app/panel/inspect/channel/[id]/page.tsx
@@ -1,5 +1,6 @@
import { ChannelCard } from "@/components/cards/ChannelCard";
import { JsonCard } from "@/components/cards/JsonCard";
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
import { ServerCard } from "@/components/cards/ServerCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
@@ -34,6 +35,8 @@ export default async function Message({ params }: { params: { id: string } }) {
)}
+
+
{participants.length ? (
<>
diff --git a/app/panel/inspect/message/[id]/page.tsx b/app/panel/inspect/message/[id]/page.tsx
index b9b8702..d4b8ab2 100644
--- a/app/panel/inspect/message/[id]/page.tsx
+++ b/app/panel/inspect/message/[id]/page.tsx
@@ -1,5 +1,6 @@
import { ChannelCard } from "@/components/cards/ChannelCard";
import { JsonCard } from "@/components/cards/JsonCard";
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { Card, CardHeader } from "@/components/ui/card";
@@ -28,6 +29,8 @@ export default async function Message({
+
+
{author && (
diff --git a/app/panel/inspect/server/[id]/page.tsx b/app/panel/inspect/server/[id]/page.tsx
index 41d6cd3..2f10bab 100644
--- a/app/panel/inspect/server/[id]/page.tsx
+++ b/app/panel/inspect/server/[id]/page.tsx
@@ -1,4 +1,5 @@
import { JsonCard } from "@/components/cards/JsonCard";
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
import { ServerCard } from "@/components/cards/ServerCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
@@ -21,6 +22,7 @@ export default async function Server({ params }: { params: { id: string } }) {
Inspecting Server
+
{server.description && (
diff --git a/app/panel/inspect/user/[id]/page.tsx b/app/panel/inspect/user/[id]/page.tsx
index 52a3f6a..1e88faa 100644
--- a/app/panel/inspect/user/[id]/page.tsx
+++ b/app/panel/inspect/user/[id]/page.tsx
@@ -1,4 +1,5 @@
import { JsonCard } from "@/components/cards/JsonCard";
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
import { UserCard } from "@/components/cards/UserCard";
import { NavigationToolbar } from "@/components/common/NavigationToolbar";
import { RecentMessages } from "@/components/pages/inspector/RecentMessages";
@@ -76,6 +77,7 @@ export default async function User({
+
{user.profile?.content && (
diff --git a/app/panel/page.tsx b/app/panel/page.tsx
index fa1735a..8160e4b 100644
--- a/app/panel/page.tsx
+++ b/app/panel/page.tsx
@@ -1,3 +1,7 @@
+import SafetyNotesCard from "@/components/cards/SafetyNotesCard";
+
export default function Home() {
- return this is the admin panel ever;
+ return
+
+ ;
}
diff --git a/components/cards/SafetyNotesCard.tsx b/components/cards/SafetyNotesCard.tsx
new file mode 100644
index 0000000..93f842f
--- /dev/null
+++ b/components/cards/SafetyNotesCard.tsx
@@ -0,0 +1,89 @@
+"use client"
+
+import { useEffect, useState } from "react";
+import { Textarea } from "../ui/textarea";
+import { toast } from "../ui/use-toast";
+import { SafetyNotes, fetchSafetyNote, updateSafetyNote } from "@/lib/db";
+import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "../ui/card";
+import { useSession } from "next-auth/react";
+import dayjs from "dayjs";
+import relativeTime from "dayjs/plugin/relativeTime";
+dayjs.extend(relativeTime);
+
+export default function SafetyNotesCard({ objectId, type, title }: {
+ objectId: string,
+ type: SafetyNotes["_id"]["type"],
+ title?: string
+}) {
+ const session = useSession();
+ const [draft, setDraft] = useState("");
+ const [value, setValue] = useState(null);
+ const [ready, setReady] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ fetchSafetyNote(objectId, type)
+ .then((note) => {
+ setDraft(note?.text || "");
+ setValue(note);
+ setReady(true);
+ })
+ .catch((e) => {
+ setError(String(e));
+ });
+ }, [objectId, type]);
+
+ return (
+
+
+ {title ?? type.charAt(0).toUpperCase() + type.slice(1) + " notes"}
+
+
+
+
+
+ {
+ value
+ ? <>Last edited {dayjs(value.edited_at).fromNow()} by {value.edited_by}>
+ : <>No object note set>
+ }
+
+
+
+ )
+}
diff --git a/lib/accessPermissions.ts b/lib/accessPermissions.ts
index 5703b43..0c665e4 100644
--- a/lib/accessPermissions.ts
+++ b/lib/accessPermissions.ts
@@ -1,5 +1,5 @@
import { getServerSession } from "next-auth";
-import { insertAuditLog } from "./db";
+import { SafetyNotes, insertAuditLog } from "./db";
type Permission =
| "authifier"
@@ -49,7 +49,11 @@ type Permission =
| "/relations"}`
| `/create${"" | "/alert" | "/strike"}`
| `/update${"" | "/badges"}`
- | `/action${"" | "/unsuspend" | "/suspend" | "/wipe" | "/ban" | "/wipe-profile"}`}`;
+ | `/action${"" | "/unsuspend" | "/suspend" | "/wipe" | "/ban" | "/wipe-profile"}`}`
+ | `safety_notes${
+ | ""
+ | `/fetch${"" | `/${SafetyNotes["_id"]["type"]}`}`
+ | `/update${"" | `/${SafetyNotes["_id"]["type"]}`}`}`;
const PermissionSets = {
// Admin
@@ -65,6 +69,7 @@ const PermissionSets = {
"sessions",
"servers",
"users",
+ "safety_notes",
] as Permission[],
// View open reports
@@ -92,6 +97,12 @@ const PermissionSets = {
"bots/fetch/by-id",
"bots/update/discoverability",
+
+ "safety_notes/fetch/global",
+ "safety_notes/fetch/server",
+ "safety_notes/fetch/user",
+ "safety_notes/update/server",
+ "safety_notes/update/user",
] as Permission[],
// User support
@@ -112,6 +123,9 @@ const PermissionSets = {
"channels/update/invites",
"channels/fetch/invites",
+
+ "safety_notes/fetch",
+ "safety_notes/update",
] as Permission[],
// Moderate users
@@ -147,6 +161,9 @@ const PermissionSets = {
"publish_message",
"chat_message",
+
+ "safety_notes/fetch",
+ "safety_notes/update",
] as Permission[],
};
diff --git a/lib/db.ts b/lib/db.ts
index de6652a..f895f39 100644
--- a/lib/db.ts
+++ b/lib/db.ts
@@ -560,3 +560,40 @@ export async function fetchAuthifierEmailClassification(provider: string) {
.collection("email_classification")
.findOne({ _id: provider });
}
+
+export type SafetyNotes = {
+ _id: { id: string, type: "message" | "channel" | "server" | "user" | "account" | "global" };
+ text: string;
+ edited_by: string;
+ edited_at: Date;
+}
+
+export async function fetchSafetyNote(objectId: string, type: SafetyNotes["_id"]["type"]) {
+ await checkPermission(`safety_notes/fetch/${type}`, objectId);
+
+ return mongo()
+ .db("revolt")
+ .collection("safety_notes")
+ .findOne({ _id: { id: objectId, type: type } });
+}
+
+export async function updateSafetyNote(objectId: string, type: SafetyNotes["_id"]["type"], note: string) {
+ await checkPermission(`safety_notes/update/${type}`, objectId);
+
+ const session = await getServerSession();
+
+ return mongo()
+ .db("revolt")
+ .collection("safety_notes")
+ .updateOne(
+ { _id: { id: objectId, type: type } },
+ {
+ $set: {
+ text: note,
+ edited_at: new Date(Date.now()),
+ edited_by: session?.user?.email ?? "",
+ },
+ },
+ { upsert: true },
+ );
+}