Simplify posts
We had a lot of unnecessary complexity here due to post types that never ended up getting used. We also made the post slug field reactive and bound to the title field. We also fixed filters on the Universe admin page so we can filter by unpublished posts too (WIP) We also fixed the hover state of BackButton
This commit is contained in:
parent
3d993d76ed
commit
c6ce13a530
14 changed files with 109 additions and 128 deletions
|
|
@ -0,0 +1 @@
|
||||||
|
-- This is an empty migration.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- Update existing postType values
|
||||||
|
UPDATE "Post" SET "postType" = 'essay' WHERE "postType" = 'blog';
|
||||||
|
UPDATE "Post" SET "postType" = 'post' WHERE "postType" = 'microblog';
|
||||||
|
|
@ -43,9 +43,9 @@ model Project {
|
||||||
model Post {
|
model Post {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
slug String @unique @db.VarChar(255)
|
slug String @unique @db.VarChar(255)
|
||||||
postType String @db.VarChar(50) // blog, microblog
|
postType String @db.VarChar(50) // post, essay
|
||||||
title String? @db.VarChar(255) // Optional for microblog posts
|
title String? @db.VarChar(255) // Optional for post type
|
||||||
content Json? // BlockNote JSON for blog/microblog
|
content Json? // JSON content for posts and essays
|
||||||
|
|
||||||
featuredImage String? @db.VarChar(500)
|
featuredImage String? @db.VarChar(500)
|
||||||
attachments Json? // Array of media IDs for photo attachments
|
attachments Json? // Array of media IDs for photo attachments
|
||||||
|
|
|
||||||
|
|
@ -4,54 +4,54 @@ import { execSync } from 'child_process'
|
||||||
const prisma = new PrismaClient()
|
const prisma = new PrismaClient()
|
||||||
|
|
||||||
async function isDatabaseInitialized(): Promise<boolean> {
|
async function isDatabaseInitialized(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Check if we have any completed migrations
|
// Check if we have any completed migrations
|
||||||
const migrationCount = await prisma.$queryRaw<[{ count: bigint }]>`
|
const migrationCount = await prisma.$queryRaw<[{ count: bigint }]>`
|
||||||
SELECT COUNT(*) as count
|
SELECT COUNT(*) as count
|
||||||
FROM _prisma_migrations
|
FROM _prisma_migrations
|
||||||
WHERE finished_at IS NOT NULL
|
WHERE finished_at IS NOT NULL
|
||||||
`
|
`
|
||||||
|
|
||||||
return migrationCount[0].count > 0n
|
return migrationCount[0].count > 0n
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// If the table doesn't exist, database is not initialized
|
// If the table doesn't exist, database is not initialized
|
||||||
console.log('📊 Migration table check failed (expected on first deploy):', error.message)
|
console.log('📊 Migration table check failed (expected on first deploy):', error.message)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function initializeDatabase() {
|
async function initializeDatabase() {
|
||||||
console.log('🔍 Checking database initialization status...')
|
console.log('🔍 Checking database initialization status...')
|
||||||
|
|
||||||
// Give the database a moment to be ready
|
// Give the database a moment to be ready
|
||||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const isInitialized = await isDatabaseInitialized()
|
const isInitialized = await isDatabaseInitialized()
|
||||||
|
|
||||||
if (!isInitialized) {
|
if (!isInitialized) {
|
||||||
console.log('📦 First time setup detected. Initializing database...')
|
console.log('📦 First time setup detected. Initializing database...')
|
||||||
|
|
||||||
// Run migrations
|
// Run migrations
|
||||||
console.log('🔄 Running database migrations...')
|
console.log('🔄 Running database migrations...')
|
||||||
execSync('npx prisma migrate deploy', { stdio: 'inherit' })
|
execSync('npx prisma migrate deploy', { stdio: 'inherit' })
|
||||||
|
|
||||||
// Run seeds
|
// Run seeds
|
||||||
console.log('🌱 Seeding database...')
|
console.log('🌱 Seeding database...')
|
||||||
execSync('npx prisma db seed', { stdio: 'inherit' })
|
execSync('npx prisma db seed', { stdio: 'inherit' })
|
||||||
|
|
||||||
console.log('✅ Database initialization complete!')
|
console.log('✅ Database initialization complete!')
|
||||||
} else {
|
} else {
|
||||||
console.log('✅ Database already initialized. Running migrations only...')
|
console.log('✅ Database already initialized. Running migrations only...')
|
||||||
execSync('npx prisma migrate deploy', { stdio: 'inherit' })
|
execSync('npx prisma migrate deploy', { stdio: 'inherit' })
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Database initialization failed:', error)
|
console.error('❌ Database initialization failed:', error)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
} finally {
|
} finally {
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the initialization
|
// Run the initialization
|
||||||
initializeDatabase()
|
initializeDatabase()
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $unit-half;
|
gap: $unit-half;
|
||||||
padding: $unit $unit-2x;
|
padding: $unit 0;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: $red-60;
|
color: $red-60;
|
||||||
|
|
@ -46,8 +46,6 @@
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba($red-60, 0.08);
|
|
||||||
|
|
||||||
:global(.arrow-icon) {
|
:global(.arrow-icon) {
|
||||||
transform: translateX(-3px);
|
transform: translateX(-3px);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
{#if post.album && post.album.photos && post.album.photos.length > 0}
|
{#if post.album && post.album.photos && post.album.photos.length > 0}
|
||||||
<!-- Album slideshow -->
|
<!-- Album slideshow -->
|
||||||
<div class="post-album">
|
<div class="post-album">
|
||||||
|
|
@ -78,7 +77,7 @@
|
||||||
.post-content {
|
.post-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
max-width: 784px;
|
width: 100%;
|
||||||
gap: $unit-3x;
|
gap: $unit-3x;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|
||||||
|
|
@ -95,6 +94,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.essay {
|
&.essay {
|
||||||
|
max-width: 100%; // Full width for essays
|
||||||
|
|
||||||
.post-body {
|
.post-body {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@
|
||||||
const payload = {
|
const payload = {
|
||||||
title,
|
title,
|
||||||
slug,
|
slug,
|
||||||
type: 'blog', // 'blog' is the database value for essays
|
type: 'essay', // No mapping needed anymore
|
||||||
status,
|
status,
|
||||||
content,
|
content,
|
||||||
tags
|
tags
|
||||||
|
|
@ -261,7 +261,13 @@
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<Input label="Title" size="jumbo" bind:value={title} required placeholder="Essay title" />
|
<Input
|
||||||
|
label="Title"
|
||||||
|
size="jumbo"
|
||||||
|
bind:value={title}
|
||||||
|
required
|
||||||
|
placeholder="Essay title"
|
||||||
|
/>
|
||||||
|
|
||||||
<Input label="Slug" bind:value={slug} placeholder="essay-url-slug" />
|
<Input label="Slug" bind:value={slug} placeholder="essay-url-slug" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,7 @@
|
||||||
|
|
||||||
const postTypeLabels: Record<string, string> = {
|
const postTypeLabels: Record<string, string> = {
|
||||||
post: 'Post',
|
post: 'Post',
|
||||||
essay: 'Essay',
|
essay: 'Essay'
|
||||||
// Map database types to display names
|
|
||||||
blog: 'Essay',
|
|
||||||
microblog: 'Post'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handlePostClick() {
|
function handlePostClick() {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
onRemoveTag: (tag: string) => void
|
onRemoveTag: (tag: string) => void
|
||||||
onDelete: () => void
|
onDelete: () => void
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
|
onFieldUpdate?: (key: string, value: any) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|
@ -24,12 +25,14 @@
|
||||||
onAddTag,
|
onAddTag,
|
||||||
onRemoveTag,
|
onRemoveTag,
|
||||||
onDelete,
|
onDelete,
|
||||||
onClose = () => {}
|
onClose = () => {},
|
||||||
|
onFieldUpdate
|
||||||
}: Props = $props()
|
}: Props = $props()
|
||||||
|
|
||||||
function handleFieldUpdate(key: string, value: any) {
|
function handleFieldUpdate(key: string, value: any) {
|
||||||
if (key === 'slug') {
|
if (key === 'slug') {
|
||||||
slug = value
|
slug = value
|
||||||
|
onFieldUpdate?.(key, value)
|
||||||
} else if (key === 'tagInput') {
|
} else if (key === 'tagInput') {
|
||||||
tagInput = value
|
tagInput = value
|
||||||
}
|
}
|
||||||
|
|
@ -92,4 +95,4 @@
|
||||||
{onAddTag}
|
{onAddTag}
|
||||||
{onRemoveTag}
|
{onRemoveTag}
|
||||||
{onClose}
|
{onClose}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchToEssay() {
|
function switchToEssay() {
|
||||||
const contentParam = content ? encodeURIComponent(JSON.stringify(content)) : ''
|
// Store content in sessionStorage to avoid messy URLs
|
||||||
goto(`/admin/posts/new?type=essay${contentParam ? `&content=${contentParam}` : ''}`)
|
if (content && content.content && content.content.length > 0) {
|
||||||
|
sessionStorage.setItem('draft_content', JSON.stringify(content))
|
||||||
|
}
|
||||||
|
goto('/admin/posts/new?type=essay')
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSlug(title: string): string {
|
function generateSlug(title: string): string {
|
||||||
|
|
@ -92,7 +95,6 @@
|
||||||
essaySlug = generateSlug(essayTitle)
|
essaySlug = generateSlug(essayTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function handlePhotoUpload() {
|
function handlePhotoUpload() {
|
||||||
fileInput.click()
|
fileInput.click()
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +203,7 @@
|
||||||
if (postType === 'essay') {
|
if (postType === 'essay') {
|
||||||
postData = {
|
postData = {
|
||||||
...postData,
|
...postData,
|
||||||
type: 'blog', // 'blog' is the database value for essays
|
type: 'essay', // No mapping needed anymore
|
||||||
title: essayTitle,
|
title: essayTitle,
|
||||||
slug: essaySlug,
|
slug: essaySlug,
|
||||||
excerpt: essayExcerpt,
|
excerpt: essayExcerpt,
|
||||||
|
|
@ -211,7 +213,7 @@
|
||||||
// All other content is just a "post" with attachments
|
// All other content is just a "post" with attachments
|
||||||
postData = {
|
postData = {
|
||||||
...postData,
|
...postData,
|
||||||
type: 'microblog' // 'microblog' is for shorter posts
|
type: 'post' // No mapping needed anymore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,7 +303,6 @@
|
||||||
class="composer-editor"
|
class="composer-editor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
{#if attachedPhotos.length > 0}
|
{#if attachedPhotos.length > 0}
|
||||||
<div class="attached-photos">
|
<div class="attached-photos">
|
||||||
{#each attachedPhotos as photo}
|
{#each attachedPhotos as photo}
|
||||||
|
|
@ -335,7 +336,6 @@
|
||||||
|
|
||||||
<div class="composer-footer">
|
<div class="composer-footer">
|
||||||
<div class="footer-left">
|
<div class="footer-left">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
iconOnly
|
iconOnly
|
||||||
|
|
@ -499,7 +499,6 @@
|
||||||
class="inline-composer-editor"
|
class="inline-composer-editor"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
{#if attachedPhotos.length > 0}
|
{#if attachedPhotos.length > 0}
|
||||||
<div class="attached-photos">
|
<div class="attached-photos">
|
||||||
{#each attachedPhotos as photo}
|
{#each attachedPhotos as photo}
|
||||||
|
|
@ -533,7 +532,6 @@
|
||||||
|
|
||||||
<div class="composer-footer">
|
<div class="composer-footer">
|
||||||
<div class="footer-left">
|
<div class="footer-left">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
iconOnly
|
iconOnly
|
||||||
|
|
|
||||||
|
|
@ -127,9 +127,7 @@
|
||||||
if (selectedFilter === 'all') {
|
if (selectedFilter === 'all') {
|
||||||
filteredPosts = posts
|
filteredPosts = posts
|
||||||
} else if (selectedFilter === 'post') {
|
} else if (selectedFilter === 'post') {
|
||||||
filteredPosts = posts.filter((post) =>
|
filteredPosts = posts.filter((post) => ['post', 'microblog'].includes(post.postType))
|
||||||
['post', 'microblog'].includes(post.postType)
|
|
||||||
)
|
|
||||||
} else if (selectedFilter === 'essay') {
|
} else if (selectedFilter === 'essay') {
|
||||||
filteredPosts = posts.filter((post) => ['essay', 'blog'].includes(post.postType))
|
filteredPosts = posts.filter((post) => ['essay', 'blog'].includes(post.postType))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
let loading = $state(true)
|
let loading = $state(true)
|
||||||
let saving = $state(false)
|
let saving = $state(false)
|
||||||
let loadError = $state('')
|
let loadError = $state('')
|
||||||
|
let contentReady = $state(false)
|
||||||
|
|
||||||
let title = $state('')
|
let title = $state('')
|
||||||
let postType = $state<'post' | 'essay'>('post')
|
let postType = $state<'post' | 'essay'>('post')
|
||||||
|
|
@ -254,7 +255,7 @@
|
||||||
|
|
||||||
// Populate form fields
|
// Populate form fields
|
||||||
title = post.title || ''
|
title = post.title || ''
|
||||||
postType = post.postType || 'post'
|
postType = post.postType // No mapping needed anymore
|
||||||
status = post.status || 'draft'
|
status = post.status || 'draft'
|
||||||
slug = post.slug || ''
|
slug = post.slug || ''
|
||||||
excerpt = post.excerpt || ''
|
excerpt = post.excerpt || ''
|
||||||
|
|
@ -269,6 +270,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
tags = post.tags || []
|
tags = post.tags || []
|
||||||
|
|
||||||
|
// Set content ready after all data is loaded
|
||||||
|
contentReady = true
|
||||||
} else {
|
} else {
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
loadError = 'Post not found'
|
loadError = 'Post not found'
|
||||||
|
|
@ -315,12 +319,10 @@
|
||||||
const postData = {
|
const postData = {
|
||||||
title: config?.showTitle ? title : null,
|
title: config?.showTitle ? title : null,
|
||||||
slug,
|
slug,
|
||||||
type: postType,
|
type: postType, // No mapping needed anymore
|
||||||
status: newStatus || status,
|
status: newStatus || status,
|
||||||
content: config?.showContent ? saveContent : null,
|
content: config?.showContent ? saveContent : null,
|
||||||
excerpt: postType === 'essay' ? excerpt : undefined,
|
excerpt: postType === 'essay' ? excerpt : undefined,
|
||||||
link_url: undefined,
|
|
||||||
linkDescription: undefined,
|
|
||||||
tags
|
tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -476,7 +478,7 @@
|
||||||
<input type="text" bind:value={title} placeholder="Title" class="title-input" />
|
<input type="text" bind:value={title} placeholder="Title" class="title-input" />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if config?.showContent}
|
{#if config?.showContent && contentReady}
|
||||||
<div class="editor-wrapper">
|
<div class="editor-wrapper">
|
||||||
<Editor bind:data={content} placeholder="Continue writing..." />
|
<Editor bind:data={content} placeholder="Continue writing..." />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
let postType = $state<'post' | 'essay'>('post')
|
let postType = $state<'post' | 'essay'>('post')
|
||||||
let status = $state<'draft' | 'published'>('draft')
|
let status = $state<'draft' | 'published'>('draft')
|
||||||
let slug = $state('')
|
let slug = $state('')
|
||||||
|
let slugManuallySet = $state(false)
|
||||||
let excerpt = $state('')
|
let excerpt = $state('')
|
||||||
let content = $state<JSONContent>({ type: 'doc', content: [] })
|
let content = $state<JSONContent>({ type: 'doc', content: [] })
|
||||||
let tags = $state<string[]>([])
|
let tags = $state<string[]>([])
|
||||||
|
|
@ -23,6 +24,17 @@
|
||||||
let showMetadata = $state(false)
|
let showMetadata = $state(false)
|
||||||
let metadataButtonRef: HTMLButtonElement
|
let metadataButtonRef: HTMLButtonElement
|
||||||
|
|
||||||
|
// Auto-generate slug from title when title changes and slug hasn't been manually set
|
||||||
|
$effect(() => {
|
||||||
|
if (title && !slugManuallySet) {
|
||||||
|
slug = title
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9\s]+/g, '') // Remove special characters but keep spaces
|
||||||
|
.replace(/\s+/g, '-') // Replace spaces with dashes
|
||||||
|
.replace(/^-+|-+$/g, '') // Remove leading/trailing dashes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const postTypeConfig = {
|
const postTypeConfig = {
|
||||||
post: { icon: '💭', label: 'Post', showTitle: false, showContent: true },
|
post: { icon: '💭', label: 'Post', showTitle: false, showContent: true },
|
||||||
essay: { icon: '📝', label: 'Essay', showTitle: true, showContent: true }
|
essay: { icon: '📝', label: 'Essay', showTitle: true, showContent: true }
|
||||||
|
|
@ -37,23 +49,15 @@
|
||||||
postType = type as typeof postType
|
postType = type as typeof postType
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate initial slug based on title
|
// Check for draft content in sessionStorage
|
||||||
generateSlug()
|
const draftContent = sessionStorage.getItem('draft_content')
|
||||||
})
|
if (draftContent) {
|
||||||
|
try {
|
||||||
function generateSlug() {
|
content = JSON.parse(draftContent)
|
||||||
if (title) {
|
sessionStorage.removeItem('draft_content') // Clean up after use
|
||||||
slug = title
|
} catch (e) {
|
||||||
.toLowerCase()
|
console.error('Failed to parse draft content:', e)
|
||||||
.replace(/[^a-z0-9]+/g, '-')
|
}
|
||||||
.replace(/^-+|-+$/g, '')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-generate slug when title changes (only if slug is empty)
|
|
||||||
$effect(() => {
|
|
||||||
if (title && (!slug || slug === '')) {
|
|
||||||
generateSlug()
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -75,15 +79,11 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!slug) {
|
|
||||||
generateSlug()
|
|
||||||
}
|
|
||||||
|
|
||||||
saving = true
|
saving = true
|
||||||
const postData = {
|
const postData = {
|
||||||
title: config?.showTitle ? title : null,
|
title: config?.showTitle ? title : null,
|
||||||
slug: slug || `post-${Date.now()}`,
|
slug: slug || `post-${Date.now()}`,
|
||||||
postType,
|
type: postType, // No mapping needed anymore
|
||||||
status: publishStatus || status,
|
status: publishStatus || status,
|
||||||
content: config?.showContent ? content : null,
|
content: config?.showContent ? content : null,
|
||||||
excerpt: postType === 'essay' ? excerpt : undefined,
|
excerpt: postType === 'essay' ? excerpt : undefined,
|
||||||
|
|
@ -170,6 +170,11 @@
|
||||||
onRemoveTag={removeTag}
|
onRemoveTag={removeTag}
|
||||||
onDelete={() => {}}
|
onDelete={() => {}}
|
||||||
onClose={() => (showMetadata = false)}
|
onClose={() => (showMetadata = false)}
|
||||||
|
onFieldUpdate={(key, value) => {
|
||||||
|
if (key === 'slug') {
|
||||||
|
slugManuallySet = true
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,38 +13,7 @@ export const GET: RequestHandler = async (event) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const post = await prisma.post.findUnique({
|
const post = await prisma.post.findUnique({
|
||||||
where: { slug },
|
where: { slug }
|
||||||
include: {
|
|
||||||
album: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
slug: true,
|
|
||||||
title: true,
|
|
||||||
description: true,
|
|
||||||
photos: {
|
|
||||||
orderBy: { displayOrder: 'asc' },
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
url: true,
|
|
||||||
thumbnailUrl: true,
|
|
||||||
caption: true,
|
|
||||||
width: true,
|
|
||||||
height: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
photo: {
|
|
||||||
select: {
|
|
||||||
id: true,
|
|
||||||
url: true,
|
|
||||||
thumbnailUrl: true,
|
|
||||||
caption: true,
|
|
||||||
width: true,
|
|
||||||
height: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!post) {
|
if (!post) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue