Skip to content

Commit

Permalink
automatically configure sanity cors origin and preview token
Browse files Browse the repository at this point in the history
  • Loading branch information
mikehwagz committed Apr 24, 2024
1 parent 96bd6d5 commit 3c6496f
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 24 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
node_modules
studio
coverage
dist
.vscode
Expand Down
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"printWidth": 120,
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": false,
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
],
"scripts": {
"build": "unbuild",
"clean": "rm -rf .nuxt dist node_modules",
"dev:prepare": "unbuild --stub",
"preinstall": "pnpm clean",
"postinstall": "pnpm dev:prepare",
"lint": "prettier -c src",
"lint:fix": "automd && prettier -w src",
"prepack": "pnpm build",
Expand Down
91 changes: 73 additions & 18 deletions src/init.ts → src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ import { defineCommand } from 'citty'
import { execa } from 'execa'
import { titleCase } from 'scule'
import { writeFile, symlink } from 'node:fs/promises'
import { createEnvContent, extractProjectId } from './utils'
import {
extractSanityCliToken,
extractSanityProjectId,
createEnvContent,
createSanityCli,
} from '../utils'

const TEMPLATE_NAME = 'github:selfawarestudio/greta#template/nuxt-sanity'
const PACKAGE_MANAGER = 'pnpm'
Expand All @@ -26,21 +31,36 @@ export default defineCommand({
},
},
async run(ctx) {
const cwd = resolve('.')

let dir
let sanityCliToken
let sanityProjectId
let sanityProjectToken
let template: DownloadTemplateResult

consola.start('Checking Sanity CLI login status...')

try {
await execa('pnpm', ['dlx', '@sanity/cli', 'projects', 'list'], { stdout: 'pipe' })
const { stdout } = await execa(
'pnpm',
['dlx', '@sanity/cli', 'debug', '--secrets'],
{ stdout: 'pipe' },
)

sanityCliToken = extractSanityCliToken(stdout)

if (!sanityCliToken) {
throw new Error(
'You must be logged into Sanity CLI. Please run `pnpm dlx @sanity/cli login` and then try again.',
)
}
} catch (err) {
consola.error('You must be logged into Sanity CLI. Please run `pnpm dlx @sanity/cli login` and then try again.')
consola.error((err as Error).toString())
process.exit(1)
}

// Now prepare to download our prpject template
const cwd = resolve('.')

let dir
let sanityProjectId
let template: DownloadTemplateResult
// Now prepare to download our project template

if (ctx.args.dir) {
dir = ctx.args.dir
Expand All @@ -63,11 +83,14 @@ export default defineCommand({
}

// Next create a new Sanity project and set the project ID and other env vars
const sanityProjectTitle = await consola.prompt('Enter a title for the Sanity project', {
type: 'text',
placeholder: titleCase(dir),
default: titleCase(dir),
})
const sanityProjectTitle = await consola.prompt(
'Enter a title for the Sanity project',
{
type: 'text',
placeholder: titleCase(dir),
default: titleCase(dir),
},
)

consola.start('Initializing a new Sanity project...')
try {
Expand All @@ -87,13 +110,41 @@ export default defineCommand({
{ stdout: 'pipe' },
)

sanityProjectId = extractProjectId(stdout)
sanityProjectId = extractSanityProjectId(stdout)

if (!sanityProjectId) {
throw new Error('Failed to extract project ID from Sanity CLI output')
}

consola.success('Successfully created a new Sanity project. The Project ID is', sanityProjectId)
consola.success(
'Successfully created a new Sanity project. The Project ID is',
sanityProjectId,
)
} catch (err) {
consola.error((err as Error).toString())
process.exit(1)
}

// Now we can use the Sanity API to create a token for the project that will be used for website previews
consola.start('Setting up Sanity CORS and creating a preview token...')

const sanityCli = createSanityCli({
projectId: sanityProjectId,
token: sanityCliToken,
})

try {
await sanityCli('/cors', {
origin: 'http://localhost:3000',
allowCredentials: false,
})

const { key } = await sanityCli('/tokens', {
label: 'Website preview',
roleName: 'editor',
})

sanityProjectToken = key
} catch (err) {
consola.error((err as Error).toString())
process.exit(1)
Expand All @@ -103,12 +154,16 @@ export default defineCommand({
// Create a file called .env and write the project ID to it as SANITY_STUDIO_PROJECT_ID then write the file to template.dir
const envFile = resolve(template.dir, '.env')
const studioEnvFile = resolve(template.dir, 'studio', '.env')
const envContent = createEnvContent(sanityProjectTitle, sanityProjectId)
const envContent = createEnvContent(
sanityProjectTitle,
sanityProjectId,
sanityProjectToken,
)

try {
await writeFile(envFile, envContent)
await symlink(envFile, studioEnvFile)
consola.success('.env file created and symbollically linked to studio folder.')
consola.success('.env file created and symlinked to studio folder.')
} catch (err) {
consola.error((err as Error).toString())
process.exit(1)
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const main = defineCommand({
description: gretaPkg.description,
},
subCommands: {
init: () => import('./init').then(_rDefault),
init: () => import('./commands/init').then(_rDefault),
},
})

Expand Down
8 changes: 6 additions & 2 deletions src/utils/create-env-content.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
export function createEnvContent(sanityProjectTitle: string, sanityProjectId: string) {
export function createEnvContent(
sanityProjectTitle: string,
sanityProjectId: string,
sanityProjectToken: string,
) {
const currentDate = new Date().toISOString().slice(0, 10)
return (
`SANITY_STUDIO_PROJECT_TITLE="${sanityProjectTitle}"\n` +
`SANITY_STUDIO_PROJECT_ID=${sanityProjectId}\n` +
`SANITY_STUDIO_DATASET=production\n` +
`SANITY_STUDIO_API_VERSION=${currentDate}\n` +
`SANITY_STUDIO_API_TOKEN=\n` +
`SANITY_STUDIO_API_TOKEN=${sanityProjectToken}\n` +
`SANITY_STUDIO_SLUG_INPUT_URL_PREFIX=\n` +
`SANITY_STUDIO_PROD_PREVIEW_URL=\n`
)
Expand Down
10 changes: 10 additions & 0 deletions src/utils/extract-sanity-cli-token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function extractSanityCliToken(output: string): string | null {
// This regular expression looks for the phrase "Auth token: " followed by any characters
// that are not whitespace, capturing this sequence into a group.
const regex = /Auth token:\s+(\S+)/
const match = output.match(regex)

// If a match is found, return the first captured group, which contains the token, split on single quotes and get the token itself.
// Otherwise, return null if no match is found.
return match?.[1]?.split(`'`)[1] ?? null
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function extractProjectId(output: string): string | null {
export function extractSanityProjectId(output: string): string | null {
// This regular expression looks for the phrase "Project ID: " followed by any characters
// that are not whitespace, capturing this sequence into a group.
const regex = /Project ID:\s+(\S+)/
Expand Down
4 changes: 3 additions & 1 deletion src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export { extractProjectId } from './extract-project-id'
export { createSanityCli } from './sanity-cli'
export { extractSanityProjectId } from './extract-sanity-project-id'
export { extractSanityCliToken } from './extract-sanity-cli-token'
export { createEnvContent } from './create-env-content'
27 changes: 27 additions & 0 deletions src/utils/sanity-cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export function createSanityCli({
projectId,
token,
}: {
projectId: string
token: string
}) {
return async (endpoint: string, body: Record<string, any>) => {
const response = await fetch(
`https://api.sanity.io/v2021-06-07/projects/${projectId}${endpoint}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(body),
},
)

if (!response.ok) {
throw new Error(`The Sanity CLI response was not ok.`)
}

return await response.json()
}
}

0 comments on commit 3c6496f

Please sign in to comment.