generated from autonomic-cooperative/astro-payload-template
Compare commits
29 Commits
c90d2c0d9b
...
e9fb68ec9f
Author | SHA1 | Date | |
---|---|---|---|
|
e9fb68ec9f | ||
|
f37a1719b7 | ||
|
d62e7d788e | ||
|
8fdfb2fbe4 | ||
|
0bb0644b55 | ||
|
b85c62f3fc | ||
910f943a2d | |||
|
10cb01964b | ||
|
91073bd498 | ||
|
cc0a5cb1c5 | ||
|
7dbc81c5b2 | ||
|
4b30d58db6 | ||
|
62e50496a2 | ||
|
2a494425ad | ||
|
b91dd893a4 | ||
|
691729c53c | ||
|
09ad9bdc6d | ||
|
f94bc5d822 | ||
|
370eac2b25 | ||
|
ba6ca90c3a | ||
|
985774bf24 | ||
|
747ada89e9 | ||
|
ff564a62ec | ||
|
9629c93ceb | ||
|
d7f22fdd5f | ||
|
1ccf660f5b | ||
|
1dbb075cd8 | ||
|
386b5c17f9 | ||
|
f017fa28dc |
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
data
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
#payload-types.ts
|
5
astro/.gitignore
vendored
5
astro/.gitignore
vendored
@ -15,7 +15,4 @@ pnpm-debug.log*
|
||||
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# types
|
||||
src/types.ts
|
||||
.DS_Store
|
36
astro/src/assets/404.svg
Normal file
36
astro/src/assets/404.svg
Normal file
@ -0,0 +1,36 @@
|
||||
<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>
|
After Width: | Height: | Size: 7.5 KiB |
27
astro/src/components/Author.astro
Normal file
27
astro/src/components/Author.astro
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
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,5 +1,10 @@
|
||||
---
|
||||
import { Image } from "@astrojs/image/components";
|
||||
import type { Post } from "@/types/payload-types";
|
||||
|
||||
interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
---
|
@ -1,5 +1,10 @@
|
||||
---
|
||||
import Post from "@/components/Post.astro"
|
||||
import PostEntry from "@/components/PostEntry.astro"
|
||||
import type { Post } from "@/types/payload-types";
|
||||
|
||||
interface Props {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
---
|
||||
@ -10,8 +15,9 @@ const { posts } = Astro.props;
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
posts.map((post) => (
|
||||
<Post post={post}/>
|
||||
))
|
||||
typeof(post) === 'object') &&
|
||||
<PostEntry post={post}/>
|
||||
)
|
||||
) : (
|
||||
<p>No posts available</p>
|
||||
)
|
||||
|
@ -22,10 +22,7 @@
|
||||
|
||||
.dark {
|
||||
|
||||
--black: 10, 16, 19;:root {
|
||||
|
||||
}
|
||||
|
||||
--black: 10, 16, 19;
|
||||
--white: 243, 252, 248;
|
||||
--absolute-black: 0, 0, 0;
|
||||
--absolute-white: 255, 255, 255;
|
||||
@ -81,7 +78,7 @@
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
@apply text-xl font-semibold;
|
||||
@apply text-2xl font-medium;
|
||||
}
|
||||
|
||||
p, span, li, a {
|
||||
|
18
astro/src/pages/404.astro
Normal file
18
astro/src/pages/404.astro
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
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,8 +1,9 @@
|
||||
---
|
||||
import ContentLayout from "@/layouts/ContentLayout.astro";
|
||||
import Content from "@/components/Content.astro";
|
||||
import type { Post } from "@/types";
|
||||
import type { Post } from "@/types/payload-types";
|
||||
import { getPost, getPosts } from "@/utils/payload";
|
||||
import Author from "@/components/Author.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getPosts();
|
||||
@ -18,11 +19,15 @@ const post = id && (await getPost(id));
|
||||
|
||||
{
|
||||
post ? (
|
||||
<ContentLayout title={`Paystro | ${post.title!}`}>
|
||||
<ContentLayout title={`${post.title!}`}>
|
||||
<article class="space-y-3 my-3 max-w-prose">
|
||||
<h1 class="">{post.title}</h1>
|
||||
{post.content && <Content content={post.content} />}
|
||||
</article>
|
||||
{typeof post.author === 'object' &&
|
||||
<aside class="mt-8">
|
||||
<Author author={post.author} />
|
||||
</aside>}
|
||||
</ContentLayout>
|
||||
) : (
|
||||
<div>404</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Post } from "@/types";
|
||||
import type { Post } from "@/types/payload-types";
|
||||
|
||||
const url = import.meta.env.DEV
|
||||
? "http://payload:3001"
|
||||
|
@ -4,7 +4,8 @@
|
||||
"types": ["@astrojs/image/client"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
"@/*": ["src/*"],
|
||||
"@/types/*": ["../*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,9 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"version": "1.2",
|
||||
"scripts": {
|
||||
"dev": "docker compose up --build",
|
||||
"dev": "yarn dev:docker & yarn payload:types",
|
||||
"dev:docker": "docker compose up --build",
|
||||
"payload:types": "yarn --cwd ./payload run generate:types:listen",
|
||||
"stop": "docker compose down",
|
||||
"dev:nobuild": "docker compose up"
|
||||
}
|
||||
|
66
payload-types.ts
Normal file
66
payload-types.ts
Normal file
@ -0,0 +1,66 @@
|
||||
/* 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,17 +6,22 @@
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"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": "yarn build:payload && yarn build:server",
|
||||
"build": "yarn generate:types && yarn build:payload && yarn build:server",
|
||||
"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": {
|
||||
"@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",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.17.1",
|
||||
"payload": "^1.15.6",
|
||||
"payload": "^2.18.3",
|
||||
"tsconfig-paths": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
12
payload/src/access/isAdmin.ts
Normal file
12
payload/src/access/isAdmin.ts
Normal file
@ -0,0 +1,12 @@
|
||||
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'));
|
||||
}
|
21
payload/src/access/isAdminOrSelf.ts
Normal file
21
payload/src/access/isAdminOrSelf.ts
Normal file
@ -0,0 +1,21 @@
|
||||
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;
|
||||
}
|
12
payload/src/access/isEditor.ts
Normal file
12
payload/src/access/isEditor.ts
Normal file
@ -0,0 +1,12 @@
|
||||
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)));
|
||||
}
|
12
payload/src/access/isSSG.ts
Normal file
12
payload/src/access/isSSG.ts
Normal file
@ -0,0 +1,12 @@
|
||||
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)));
|
||||
}
|
13
payload/src/access/isUser.ts
Normal file
13
payload/src/access/isUser.ts
Normal file
@ -0,0 +1,13 @@
|
||||
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)));
|
||||
}
|
49
payload/src/collections/Authors.ts
Normal file
49
payload/src/collections/Authors.ts
Normal file
@ -0,0 +1,49 @@
|
||||
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,3 +1,4 @@
|
||||
import { isUser } from "@/access/isUser";
|
||||
import { CollectionConfig } from "payload/types";
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
@ -5,15 +6,15 @@ export const Media: CollectionConfig = {
|
||||
admin: {},
|
||||
access: {
|
||||
read: (): boolean => true,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
create: isUser,
|
||||
update: isUser,
|
||||
delete: isUser,
|
||||
},
|
||||
upload: {
|
||||
staticURL: "/media",
|
||||
staticDir: "media",
|
||||
mimeTypes: ["image/*"],
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: "alt",
|
||||
|
@ -1,4 +1,10 @@
|
||||
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 = {
|
||||
slug: "posts",
|
||||
versions: true,
|
||||
@ -7,9 +13,11 @@ const Posts: CollectionConfig = {
|
||||
useAsTitle: "title",
|
||||
},
|
||||
access: {
|
||||
//TODO: Author can CRUD own post
|
||||
create: isUser,
|
||||
read: () => true,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
update: isEditor,
|
||||
delete: isEditor,
|
||||
},
|
||||
hooks: {
|
||||
afterChange: [
|
||||
@ -61,33 +69,34 @@ const Posts: CollectionConfig = {
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
admin: {
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
|
||||
leaves: ["bold", "italic", "underline"],
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
name: "image",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: slateEditor({
|
||||
admin: {
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload", "blockquote", "indent"],
|
||||
leaves: ["bold", "italic", "underline", "strikethrough"],
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
name: "image",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
name: "author",
|
||||
//TODO: Add active user as default
|
||||
type: 'relationship',
|
||||
relationTo: 'users',
|
||||
hasMany: false,
|
||||
required: false,
|
||||
relationTo: 'authors',
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
},
|
||||
@ -95,6 +104,7 @@ const Posts: CollectionConfig = {
|
||||
{
|
||||
name: "status",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: "draft",
|
||||
|
@ -1,20 +1,41 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
import { isAdmin, isAdminFieldLevel } from '../access/isAdmin';
|
||||
import { isAdminOrSelf } from '../access/isAdminOrSelf';
|
||||
|
||||
const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
defaultColumns: ["roles", "email"],
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
create: isAdmin,
|
||||
read: isAdminOrSelf,
|
||||
update: isAdminOrSelf,
|
||||
delete: isAdmin,
|
||||
},
|
||||
fields: [
|
||||
// Email added by default
|
||||
fields: [
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
}
|
||||
name: 'roles',
|
||||
type: 'select',
|
||||
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,12 +2,19 @@ import { buildConfig } from "payload/config";
|
||||
import path from "path";
|
||||
import Posts from "@/collections/Posts";
|
||||
import Users from "@/collections/Users";
|
||||
import Authors from "./collections/Authors";
|
||||
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({
|
||||
serverURL: process.env.PAYLOAD_URL,
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
bundler: webpackBundler(),
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
resolve: {
|
||||
@ -19,8 +26,13 @@ export default buildConfig({
|
||||
},
|
||||
}),
|
||||
},
|
||||
collections: [Posts, Users, Media],
|
||||
collections: [Posts, Users, Authors, Media],
|
||||
typescript: {
|
||||
outputFile: path.resolve("/", "types.ts"),
|
||||
outputFile: path.resolve("../", "payload-types.ts"),
|
||||
},
|
||||
});
|
||||
plugins: [payloadCloud()],
|
||||
editor: slateEditor({}),
|
||||
db: mongooseAdapter({
|
||||
url: process.env.MONGODB_URI,
|
||||
}),
|
||||
});
|
17
payload/src/seed.ts
Normal file
17
payload/src/seed.ts
Normal file
@ -0,0 +1,17 @@
|
||||
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({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
//mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||
|
@ -7,7 +7,8 @@
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"]
|
||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"],
|
||||
"@/types/*": ["../*"]
|
||||
},
|
||||
"jsx": "react"
|
||||
},
|
||||
|
4334
payload/yarn.lock
4334
payload/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user