generated from autonomic-cooperative/astro-payload-template
Compare commits
1 Commits
deploy-exp
...
8a329feb3e
Author | SHA1 | Date | |
---|---|---|---|
8a329feb3e |
61
.drone.yml
61
.drone.yml
@ -4,7 +4,7 @@ name: publish pipeline
|
||||
steps:
|
||||
- name: publish astro container
|
||||
image: plugins/docker
|
||||
settings: &docker-build-settings
|
||||
settings:
|
||||
username: 3wordchant
|
||||
password:
|
||||
from_secret: git_autonomic_zone_token_3wc
|
||||
@ -14,39 +14,23 @@ steps:
|
||||
registry: git.autonomic.zone
|
||||
context: astro
|
||||
dockerfile: astro/Dockerfile
|
||||
when: &exclude-event-custom
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
exclude:
|
||||
- custom
|
||||
|
||||
- name: publish payload dev container
|
||||
image: plugins/docker
|
||||
settings: &payload-build-settings
|
||||
<<: *docker-build-settings
|
||||
# NOTE: edit this if you want your image called something else
|
||||
repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload-dev
|
||||
context: payload
|
||||
dockerfile: payload/Dockerfile
|
||||
target: dev
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
|
||||
- name: publish payload prod container
|
||||
- name: publish payload container
|
||||
image: plugins/docker
|
||||
settings:
|
||||
<<: *payload-build-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/autonomic-cooperative/astro-payload-test-payload
|
||||
auto_tag: true
|
||||
registry: git.autonomic.zone
|
||||
context: payload
|
||||
dockerfile: payload/Dockerfile
|
||||
target: prod
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
|
||||
# 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:
|
||||
compose: docker-compose.prod.yml
|
||||
stack: paystro_swarm-demo_autonomic_zone
|
||||
host: swarm-demo.autonomic.zone
|
||||
deploy_key:
|
||||
@ -59,13 +43,21 @@ steps:
|
||||
SECRET_MONGO_PASSWORD_VERSION: v1
|
||||
NGINX_CONF_VERSION: v1
|
||||
REPOSITORY: "autonomic-cooperative/astro-payload-test"
|
||||
DRONE_URL: "https://drone.autonomic.zone"
|
||||
PAYLOAD_URL: "https://admin.paystro.swarm-demo.autonomic.zone"
|
||||
depends_on:
|
||||
- publish payload prod container
|
||||
when:
|
||||
<<: *exclude-event-custom
|
||||
# - publish astro container
|
||||
- publish payload container
|
||||
trigger:
|
||||
branch:
|
||||
- main
|
||||
event:
|
||||
exclude:
|
||||
- custom
|
||||
---
|
||||
kind: pipeline
|
||||
name: build astro
|
||||
|
||||
steps:
|
||||
- name: build astro content
|
||||
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro:latest
|
||||
environment:
|
||||
@ -74,9 +66,6 @@ steps:
|
||||
- cd astro
|
||||
- mv /base/node_modules .
|
||||
- yarn build
|
||||
depends_on:
|
||||
- deploy stack
|
||||
|
||||
- name: copy built content to stack
|
||||
image: git.coopcloud.tech/coop-cloud/docker-cp-deploy:latest
|
||||
settings:
|
||||
@ -88,5 +77,7 @@ steps:
|
||||
dest: /usr/share/nginx/html/
|
||||
deploy_key:
|
||||
from_secret: drone_ssh_swarm-demo.autonomic.zone
|
||||
depends_on:
|
||||
- build astro content
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- custom
|
||||
|
@ -1,7 +1,8 @@
|
||||
# This file is just for local dev; see .drone.yml for live deployment
|
||||
# This file is just for local dev; see <somewhere else> for live deployment
|
||||
NAME=astroad
|
||||
|
||||
PAYLOAD_PORT=3001
|
||||
PAYLOAD_URL=http://localhost:3003
|
||||
PAYLOAD_PORT=3003
|
||||
|
||||
##############################################################################
|
||||
# Infinite nerd depth settings, you probably don't need to change these #
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,3 @@
|
||||
/.env
|
||||
data
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
#payload-types.ts
|
103
README.md
103
README.md
@ -1,65 +1,66 @@
|
||||
# Paystro
|
||||
# Astroad
|
||||
|
||||
Paystro is a pre-configured setup for Astro and Payloadcms, designed to make it easy for you to start building your website. With Paystro, 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.
|
||||
|
||||
## Architecture
|
||||
|
||||
Paystro is a fork of [Astroad](https://github.com/mooxl/astroad).
|
||||
|
||||
Unlike Astroad – where the "Astro" image is the built static site served with Nginx – Paystro's Astro image is a builder image.
|
||||
|
||||
In Paystro, the Docker stack just contains Payload and a generic Nginx container.
|
||||
|
||||
## How to use this template
|
||||
|
||||
1. Create a new Gitea repository based on this template repo (choose at least
|
||||
"git content")
|
||||
2. Edit `.env.sample` to define the project name
|
||||
3. Remove this notice (everything down to the `---`)
|
||||
|
||||
To set up deployment:
|
||||
|
||||
1. Make sure the server you want to deploy to is set up for Drone access,
|
||||
including adding an SSH key as an organisational secret for the organisation
|
||||
this project is in (see [Autonomic internal
|
||||
docs](https://docz.autonomic.zone/doc/setting-up-auto-deployment-using-drone-I4j2onjaKT))
|
||||
2. Edit `.drone.yml` to set variables:
|
||||
- `HOST`: hostname or IP address of the server to deploy to (e.g.
|
||||
`vps.example.tld`)
|
||||
- `DOMAIN`: domain name of the live site (e.g. `site.example.tld`)
|
||||
- `STACK_NAME`: the Docker stack name, usually `$DOMAIN` with dots replaced
|
||||
with underscores (e.g. `site_example_tld`)
|
||||
- `DRONE_URL`: root URL for the Drone instance (e.g.
|
||||
`https://drone.example.tld`)
|
||||
3. Make sure that DNS records for `$DOMAIN` and `admin.$DOMAIN`, pointing to
|
||||
`$HOST`, are defined.
|
||||
4. Generate secrets (`openssl rand -hex 32` works well) for `PAYLOAD_SECRET` and
|
||||
`MONGO_PASSWORD`. Insert the personal user token for a Drone bot user (e.g.
|
||||
`autono-bot`) as `TOKEN`). For all of them, use something like `echo "foobar"
|
||||
| docker secret create paystro_swarm-demo_autonomic_zone_token_v1 -`, where
|
||||
`paystro_swarm-demo_autonomic_zone` is the `STACK_NAME`.
|
||||
5. Activate the repo in Drone
|
||||
|
||||
---
|
||||
|
||||
# ${REPO_NAME}
|
||||
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, make sure you have Docker installed (and, if your Docker doesn't include `docker compose`, the separate `docker-compose` tool).
|
||||
Before getting started with Astroad, make sure you have the necessary software installed:
|
||||
|
||||
- Docker
|
||||
- Node.js
|
||||
- Yarn
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `.env.sample` to `.env` and edit as appropriate (e.g. to change the port for Payload).
|
||||
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
|
||||
|
||||
Launch local containers using `yarn dev` or, if you don't have yarn installed, plain `docker-compose up` / `docker compose up`.
|
||||
|
||||
The `docker-compose.yml` file 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.
|
||||
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
|
||||
|
||||
Whenever the repository is updated, Drone builds new Docker images for Payload and Astro, and deploys a new Docker Swarm stack to the `HOST` configured in `.drone.yml`.
|
||||
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`.
|
||||
|
||||
Whenever changes are made to Payload content on the live site, Drone uses the Astro builder image to regenerate the static site, and publish it to the Nginx container by copying it into the Docker volume.
|
||||
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/.gitignore
vendored
3
astro/.gitignore
vendored
@ -16,3 +16,6 @@ pnpm-debug.log*
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# types
|
||||
src/types.ts
|
@ -9,8 +9,8 @@ export default defineConfig({
|
||||
build: {
|
||||
inlineStylesheets: "auto",
|
||||
},
|
||||
server: {
|
||||
port: 3000
|
||||
experimental: {
|
||||
viewTransitions: true,
|
||||
},
|
||||
integrations: [
|
||||
tailwind({
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paystro",
|
||||
"description": "Paystro - Astro",
|
||||
"name": "astroad",
|
||||
"description": "Astroad - Astro",
|
||||
"type": "module",
|
||||
"version": "1.1",
|
||||
"license": "MIT",
|
||||
@ -9,11 +9,11 @@
|
||||
"build": "astro build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/image": "0.18.0",
|
||||
"@astrojs/prefetch": "0.4.1",
|
||||
"@astrojs/sitemap": "3.1.4",
|
||||
"@astrojs/tailwind": "5.1.0",
|
||||
"astro": "4.8.6",
|
||||
"@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",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB |
@ -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>
|
||||
}
|
@ -5,20 +5,23 @@ import { getImageSrc } from "@/utils/payload";
|
||||
const { content } = Astro.props;
|
||||
const contentArray = getContentArray(content);
|
||||
---
|
||||
|
||||
<div>
|
||||
{
|
||||
contentArray.map((value) => {
|
||||
if (typeof value === "string") {
|
||||
return <section set:html={value} />;
|
||||
return <article set:html={value} />;
|
||||
} else {
|
||||
return (
|
||||
<Image
|
||||
src={getImageSrc(value.src) || "/not-found.png"}
|
||||
src={getImageSrc(value.src)}
|
||||
width={value.width}
|
||||
height={value.height}
|
||||
format="webp"
|
||||
alt={value.alt}
|
||||
alt="hallo"
|
||||
/>
|
||||
);
|
||||
}
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
import { getCurrentYear } from "@/utils/date";
|
||||
import ThemeSwitcher from "./ThemeSwitcher.astro";
|
||||
---
|
||||
|
||||
<footer class="flex justify-between items-center py-4">
|
||||
<p class="text-lg font-semibold">
|
||||
© {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
|
||||
</p>
|
||||
<ThemeSwitcher/>
|
||||
</footer>
|
@ -1,36 +0,0 @@
|
||||
---
|
||||
const links = [
|
||||
{
|
||||
label: "About",
|
||||
link: "/about"
|
||||
},
|
||||
{
|
||||
label: "Services",
|
||||
link: "/services"
|
||||
},
|
||||
{
|
||||
label: "Contact",
|
||||
link: "/contact"
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
|
||||
<header class="flex justify-between py-6">
|
||||
<div>
|
||||
<a href="/" class="decoration-transparent">
|
||||
<span class="text-2xl font-extrabold ">
|
||||
Logo
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<nav>
|
||||
<ul class="flex gap-4 font-bold list-none">
|
||||
{links.map((item, index) => (
|
||||
<li class="decoration-transparent">
|
||||
<a href={item.link}>{item.label}</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
@ -1,43 +0,0 @@
|
||||
---
|
||||
import { Image } from "@astrojs/image/components";
|
||||
import type { Post } from "@/types/payload-types";
|
||||
|
||||
interface Props {
|
||||
post: Post;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
---
|
||||
<a
|
||||
class="py-4 border-secondary decoration-transparent"
|
||||
href={`/posts/${post.id}/`}
|
||||
>
|
||||
<article class="flex px-5 py-3 gap-8">
|
||||
<Image
|
||||
src={post.thumbnail.url || "/not-found"}
|
||||
height={150}
|
||||
aspectRatio={1}
|
||||
format="webp"
|
||||
alt={post.thumbnail.alt}
|
||||
/>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<div class="flex justify-between w-full">
|
||||
<h3 class="">{post.title}</h3>
|
||||
{post.publishedDate && (
|
||||
<p class="font-light">
|
||||
{new Date(post.publishedDate).toLocaleDateString("de-DE")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{post.summary && <p class="max-w-prose">
|
||||
{post.summary}
|
||||
</p>}
|
||||
</div>
|
||||
<!-- {post.author.name && (
|
||||
<p>
|
||||
{post.author.name}
|
||||
</p>
|
||||
)} -->
|
||||
</article>
|
||||
</a>
|
@ -1,26 +0,0 @@
|
||||
---
|
||||
import PostEntry from "@/components/PostEntry.astro"
|
||||
import type { Post } from "@/types/payload-types";
|
||||
|
||||
interface Props {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const { posts } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="flex flex-col">
|
||||
<h2 class="text-secondary mb-4">Posts</h2>
|
||||
<div class="border-y-2 flex flex-col divide-y-2 border-secondary divide-secondary">
|
||||
{
|
||||
posts.length > 0 ? (
|
||||
posts.map((post) => (
|
||||
typeof(post) === 'object') &&
|
||||
<PostEntry post={post}/>
|
||||
)
|
||||
) : (
|
||||
<p>No posts available</p>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
---
|
||||
<select id="theme-selector">
|
||||
<option value="autonomic">Autonomic</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
</select>
|
||||
|
||||
<script>
|
||||
import { handleThemeChange, switchTheme, initializeThemeSelector } from "@/utils/theme"
|
||||
initializeThemeSelector()
|
||||
</script>
|
@ -1,107 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
.light {
|
||||
|
||||
--black: 10, 16, 19;
|
||||
--white: 243, 252, 248;
|
||||
--absolute-black: 0, 0, 0;
|
||||
--absolute-white: 255, 255, 255;
|
||||
|
||||
/* Use these in tailwind.config.cjs */
|
||||
--primary: var(--black);
|
||||
--secondary: var(--black);
|
||||
--tertiary: var(--black);
|
||||
|
||||
--text: var(--black);
|
||||
--textInverted: var(--absolute-white);
|
||||
--link: var(--black);
|
||||
--background: var(--white);
|
||||
}
|
||||
|
||||
.dark {
|
||||
|
||||
--black: 10, 16, 19;
|
||||
--white: 243, 252, 248;
|
||||
--absolute-black: 0, 0, 0;
|
||||
--absolute-white: 255, 255, 255;
|
||||
|
||||
/* Use these in tailwind.config.cjs */
|
||||
--primary: var(--white);
|
||||
--secondary: var(--white);
|
||||
--tertiary: var(--white);
|
||||
|
||||
--text: var(--white);
|
||||
--textInverted: var(--absolute-black);
|
||||
--link: var(--white);
|
||||
--background: var(--black);
|
||||
}
|
||||
|
||||
.autonomic {
|
||||
/* Palette */
|
||||
/* RGBA instead of hex so that opacity will work */
|
||||
--absolute-white: 255, 255, 255;
|
||||
--white: 243, 252, 248;
|
||||
--shocking-pink: 253, 0, 159;
|
||||
--shamrock-green: 0, 198, 88;
|
||||
--bubblegum-pink: 255, 133, 203;
|
||||
--bright-orange: 255, 96, 21;
|
||||
|
||||
/* Use these in tailwind.config.cjs */
|
||||
--primary: var(--shocking-pink);
|
||||
--secondary: var(--shamrock-green);
|
||||
--tertiary: var(--bright-orange);
|
||||
|
||||
--text: var(--shocking-pink);
|
||||
--textInverted: var(--absolute-white);
|
||||
--link: var(--shamrock-green);
|
||||
--background: var(--white);
|
||||
}
|
||||
|
||||
@layer components {
|
||||
|
||||
:root {
|
||||
@apply ring-secondary;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-5xl font-bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-4xl font-semibold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-2xl font-semibold;
|
||||
}
|
||||
|
||||
h4, h5, h6 {
|
||||
@apply text-2xl font-medium;
|
||||
}
|
||||
|
||||
p, span, li, a {
|
||||
@apply text-xl;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-link underline;
|
||||
}
|
||||
|
||||
ol {
|
||||
@apply list-decimal;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply list-disc;
|
||||
}
|
||||
|
||||
select {
|
||||
@apply bg-background border-primary border px-2 h-8 text-lg;
|
||||
}
|
||||
|
||||
input {
|
||||
@apply bg-background border-primary border px-2 h-8 text-lg;
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
import BaseLayout from "./BaseLayout.astro";
|
||||
import Header from "@/components/Header.astro";
|
||||
import Footer from "@/components/Footer.astro";
|
||||
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout title={title}>
|
||||
<Header/>
|
||||
<main class="flex-grow">
|
||||
<slot/>
|
||||
</main>
|
||||
<Footer/>
|
||||
</BaseLayout>
|
@ -4,7 +4,6 @@ export interface Props {
|
||||
title: string;
|
||||
}
|
||||
const { title } = Astro.props;
|
||||
import "@/global.css"
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
@ -31,7 +30,7 @@ import "@/global.css"
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="autonomic min-h-screen flex flex-col mx-auto max-w-7xl px-6 text-primary bg-background">
|
||||
<body class="mx-auto max-w-7xl bg-gray px-6 py-8 font-plex text-gray-200">
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
@ -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,17 +1,16 @@
|
||||
---
|
||||
import Posts from "@/components/Posts.astro";
|
||||
import ContentLayout from "@/layouts/ContentLayout.astro";
|
||||
import Layout from "@/layouts/Layout.astro";
|
||||
import { getPosts } from "@/utils/payload";
|
||||
|
||||
const posts = await getPosts();
|
||||
---
|
||||
|
||||
<ContentLayout title="Paystro">
|
||||
<main class="flex flex-col gap-4" >
|
||||
<h1 class="">Paystro</h1>
|
||||
<p class="mt-3">
|
||||
Paystro is a pre-configured setup for Astro and Payloadcms that makes it
|
||||
easy to get started with building your website. With Paystro, you'll have
|
||||
<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.
|
||||
@ -21,8 +20,26 @@ const posts = await getPosts();
|
||||
reverse proxy. This setup provides a secure and scalable production
|
||||
environment for your website.
|
||||
</p>
|
||||
<section class="mt-4">
|
||||
<Posts posts={posts}/>
|
||||
</section>
|
||||
<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>
|
||||
</ContentLayout>
|
||||
</Layout>
|
||||
|
@ -1,9 +1,8 @@
|
||||
---
|
||||
import ContentLayout from "@/layouts/ContentLayout.astro";
|
||||
import Layout from "@/layouts/Layout.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 Author from "@/components/Author.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getPosts();
|
||||
@ -19,16 +18,13 @@ const post = id && (await getPost(id));
|
||||
|
||||
{
|
||||
post ? (
|
||||
<ContentLayout title={`${post.title!}`}>
|
||||
<article class="space-y-3 my-3 max-w-prose">
|
||||
<h1 class="">{post.title}</h1>
|
||||
<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} />}
|
||||
</article>
|
||||
{typeof post.author === 'object' &&
|
||||
<aside class="mt-8">
|
||||
<Author author={post.author} />
|
||||
</aside>}
|
||||
</ContentLayout>
|
||||
</div>
|
||||
</Layout>
|
||||
) : (
|
||||
<div>404</div>
|
||||
)
|
||||
|
@ -1,4 +0,0 @@
|
||||
export const getCurrentYear = (): number => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return currentYear;
|
||||
}
|
@ -12,13 +12,12 @@ export const getContentArray = (content: any) => {
|
||||
src: node.value.filename,
|
||||
width: `${node.value.width}`,
|
||||
height: `${node.value.height}`,
|
||||
alt: `${node.value.alt}`,
|
||||
}),
|
||||
},
|
||||
}).replaceAll("<p></p>", "<p> </p>");
|
||||
const htmlImageArray: (
|
||||
| string
|
||||
| { src: string; width: number; height: number, alt: string }
|
||||
| { src: string; width: number; height: number }
|
||||
)[] = [];
|
||||
let lastIndex = 0;
|
||||
while (true) {
|
||||
@ -34,7 +33,6 @@ export const getContentArray = (content: any) => {
|
||||
src: imgTag.match(/src="(.*?)"/)![1],
|
||||
width: +imgTag.match(/width="(.*?)"/)![1],
|
||||
height: +imgTag.match(/height="(.*?)"/)![1],
|
||||
alt: imgTag.match(/alt="(.*?)"/)![1],
|
||||
};
|
||||
htmlImageArray.push(remainingHtml, imgObject);
|
||||
lastIndex = imgEndIndex;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { Post } from "@/types/payload-types";
|
||||
import type { Post } from "@/types";
|
||||
|
||||
const url = import.meta.env.DEV
|
||||
? "http://payload:3001"
|
||||
|
@ -1,21 +0,0 @@
|
||||
export function switchTheme(themeClass: string): void {
|
||||
const bodyElement = document.body;
|
||||
const classesToRemove = ['light', 'dark', 'autonomic'];
|
||||
|
||||
bodyElement.classList.remove(...classesToRemove);
|
||||
bodyElement.classList.add(themeClass);
|
||||
}
|
||||
|
||||
export function handleThemeChange(event: Event): void {
|
||||
const selectElement = event.target as HTMLSelectElement;
|
||||
const selectedTheme = selectElement.value;
|
||||
|
||||
switchTheme(selectedTheme);
|
||||
}
|
||||
|
||||
export function initializeThemeSelector(): void {
|
||||
const themeSelector = document.getElementById('theme-selector');
|
||||
if (themeSelector) {
|
||||
themeSelector.addEventListener('change', handleThemeChange);
|
||||
}
|
||||
}
|
@ -7,13 +7,11 @@ module.exports = {
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
primary: "rgba(var(--primary))",
|
||||
secondary: "rgba(var(--secondary))",
|
||||
tertiary: "rgba(var(--tertiary))",
|
||||
text: "rgba(var(--text))",
|
||||
textInverted: "rgba(var(--textInverted))",
|
||||
link: "rgba(var(--link))",
|
||||
background: "rgba(var(--background))",
|
||||
gray: {
|
||||
DEFAULT: "#111111",
|
||||
light: "#888888",
|
||||
dark: "#222222",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -4,8 +4,7 @@
|
||||
"types": ["@astrojs/image/client"],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@/types/*": ["../*"]
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3481
astro/yarn.lock
3481
astro/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -35,7 +35,6 @@ services:
|
||||
- "MONGODB_PASSWORD_FILE=/run/secrets/mongo_password"
|
||||
- "TOKEN_FILE=/run/secrets/token"
|
||||
- "REPOSITORY"
|
||||
- "DRONE_URL"
|
||||
secrets:
|
||||
- mongo_password
|
||||
- payload_secret
|
@ -19,12 +19,11 @@ services:
|
||||
- payload
|
||||
|
||||
payload:
|
||||
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload-dev:latest
|
||||
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload:latest
|
||||
container_name: ${NAME}-payload
|
||||
restart: unless-stopped
|
||||
build:
|
||||
context: payload
|
||||
target: dev
|
||||
environment:
|
||||
DEV: 1
|
||||
NAME: ${NAME}
|
||||
@ -32,11 +31,16 @@ services:
|
||||
PAYLOAD_URL: "http://localhost:${PAYLOAD_PORT}"
|
||||
PAYLOAD_PORT: ${PAYLOAD_PORT}
|
||||
PAYLOAD_SECRET: ${PAYLOAD_SECRET}
|
||||
MONGODB_URI: "mongodb://$MONGODB_USER:$MONGODB_PASSWORD@mongo:27017"
|
||||
MONGODB_USER:
|
||||
MONGODB_HOST: mongo
|
||||
MONGODB_PASSWORD:
|
||||
MONGODB_PORT: 27017
|
||||
TOKEN: ${TOKEN}
|
||||
volumes:
|
||||
- ./payload/src:/base/src
|
||||
# - ./astro/src/types.ts:/types.ts
|
||||
- payload_media:/base/src/media
|
||||
- ./astro/src/types.ts:/types.ts
|
||||
volumes:
|
||||
- payload_media:/prod/dist/media
|
||||
ports:
|
||||
- ${PAYLOAD_PORT}:${PAYLOAD_PORT}
|
||||
networks:
|
||||
|
14
package.json
14
package.json
@ -1,13 +1,11 @@
|
||||
{
|
||||
"name": "paystro",
|
||||
"name": "astroad",
|
||||
"description": "A pre-configured setup for easy website development with Astro and Payload CMS using Docker.",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"version": "1.2",
|
||||
"license": "MIT",
|
||||
"version": "1.1",
|
||||
"scripts": {
|
||||
"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"
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -9,6 +9,7 @@ ENV NODE_ENV=development
|
||||
EXPOSE 3001
|
||||
CMD ["yarn","dev"]
|
||||
|
||||
|
||||
FROM base AS build
|
||||
ENV NODE_ENV=production
|
||||
WORKDIR /build
|
||||
|
@ -1,27 +1,22 @@
|
||||
{
|
||||
"name": "paystro",
|
||||
"description": "Paystro - Payload",
|
||||
"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: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 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",
|
||||
"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'"
|
||||
"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": {
|
||||
"@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": "^2.18.3",
|
||||
"payload": "^1.15.6",
|
||||
"tsconfig-paths": "^4.2.0"
|
||||
},
|
||||
"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";
|
||||
|
||||
export const Media: CollectionConfig = {
|
||||
@ -6,20 +5,19 @@ export const Media: CollectionConfig = {
|
||||
admin: {},
|
||||
access: {
|
||||
read: (): boolean => true,
|
||||
create: isUser,
|
||||
update: isUser,
|
||||
delete: isUser,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
},
|
||||
upload: {
|
||||
staticURL: "/media",
|
||||
staticDir: "media",
|
||||
mimeTypes: ["image/*"],
|
||||
},
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: "alt",
|
||||
type: "text",
|
||||
required: true
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -1,23 +1,14 @@
|
||||
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,
|
||||
admin: {
|
||||
defaultColumns: ["title", "author", "status"],
|
||||
useAsTitle: "title",
|
||||
},
|
||||
access: {
|
||||
//TODO: Author can CRUD own post
|
||||
create: isUser,
|
||||
read: () => true,
|
||||
update: isEditor,
|
||||
delete: isEditor,
|
||||
create: () => true,
|
||||
update: () => true,
|
||||
},
|
||||
hooks: {
|
||||
afterChange: [
|
||||
@ -28,7 +19,7 @@ const Posts: CollectionConfig = {
|
||||
process.env.NODE_ENV !== "development" &&
|
||||
console.log(
|
||||
await fetch(
|
||||
`${process.env.DRONE_URL}/api/repos/${process.env.REPOSITORY}/builds`,
|
||||
`https://drone.autonomic.zone/api/repos/${process.env.REPOSITORY}/builds`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
@ -47,40 +38,28 @@ const Posts: CollectionConfig = {
|
||||
{
|
||||
name: "title",
|
||||
type: "text",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: "summary",
|
||||
name: "hallo",
|
||||
type: "text",
|
||||
required: false
|
||||
},
|
||||
{
|
||||
name: "publishedDate",
|
||||
type: "date",
|
||||
required: false,
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name: "thumbnail",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: slateEditor({
|
||||
name: "content",
|
||||
type: "richText",
|
||||
admin: {
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload", "blockquote", "indent"],
|
||||
leaves: ["bold", "italic", "underline", "strikethrough"],
|
||||
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"],
|
||||
leaves: ["bold", "italic", "underline"],
|
||||
upload: {
|
||||
collections: {
|
||||
media: {
|
||||
fields: [
|
||||
{
|
||||
name: "image",
|
||||
name: "imagel",
|
||||
type: "upload",
|
||||
relationTo: "media",
|
||||
required: true,
|
||||
@ -90,21 +69,10 @@ const Posts: CollectionConfig = {
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
{
|
||||
name: "author",
|
||||
//TODO: Add active user as default
|
||||
type: 'relationship',
|
||||
relationTo: 'authors',
|
||||
admin: {
|
||||
position: "sidebar",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "status",
|
||||
type: "select",
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
value: "draft",
|
||||
@ -114,10 +82,6 @@ const Posts: CollectionConfig = {
|
||||
value: "published",
|
||||
label: "Published",
|
||||
},
|
||||
{
|
||||
value: "archived",
|
||||
label: "Archived",
|
||||
},
|
||||
],
|
||||
defaultValue: "draft",
|
||||
admin: {
|
||||
|
@ -1,41 +1,20 @@
|
||||
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: {
|
||||
create: isAdmin,
|
||||
read: isAdminOrSelf,
|
||||
update: isAdminOrSelf,
|
||||
delete: isAdmin,
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
// Email added by default
|
||||
{
|
||||
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,
|
||||
},
|
||||
},
|
||||
name: 'name',
|
||||
type: 'text',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -2,19 +2,12 @@ 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: {
|
||||
@ -26,13 +19,8 @@ export default buildConfig({
|
||||
},
|
||||
}),
|
||||
},
|
||||
collections: [Posts, Users, Authors, Media],
|
||||
collections: [Posts, Users, Media],
|
||||
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({
|
||||
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,8 +7,7 @@
|
||||
"skipLibCheck": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"],
|
||||
"@/types/*": ["../*"]
|
||||
"@/*": ["./src/*", "./dist/*", "./dist/src/*"]
|
||||
},
|
||||
"jsx": "react"
|
||||
},
|
||||
|
4442
payload/yarn.lock
4442
payload/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user