generated from autonomic-cooperative/astro-payload-template
Initial commit
This commit is contained in:
commit
d07472434f
54
.drone.yml
Normal file
54
.drone.yml
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: publish pipeline
|
||||
steps:
|
||||
- name: publish astro container
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username: 3wordchant
|
||||
password:
|
||||
from_secret: git_autonomic_zone_token_3wc
|
||||
# NOTE: edit this if you want your image called something else
|
||||
repo: git.autonomic.zone/DRONE_REPO-astro
|
||||
auto_tag: true
|
||||
registry: git.autonomic.zone
|
||||
context: astro
|
||||
dockerfile: astro/Dockerfile
|
||||
- name: publish payload container
|
||||
image: plugins/docker
|
||||
settings:
|
||||
username: 3wordchant
|
||||
password:
|
||||
from_secret: git_autonomic_zone_token_3wc
|
||||
# NOTE: edit this if you want your image called something else
|
||||
repo: git.autonomic.zone/DRONE_REPO-payload
|
||||
auto_tag: true
|
||||
registry: git.autonomic.zone
|
||||
context: payload
|
||||
dockerfile: payload/Dockerfile
|
||||
target: prod
|
||||
# See https://docz.autonomic.zone/doc/setting-up-auto-deployment-using-drone-I4j2onjaKT
|
||||
- name: deploy stack
|
||||
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
|
||||
settings:
|
||||
stack: <FILL IN e.g. subdomain_domain_tld>
|
||||
host: <FILL IN HOST HERE>
|
||||
deploy_key:
|
||||
from_secret: drone_ssh_<FILL IN>
|
||||
environment:
|
||||
DOMAIN: <FILL IN>
|
||||
STACK_NAME: <FILL IN e.g. subdomain_domain_tld>
|
||||
SECRET_PAYLOAD_SECRET_VERSION: v1
|
||||
SECRET_TOKEN_VERSION: v1
|
||||
SECRET_MONGO_PASSWORD_VERSION: v1
|
||||
NGINX_CONF_VERSION: v1
|
||||
depends_on:
|
||||
- publish astro container
|
||||
- publish payload container
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
exclude:
|
||||
- pull_request
|
||||
|
11
.env
Normal file
11
.env
Normal file
@ -0,0 +1,11 @@
|
||||
NAME=astroad
|
||||
ASTRO_HOST=localhost:3000
|
||||
PAYLOAD_HOST=payload:3001
|
||||
PAYLOAD_URL=http://localhost:3001
|
||||
PAYLOAD_PORT=3001
|
||||
PAYLOAD_SECRET=supersecretkey
|
||||
MONGODB_URI=mongodb://payload:test@mongo:27017
|
||||
MONGODB_USER=payload
|
||||
MONGODB_PW=test
|
||||
TOKEN=supersecrettoken
|
||||
REPOSITORY=mooxl/astroad
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
data
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
20
LICENSE
Normal file
20
LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Max Schmidt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
66
README.md
Normal file
66
README.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Astroad
|
||||
|
||||
Astroad is a pre-configured setup for Astro and Payloadcms, designed to make it easy for you to start building your website. With Astroad, you'll have a complete development environment that you can run locally using Docker. This setup simplifies the testing and development of your website before deploying it to a production environment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before getting started with Astroad, make sure you have the necessary software installed:
|
||||
|
||||
- Docker
|
||||
- Node.js
|
||||
- Yarn
|
||||
|
||||
## Configuration
|
||||
|
||||
While there's no configuration necessary for local development, deployment via Github Workflows requires specific secrets and variables to be set.
|
||||
|
||||
### Secrets:
|
||||
|
||||
- `USER`: User on the server
|
||||
- `HOST`: IP or URL of the server
|
||||
- `KEY`: SSH KEY for connecting to the server
|
||||
- `MONGODB_PW`: Password for MongoDB
|
||||
- `MONGODB_USER`: User for MongoDB
|
||||
- `PATH`: Path where the repository resides on the server
|
||||
- `PAYLOAD_PORT`: Port at which Payload listens
|
||||
- `PAYLOAD_SECRET`: String to encrypt Payload data
|
||||
- `TOKEN`: Github Access Token for the webhook to trigger the payload.yml workflow and execute a new Astro build
|
||||
|
||||
### Variables:
|
||||
|
||||
- `ASTRO_HOST`: Hostdomain of the Frontend
|
||||
- `PAYLOAD_HOST`: Hostdomain of the CMS
|
||||
- `PAYLOAD_URL`: URL of the CMS
|
||||
- `NAME`: Name of the Container and Project
|
||||
|
||||
Please remember to set these secrets and variables in your repository settings to ensure a successful deployment through Github Workflows.
|
||||
|
||||
Once the secrets and variables are set on GitHub, they will replace the existing ones in the `.env` file on the server during deployment. This is done by the push.yml workflow, which replaces the placeholders in the `.env` with the actual secrets and variables defined in the repository settings. Please ensure that the names of your secrets and variables match with the placeholders in the `.env` file.
|
||||
|
||||
## Getting started
|
||||
|
||||
To get started with Astroad, you'll need to have Docker and NPM || Yarn || PNPM installed on your machine.
|
||||
|
||||
You have two options for getting the repository:
|
||||
|
||||
1. Use the 'Use this template' button on the Github repository. This will create a new repository in your Github account with the same directory structure and files as Astroad. After the new repository is created, you can clone it to your local machine.
|
||||
1. Alternatively, you can directly clone the Astroad repository: git clone https://github.com/mooxl/astroad.git. If you choose this option, remember to change the origin of your remote repository to a new one to avoid pushing changes directly to the Astroad repository. This can be done with the command: git remote set-url origin https://github.com/USERNAME/REPOSITORY.git where USERNAME is your username and REPOSITORY is the name of your new repository.
|
||||
|
||||
Once you've cloned the repository or created your own from the template, follow these steps:
|
||||
|
||||
1. Change into the repository directory: `cd {newName}`
|
||||
1. Start the containers: `yarn dev`
|
||||
|
||||
This will start up the Astro, Payloadcms and Mongo containers and make them available on your local machine. Astro will be served at http://localhost:3000 and the Payload will be available at http://localhost:3001.
|
||||
|
||||
## Development
|
||||
|
||||
The `docker-compose.yml` and `docker-compose-dev.yml` files includes everything you need to run the containers. The containers use the environment variables declared in the `.env` file and mounted volumes to store data persistently even after the containers are stopped and started.
|
||||
|
||||
## Deployment
|
||||
|
||||
Deployment is handled by a Github Actions Workflow on every push. It logs into the server via SSH, pulls or clones the latest version of the repository, and runs `yarn prod`.
|
||||
|
||||
Because Astro is completely static, a content change in the CMS must trigger a new build of Astro. Therefore, there’s a `payload.yml` workflow that gets triggered by a webhook after every content change from Payload.
|
||||
|
||||
Ensure you have Traefik set up as a reverse proxy before deployment. The prod script will launch your site in a production-ready environment.
|
3
astro/.dockerignore
Normal file
3
astro/.dockerignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
Dockerfile
|
||||
README.md
|
0
astro/.env
Normal file
0
astro/.env
Normal file
21
astro/.gitignore
vendored
Normal file
21
astro/.gitignore
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# types
|
||||
src/types.ts
|
11
astro/.prettierrc
Normal file
11
astro/.prettierrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
16
astro/Dockerfile
Normal file
16
astro/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
FROM node:lts-alpine as base
|
||||
WORKDIR /base
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
COPY . .
|
||||
|
||||
FROM base AS dev
|
||||
ENV NODE_ENV=development
|
||||
EXPOSE 3000
|
||||
CMD ["yarn","dev"]
|
||||
|
||||
FROM base AS build
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /build
|
||||
COPY --from=base /base .
|
||||
ADD "https://random-uuid.deno.dev" skipcache
|
29
astro/astro.config.mjs
Normal file
29
astro/astro.config.mjs
Normal file
@ -0,0 +1,29 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
import image from "@astrojs/image";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import prefetch from "@astrojs/prefetch";
|
||||
|
||||
export default defineConfig({
|
||||
compressHTML: true,
|
||||
build: {
|
||||
inlineStylesheets: "auto",
|
||||
},
|
||||
experimental: {
|
||||
viewTransitions: true,
|
||||
},
|
||||
integrations: [
|
||||
tailwind({
|
||||
config: {
|
||||
applyBaseStyles: false,
|
||||
},
|
||||
}),
|
||||
image({
|
||||
serviceEntryPoint: "@astrojs/image/sharp",
|
||||
}),
|
||||
prefetch({
|
||||
selector: "a",
|
||||
}),
|
||||
sitemap(),
|
||||
],
|
||||
});
|
36
astro/nginx.conf
Normal file
36
astro/nginx.conf
Normal file
@ -0,0 +1,36 @@
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include mime.types;
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location /error_page.html {
|
||||
internal;
|
||||
}
|
||||
|
||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|htm|html)$ {
|
||||
root /usr/share/nginx/html;
|
||||
expires 30d;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
error_page 404 /error_page.html;
|
||||
}
|
||||
}
|
27
astro/package.json
Normal file
27
astro/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "astroad",
|
||||
"description": "Astroad - Astro",
|
||||
"type": "module",
|
||||
"version": "1.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev": "astro dev --host",
|
||||
"build": "astro build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/image": "^0.17.3",
|
||||
"@astrojs/prefetch": "^0.3.0",
|
||||
"@astrojs/sitemap": "^2.0.2",
|
||||
"@astrojs/tailwind": "4.0.0",
|
||||
"astro": "^2.10.12",
|
||||
"css-select": "5.1.0",
|
||||
"sharp": "^0.32.6",
|
||||
"slate-serializers": "0.4.1",
|
||||
"tailwindcss": "^3.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.0.2",
|
||||
"prettier-plugin-astro": "^0.11.1",
|
||||
"prettier-plugin-tailwindcss": "^0.5.4"
|
||||
}
|
||||
}
|
9
astro/public/favicon.svg
Normal file
9
astro/public/favicon.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
After Width: | Height: | Size: 749 B |
BIN
astro/public/fonts/Plex-Bold.woff2
Normal file
BIN
astro/public/fonts/Plex-Bold.woff2
Normal file
Binary file not shown.
BIN
astro/public/fonts/Plex-Regular.woff2
Normal file
BIN
astro/public/fonts/Plex-Regular.woff2
Normal file
Binary file not shown.
27
astro/src/components/Content.astro
Normal file
27
astro/src/components/Content.astro
Normal file
@ -0,0 +1,27 @@
|
||||
---
|
||||
import { Image } from "@astrojs/image/components";
|
||||
import { getContentArray } from "@/utils/helpers";
|
||||
import { getImageSrc } from "@/utils/payload";
|
||||
const { content } = Astro.props;
|
||||
const contentArray = getContentArray(content);
|
||||
---
|
||||
|
||||
<div>
|
||||
{
|
||||
contentArray.map((value) => {
|
||||
if (typeof value === "string") {
|
||||
return <article set:html={value} />;
|
||||
} else {
|
||||
return (
|
||||
<Image
|
||||
src={getImageSrc(value.src)}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
format="webp"
|
||||
alt="hallo"
|
||||
/>
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
1
astro/src/env.d.ts
vendored
Normal file
1
astro/src/env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="astro/client" />
|
36
astro/src/layouts/Layout.astro
Normal file
36
astro/src/layouts/Layout.astro
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
|
||||
export interface Props {
|
||||
title: string;
|
||||
}
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<title>{title}</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Plex";
|
||||
src: url("/fonts/Plex-Regular.woff2") format("woff2");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Plex";
|
||||
src: url("/fonts/Plex-Bold.woff2") format("woff2");
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="mx-auto max-w-7xl bg-gray px-6 py-8 font-plex text-gray-200">
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
45
astro/src/pages/index.astro
Normal file
45
astro/src/pages/index.astro
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getPosts } from "@/utils/payload";
|
||||
|
||||
const posts = await getPosts();
|
||||
---
|
||||
|
||||
<Layout title="Astroad">
|
||||
<main class="" >
|
||||
<h1 class="font-bold text-5xl">Astroad</h1>
|
||||
<p class="mt-3 text-lg">
|
||||
Astroad is a pre-configured setup for Astro and Payloadcms that makes it
|
||||
easy to get started with building your website. With Astroad, you'll have
|
||||
a complete development environment that you can run locally using Docker.
|
||||
This makes it easy to test and develop your website before deploying it to
|
||||
a production environment.
|
||||
<br />
|
||||
When you're ready to deploy the website on your own server, Astrotus
|
||||
comes with a production environment that requires the use of Traefik as a
|
||||
reverse proxy. This setup provides a secure and scalable production
|
||||
environment for your website.
|
||||
</p>
|
||||
<h2 class="mt-6 font-bold text-2xl">Posts</h2>
|
||||
<div class="flex gap-4 mt-3 flex-wrap">
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
posts.map((post) => (
|
||||
<a href={`/posts/${post.id}/`}>
|
||||
<article class="text-gray bg-gray-light px-5 py-3 rounded-md shadow-md w-64 text-center hover:-translate-y-1 transition-transform">
|
||||
<h3 class="font-bold text-lg" transition:name=`title-${post.id}`>{post.title}</h3>
|
||||
{post.publishedDate && (
|
||||
<p>
|
||||
{new Date(post.publishedDate).toLocaleDateString("de-DE")}
|
||||
</p>
|
||||
)}
|
||||
</article>
|
||||
</a>
|
||||
))
|
||||
) : (
|
||||
<p>Add Posts in Payloadcms</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
31
astro/src/pages/posts/[id].astro
Normal file
31
astro/src/pages/posts/[id].astro
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import Content from "@/components/Content.astro";
|
||||
import type { Post } from "@/types";
|
||||
import { getPost, getPosts } from "@/utils/payload";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getPosts();
|
||||
const paths = posts.map((post: Post) => ({
|
||||
params: { id: post.id },
|
||||
}));
|
||||
return paths;
|
||||
}
|
||||
|
||||
const { id } = Astro.params;
|
||||
const post = id && (await getPost(id));
|
||||
---
|
||||
|
||||
{
|
||||
post ? (
|
||||
<Layout title={`Astroad | ${post.title!}`}>
|
||||
<div class="space-y-3 my-3">
|
||||
<a href="/">BACK</a>
|
||||
<h1 class="font-bold text-5xl" transition:name=`title-${post.id}`>{post.title}</h1>
|
||||
{post.content && <Content content={post.content} />}
|
||||
</div>
|
||||
</Layout>
|
||||
) : (
|
||||
<div>404</div>
|
||||
)
|
||||
}
|
41
astro/src/utils/helpers.ts
Normal file
41
astro/src/utils/helpers.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { payloadSlateToDomConfig, slateToHtml } from "slate-serializers";
|
||||
import { Element } from "domhandler";
|
||||
|
||||
export const getContentArray = (content: any) => {
|
||||
const html = slateToHtml(content, {
|
||||
...payloadSlateToDomConfig,
|
||||
elementTransforms: {
|
||||
...payloadSlateToDomConfig.elementTransforms,
|
||||
upload: ({ node }) =>
|
||||
// @ts-ignore
|
||||
new Element("img", {
|
||||
src: node.value.filename,
|
||||
width: `${node.value.width}`,
|
||||
height: `${node.value.height}`,
|
||||
}),
|
||||
},
|
||||
}).replaceAll("<p></p>", "<p> </p>");
|
||||
const htmlImageArray: (
|
||||
| string
|
||||
| { src: string; width: number; height: number }
|
||||
)[] = [];
|
||||
let lastIndex = 0;
|
||||
while (true) {
|
||||
const imgStartIndex = html.indexOf("<img", lastIndex);
|
||||
if (imgStartIndex === -1) {
|
||||
htmlImageArray.push(html.substring(lastIndex));
|
||||
break;
|
||||
}
|
||||
const imgEndIndex = html.indexOf(">", imgStartIndex) + 1;
|
||||
const imgTag = html.substring(imgStartIndex, imgEndIndex);
|
||||
const remainingHtml = html.substring(lastIndex, imgStartIndex);
|
||||
const imgObject = {
|
||||
src: imgTag.match(/src="(.*?)"/)![1],
|
||||
width: +imgTag.match(/width="(.*?)"/)![1],
|
||||
height: +imgTag.match(/height="(.*?)"/)![1],
|
||||
};
|
||||
htmlImageArray.push(remainingHtml, imgObject);
|
||||
lastIndex = imgEndIndex;
|
||||
}
|
||||
return htmlImageArray;
|
||||
};
|
13
astro/src/utils/payload.ts
Normal file
13
astro/src/utils/payload.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { Post } from "@/types";
|
||||
|
||||
const url = import.meta.env.DEV
|
||||
? "http://payload:3001"
|
||||
: `${import.meta.env.PAYLOAD_URL}`;
|
||||
|
||||
export const getPosts = async () =>
|
||||
(await (await fetch(`${url}/api/posts`)).json()).docs as Post[];
|
||||
|
||||
export const getPost = async (id: string) =>
|
||||
(await (await fetch(`${url}/api/posts/${id}`)).json()) as Post;
|
||||
|
||||
export const getImageSrc = (src: string) => `${url}/media/${src}`;
|
20
astro/tailwind.config.cjs
Normal file
20
astro/tailwind.config.cjs
Normal file
@ -0,0 +1,20 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
fontFamily: {
|
||||
plex: ["Plex", "sans-serif"],
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
gray: {
|
||||
DEFAULT: "#111111",
|
||||
light: "#888888",
|
||||
dark: "#222222",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
safelist: [],
|
||||
plugins: [],
|
||||
};
|
10
astro/tsconfig.json
Normal file
10
astro/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"types": ["@astrojs/image/client"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
4514
astro/yarn.lock
Normal file
4514
astro/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
95
compose.yml
Normal file
95
compose.yml
Normal file
@ -0,0 +1,95 @@
|
||||
---
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.26.0
|
||||
networks:
|
||||
- proxy
|
||||
volumes:
|
||||
- html_content:/usr/share/nginx/html
|
||||
configs:
|
||||
- source: nginx_default_conf
|
||||
target: /etc/nginx/conf.d/default.conf
|
||||
deploy:
|
||||
update_config:
|
||||
failure_action: rollback
|
||||
order: start-first
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.${STACK_NAME}-astro.loadbalancer.server.port=80"
|
||||
- "traefik.http.routers.${STACK_NAME}-astro.rule=Host(`${DOMAIN}`)"
|
||||
- "traefik.http.routers.${STACK_NAME}-astro.entrypoints=web-secure"
|
||||
- "traefik.http.routers.${STACK_NAME}-astro.tls.certresolver=production"
|
||||
|
||||
payload:
|
||||
image: git.autonomic.zone/autonomic-cooperative/justice-equity-technology-payload:latest
|
||||
environment:
|
||||
- "NAME=${STACK_NAME}"
|
||||
- "PAYLOAD_URL=${STACK_NAME}_payload"
|
||||
- "PAYLOAD_PORT=3001"
|
||||
- "PAYLOAD_SECRET_FILE=/run/secrets/payload_secret"
|
||||
- "MONGODB_USER=mongo"
|
||||
- "MONGODB_HOST=${STACK_NAME}_mongo"
|
||||
- "MONGODB_PORT=27017"
|
||||
- "MONGODB_PASSWORD_FILE=/run/secrets/mongo_password"
|
||||
- "TOKEN_FILE=/run/secrets/token"
|
||||
secrets:
|
||||
- mongo_password
|
||||
- payload_secret
|
||||
- token
|
||||
networks:
|
||||
- proxy
|
||||
- internal
|
||||
deploy:
|
||||
update_config:
|
||||
failure_action: rollback
|
||||
order: start-first
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.${STACK_NAME}-payload.loadbalancer.server.port=3001"
|
||||
# FIXME switch to /admin probably using PathPrefix
|
||||
- "traefik.http.routers.${STACK_NAME}-payload.rule=Host(`admin.${DOMAIN}`)"
|
||||
- "traefik.http.routers.${STACK_NAME}-payload.entrypoints=web-secure"
|
||||
- "traefik.http.routers.${STACK_NAME}-payload.tls.certresolver=production"
|
||||
|
||||
mongo:
|
||||
image: mongo:6.0.5
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- mongo:/data/db
|
||||
command:
|
||||
- --storageEngine=wiredTiger
|
||||
environment:
|
||||
- "MONGO_INITDB_ROOT_USERNAME=mongo"
|
||||
- "MONGO_INITDB_ROOT_PASSWORD_FILE=/run/secrets/mongo_password"
|
||||
secrets:
|
||||
- mongo_password
|
||||
networks:
|
||||
- internal
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
external: true
|
||||
internal:
|
||||
|
||||
secrets:
|
||||
payload_secret:
|
||||
external: true
|
||||
name: ${STACK_NAME}_payload_secret_${SECRET_PAYLOAD_SECRET_VERSION}
|
||||
token:
|
||||
external: true
|
||||
name: ${STACK_NAME}_token_${SECRET_TOKEN_VERSION}
|
||||
mongo_password:
|
||||
external: true
|
||||
name: ${STACK_NAME}_mongo_password_${SECRET_MONGO_PASSWORD_VERSION}
|
||||
|
||||
volumes:
|
||||
mongo:
|
||||
html_content:
|
||||
|
||||
configs:
|
||||
nginx_conf:
|
||||
name: ${STACK_NAME}_nginx_conf_${NGINX_CONF_VERSION}
|
||||
file: astro/nginx.conf
|
||||
template_driver: golang
|
59
docker-compose.yml
Normal file
59
docker-compose.yml
Normal file
@ -0,0 +1,59 @@
|
||||
services:
|
||||
astro:
|
||||
container_name: ${NAME}-astro
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: astro
|
||||
target: de
|
||||
networks:
|
||||
- front
|
||||
volumes:
|
||||
- ./astro:/base
|
||||
- /base/node_modules/
|
||||
ports:
|
||||
- 3000:3000
|
||||
depends_on:
|
||||
- payload
|
||||
|
||||
payload:
|
||||
container_name: ${NAME}-payload
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: payload
|
||||
environment:
|
||||
NAME: ${NAME}
|
||||
PAYLOAD_URL: ${PAYLOAD_URL}
|
||||
PAYLOAD_PORT: ${PAYLOAD_PORT}
|
||||
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
|
||||
MONGODB_URI: ${MONGODB_URI}
|
||||
TOKEN: ${TOKEN}
|
||||
volumes:
|
||||
- ./payload/src:/base/src
|
||||
- ./astro/src/types.ts:/types.ts
|
||||
ports:
|
||||
- 3001:3001
|
||||
networks:
|
||||
- front
|
||||
- back
|
||||
depends_on:
|
||||
- mongo
|
||||
|
||||
mongo:
|
||||
container_name: ${NAME}-mongo
|
||||
image: mongo:6.0.5
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./data/mongo:/data/db
|
||||
command:
|
||||
- --storageEngine=wiredTiger
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_USER}
|
||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PW}
|
||||
networks:
|
||||
- back
|
||||
ports:
|
||||
- 27017:27017
|
||||
|
||||
networks:
|
||||
front:
|
||||
back:
|
11
package.json
Normal file
11
package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "astroad",
|
||||
"description": "A pre-configured setup for easy website development with Astro and Payload CMS using Docker.",
|
||||
"license": "MIT",
|
||||
"version": "1.1",
|
||||
"scripts": {
|
||||
"dev": "docker compose -f docker-compose.yml -f docker-compose-dev.yml up --build",
|
||||
"prod": "docker compose -f docker-compose.yml -f docker-compose-prod.yml up -d --build",
|
||||
"stop": "docker compose -f docker-compose.yml -f docker-compose-dev.yml down && docker compose -f docker-compose.yml -f docker-compose-prod.yml down"
|
||||
}
|
||||
}
|
2
payload/.dockerignore
Normal file
2
payload/.dockerignore
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
Dockerfile
|
5
payload/.gitignore
vendored
Normal file
5
payload/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
.env
|
||||
build
|
||||
dist
|
||||
src/media
|
31
payload/Dockerfile
Normal file
31
payload/Dockerfile
Normal file
@ -0,0 +1,31 @@
|
||||
FROM node:lts as base
|
||||
WORKDIR /base
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
COPY . .
|
||||
|
||||
FROM base AS dev
|
||||
ENV NODE_ENV=development
|
||||
EXPOSE 3001
|
||||
CMD ["yarn","dev"]
|
||||
|
||||
|
||||
FROM base AS build
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /build
|
||||
COPY --from=base /base .
|
||||
RUN yarn build
|
||||
|
||||
FROM build as prod
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /prod
|
||||
COPY package*.json .
|
||||
RUN yarn install --production
|
||||
|
||||
COPY --from=build /build/tsconfig.json ./tsconfig.json
|
||||
COPY --from=build /build/dist ./dist
|
||||
COPY --from=build /build/build ./build
|
||||
EXPOSE 3000
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["yarn", "serve"]
|
32
payload/docker-entrypoint.sh
Executable file
32
payload/docker-entrypoint.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -eu
|
||||
|
||||
file_env() {
|
||||
local var="$1"
|
||||
local fileVar="${var}_FILE"
|
||||
local def="${2:-}"
|
||||
|
||||
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
|
||||
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local val="$def"
|
||||
if [ "${!var:-}" ]; then
|
||||
val="${!var}"
|
||||
elif [ "${!fileVar:-}" ]; then
|
||||
val="$(< "${!fileVar}")"
|
||||
fi
|
||||
|
||||
export "$var"="$val"
|
||||
unset "$fileVar"
|
||||
}
|
||||
|
||||
file_env "TOKEN"
|
||||
file_env "MONGODB_PASSWORD"
|
||||
file_env "PAYLOAD_SECRET"
|
||||
|
||||
export MONGODB_URI="mongodb://$MONGODB_USER:$MONGODB_PASSWORD@$MONGODB_HOST:$MONGODB_PORT"
|
||||
|
||||
"$@"
|
5
payload/nodemon.json
Normal file
5
payload/nodemon.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"ext": "ts",
|
||||
"ignore": ["src/types.ts"],
|
||||
"exec": "yarn generate:types && ts-node src/server.ts"
|
||||
}
|
28
payload/package.json
Normal file
28
payload/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "astroad",
|
||||
"description": "Astroad - Payload",
|
||||
"version": "1.1",
|
||||
"main": "dist/server.js",
|
||||
"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:server": "tsc",
|
||||
"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",
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^16.3.1",
|
||||
"express": "^4.17.1",
|
||||
"payload": "^1.15.6",
|
||||
"tsconfig-paths": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.18",
|
||||
"nodemon": "^3.0.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.6"
|
||||
}
|
||||
}
|
25
payload/src/collections/Media.ts
Normal file
25
payload/src/collections/Media.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { CollectionConfig } from "payload/types";
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
slug: "media",
|
||||
admin: {},
|
||||
access: {
|
||||
read: (): boolean => true,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
},
|
||||
upload: {
|
||||
staticURL: "/media",
|
||||
staticDir: "media",
|
||||
mimeTypes: ["image/*"],
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: "alt",
|
||||
type: "text",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Media;
|
98
payload/src/collections/Posts.ts
Normal file
98
payload/src/collections/Posts.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import { CollectionConfig } from "payload/types";
|
||||
const Posts: CollectionConfig = {
|
||||
slug: "posts",
|
||||
admin: {
|
||||
defaultColumns: ["title", "author", "status"],
|
||||
useAsTitle: "title",
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
},
|
||||
hooks: {
|
||||
afterChange: [
|
||||
async () => {
|
||||
console.log(process.env.TOKEN);
|
||||
|
||||
try {
|
||||
process.env.NODE_ENV !== "development" &&
|
||||
console.log(
|
||||
await fetch(
|
||||
`https://api.github.com/repos/${process.env.REPOSITORY}/dispatches`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/vnd.github.everest-preview+json",
|
||||
Authorization: `token ${process.env.TOKEN}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
event_type: "payload_update",
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
name: "hallo",
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
name: "publishedDate",
|
||||
type: "date",
|
||||
},
|
||||
|
||||
{
|
||||
name: "content",
|
||||
type: "richText",
|
||||
admin: {
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
|
||||
leaves: ["bold", "italic", "underline"],
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
name: "imagel",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
type: "select",
|
||||
options: [
|
||||
{
|
||||
value: "draft",
|
||||
label: "Draft",
|
||||
},
|
||||
{
|
||||
value: "published",
|
||||
label: "Published",
|
||||
},
|
||||
],
|
||||
defaultValue: "draft",
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default Posts;
|
21
payload/src/collections/Users.ts
Normal file
21
payload/src/collections/Users.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { CollectionConfig } from 'payload/types';
|
||||
|
||||
const Users: CollectionConfig = {
|
||||
slug: 'users',
|
||||
auth: true,
|
||||
admin: {
|
||||
useAsTitle: 'email',
|
||||
},
|
||||
access: {
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
// Email added by default
|
||||
{
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
export default Users;
|
26
payload/src/payload.config.ts
Normal file
26
payload/src/payload.config.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { buildConfig } from "payload/config";
|
||||
import path from "path";
|
||||
import Posts from "@/collections/Posts";
|
||||
import Users from "@/collections/Users";
|
||||
import Media from "@/collections/Media";
|
||||
|
||||
export default buildConfig({
|
||||
serverURL: process.env.PAYLOAD_URL,
|
||||
admin: {
|
||||
user: Users.slug,
|
||||
webpack: (config) => ({
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
alias: {
|
||||
...config.resolve.alias,
|
||||
"@": path.resolve(__dirname, "./"),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
collections: [Posts, Users, Media],
|
||||
typescript: {
|
||||
outputFile: path.resolve("/", "types.ts"),
|
||||
},
|
||||
});
|
21
payload/src/server.ts
Normal file
21
payload/src/server.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import express from "express";
|
||||
import payload from "payload";
|
||||
|
||||
require("dotenv").config();
|
||||
const app = express();
|
||||
|
||||
app.get("/", (_, res) => {
|
||||
res.redirect("/admin");
|
||||
});
|
||||
|
||||
payload.init({
|
||||
secret: process.env.PAYLOAD_SECRET,
|
||||
mongoURL: process.env.MONGODB_URI,
|
||||
express: app,
|
||||
onInit: () => {
|
||||
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
|
||||
},
|
||||
});
|
||||
|
||||
app.use("/media", express.static("media"));
|
||||
app.listen(process.env.PAYLOAD_PORT);
|
18
payload/tsconfig.json
Normal file
18
payload/tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"strict": false,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"]
|
||||
},
|
||||
"jsx": "react"
|
||||
},
|
||||
"ts-node": {
|
||||
"transpileOnly": true,
|
||||
"require": ["tsconfig-paths/register"]
|
||||
}
|
||||
}
|
6974
payload/yarn.lock
Normal file
6974
payload/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"include": ["*.ts"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./astro/tsconfig.json"
|
||||
},
|
||||
{
|
||||
"path": "./payload/tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue
Block a user