Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import { CollectionConfig } from "payload";
|
|
import type { User } from "@/payload-types";
|
|
import hasRoles from "@/utils/access-control/hasRoles";
|
|
import { isSuperuser } from "@/utils/access-control/hasPermission";
|
|
|
|
export const VoiceSubmissions: CollectionConfig = {
|
|
slug: "voice-submissions",
|
|
admin: {
|
|
group: "Voiceover",
|
|
useAsTitle: "id",
|
|
defaultColumns: ["script", "user", "status", "createdAt"],
|
|
},
|
|
access: {
|
|
read: async ({ req }) => {
|
|
const user = req.user as User | null;
|
|
if (!user) return false;
|
|
// Elevation bypasses the owner scope so reviewers see every submission;
|
|
// regular users only ever see their own.
|
|
if (await isSuperuser(req.payload, user)) return true;
|
|
if (hasRoles(["admin", "developer"], user)) return true;
|
|
return { user: { equals: user.id } };
|
|
},
|
|
create: ({ req, data }) => {
|
|
const user = req.user as User | null;
|
|
if (!user) return false;
|
|
if (hasRoles(["admin", "developer"], user)) return true;
|
|
// Submitters may only create their own submissions. Server actions that
|
|
// write on behalf of users run with overrideAccess and set `user` themselves.
|
|
const submittedUser =
|
|
typeof data?.user === "object" && data?.user !== null ? data.user.id : data?.user;
|
|
return submittedUser === user.id;
|
|
},
|
|
// Status changes are review actions — submitters must not edit their own docs.
|
|
update: ({ req }) => hasRoles(["admin", "developer"], req.user),
|
|
delete: ({ req }) => hasRoles(["developer"], req.user),
|
|
},
|
|
fields: [
|
|
{
|
|
name: "script",
|
|
type: "relationship",
|
|
relationTo: "voice-scripts",
|
|
required: true,
|
|
index: true,
|
|
},
|
|
{
|
|
name: "user",
|
|
type: "relationship",
|
|
relationTo: "users",
|
|
required: true,
|
|
index: true,
|
|
admin: {
|
|
description: "Submitter",
|
|
},
|
|
},
|
|
{
|
|
name: "description",
|
|
type: "textarea",
|
|
maxLength: 300,
|
|
admin: {
|
|
description: "Optional short note from the submitter about this take.",
|
|
},
|
|
},
|
|
{
|
|
name: "status",
|
|
type: "select",
|
|
required: true,
|
|
defaultValue: "pending",
|
|
options: [
|
|
{ label: "Pending", value: "pending" },
|
|
{ label: "Accepted", value: "accepted" },
|
|
{ label: "Rejected", value: "rejected" },
|
|
],
|
|
},
|
|
{
|
|
name: "reviewNote",
|
|
type: "textarea",
|
|
admin: {
|
|
description: "Optional reviewer feedback. Shown to the submitter alongside their take.",
|
|
},
|
|
},
|
|
],
|
|
// No `upload.mimeTypes` allowlist on purpose: Payload content-sniffs uploads
|
|
// (file-type) and reports WebM recordings as `video/webm`, so an audio/* MIME
|
|
// allowlist rejects browser-recorded takes. The submit action is the gate
|
|
// instead (audio/* base type + size cap); restricted file types (executables,
|
|
// scripts, …) stay blocked by Payload's built-in checks.
|
|
upload: true,
|
|
timestamps: true,
|
|
};
|