Unlock seamless workflows and faster delivery with our latest releases - Join the deep dive

Discussion on syncing user data and customizing the Sanity studio based on user access.

14 replies
Last updated: Sep 13, 2022
Hi! I'm trying to create a document for all users like this: https://www.sanity.io/schemas/create-a-document-for-all-current-project-users-68b6f0db but I really want it to try and sync each time the studio loads. Any ideas on how to go about this?
I've tried doing this:
https://www.sanity.io/schemas/custom-default-desk-pane-35148d61 but it doesn't load with a custom desk structure. I've also tried making my desk structure into a React component but that doesn't work.
Sep 6, 2022, 5:01 PM
I think I found a way to do this, but it feels illegal:
Basically made the Desk structure async..


const DeskStructure = async () => {
  await syncUsers()
  return S.list()
}
Sep 6, 2022, 5:15 PM
Wait where do you want to get the user docs into? This seems indeed strange to do. Can you give me more input, so I can try understand what your goal is?
Sep 8, 2022, 2:04 PM
(this still makes me giggle btw: I think I found a way to do this, but it feels illegal)
Sep 8, 2022, 2:08 PM
Hi Saskia! I have a studio that runs 5 different frontend sites. We've organized it so that most documents (pages/articles etc) reference a "domain document" and fetch the correct frontend using multiple deployments in Vercel and environment variables to set the domain id.
Now we want to have a different view for the editors based on which page they can access. This is purely visual I know since no enterprise, but works well for us.

The jist is:
• Sync Sanity users to a document called 'sanityUser' on each studio load.
• The sanityUser document contains an array of references to domains, the "sanity id" of the user, as well as a "superadmin" boolean
• In the DeskStructure we then find the current users ID and find the corresponding document. We then filter the desk structure based on their access
Sep 8, 2022, 2:40 PM
If you are interested it looks like this:


// Desk structure
const DeskStructure = async () => {
  await syncUsers()
  const currentUser = await userStore.getCurrentUser()
  const { superAdmin, siteAccess } = await studioClient.fetch(
    `*[_type == 'sanityUser' && id == $id][0]{
      superAdmin,
      "siteAccess": siteAccess[]._ref
    }`,
    { id: currentUser.id }
  )

  if (!superAdmin) {
    return S.documentTypeList("domain")
      .filter("_id in $siteAccess")
      .params({ siteAccess })
      .title("Nettsider")
      .child((domainID) => domainDocumentList(domainID))
  }
  
  // Superadmin view
  return S.list() 
}

// Sync users
import client from "part:@sanity/base/client"
import cq from "concurrent-queue"
import userStore from "part:@sanity/base/user"
const studioClient = client.withConfig({ apiVersion: "2022-03-25" })

// Create a queue to limit the rate at which you write changes to Sanity
let queue = cq()
  .limit({ concurrency: 2 })
  .process(function (task) {
    return new Promise(function (resolve, reject) {
      setTimeout(resolve.bind(undefined, task), 1000)
    })
  })

const syncUsers = async () => {
  //query for all members, then use the userStore to get their details

  const [allUsers, existingUsers] = await Promise.all([
    studioClient.fetch(`*[_id in path('_.groups.*')].members[@ != 'everyone']`),
    studioClient.fetch(`*[_type == "sanityUser"].id`),
  ])

  // Filter to to the users that are not already existing
  const newUsers = allUsers
    .flat()
    .filter((user) => !existingUsers.includes(user))

  if (newUsers.length == 0) return

  // Fetch the user details from the userStore
  const userDetails = await userStore.getUsers(newUsers)

  for (const user of userDetails) {
    const doc = {
      _type: "sanityUser",
      _id: "sanityUser." + user.id,
      id: user.id,
      name: user.displayName,
    }
    queue(doc).then(async () => {
      //create the doc via the client
      studioClient
        .create(doc)
        .then((updatedDoc) => {
          console.log(
            `Hurray, the doc is updated! New document:${updatedDoc._id}`
          )
        })
        .catch((err) => {
          console.error("Oh no, the update failed: ", err.message)
        })
    })
  }
}

export default syncUsers
Sep 8, 2022, 2:43 PM
So it works haha. And I think this technique can be used to fully customize the studio based on document types etc 😄
Sep 8, 2022, 2:54 PM
I will def have a more detailed look on Monday and try out your approach 🫡If it's illegal we will both go to jail then
🚨 #partnersInCrime
Sep 11, 2022, 8:36 PM
I am just spitballing here, but maybe another approach (not sure if better or worse) would be to get some thing working in the stucture builder `hidden doc types`….

// /deskStructure.js

// Hide document types that we already have a structure definition for
const hiddenDocTypes = (listItem) =>
    ![
        "siteSettings",
        //some async functionality here

    ].includes(listItem.getId());

export default () =>
    S.list()
        .title('Content')
        .items([
            site1Docs,
            S.divider(),
            // your other desk partitions
            S.divider(),
            siteSettings,

            ...S.documentTypeListItems().filter(hiddenDocTypes)
        ])
Sep 13, 2022, 12:15 PM
This def works and is a solution we've used a lot, but it doesn't show a different view based on what user is logged in
Sep 13, 2022, 12:33 PM
i mean, that you could try the same kind of rendering which you have now, but use the userStore info … dont know if it works, but I am always for testing our performance…
Sep 13, 2022, 12:41 PM
And tbh, as long as it works, I would try stuff, thats the beauty of open source right?
Sep 13, 2022, 12:42 PM
Indeed, very happy with this discovery as this means we can basically make custom roles on the free plan
Sep 13, 2022, 12:45 PM
Everyone of course has the same access via the actual API etc but this will suit our projects nicely
Sep 13, 2022, 12:46 PM
yeah its more render access, but a workaround.
Sep 13, 2022, 1:12 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?