generated from autonomic-cooperative/astro-payload-template
Compare commits
19 Commits
09ad9bdc6d
...
t-work
Author | SHA1 | Date | |
---|---|---|---|
f37a1719b7 | |||
d62e7d788e | |||
8fdfb2fbe4 | |||
0bb0644b55 | |||
b85c62f3fc | |||
910f943a2d | |||
10cb01964b | |||
91073bd498 | |||
cc0a5cb1c5 | |||
7dbc81c5b2 | |||
4b30d58db6 | |||
62e50496a2 | |||
2a494425ad | |||
b91dd893a4 | |||
691729c53c | |||
c90d2c0d9b | |||
4544c7942c | |||
7997268fad | |||
d8cfd644f8 |
14
.drone.yml
14
.drone.yml
@ -14,7 +14,7 @@ steps:
|
||||
registry: git.autonomic.zone
|
||||
context: astro
|
||||
dockerfile: astro/Dockerfile
|
||||
trigger: &exclude-event-custom
|
||||
when: &exclude-event-custom
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
@ -30,8 +30,8 @@ steps:
|
||||
context: payload
|
||||
dockerfile: payload/Dockerfile
|
||||
target: dev
|
||||
trigger:
|
||||
<< *exclude-event-custom
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
|
||||
- name: publish payload prod container
|
||||
image: plugins/docker
|
||||
@ -40,8 +40,8 @@ steps:
|
||||
username: 3wordchant
|
||||
repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload
|
||||
target: prod
|
||||
trigger:
|
||||
<< *exclude-event-custom
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
|
||||
- name: deploy stack
|
||||
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
|
||||
@ -63,8 +63,8 @@ steps:
|
||||
PAYLOAD_URL: "https://admin.paystro.swarm-demo.autonomic.zone"
|
||||
depends_on:
|
||||
- publish payload prod container
|
||||
trigger:
|
||||
<< *exclude-event-custom
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
|
||||
- name: build astro content
|
||||
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro:latest
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,4 +3,4 @@ data
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
payload-types.ts
|
||||
#payload-types.ts
|
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>
|
||||
)
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
@ -8,15 +8,20 @@
|
||||
"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:server": "tsc",
|
||||
"build": "yarn build:payload && yarn build:server",
|
||||
"build": "yarn generate:types && 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": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Access, FieldAccess } from "payload/types";
|
||||
import { User } 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Access, FieldAccess } from "payload/types";
|
||||
import { User } 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Access, FieldAccess } from "payload/types";
|
||||
import { User } 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Access, FieldAccess } from "payload/types";
|
||||
import { User } 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
|
||||
|
@ -3,12 +3,11 @@ import { isAdmin } from "@/access/isAdmin";
|
||||
import { isEditor } from "@/access/isEditor";
|
||||
import { isSSG } from "@/access/isSSG";
|
||||
import { isUser } from "@/access/isUser";
|
||||
import { isEditorOrSelf } from "@/access/isEditorOrSelf";
|
||||
|
||||
const Authors: CollectionConfig = {
|
||||
slug: "authors",
|
||||
admin: {
|
||||
defaultColumns: ["avatar","name"],
|
||||
defaultColumns: ["name"],
|
||||
useAsTitle: "name",
|
||||
},
|
||||
access: {
|
||||
@ -19,20 +18,26 @@ const Authors: CollectionConfig = {
|
||||
delete: isEditor,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "avatar",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "name",
|
||||
type: "text",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "avatar",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
name: "bio",
|
||||
type: "text",
|
||||
required: false,
|
||||
},
|
||||
|
||||
{
|
||||
name: "user",
|
||||
type: "relationship",
|
||||
type: "upload",
|
||||
relationTo: "users",
|
||||
admin: {
|
||||
description: 'The selected user will be able to edit this author'
|
||||
|
@ -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",
|
||||
|
@ -3,6 +3,7 @@ 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",
|
||||
@ -68,11 +69,12 @@ const Posts: CollectionConfig = {
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: slateEditor({
|
||||
admin: {
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
|
||||
leaves: ["bold", "italic", "underline"],
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload", "blockquote", "indent"],
|
||||
leaves: ["bold", "italic", "underline", "strikethrough"],
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
@ -88,13 +90,13 @@ const Posts: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
name: "authors",
|
||||
name: "author",
|
||||
//TODO: Add active user as default
|
||||
type: 'relationship',
|
||||
relationTo: 'authors',
|
||||
hasMany: true,
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
defaultColumns: ["roles", "email"],
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
@ -22,7 +23,7 @@ const Users: CollectionConfig = {
|
||||
{ label: 'ssg', value: 'ssg' }, //cRud
|
||||
{ label: 'Admin', value: 'admin' }, //CRUD, role creation
|
||||
{ label: 'Editor', value: 'editor' }, //CRUD
|
||||
{ label: 'User', value: 'user' }, //CRUd
|
||||
{ label: 'User', value: 'user' }, //cRud, CRUD own entries
|
||||
],
|
||||
required: true,
|
||||
defaultValue: "user",
|
||||
|
@ -5,10 +5,16 @@ 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: {
|
||||
@ -24,4 +30,9 @@ export default buildConfig({
|
||||
typescript: {
|
||||
outputFile: path.resolve("../", "payload-types.ts"),
|
||||
},
|
||||
plugins: [payloadCloud()],
|
||||
editor: slateEditor({}),
|
||||
db: mongooseAdapter({
|
||||
url: process.env.MONGODB_URI,
|
||||
}),
|
||||
});
|
@ -1,15 +1,17 @@
|
||||
import { Payload } from "payload";
|
||||
import { User } from "./payload-types";
|
||||
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
|
||||
await payload.create<User>({
|
||||
|
||||
/* 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
Reference in New Issue
Block a user