Author SHA1 Message Date
tobias e9fb68ec9f Fix typo in build script
continuous-integration/drone/push Build is passing
2024-06-04 10:02:40 +02:00
tobias f37a1719b7 Update payload to 2.18.3
continuous-integration/drone/push Build is failing
2024-05-27 11:24:42 +02:00
tobias d62e7d788e Restore dev, build & gen:types scripts
continuous-integration/drone/push Build is passing
2024-05-25 08:18:33 +02:00
tobias 8fdfb2fbe4 Check if dev script is stopping build (??)
continuous-integration/drone/push Build is passing
2024-05-24 09:10:43 +02:00
tobias 0bb0644b55 Move payload types to within payload
continuous-integration/drone/push Build is passing
2024-05-22 09:21:31 +02:00
tobias b85c62f3fc Try to make build work
continuous-integration/drone/push Build is failing
2024-05-22 09:13:09 +02:00
3wordchant 910f943a2d Tweak Drone pipeline to trigger build
continuous-integration/drone/push Build was killed
2024-05-21 18:15:14 -03:00
tobias 10cb01964b Change order of payload build steps
continuous-integration/drone/push Build is failing
2024-05-21 12:52:07 +02:00
tobias 91073bd498 Generate types before building payload
continuous-integration/drone/push Build is failing
2024-05-21 12:22:55 +02:00
tobias cc0a5cb1c5 Add types dir to payload tsconfig
continuous-integration/drone/push Build is failing
2024-05-21 12:14:53 +02:00
tobias 7dbc81c5b2 Add more blocks to posts
continuous-integration/drone/push Build is failing
2024-05-21 12:07:14 +02:00
tobias 4b30d58db6 Type posts 2024-05-21 12:04:23 +02:00
tobias 62e50496a2 Merge branch 'main' into t-work 2024-05-21 11:39:36 +02:00
tobias 2a494425ad Create listen script for payload types
continuous-integration/drone/push Build is passing
2024-05-21 11:39:10 +02:00
tobias b91dd893a4 Collection access tweaks 2024-05-21 11:17:51 +02:00
tobias 691729c53c Create author component 2024-05-21 11:17:40 +02:00
3wordchant c90d2c0d9b Fix YAML syntax
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is passing
2024-05-20 23:33:07 -03:00
3wordchant 4544c7942c Further drone boop
continuous-integration/drone/push Build encountered an error
2024-05-20 23:31:19 -03:00
3wordchant 7997268fad Boop drone
continuous-integration/drone Build was killed
continuous-integration/drone/push Build is passing
2024-05-20 23:30:08 -03:00
3wordchant d8cfd644f8 Another dependency experiment
continuous-integration/drone/push Build encountered an error
2024-05-20 23:29:07 -03:00
tobias 09ad9bdc6d Move payload types again
continuous-integration/drone/push Build is passing
2024-05-20 15:58:11 +02:00
tobias f94bc5d822 Move payload types so dev container doesn't crash
continuous-integration/drone/push Build is passing
2024-05-20 14:20:26 +02:00
tobias 370eac2b25 Save generated payload types in astro types folder
continuous-integration/drone/push Build is passing
2024-05-20 14:06:01 +02:00
tobias ba6ca90c3a Fix css typo
continuous-integration/drone/push Build is passing
2024-05-20 13:40:01 +02:00
tobias 985774bf24 Add payload seed 2024-05-20 13:31:57 +02:00
tobias 747ada89e9 Update ssg and editor access functions
continuous-integration/drone/push Build is passing
2024-05-20 11:41:41 +02:00
tobias ff564a62ec Add author collection 2024-05-20 11:40:57 +02:00
tobias 9629c93ceb Merge branch 'main' into t-work 2024-05-20 09:43:08 +02:00
tobias d7f22fdd5f Add posts full CRUD for editor role 2024-05-20 09:42:31 +02:00
tobias 1ccf660f5b Configure user access 2024-05-20 09:10:08 +02:00
tobias 1dbb075cd8 Style h4, h5, h6 2024-05-20 08:22:47 +02:00
tobias 386b5c17f9 Create 404 page 2024-05-20 08:17:41 +02:00
3wordchant 3aa5ec6a69 Dependency experiment
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-05-20 01:17:29 -03:00
3wordchant db712ecffc Add missing drone dep
continuous-integration/drone/push Build is passing
2024-05-20 01:14:39 -03:00
3wordchant e1a782ad39 Fix YAML syntax
continuous-integration/drone/push Build is failing
2024-05-20 01:11:19 -03:00
3wordchant b8b10670f9 Astro build on code changes
continuous-integration/drone/push Build encountered an error
2024-05-20 01:08:42 -03:00
3wordchant b83ce8c97a Fix YAML syntax error
continuous-integration/drone/push Build is passing
2024-05-19 19:19:41 -03:00
3wordchant 0a7b0e98b2 README and .drone.yml updates
continuous-integration/drone/push Build encountered an error
Re autonomic-cooperative/astro-payload-template#3
2024-05-19 19:17:59 -03:00
tobias f017fa28dc Restrict user collection to admins 2024-05-19 23:17:39 +02:00
tobias 40d257a0eb Add version control to posts
continuous-integration/drone/push Build is passing
2024-05-19 22:15:43 +02:00
tobias 200bfcce2a Add input base styles
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-05-19 21:07:09 +02:00
tobias e3f725bf5c Tiny footer CSS enhancements
continuous-integration/drone/push Build is passing
2024-05-19 20:58:07 +02:00
tobias 526bd24742 Reasonabler CSS defaults 2024-05-19 20:46:41 +02:00
tobias 2cbd830255 Add light & dark theme 2024-05-19 20:45:22 +02:00
3wordchant ad7480d13a Make sure DRONE_URL is set for Payload?
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2024-05-19 14:24:48 -03:00
tobias 0a2dde8009 Add fallback for img src's
continuous-integration/drone/push Build is passing
2024-05-19 18:59:51 +02:00
tobias 523b7c9fd7 Merge branch 'main' into t-work 2024-05-19 18:52:44 +02:00
3wordchant e99eb97462 Ensure we build -dev container locally
continuous-integration/drone/push Build is passing
Re autonomic-cooperative/astro-payload-template#22
2024-05-19 13:41:14 -03:00
tobias 6d213b7644 Merge branch 'main' into t-work 2024-05-19 18:30:12 +02:00
tobias e20afa3313 Add vanilla theme switcher 2024-05-19 18:06:38 +02:00
3wordchant d30cd24f54 Fix media volume path
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
Ref autonomic-cooperative/astro-payload-template#22
2024-05-19 13:00:59 -03:00
3wordchant a87bff676b Fix Payload local instance
continuous-integration/drone/push Build is passing
Fixes autonomic-cooperative/astro-payload-template#23
2024-05-19 12:55:40 -03:00
3wordchant 5d84e7b38d Fix Drone config
continuous-integration/drone/push Build is passing
2024-05-19 12:37:14 -03:00
tobias aa7a5acf94 Constrain post summary width for EZ reading 2024-05-19 17:36:20 +02:00
3wordchant b1e2c8d30b Publish payload dev container also
continuous-integration/drone/push Build encountered an error
2024-05-19 12:36:14 -03:00
tobias 7d2f0822fa Create nice layout for post list
continuous-integration/drone/push Build is passing
2024-05-19 17:08:00 +02:00
tobias 456db37eb6 Configure reasonable-ish CSS defaults 2024-05-19 15:33:37 +02:00
tobias 36b5ab7cd2 Create autonomic color theme
continuous-integration/drone/push Build is passing
2024-05-19 12:22:42 +02:00
tobias 03c14f0ca7 fix: Alt attr. string 2024-05-19 09:01:15 +02:00
tobias 421394a794 Add dev:build script
continuous-integration/drone/push Build is passing
2024-05-19 08:50:33 +02:00
tobias a8362fa88f Add alt attribute to imgs 2024-05-19 08:50:00 +02:00
tobias 467b4bb0ad Fix typo & remove back button 2024-05-19 08:49:23 +02:00
tobias 7234cef7e1 Logo is home link 2024-05-19 08:48:18 +02:00
tobias 7ba1f78721 Add astro port to astro.config 2024-05-19 08:24:00 +02:00
tobias da435c8fc1 Remove old style
continuous-integration/drone/push Build is passing
2024-05-19 07:13:09 +02:00
tobias 68972aba1a Merge branch 't-work'
continuous-integration/drone/push Build is passing
2024-05-19 07:01:50 +02:00
3wordchant 670300a8dc Fix DRONE_URL, tidy up README and .drone.yml
continuous-integration/drone/push Build is passing
2024-05-18 21:17:26 -03:00
3wordchant 7f2710e9a2 Update README
continuous-integration/drone/push Build is passing
Closes autonomic-cooperative/astro-payload-template#6
2024-05-18 18:29:52 -03:00
3wordchant 7d071a2672 Split DRONE_URL out into setting 2024-05-18 18:29:42 -03:00
45 changed files with 3407 additions and 1890 deletions
+35 -26
View File
@@ -4,30 +4,45 @@ name: publish pipeline
steps: steps:
- name: publish astro container - name: publish astro container
image: plugins/docker image: plugins/docker
settings: settings: &docker-build-settings
username: 3wordchant username: 3wordchant
password: password:
from_secret: git_autonomic_zone_token_3wc from_secret: git_autonomic_zone_token_3wc
# NOTE: edit this if you want your image called something else # NOTE: edit this if you want your image called something else
repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro
auto_tag: true auto_tag: true
registry: git.autonomic.zone registry: git.autonomic.zone
context: astro context: astro
dockerfile: astro/Dockerfile dockerfile: astro/Dockerfile
- name: publish payload container when: &exclude-event-custom
branch:
- main
event:
exclude:
- custom
- name: publish payload dev container
image: plugins/docker image: plugins/docker
settings: settings: &payload-build-settings
username: 3wordchant <<: *docker-build-settings
password:
from_secret: git_autonomic_zone_token_3wc
# NOTE: edit this if you want your image called something else # NOTE: edit this if you want your image called something else
repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload-dev
auto_tag: true
registry: git.autonomic.zone
context: payload context: payload
dockerfile: payload/Dockerfile dockerfile: payload/Dockerfile
target: dev
when:
<<: *exclude-event-custom
- name: publish payload prod container
image: plugins/docker
settings:
<<: *payload-build-settings
username: 3wordchant
repo: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload
target: prod target: prod
# See https://docz.autonomic.zone/doc/setting-up-auto-deployment-using-drone-I4j2onjaKT when:
<<: *exclude-event-custom
- name: deploy stack - name: deploy stack
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest
settings: settings:
@@ -44,20 +59,13 @@ steps:
SECRET_MONGO_PASSWORD_VERSION: v1 SECRET_MONGO_PASSWORD_VERSION: v1
NGINX_CONF_VERSION: v1 NGINX_CONF_VERSION: v1
REPOSITORY: "autonomic-cooperative/astro-payload-test" REPOSITORY: "autonomic-cooperative/astro-payload-test"
DRONE_URL: "https://drone.autonomic.zone"
PAYLOAD_URL: "https://admin.paystro.swarm-demo.autonomic.zone" PAYLOAD_URL: "https://admin.paystro.swarm-demo.autonomic.zone"
depends_on: depends_on:
- publish payload container - publish payload prod container
trigger: when:
branch: <<: *exclude-event-custom
- main
event:
exclude:
- custom
---
kind: pipeline
name: build astro
steps:
- name: build astro content - name: build astro content
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro:latest image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-astro:latest
environment: environment:
@@ -66,6 +74,9 @@ steps:
- cd astro - cd astro
- mv /base/node_modules . - mv /base/node_modules .
- yarn build - yarn build
depends_on:
- deploy stack
- name: copy built content to stack - name: copy built content to stack
image: git.coopcloud.tech/coop-cloud/docker-cp-deploy:latest image: git.coopcloud.tech/coop-cloud/docker-cp-deploy:latest
settings: settings:
@@ -77,7 +88,5 @@ steps:
dest: /usr/share/nginx/html/ dest: /usr/share/nginx/html/
deploy_key: deploy_key:
from_secret: drone_ssh_swarm-demo.autonomic.zone from_secret: drone_ssh_swarm-demo.autonomic.zone
depends_on:
trigger: - build astro content
event:
- custom
+2
View File
@@ -2,3 +2,5 @@
data data
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
#payload-types.ts
+50 -51
View File
@@ -1,66 +1,65 @@
# Paystro # Paystro
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. 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}
## Prerequisites ## Prerequisites
Before getting started with Paystro, make sure you have the necessary software installed: Before getting started, make sure you have Docker installed (and, if your Docker doesn't include `docker compose`, the separate `docker-compose` tool).
- Docker
- Node.js
- Yarn
## Configuration ## Configuration
While there's no configuration necessary for local development, deployment via Github Workflows requires specific secrets and variables to be set. Copy `.env.sample` to `.env` and edit as appropriate (e.g. to change the port for Payload).
### 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 Paystro, 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 Paystro. After the new repository is created, you can clone it to your local machine.
1. Alternatively, you can directly clone the Paystro repository: git clone https://github.com/mooxl/paystro.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 Paystro 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 ## 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. 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.
## Deployment ## 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`. 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`.
Because Astro is completely static, a content change in the CMS must trigger a new build of Astro. Therefore, theres a `payload.yml` workflow that gets triggered by a webhook after every content change from Payload. 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.
Ensure you have Traefik set up as a reverse proxy before deployment. The prod script will launch your site in a production-ready environment.
+1 -4
View File
@@ -15,7 +15,4 @@ pnpm-debug.log*
# macOS-specific files # macOS-specific files
.DS_Store .DS_Store
# types
src/types.ts
+3
View File
@@ -9,6 +9,9 @@ export default defineConfig({
build: { build: {
inlineStylesheets: "auto", inlineStylesheets: "auto",
}, },
server: {
port: 3000
},
integrations: [ integrations: [
tailwind({ tailwind({
config: { config: {
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

+36
View File
@@ -0,0 +1,36 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 500 500" style="width: 178px;" xml:space="preserve" data-imageid="404-page-not-found-1-77" imageName="404 Page Not Found 1" class="illustrations_image">
<style type="text/css">
.st0_404-page-not-found-1-77 { fill: var(--primary); }
.st1_404-page-not-found-1-77 { fill: var(--secondary);}
.st2_404-page-not-found-1-77{fill:var(--tertiary);}
.st3_404-page-not-found-1-77{fill:var(--secondary);}
</style>
<g id="Numbers_404-page-not-found-1-77">
<path class="st0_404-page-not-found-1-77 targetColor" d="M107.9,303.8H39.1v-27.2l65.2-101.8h31.1v104.3h23.6v24.7h-23.6v32.9h-27.4V303.8z M107.9,213.7h-0.5L67,279.1&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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 &#9;"/>
<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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9; 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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#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&#xA;&#9;&#9;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&#xA;&#9;&#9;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&#xA;&#9;&#9;c1.3,2,12.7,19.3,9.7,25.2c-0.7,1.3-2.1,2.1-3.5,2C262.8,263.8,262.2,263.9,261.6,263.9z"/>
<ellipse class="st3_404-page-not-found-1-77" cx="304.2" cy="235.7" rx="6.2" ry="10.6"/>
<ellipse class="st2_404-page-not-found-1-77" cx="228.5" cy="235.7" rx="4.7" ry="8.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.5 KiB

+27
View 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>
}
+4 -7
View File
@@ -5,23 +5,20 @@ import { getImageSrc } from "@/utils/payload";
const { content } = Astro.props; const { content } = Astro.props;
const contentArray = getContentArray(content); const contentArray = getContentArray(content);
--- ---
<div>
{ {
contentArray.map((value) => { contentArray.map((value) => {
if (typeof value === "string") { if (typeof value === "string") {
return <article set:html={value} />; return <section set:html={value} />;
} else { } else {
return ( return (
<Image <Image
src={getImageSrc(value.src)} src={getImageSrc(value.src) || "/not-found.png"}
width={value.width} width={value.width}
height={value.height} height={value.height}
format="webp" format="webp"
alt="hallo" alt={value.alt}
/> />
); );
} }
}) })
} }
</div>
+4 -2
View File
@@ -1,9 +1,11 @@
--- ---
import { getCurrentYear } from "@/utils/date"; import { getCurrentYear } from "@/utils/date";
import ThemeSwitcher from "./ThemeSwitcher.astro";
--- ---
<footer> <footer class="flex justify-between items-center py-4">
<p class="text-lg font-semibold"> <p class="text-lg font-semibold">
&copy; {getCurrentYear()} Example Website. All rights reserved. &copy; {getCurrentYear()} Autonomic. Template distributed under AGPL 3.0.
</p> </p>
<ThemeSwitcher/>
</footer> </footer>
+7 -9
View File
@@ -1,9 +1,5 @@
--- ---
const links = [ const links = [
{
label: "Home",
link: "/home"
},
{ {
label: "About", label: "About",
link: "/about" link: "/about"
@@ -22,14 +18,16 @@ const links = [
<header class="flex justify-between py-6"> <header class="flex justify-between py-6">
<div> <div>
<span class="font-extrabold"> <a href="/" class="decoration-transparent">
Logo <span class="text-2xl font-extrabold ">
</span> Logo
</span>
</a>
</div> </div>
<nav> <nav>
<ul class="flex gap-4 font-bold"> <ul class="flex gap-4 font-bold list-none">
{links.map((item, index) => ( {links.map((item, index) => (
<li> <li class="decoration-transparent">
<a href={item.link}>{item.label}</a> <a href={item.link}>{item.label}</a>
</li> </li>
))} ))}
+43
View File
@@ -0,0 +1,43 @@
---
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>
+26
View File
@@ -0,0 +1,26 @@
---
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>
+12
View File
@@ -0,0 +1,12 @@
---
---
<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>
+107
View File
@@ -0,0 +1,107 @@
@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;
}
}
+2 -1
View File
@@ -4,6 +4,7 @@ export interface Props {
title: string; title: string;
} }
const { title } = Astro.props; const { title } = Astro.props;
import "@/global.css"
--- ---
<!DOCTYPE html> <!DOCTYPE html>
@@ -30,7 +31,7 @@ const { title } = Astro.props;
} }
</style> </style>
</head> </head>
<body class="min-h-screen flex flex-col mx-auto max-w-7xl bg-gray px-6 py-8 font-plex text-gray-200"> <body class="autonomic min-h-screen flex flex-col mx-auto max-w-7xl px-6 text-primary bg-background">
<slot /> <slot />
</body> </body>
</html> </html>
+4 -6
View File
@@ -8,10 +8,8 @@ const { title } = Astro.props;
<BaseLayout title={title}> <BaseLayout title={title}>
<Header/> <Header/>
<div class="min-h-screen flex flex-col"> <main class="flex-grow">
<main class="flex-grow"> <slot/>
<slot/> </main>
</main> <Footer/>
<Footer/>
</div>
</BaseLayout> </BaseLayout>
+18
View File
@@ -0,0 +1,18 @@
---
import ContentLayout from "@/layouts/ContentLayout.astro"
import { Image } from "@astrojs/image/components";
import notFound from "@/assets/404.svg"
---
<ContentLayout>
<div class="flex flex-col justify-center items-center gap-8">
<Image
src={notFound}
width={360}
aspectRatio={1}
format="svg"
alt={"404"}
/>
<h1>404: Page not found</h1>
</div>
</ContentLayout>
+9 -26
View File
@@ -1,4 +1,5 @@
--- ---
import Posts from "@/components/Posts.astro";
import ContentLayout from "@/layouts/ContentLayout.astro"; import ContentLayout from "@/layouts/ContentLayout.astro";
import { getPosts } from "@/utils/payload"; import { getPosts } from "@/utils/payload";
@@ -6,9 +7,9 @@ const posts = await getPosts();
--- ---
<ContentLayout title="Paystro"> <ContentLayout title="Paystro">
<main class="" > <main class="flex flex-col gap-4" >
<h1 class="font-bold text-5xl">Paystro</h1> <h1 class="">Paystro</h1>
<p class="mt-3 text-lg"> <p class="mt-3">
Paystro is a pre-configured setup for Astro and Payloadcms that makes it 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 easy to get started with building your website. With Paystro, you'll have
a complete development environment that you can run locally using Docker. a complete development environment that you can run locally using Docker.
@@ -20,26 +21,8 @@ const posts = await getPosts();
reverse proxy. This setup provides a secure and scalable production reverse proxy. This setup provides a secure and scalable production
environment for your website. environment for your website.
</p> </p>
<h2 class="mt-6 font-bold text-2xl">Posts</h2> <section class="mt-4">
<div class="flex gap-4 mt-3 flex-wrap"> <Posts posts={posts}/>
{ </section>
posts.length > 0 ? ( </main>
posts.map((post) => ( </ContentLayout>
<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">{post.title}</h3>
{post.publishedDate && (
<p>
{new Date(post.publishedDate).toLocaleDateString("de-DE")}
</p>
)}
</article>
</a>
))
) : (
<p>Add Posts in Payloadcms</p>
)
}
</div>
</main>
</CLayout>
+11 -7
View File
@@ -1,8 +1,9 @@
--- ---
import ContentLayout from "@/layouts/ContentLayout.astro"; import ContentLayout from "@/layouts/ContentLayout.astro";
import Content from "@/components/Content.astro"; import Content from "@/components/Content.astro";
import type { Post } from "@/types"; import type { Post } from "@/types/payload-types";
import { getPost, getPosts } from "@/utils/payload"; import { getPost, getPosts } from "@/utils/payload";
import Author from "@/components/Author.astro";
export async function getStaticPaths() { export async function getStaticPaths() {
const posts = await getPosts(); const posts = await getPosts();
@@ -18,13 +19,16 @@ const post = id && (await getPost(id));
{ {
post ? ( post ? (
<ContentLayout title={`Paystro | ${post.title!}`}> <ContentLayout title={`${post.title!}`}>
<div class="space-y-3 my-3"> <article class="space-y-3 my-3 max-w-prose">
<a href="/">BACK</a> <h1 class="">{post.title}</h1>
<h1 class="font-bold text-5xl">{post.title}</h1>
{post.content && <Content content={post.content} />} {post.content && <Content content={post.content} />}
</div> </article>
</CLayout> {typeof post.author === 'object' &&
<aside class="mt-8">
<Author author={post.author} />
</aside>}
</ContentLayout>
) : ( ) : (
<div>404</div> <div>404</div>
) )
+3 -1
View File
@@ -12,12 +12,13 @@ export const getContentArray = (content: any) => {
src: node.value.filename, src: node.value.filename,
width: `${node.value.width}`, width: `${node.value.width}`,
height: `${node.value.height}`, height: `${node.value.height}`,
alt: `${node.value.alt}`,
}), }),
}, },
}).replaceAll("<p></p>", "<p>&nbsp;</p>"); }).replaceAll("<p></p>", "<p>&nbsp;</p>");
const htmlImageArray: ( const htmlImageArray: (
| string | string
| { src: string; width: number; height: number } | { src: string; width: number; height: number, alt: string }
)[] = []; )[] = [];
let lastIndex = 0; let lastIndex = 0;
while (true) { while (true) {
@@ -33,6 +34,7 @@ export const getContentArray = (content: any) => {
src: imgTag.match(/src="(.*?)"/)![1], src: imgTag.match(/src="(.*?)"/)![1],
width: +imgTag.match(/width="(.*?)"/)![1], width: +imgTag.match(/width="(.*?)"/)![1],
height: +imgTag.match(/height="(.*?)"/)![1], height: +imgTag.match(/height="(.*?)"/)![1],
alt: imgTag.match(/alt="(.*?)"/)![1],
}; };
htmlImageArray.push(remainingHtml, imgObject); htmlImageArray.push(remainingHtml, imgObject);
lastIndex = imgEndIndex; lastIndex = imgEndIndex;
+1 -1
View File
@@ -1,4 +1,4 @@
import type { Post } from "@/types"; import type { Post } from "@/types/payload-types";
const url = import.meta.env.DEV const url = import.meta.env.DEV
? "http://payload:3001" ? "http://payload:3001"
+21
View File
@@ -0,0 +1,21 @@
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 -5
View File
@@ -7,11 +7,13 @@ module.exports = {
}, },
extend: { extend: {
colors: { colors: {
gray: { primary: "rgba(var(--primary))",
DEFAULT: "#111111", secondary: "rgba(var(--secondary))",
light: "#888888", tertiary: "rgba(var(--tertiary))",
dark: "#222222", text: "rgba(var(--text))",
}, textInverted: "rgba(var(--textInverted))",
link: "rgba(var(--link))",
background: "rgba(var(--background))",
}, },
}, },
}, },
+2 -1
View File
@@ -4,7 +4,8 @@
"types": ["@astrojs/image/client"], "types": ["@astrojs/image/client"],
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"] "@/*": ["src/*"],
"@/types/*": ["../*"]
} }
} }
} }
+1
View File
@@ -35,6 +35,7 @@ services:
- "MONGODB_PASSWORD_FILE=/run/secrets/mongo_password" - "MONGODB_PASSWORD_FILE=/run/secrets/mongo_password"
- "TOKEN_FILE=/run/secrets/token" - "TOKEN_FILE=/run/secrets/token"
- "REPOSITORY" - "REPOSITORY"
- "DRONE_URL"
secrets: secrets:
- mongo_password - mongo_password
- payload_secret - payload_secret
+5 -8
View File
@@ -19,11 +19,12 @@ services:
- payload - payload
payload: payload:
image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload:latest image: git.autonomic.zone/autonomic-cooperative/astro-payload-test-payload-dev:latest
container_name: ${NAME}-payload container_name: ${NAME}-payload
restart: unless-stopped restart: unless-stopped
build: build:
context: payload context: payload
target: dev
environment: environment:
DEV: 1 DEV: 1
NAME: ${NAME} NAME: ${NAME}
@@ -31,15 +32,11 @@ services:
PAYLOAD_URL: "http://localhost:${PAYLOAD_PORT}" PAYLOAD_URL: "http://localhost:${PAYLOAD_PORT}"
PAYLOAD_PORT: ${PAYLOAD_PORT} PAYLOAD_PORT: ${PAYLOAD_PORT}
PAYLOAD_SECRET: ${PAYLOAD_SECRET} PAYLOAD_SECRET: ${PAYLOAD_SECRET}
MONGODB_USER: MONGODB_URI: "mongodb://$MONGODB_USER:$MONGODB_PASSWORD@mongo:27017"
MONGODB_HOST: mongo
MONGODB_PASSWORD:
MONGODB_PORT: 27017
volumes: volumes:
- ./payload/src:/base/src - ./payload/src:/base/src
- ./astro/src/types.ts:/types.ts # - ./astro/src/types.ts:/types.ts
volumes: - payload_media:/base/src/media
- payload_media:/prod/dist/media
ports: ports:
- ${PAYLOAD_PORT}:${PAYLOAD_PORT} - ${PAYLOAD_PORT}:${PAYLOAD_PORT}
networks: networks:
+5 -2
View File
@@ -4,7 +4,10 @@
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"version": "1.2", "version": "1.2",
"scripts": { "scripts": {
"dev": "docker compose up", "dev": "yarn dev:docker & yarn payload:types",
"stop": "docker compose down" "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
View 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;
}
-1
View File
@@ -9,7 +9,6 @@ ENV NODE_ENV=development
EXPOSE 3001 EXPOSE 3001
CMD ["yarn","dev"] CMD ["yarn","dev"]
FROM base AS build FROM base AS build
ENV NODE_ENV=production ENV NODE_ENV=production
WORKDIR /build WORKDIR /build
+9 -4
View File
@@ -6,17 +6,22 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon", "dev": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon",
"build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js build", "build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js build ",
"build:server": "tsc", "build:server": "tsc",
"build": "yarn build:payload && yarn build:server", "build": "yarn generate:types && yarn build:payload && yarn build:server",
"serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js node -r tsconfig-paths/register dist/server.js", "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js node -r tsconfig-paths/register dist/server.js",
"generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js generate:types" "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts node -r tsconfig-paths/register node_modules/payload/dist/bin/index.js generate:types",
"generate:types:listen": "nodemon --watch src --ext ts --exec 'npm run generate:types'"
}, },
"dependencies": { "dependencies": {
"@payloadcms/bundler-webpack": "^1.0.6",
"@payloadcms/db-mongodb": "^1.5.1",
"@payloadcms/plugin-cloud": "^3.0.1",
"@payloadcms/richtext-slate": "^1.5.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"express": "^4.17.1", "express": "^4.17.1",
"payload": "^1.15.6", "payload": "^2.18.3",
"tsconfig-paths": "^4.2.0" "tsconfig-paths": "^4.2.0"
}, },
"devDependencies": { "devDependencies": {
+12
View File
@@ -0,0 +1,12 @@
import { Access, FieldAccess } from "payload/types";
import { User } from "@/types/payload-types";
export const isAdmin: Access<any, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an admin role
return Boolean(user?.roles?.includes('admin'));
}
export const isAdminFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an admin role
return Boolean(user?.roles?.includes('admin'));
}
+21
View File
@@ -0,0 +1,21 @@
import { Access } from "payload/config";
export const isAdminOrSelf: Access = ({ req: { user } }) => {
// Need to be logged in
if (user) {
// If user has role of 'admin'
if (user.roles?.includes('admin')) {
return true;
}
// If any other type of user, only provide access to themselves
return {
id: {
equals: user.id,
}
}
}
// Reject everyone else
return false;
}
+12
View File
@@ -0,0 +1,12 @@
import { Access, FieldAccess } from "payload/types";
import { User } from "@/types/payload-types";
export const isEditor: Access<any, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an editor role
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
}
export const isEditorFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an editor role
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
}
+12
View File
@@ -0,0 +1,12 @@
import { Access, FieldAccess } from "payload/types";
import { User } from "@/types/payload-types";
export const isSSG: Access<any, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an ssg or admin role
return Boolean(user?.roles?.some(role => ['ssg', 'admin'].includes(role)));
}
export const isSSGFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an ssg or admin role
return Boolean(user?.roles?.some(role => ['ssg', 'admin'].includes(role)));
}
+13
View File
@@ -0,0 +1,13 @@
import { Access, FieldAccess } from "payload/types";
import { User } from "@/types/payload-types";
export const isUser: Access<any, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an ssg or admin role
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
}
export const isUserFieldLevel: FieldAccess<{ id: string }, unknown, User> = ({ req: { user } }) => {
// Return true or false based on if the user has an ssg or admin role
return Boolean(user?.roles?.some(role => ['user', 'editor', 'admin'].includes(role)));
}
+49
View File
@@ -0,0 +1,49 @@
import { CollectionConfig } from "payload/types";
import { isAdmin } from "@/access/isAdmin";
import { isEditor } from "@/access/isEditor";
import { isSSG } from "@/access/isSSG";
import { isUser } from "@/access/isUser";
const Authors: CollectionConfig = {
slug: "authors",
admin: {
defaultColumns: ["name"],
useAsTitle: "name",
},
access: {
//TODO: Author can CRUD own post
create: isUser,
read: () => true,
update: isEditor,
delete: isEditor,
},
fields: [
{
name: "avatar",
type: "upload",
relationTo: "media",
required: true,
},
{
name: "name",
type: "text",
required: true
},
{
name: "bio",
type: "text",
required: false,
},
{
name: "user",
type: "upload",
relationTo: "users",
admin: {
description: 'The selected user will be able to edit this author'
},
}
],
};
export default Authors;
+5 -3
View File
@@ -1,3 +1,4 @@
import { isUser } from "@/access/isUser";
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
export const Media: CollectionConfig = { export const Media: CollectionConfig = {
@@ -5,19 +6,20 @@ export const Media: CollectionConfig = {
admin: {}, admin: {},
access: { access: {
read: (): boolean => true, read: (): boolean => true,
create: () => true, create: isUser,
update: () => true, update: isUser,
delete: isUser,
}, },
upload: { upload: {
staticURL: "/media", staticURL: "/media",
staticDir: "media", staticDir: "media",
mimeTypes: ["image/*"], mimeTypes: ["image/*"],
}, },
fields: [ fields: [
{ {
name: "alt", name: "alt",
type: "text", type: "text",
required: true
}, },
], ],
}; };
+58 -22
View File
@@ -1,14 +1,23 @@
import { CollectionConfig } from "payload/types"; import { CollectionConfig } from "payload/types";
import { isAdmin } from "@/access/isAdmin";
import { isEditor } from "@/access/isEditor";
import { isSSG } from "@/access/isSSG";
import { isUser } from "@/access/isUser";
import { slateEditor } from '@payloadcms/richtext-slate'
const Posts: CollectionConfig = { const Posts: CollectionConfig = {
slug: "posts", slug: "posts",
versions: true,
admin: { admin: {
defaultColumns: ["title", "author", "status"], defaultColumns: ["title", "author", "status"],
useAsTitle: "title", useAsTitle: "title",
}, },
access: { access: {
//TODO: Author can CRUD own post
create: isUser,
read: () => true, read: () => true,
create: () => true, update: isEditor,
update: () => true, delete: isEditor,
}, },
hooks: { hooks: {
afterChange: [ afterChange: [
@@ -19,7 +28,7 @@ const Posts: CollectionConfig = {
process.env.NODE_ENV !== "development" && process.env.NODE_ENV !== "development" &&
console.log( console.log(
await fetch( await fetch(
`https://drone.autonomic.zone/api/repos/${process.env.REPOSITORY}/builds`, `${process.env.DRONE_URL}/api/repos/${process.env.REPOSITORY}/builds`,
{ {
method: "POST", method: "POST",
headers: { headers: {
@@ -38,41 +47,64 @@ const Posts: CollectionConfig = {
{ {
name: "title", name: "title",
type: "text", type: "text",
required: true
}, },
{ {
name: "hallo", name: "summary",
type: "text", type: "text",
required: false
}, },
{ {
name: "publishedDate", name: "publishedDate",
type: "date", type: "date",
}, required: false,
{
name: "content",
type: "richText",
admin: { admin: {
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload"], position: "sidebar",
leaves: ["bold", "italic", "underline"], }
upload: { },
collections: { {
media: { name: "thumbnail",
fields: [ type: "upload",
{ relationTo: "media",
name: "imagel", required: true,
type: "upload", },
relationTo: "media", {
required: true, name: 'content',
}, type: 'richText',
], editor: slateEditor({
admin: {
elements: ["h2", "h3", "h4", "link", "ol", "ul", "upload", "blockquote", "indent"],
leaves: ["bold", "italic", "underline", "strikethrough"],
upload: {
collections: {
media: {
fields: [
{
name: "image",
type: "upload",
relationTo: "media",
required: true,
},
],
},
}, },
}, },
}, },
})
},
{
name: "author",
//TODO: Add active user as default
type: 'relationship',
relationTo: 'authors',
admin: {
position: "sidebar",
}, },
}, },
{ {
name: "status", name: "status",
type: "select", type: "select",
required: true,
options: [ options: [
{ {
value: "draft", value: "draft",
@@ -82,6 +114,10 @@ const Posts: CollectionConfig = {
value: "published", value: "published",
label: "Published", label: "Published",
}, },
{
value: "archived",
label: "Archived",
},
], ],
defaultValue: "draft", defaultValue: "draft",
admin: { admin: {
+27 -6
View File
@@ -1,20 +1,41 @@
import { CollectionConfig } from 'payload/types'; import { CollectionConfig } from 'payload/types';
import { isAdmin, isAdminFieldLevel } from '../access/isAdmin';
import { isAdminOrSelf } from '../access/isAdminOrSelf';
const Users: CollectionConfig = { const Users: CollectionConfig = {
slug: 'users', slug: 'users',
auth: true, auth: true,
admin: { admin: {
defaultColumns: ["roles", "email"],
useAsTitle: 'email', useAsTitle: 'email',
}, },
access: { access: {
read: () => true, create: isAdmin,
read: isAdminOrSelf,
update: isAdminOrSelf,
delete: isAdmin,
}, },
fields: [ fields: [
// Email added by default
{ {
name: 'name', name: 'roles',
type: 'text', 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,
},
},
], ],
}; };
+15 -3
View File
@@ -2,12 +2,19 @@ import { buildConfig } from "payload/config";
import path from "path"; import path from "path";
import Posts from "@/collections/Posts"; import Posts from "@/collections/Posts";
import Users from "@/collections/Users"; import Users from "@/collections/Users";
import Authors from "./collections/Authors";
import Media from "@/collections/Media"; import Media from "@/collections/Media";
import { payloadCloud } from '@payloadcms/plugin-cloud'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { webpackBundler } from '@payloadcms/bundler-webpack'
import { slateEditor } from '@payloadcms/richtext-slate'
export default buildConfig({ export default buildConfig({
serverURL: process.env.PAYLOAD_URL, serverURL: process.env.PAYLOAD_URL,
admin: { admin: {
user: Users.slug, user: Users.slug,
bundler: webpackBundler(),
webpack: (config) => ({ webpack: (config) => ({
...config, ...config,
resolve: { resolve: {
@@ -19,8 +26,13 @@ export default buildConfig({
}, },
}), }),
}, },
collections: [Posts, Users, Media], collections: [Posts, Users, Authors, Media],
typescript: { typescript: {
outputFile: path.resolve("/", "types.ts"), outputFile: path.resolve("../", "payload-types.ts"),
}, },
}); plugins: [payloadCloud()],
editor: slateEditor({}),
db: mongooseAdapter({
url: process.env.MONGODB_URI,
}),
});
+17
View File
@@ -0,0 +1,17 @@
import { Payload } from "payload";
import { User } from "@/types/payload-types";
export const seed = async (payload: Payload): Promise<void> => {
// Local API methods skip all access control by default
// so we can easily create an admin user directly in init
/* Disable to prevent mistakes */
/* await payload.create<User>({
collection: 'users',
data: {
email: 'astro@ssg.js',
password: 'password',
roles: ['ssg']
}
}) */
}
+1 -1
View File
@@ -10,7 +10,7 @@ app.get("/", (_, res) => {
payload.init({ payload.init({
secret: process.env.PAYLOAD_SECRET, secret: process.env.PAYLOAD_SECRET,
mongoURL: process.env.MONGODB_URI, //mongoURL: process.env.MONGODB_URI,
express: app, express: app,
onInit: () => { onInit: () => {
payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`); payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`);
+2 -1
View File
@@ -7,7 +7,8 @@
"skipLibCheck": true, "skipLibCheck": true,
"outDir": "./dist", "outDir": "./dist",
"paths": { "paths": {
"@/*": ["./src/*", "./dist/*", "./dist/src/*"] "@/*": ["./src/*", "./dist/*", "./dist/src/*"],
"@/types/*": ["../*"]
}, },
"jsx": "react" "jsx": "react"
}, },
+2642 -1692
View File
File diff suppressed because it is too large Load Diff