generated from autonomic-cooperative/astro-payload-template
Compare commits
No commits in common. "e9fb68ec9facf27a7cebf042e1b84ee56899e207" and "c90d2c0d9b997cd9e2dcb511fc3ccbcdeedc0c41" have entirely different histories.
e9fb68ec9f
...
c90d2c0d9b
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,3 @@
|
|||||||
data
|
data
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
#payload-types.ts
|
|
5
astro/.gitignore
vendored
5
astro/.gitignore
vendored
@ -15,4 +15,7 @@ pnpm-debug.log*
|
|||||||
|
|
||||||
|
|
||||||
# macOS-specific files
|
# macOS-specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# types
|
||||||
|
src/types.ts
|
@ -1,36 +0,0 @@
|
|||||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 500 500" style="width: 178px;" xml:space="preserve" data-imageid="404-page-not-found-1-77" imageName="404 Page Not Found 1" class="illustrations_image">
|
|
||||||
<style type="text/css">
|
|
||||||
.st0_404-page-not-found-1-77 { fill: var(--primary); }
|
|
||||||
.st1_404-page-not-found-1-77 { fill: var(--secondary);}
|
|
||||||
.st2_404-page-not-found-1-77{fill:var(--tertiary);}
|
|
||||||
.st3_404-page-not-found-1-77{fill:var(--secondary);}
|
|
||||||
</style>
|
|
||||||
<g id="Numbers_404-page-not-found-1-77">
|
|
||||||
<path class="st0_404-page-not-found-1-77 targetColor" d="M107.9,303.8H39.1v-27.2l65.2-101.8h31.1v104.3h23.6v24.7h-23.6v32.9h-27.4V303.8z M107.9,213.7h-0.5L67,279.1
		h40.9V213.7z" style="fill: rgb(104, 225, 253);"/>
|
|
||||||
<path class="st0_404-page-not-found-1-77 targetColor" d="M409.9,303.8h-68.8v-27.2l65.2-101.8h31.1v104.3h23.6v24.7h-23.6v32.9h-27.4V303.8z M414.7,213.7h-0.5
		l-40.5,65.4h40.9L414.7,213.7z" style="fill: rgb(104, 225, 253);"/>
|
|
||||||
</g>
|
|
||||||
<g id="Chraracter_404-page-not-found-1-77">
|
|
||||||
<path class="st1_404-page-not-found-1-77" d="M245.1,182.9c-40.2,0-72.7,32.5-72.8,72.7c0,40.2,32.5,72.7,72.7,72.8s72.7-32.5,72.8-72.7c0,0,0,0,0,0
		C317.8,215.5,285.2,182.9,245.1,182.9z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M245.1,329.1c-40.6,0-73.5-32.9-73.5-73.4s32.9-73.5,73.4-73.5s73.5,32.9,73.5,73.4c0,0,0,0,0,0
		C318.5,296.2,285.6,329,245.1,329.1z M245.1,183.6c-39.8,0-72,32.2-72,72s32.2,72,72,72s72-32.2,72-72l0,0
		C317,215.9,284.8,183.7,245.1,183.6L245.1,183.6z"/>
|
|
||||||
<ellipse class="st2_404-page-not-found-1-77" cx="283.2" cy="232" rx="3.4" ry="5"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M159.6,234.1l170.8-37.8l-33.6-9.8c0,0-18.3-30.2-23.1-30.8S180.6,174,180.6,174l-5.6,42.5L159.6,234.1z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M279.2,286.5c-0.3,0-0.5-0.1-0.6-0.4c-18.5-31.4-61.1-14.8-61.5-14.7c-0.4,0.1-0.8,0-0.9-0.4
		c-0.1-0.4,0-0.8,0.4-0.9c0.4-0.2,44.2-17.2,63.3,15.3c0.2,0.3,0.1,0.8-0.2,1c0,0,0,0,0,0C279.4,286.5,279.3,286.5,279.2,286.5z"/>
|
|
||||||
<polygon class="st2_404-page-not-found-1-77" points="195.2,336.4 175.9,305.9 260.3,291.5 266.1,320.7 274.7,293.8 317.8,302.3 309,336.4 	"/>
|
|
||||||
<path class="st1_404-page-not-found-1-77" d="M303.4,270.3l7.8-1.1l0,0l4,29c0.3,2-1.1,3.9-3.2,4.2h0l-0.5,0.1c-2,0.3-3.9-1.1-4.2-3.2l0,0L303.4,270.3
		L303.4,270.3z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M311,303.2c-2.2,0-4.1-1.6-4.4-3.8l-4-29c0-0.4,0.2-0.7,0.6-0.8l7.8-1.1c0.2,0,0.4,0,0.5,0.1
		c0.1,0.1,0.2,0.3,0.3,0.5l4,29c0.4,2.4-1.3,4.6-3.7,5c0,0,0,0,0,0l-0.5,0.1C311.4,303.2,311.2,303.2,311,303.2z M304.1,270.9
		l3.9,28.4c0.2,1.6,1.7,2.8,3.4,2.6l0.5-0.1l0,0c1.6-0.2,2.8-1.7,2.6-3.4l-3.9-28.3L304.1,270.9z"/>
|
|
||||||
|
|
||||||
<rect x="299.3" y="243.6" transform="matrix(0.9906 -0.1371 0.1371 0.9906 -32.4796 44.3355)" class="st1_404-page-not-found-1-77" width="12.7" height="28.7"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M301.3,273.7c-0.2,0-0.3,0-0.4-0.1c-0.1-0.1-0.2-0.3-0.3-0.5l-3.9-28.4c0-0.2,0-0.4,0.1-0.5
		c0.1-0.1,0.3-0.2,0.5-0.3l12.5-1.7c0.4,0,0.7,0.2,0.8,0.6l3.9,28.4c0,0.4-0.2,0.7-0.6,0.8L301.3,273.7L301.3,273.7z M298.2,245.2
		l3.7,27l11.2-1.5l-3.7-27L298.2,245.2z"/>
|
|
||||||
<path class="st1_404-page-not-found-1-77" d="M308.5,292.1l3.7-0.5c2.4-0.3,4.7,1.4,5,3.8l5.6,40.5c0.3,2.4-1.4,4.7-3.8,5l-3.7,0.5c-2.4,0.3-4.7-1.4-5-3.8
		l-5.6-40.5C304.3,294.7,306,292.4,308.5,292.1z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M314.7,342.2c-2.6,0-4.8-1.9-5.1-4.5l-5.6-40.5c-0.4-2.8,1.6-5.4,4.4-5.8l3.7-0.5c1.4-0.2,2.7,0.2,3.8,1
		c1.1,0.8,1.8,2.1,2,3.4l5.6,40.5c0.4,2.8-1.6,5.4-4.4,5.8l0,0l-3.7,0.5C315.1,342.2,314.9,342.2,314.7,342.2z M319,341L319,341z
		 M312.8,292.3c-0.2,0-0.3,0-0.5,0l-3.7,0.5c-2.1,0.3-3.5,2.2-3.2,4.3l5.6,40.5c0.3,2.1,2.2,3.5,4.3,3.2l3.7-0.5
		c2.1-0.3,3.5-2.2,3.2-4.2c0,0,0,0,0,0l-5.6-40.5c-0.2-1-0.7-1.9-1.5-2.5C314.4,292.5,313.6,292.3,312.8,292.3L312.8,292.3z"/>
|
|
||||||
<circle class="st1_404-page-not-found-1-77" cx="302.6" cy="236.5" r="33.7"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M302.6,270.9c-19,0-34.3-15.4-34.3-34.4s15.4-34.3,34.4-34.3c17.1,0,31.7,12.7,34,29.6l0,0
		c2.6,18.8-10.6,36.2-29.4,38.7C305.7,270.8,304.2,270.9,302.6,270.9z M302.7,203.6c-1.5,0-3,0.1-4.5,0.3
		c-18,2.5-30.6,19.1-28.1,37.2c2.5,18,19.1,30.6,37.2,28.1s30.6-19.1,28.1-37.2c0,0,0,0,0,0l0,0C333,215.7,319.1,203.6,302.7,203.6
		L302.7,203.6z"/>
|
|
||||||
<circle class="st1_404-page-not-found-1-77" cx="302.6" cy="236.5" r="27.3"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M302.6,264.5c-15.5,0.2-28.2-12.1-28.4-27.6c-0.2-14.3,10.4-26.5,24.6-28.2c15.3-2.1,29.5,8.6,31.6,23.9
		c2.1,15.3-8.6,29.5-23.9,31.6l0,0C305.2,264.5,303.9,264.5,302.6,264.5z M302.7,209.9c-1.2,0-2.5,0.1-3.7,0.2
		c-14.6,2-24.8,15.5-22.7,30c2,14.6,15.5,24.8,30,22.7c14.6-2,24.8-15.5,22.7-30c-1-7-4.7-13.3-10.3-17.6
		C314.1,211.8,308.5,209.9,302.7,209.9z"/>
|
|
||||||
<path class="st1_404-page-not-found-1-77" d="M334.8,312.4c-1.8-2.4-4.2-4.3-6.9-5.7c-8-4.1-17.2-4.1-25.7-1.4c-3,1-8.8,2.3-9.1,6.3c0,1.6,1.1,3,2.7,3.5
		c1.5,0.4,3.1,0.5,4.7,0.2c-2.2,0.1-4.5,0.3-6.3,1.6s-2.7,4-1.3,5.7c0.7,0.7,1.5,1.2,2.4,1.4c2,0.6,4.2,0.5,6.2-0.1
		c-2.2,0.4-4.3,1.1-6.4,2c-1.2,0.5-2.6,1.5-2.5,2.9s1.4,1.9,2.5,2.3c2.4,0.8,4.9,1.3,7.5,1.4c-2.2,0.6-4.4,1.2-6.1,2.6
		c-2.7,2.3-2.8,6.8,1,8.1c1.2,0.3,2.5,0.5,3.8,0.4c11.2,0,24.6-1.4,32.2-10.7c3.6-4.4,5.3-10.5,3.6-15.8
		C336.7,315.3,335.8,313.7,334.8,312.4z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M301.3,344.3c-1.3,0.1-2.6-0.1-3.9-0.5c-1.7-0.5-3-1.9-3.3-3.7c-0.3-2.1,0.5-4.2,2.1-5.6
		c0.9-0.7,1.9-1.3,3-1.8c-1.4-0.2-2.8-0.6-4.2-1.1c-1.9-0.6-2.9-1.6-3-2.9s0.9-2.7,2.9-3.6l1-0.4c-0.2,0-0.5-0.1-0.8-0.2
		c-1.1-0.2-2.1-0.8-2.8-1.6c-0.7-0.8-1-1.9-0.8-3c0.2-1.5,1-2.9,2.3-3.8c0.4-0.3,0.9-0.5,1.4-0.8c-1.6-0.7-2.7-2.2-2.7-4
		c0.3-4.1,5.4-5.6,8.7-6.6l0.9-0.3c9.4-3,18.6-2.4,26.2,1.5c2.8,1.4,5.2,3.4,7.1,5.9c1.1,1.5,2,3.1,2.5,4.9
		c1.7,5.3,0.2,11.6-3.7,16.5C326.2,343,312.5,344.3,301.3,344.3L301.3,344.3z M300,325c-1.6,0.4-3.1,0.9-4.5,1.6
		c-0.6,0.3-2.2,1.1-2,2.1c0.1,0.8,1.2,1.4,2.1,1.7c2.3,0.8,4.8,1.3,7.3,1.4c0.4,0,0.7,0.3,0.7,0.7c0,0.3-0.2,0.6-0.5,0.7
		c-2,0.5-4.2,1.1-5.8,2.5c-1.3,1-1.9,2.7-1.6,4.3c0.2,1.3,1.2,2.3,2.4,2.6c1.1,0.3,2.3,0.5,3.5,0.4c10.8,0,24.1-1.2,31.7-10.5
		c3.7-4.5,5-10.3,3.5-15.2c-0.5-1.6-1.3-3.1-2.3-4.4l0,0c-1.8-2.3-4-4.2-6.6-5.5c-7.2-3.7-16.1-4.2-25.1-1.4l-0.9,0.3
		c-3,0.9-7.5,2.2-7.7,5.4c0.1,1.3,0.9,2.4,2.1,2.7c0.8,0.3,1.7,0.4,2.5,0.3c0.7-0.1,1.3-0.1,1.9-0.1c0.4,0,0.7,0.3,0.7,0.6
		c0,0.4-0.2,0.7-0.6,0.8c-0.6,0.1-1.3,0.1-2,0.2c-1.4,0.1-2.8,0.5-4,1.3c-0.9,0.7-1.5,1.7-1.7,2.8c-0.1,0.7,0.1,1.4,0.5,1.9
		c0.6,0.6,1.3,1,2.1,1.1c1.4,0.4,2.8,0.5,4.2,0.3c0.6-0.1,1.1-0.3,1.7-0.4c0.4-0.1,0.7,0.2,0.8,0.5c0.1,0.4-0.1,0.7-0.5,0.8
		C301.1,324.8,300.6,324.9,300,325z"/>
|
|
||||||
<path class="st2_404-page-not-found-1-77" d="M261.6,263.9c-8.6,0-9.9-2.4-10.1-2.9c-0.1-0.4,0.1-0.8,0.5-0.9s0.8,0.1,0.9,0.5l0,0c0,0,1.2,2.2,10.5,1.8
		c0.9,0.1,1.8-0.4,2.3-1.2c2.2-4.3-6.2-18.6-9.6-23.7c-0.2-0.3-0.1-0.8,0.2-1c0.3-0.2,0.8-0.1,1,0.2c0,0,0,0,0,0
		c1.3,2,12.7,19.3,9.7,25.2c-0.7,1.3-2.1,2.1-3.5,2C262.8,263.8,262.2,263.9,261.6,263.9z"/>
|
|
||||||
<ellipse class="st3_404-page-not-found-1-77" cx="304.2" cy="235.7" rx="6.2" ry="10.6"/>
|
|
||||||
<ellipse class="st2_404-page-not-found-1-77" cx="228.5" cy="235.7" rx="4.7" ry="8.5"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 7.5 KiB |
@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
import { Image } from "@astrojs/image/components";
|
|
||||||
import type { Author } from "@/types/payload-types";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
author: Author;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { author } = Astro.props;
|
|
||||||
---
|
|
||||||
{ (author) &&
|
|
||||||
<div class="flex gap-6 border-t-2 border-secondary py-4 text-secondary">
|
|
||||||
{(typeof author.avatar === 'object') &&
|
|
||||||
<Image
|
|
||||||
src={author.avatar.url || ""}
|
|
||||||
width={150}
|
|
||||||
height={150}
|
|
||||||
aspectRatio={1}
|
|
||||||
alt={author.avatar.alt || ""}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<h3 class="font-semibold text-base">{author.name}</h3>
|
|
||||||
<p class="text-sm font-light">{author.bio}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -1,10 +1,5 @@
|
|||||||
---
|
---
|
||||||
import { Image } from "@astrojs/image/components";
|
import { Image } from "@astrojs/image/components";
|
||||||
import type { Post } from "@/types/payload-types";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
post: Post;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { post } = Astro.props;
|
const { post } = Astro.props;
|
||||||
---
|
---
|
@ -1,10 +1,5 @@
|
|||||||
---
|
---
|
||||||
import PostEntry from "@/components/PostEntry.astro"
|
import Post from "@/components/Post.astro"
|
||||||
import type { Post } from "@/types/payload-types";
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
posts: Post[];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { posts } = Astro.props;
|
const { posts } = Astro.props;
|
||||||
---
|
---
|
||||||
@ -15,9 +10,8 @@ const { posts } = Astro.props;
|
|||||||
{
|
{
|
||||||
posts.length > 0 ? (
|
posts.length > 0 ? (
|
||||||
posts.map((post) => (
|
posts.map((post) => (
|
||||||
typeof(post) === 'object') &&
|
<Post post={post}/>
|
||||||
<PostEntry post={post}/>
|
))
|
||||||
)
|
|
||||||
) : (
|
) : (
|
||||||
<p>No posts available</p>
|
<p>No posts available</p>
|
||||||
)
|
)
|
||||||
|
@ -22,7 +22,10 @@
|
|||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
|
|
||||||
--black: 10, 16, 19;
|
--black: 10, 16, 19;:root {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
--white: 243, 252, 248;
|
--white: 243, 252, 248;
|
||||||
--absolute-black: 0, 0, 0;
|
--absolute-black: 0, 0, 0;
|
||||||
--absolute-white: 255, 255, 255;
|
--absolute-white: 255, 255, 255;
|
||||||
@ -78,7 +81,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
h4, h5, h6 {
|
h4, h5, h6 {
|
||||||
@apply text-2xl font-medium;
|
@apply text-xl font-semibold;
|
||||||
}
|
}
|
||||||
|
|
||||||
p, span, li, a {
|
p, span, li, a {
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
import ContentLayout from "@/layouts/ContentLayout.astro"
|
|
||||||
import { Image } from "@astrojs/image/components";
|
|
||||||
import notFound from "@/assets/404.svg"
|
|
||||||
---
|
|
||||||
|
|
||||||
<ContentLayout>
|
|
||||||
<div class="flex flex-col justify-center items-center gap-8">
|
|
||||||
<Image
|
|
||||||
src={notFound}
|
|
||||||
width={360}
|
|
||||||
aspectRatio={1}
|
|
||||||
format="svg"
|
|
||||||
alt={"404"}
|
|
||||||
/>
|
|
||||||
<h1>404: Page not found</h1>
|
|
||||||
</div>
|
|
||||||
</ContentLayout>
|
|
@ -1,9 +1,8 @@
|
|||||||
---
|
---
|
||||||
import ContentLayout from "@/layouts/ContentLayout.astro";
|
import ContentLayout from "@/layouts/ContentLayout.astro";
|
||||||
import Content from "@/components/Content.astro";
|
import Content from "@/components/Content.astro";
|
||||||
import type { Post } from "@/types/payload-types";
|
import type { Post } from "@/types";
|
||||||
import { getPost, getPosts } from "@/utils/payload";
|
import { getPost, getPosts } from "@/utils/payload";
|
||||||
import Author from "@/components/Author.astro";
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getPosts();
|
const posts = await getPosts();
|
||||||
@ -19,15 +18,11 @@ const post = id && (await getPost(id));
|
|||||||
|
|
||||||
{
|
{
|
||||||
post ? (
|
post ? (
|
||||||
<ContentLayout title={`${post.title!}`}>
|
<ContentLayout title={`Paystro | ${post.title!}`}>
|
||||||
<article class="space-y-3 my-3 max-w-prose">
|
<article class="space-y-3 my-3 max-w-prose">
|
||||||
<h1 class="">{post.title}</h1>
|
<h1 class="">{post.title}</h1>
|
||||||
{post.content && <Content content={post.content} />}
|
{post.content && <Content content={post.content} />}
|
||||||
</article>
|
</article>
|
||||||
{typeof post.author === 'object' &&
|
|
||||||
<aside class="mt-8">
|
|
||||||
<Author author={post.author} />
|
|
||||||
</aside>}
|
|
||||||
</ContentLayout>
|
</ContentLayout>
|
||||||
) : (
|
) : (
|
||||||
<div>404</div>
|
<div>404</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Post } from "@/types/payload-types";
|
import type { Post } from "@/types";
|
||||||
|
|
||||||
const url = import.meta.env.DEV
|
const url = import.meta.env.DEV
|
||||||
? "http://payload:3001"
|
? "http://payload:3001"
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
"types": ["@astrojs/image/client"],
|
"types": ["@astrojs/image/client"],
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["src/*"],
|
"@/*": ["src/*"]
|
||||||
"@/types/*": ["../*"]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"version": "1.2",
|
"version": "1.2",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "yarn dev:docker & yarn payload:types",
|
"dev": "docker compose up --build",
|
||||||
"dev:docker": "docker compose up --build",
|
|
||||||
"payload:types": "yarn --cwd ./payload run generate:types:listen",
|
|
||||||
"stop": "docker compose down",
|
"stop": "docker compose down",
|
||||||
"dev:nobuild": "docker compose up"
|
"dev:nobuild": "docker compose up"
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* This file was automatically generated by Payload.
|
|
||||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
|
||||||
* and re-run `payload generate:types` to regenerate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export interface Config {
|
|
||||||
collections: {
|
|
||||||
posts: Post;
|
|
||||||
users: User;
|
|
||||||
authors: Author;
|
|
||||||
media: Media;
|
|
||||||
};
|
|
||||||
globals: {};
|
|
||||||
}
|
|
||||||
export interface Post {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
summary?: string;
|
|
||||||
publishedDate?: string;
|
|
||||||
thumbnail: string | Media;
|
|
||||||
content?: {
|
|
||||||
[k: string]: unknown;
|
|
||||||
}[];
|
|
||||||
author?: string | Author;
|
|
||||||
status: 'draft' | 'published' | 'archived';
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
}
|
|
||||||
export interface Media {
|
|
||||||
id: string;
|
|
||||||
alt: string;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
url?: string;
|
|
||||||
filename?: string;
|
|
||||||
mimeType?: string;
|
|
||||||
filesize?: number;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
}
|
|
||||||
export interface Author {
|
|
||||||
id: string;
|
|
||||||
avatar: string | Media;
|
|
||||||
name: string;
|
|
||||||
bio?: string;
|
|
||||||
user?: string | User;
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
}
|
|
||||||
export interface User {
|
|
||||||
id: string;
|
|
||||||
roles: ('ssg' | 'admin' | 'editor' | 'user')[];
|
|
||||||
updatedAt: string;
|
|
||||||
createdAt: string;
|
|
||||||
email: string;
|
|
||||||
resetPasswordToken?: string;
|
|
||||||
resetPasswordExpiration?: string;
|
|
||||||
salt?: string;
|
|
||||||
hash?: string;
|
|
||||||
loginAttempts?: number;
|
|
||||||
lockUntil?: string;
|
|
||||||
password?: string;
|
|
||||||
}
|
|
@ -6,22 +6,17 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
|
||||||
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js build ",
|
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js build",
|
||||||
"build:server": "tsc",
|
"build:server": "tsc",
|
||||||
"build": "yarn generate:types && yarn build:payload && yarn build:server",
|
"build": "yarn build:payload && yarn build:server",
|
||||||
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js node -r tsconfig-paths/register dist/server.js",
|
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js node -r tsconfig-paths/register dist/server.js",
|
||||||
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js generate:types",
|
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js generate:types"
|
||||||
"generate:types:listen": "nodemon --watch src --ext ts --exec 'npm run generate:types'"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@payloadcms/bundler-webpack": "^1.0.6",
|
|
||||||
"@payloadcms/db-mongodb": "^1.5.1",
|
|
||||||
"@payloadcms/plugin-cloud": "^3.0.1",
|
|
||||||
"@payloadcms/richtext-slate": "^1.5.2",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"payload": "^2.18.3",
|
"payload": "^1.15.6",
|
||||||
"tsconfig-paths": "^4.2.0"
|
"tsconfig-paths": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { Access, FieldAccess } from "payload/types";
|
|
||||||
import { User } from "@/types/payload-types";
|
|
||||||
|
|
||||||
export const isAdmin: Access<any, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an admin role
|
|
||||||
return Boolean(user?.roles?.includes('admin'));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isAdminFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an admin role
|
|
||||||
return Boolean(user?.roles?.includes('admin'));
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
import { Access } from "payload/config";
|
|
||||||
|
|
||||||
export const isAdminOrSelf: Access = ({ req: { user } }) => {
|
|
||||||
// Need to be logged in
|
|
||||||
if (user) {
|
|
||||||
// If user has role of 'admin'
|
|
||||||
if (user.roles?.includes('admin')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If any other type of user, only provide access to themselves
|
|
||||||
return {
|
|
||||||
id: {
|
|
||||||
equals: user.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject everyone else
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { Access, FieldAccess } from "payload/types";
|
|
||||||
import { User } from "@/types/payload-types";
|
|
||||||
|
|
||||||
export const isEditor: Access<any, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an editor role
|
|
||||||
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isEditorFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an editor role
|
|
||||||
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
import { Access, FieldAccess } from "payload/types";
|
|
||||||
import { User } from "@/types/payload-types";
|
|
||||||
|
|
||||||
export const isSSG: Access<any, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an ssg or admin role
|
|
||||||
return Boolean(user?.roles?.some(role => ['ssg', 'admin'].includes(role)));
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isSSGFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an ssg or admin role
|
|
||||||
return Boolean(user?.roles?.some(role => ['ssg', 'admin'].includes(role)));
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
import { Access, FieldAccess } from "payload/types";
|
|
||||||
import { User } from "@/types/payload-types";
|
|
||||||
|
|
||||||
export const isUser: Access<any, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an ssg or admin role
|
|
||||||
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isUserFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
|
|
||||||
// Return true or false based on if the user has an ssg or admin role
|
|
||||||
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
import { CollectionConfig } from "payload/types";
|
|
||||||
import { isAdmin } from "@/access/isAdmin";
|
|
||||||
import { isEditor } from "@/access/isEditor";
|
|
||||||
import { isSSG } from "@/access/isSSG";
|
|
||||||
import { isUser } from "@/access/isUser";
|
|
||||||
|
|
||||||
const Authors: CollectionConfig = {
|
|
||||||
slug: "authors",
|
|
||||||
admin: {
|
|
||||||
defaultColumns: ["name"],
|
|
||||||
useAsTitle: "name",
|
|
||||||
},
|
|
||||||
access: {
|
|
||||||
//TODO: Author can CRUD own post
|
|
||||||
create: isUser,
|
|
||||||
read: () => true,
|
|
||||||
update: isEditor,
|
|
||||||
delete: isEditor,
|
|
||||||
},
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: "avatar",
|
|
||||||
type: "upload",
|
|
||||||
relationTo: "media",
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "name",
|
|
||||||
type: "text",
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "bio",
|
|
||||||
type: "text",
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "user",
|
|
||||||
type: "upload",
|
|
||||||
relationTo: "users",
|
|
||||||
admin: {
|
|
||||||
description: 'The selected user will be able to edit this author'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Authors;
|
|
@ -1,4 +1,3 @@
|
|||||||
import { isUser } from "@/access/isUser";
|
|
||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
|
|
||||||
export const Media: CollectionConfig = {
|
export const Media: CollectionConfig = {
|
||||||
@ -6,15 +5,15 @@ export const Media: CollectionConfig = {
|
|||||||
admin: {},
|
admin: {},
|
||||||
access: {
|
access: {
|
||||||
read: (): boolean => true,
|
read: (): boolean => true,
|
||||||
create: isUser,
|
create: () => true,
|
||||||
update: isUser,
|
update: () => true,
|
||||||
delete: isUser,
|
|
||||||
},
|
},
|
||||||
upload: {
|
upload: {
|
||||||
staticURL: "/media",
|
staticURL: "/media",
|
||||||
staticDir: "media",
|
staticDir: "media",
|
||||||
mimeTypes: ["image/*"],
|
mimeTypes: ["image/*"],
|
||||||
},
|
},
|
||||||
|
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: "alt",
|
name: "alt",
|
||||||
|
@ -1,10 +1,4 @@
|
|||||||
import { CollectionConfig } from "payload/types";
|
import { CollectionConfig } from "payload/types";
|
||||||
import { isAdmin } from "@/access/isAdmin";
|
|
||||||
import { isEditor } from "@/access/isEditor";
|
|
||||||
import { isSSG } from "@/access/isSSG";
|
|
||||||
import { isUser } from "@/access/isUser";
|
|
||||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
|
||||||
|
|
||||||
const Posts: CollectionConfig = {
|
const Posts: CollectionConfig = {
|
||||||
slug: "posts",
|
slug: "posts",
|
||||||
versions: true,
|
versions: true,
|
||||||
@ -13,11 +7,9 @@ const Posts: CollectionConfig = {
|
|||||||
useAsTitle: "title",
|
useAsTitle: "title",
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
//TODO: Author can CRUD own post
|
|
||||||
create: isUser,
|
|
||||||
read: () => true,
|
read: () => true,
|
||||||
update: isEditor,
|
create: () => true,
|
||||||
delete: isEditor,
|
update: () => true,
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
afterChange: [
|
afterChange: [
|
||||||
@ -69,34 +61,33 @@ const Posts: CollectionConfig = {
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'content',
|
name: "content",
|
||||||
type: 'richText',
|
type: "richText",
|
||||||
editor: slateEditor({
|
admin: {
|
||||||
admin: {
|
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
|
||||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload", "blockquote", "indent"],
|
leaves: ["bold", "italic", "underline"],
|
||||||
leaves: ["bold", "italic", "underline", "strikethrough"],
|
upload: {
|
||||||
upload: {
|
collections: {
|
||||||
collections: {
|
media: {
|
||||||
media: {
|
fields: [
|
||||||
fields: [
|
{
|
||||||
{
|
name: "image",
|
||||||
name: "image",
|
type: "upload",
|
||||||
type: "upload",
|
relationTo: "media",
|
||||||
relationTo: "media",
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "author",
|
name: "author",
|
||||||
//TODO: Add active user as default
|
|
||||||
type: 'relationship',
|
type: 'relationship',
|
||||||
relationTo: 'authors',
|
relationTo: 'users',
|
||||||
|
hasMany: false,
|
||||||
|
required: false,
|
||||||
admin: {
|
admin: {
|
||||||
position: "sidebar",
|
position: "sidebar",
|
||||||
},
|
},
|
||||||
@ -104,7 +95,6 @@ const Posts: CollectionConfig = {
|
|||||||
{
|
{
|
||||||
name: "status",
|
name: "status",
|
||||||
type: "select",
|
type: "select",
|
||||||
required: true,
|
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
value: "draft",
|
value: "draft",
|
||||||
|
@ -1,41 +1,20 @@
|
|||||||
import { CollectionConfig } from 'payload/types';
|
import { CollectionConfig } from 'payload/types';
|
||||||
import { isAdmin, isAdminFieldLevel } from '../access/isAdmin';
|
|
||||||
import { isAdminOrSelf } from '../access/isAdminOrSelf';
|
|
||||||
|
|
||||||
const Users: CollectionConfig = {
|
const Users: CollectionConfig = {
|
||||||
slug: 'users',
|
slug: 'users',
|
||||||
auth: true,
|
auth: true,
|
||||||
admin: {
|
admin: {
|
||||||
defaultColumns: ["roles", "email"],
|
|
||||||
useAsTitle: 'email',
|
useAsTitle: 'email',
|
||||||
},
|
},
|
||||||
access: {
|
access: {
|
||||||
create: isAdmin,
|
read: () => true,
|
||||||
read: isAdminOrSelf,
|
|
||||||
update: isAdminOrSelf,
|
|
||||||
delete: isAdmin,
|
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
|
// Email added by default
|
||||||
{
|
{
|
||||||
name: 'roles',
|
name: 'name',
|
||||||
type: 'select',
|
type: 'text',
|
||||||
options: [
|
}
|
||||||
{ label: 'ssg', value: 'ssg' }, //cRud
|
|
||||||
{ label: 'Admin', value: 'admin' }, //CRUD, role creation
|
|
||||||
{ label: 'Editor', value: 'editor' }, //CRUD
|
|
||||||
{ label: 'User', value: 'user' }, //cRud, CRUD own entries
|
|
||||||
],
|
|
||||||
required: true,
|
|
||||||
defaultValue: "user",
|
|
||||||
// JWT so that role is accessible from 'req.user'
|
|
||||||
saveToJWT: true,
|
|
||||||
hasMany: true,
|
|
||||||
access: {
|
|
||||||
create: isAdminFieldLevel,
|
|
||||||
read: () => true,
|
|
||||||
update: isAdminFieldLevel,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,19 +2,12 @@ import { buildConfig } from "payload/config";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import Posts from "@/collections/Posts";
|
import Posts from "@/collections/Posts";
|
||||||
import Users from "@/collections/Users";
|
import Users from "@/collections/Users";
|
||||||
import Authors from "./collections/Authors";
|
|
||||||
import Media from "@/collections/Media";
|
import Media from "@/collections/Media";
|
||||||
|
|
||||||
import { payloadCloud } from '@payloadcms/plugin-cloud'
|
|
||||||
import { mongooseAdapter } from '@payloadcms/db-mongodb'
|
|
||||||
import { webpackBundler } from '@payloadcms/bundler-webpack'
|
|
||||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
|
||||||
|
|
||||||
export default buildConfig({
|
export default buildConfig({
|
||||||
serverURL: process.env.PAYLOAD_URL,
|
serverURL: process.env.PAYLOAD_URL,
|
||||||
admin: {
|
admin: {
|
||||||
user: Users.slug,
|
user: Users.slug,
|
||||||
bundler: webpackBundler(),
|
|
||||||
webpack: (config) => ({
|
webpack: (config) => ({
|
||||||
...config,
|
...config,
|
||||||
resolve: {
|
resolve: {
|
||||||
@ -26,13 +19,8 @@ export default buildConfig({
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
collections: [Posts, Users, Authors, Media],
|
collections: [Posts, Users, Media],
|
||||||
typescript: {
|
typescript: {
|
||||||
outputFile: path.resolve("../", "payload-types.ts"),
|
outputFile: path.resolve("/", "types.ts"),
|
||||||
},
|
},
|
||||||
plugins: [payloadCloud()],
|
});
|
||||||
editor: slateEditor({}),
|
|
||||||
db: mongooseAdapter({
|
|
||||||
url: process.env.MONGODB_URI,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
import { Payload } from "payload";
|
|
||||||
import { User } from "@/types/payload-types";
|
|
||||||
|
|
||||||
export const seed = async (payload: Payload): Promise<void> => {
|
|
||||||
// Local API methods skip all access control by default
|
|
||||||
// so we can easily create an admin user directly in init
|
|
||||||
|
|
||||||
/* Disable to prevent mistakes */
|
|
||||||
/* await payload.create<User>({
|
|
||||||
collection: 'users',
|
|
||||||
data: {
|
|
||||||
email: 'astro@ssg.js',
|
|
||||||
password: 'password',
|
|
||||||
roles: ['ssg']
|
|
||||||
}
|
|
||||||
}) */
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ app.get("/", (_, res) => {
|
|||||||
|
|
||||||
payload.init({
|
payload.init({
|
||||||
secret: process.env.PAYLOAD_SECRET,
|
secret: process.env.PAYLOAD_SECRET,
|
||||||
//mongoURL: process.env.MONGODB_URI,
|
mongoURL: process.env.MONGODB_URI,
|
||||||
express: app,
|
express: app,
|
||||||
onInit: () => {
|
onInit: () => {
|
||||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||||
|
@ -7,8 +7,7 @@
|
|||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"],
|
"@/*": ["./src/*", "./dist/*", "./dist/src/*"]
|
||||||
"@/types/*": ["../*"]
|
|
||||||
},
|
},
|
||||||
"jsx": "react"
|
"jsx": "react"
|
||||||
},
|
},
|
||||||
|
4442
payload/yarn.lock
4442
payload/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user