feat: init
5
.dockerignore
Normal file
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
.astro
|
||||
.vscode
|
||||
node_modules
|
||||
dist
|
||||
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# ide
|
||||
.vscode/
|
||||
site-profile.code-workspace
|
||||
17
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: check-merge-conflict
|
||||
- id: check-json
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v4.0.0-alpha.11
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [javascript, typescript, css, scss, html, json, yaml, markdown]
|
||||
additional_dependencies:
|
||||
- prettier
|
||||
- prettier-plugin-astro
|
||||
- prettier-plugin-tailwindcss
|
||||
18
.releaserc.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"branches": ["release"],
|
||||
"tagFormat": "${version}",
|
||||
"plugins": [
|
||||
"@semantic-release/commit-analyzer",
|
||||
"@semantic-release/release-notes-generator",
|
||||
"@semantic-release/changelog",
|
||||
"semantic-release-export-data",
|
||||
["@semantic-release/npm", { "npmPublish": false }],
|
||||
["@semantic-release/git", {
|
||||
"assets": ["package.json", "CHANGELOG.md"],
|
||||
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
||||
}],
|
||||
["@saithodev/semantic-release-gitea", {
|
||||
"giteaUrl": "https://gitea.alexlebens.dev"
|
||||
}]
|
||||
]
|
||||
}
|
||||
0
CHANGELOG.md
Normal file
35
Dockerfile
Normal file
@@ -0,0 +1,35 @@
|
||||
ARG REGISTRY=dhi.io
|
||||
FROM ${REGISTRY}/bun:1.3.11-alpine3.22-dev AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json bun.lock ./
|
||||
|
||||
FROM builder AS prod-deps
|
||||
RUN --mount=type=cache,id=bun,target=/root/.bun/install/cache \
|
||||
bun install --production --frozen-lockfile
|
||||
|
||||
FROM builder AS build-deps
|
||||
RUN --mount=type=cache,id=bun,target=/root/.bun/install/cache \
|
||||
bun install --frozen-lockfile
|
||||
|
||||
FROM build-deps AS build
|
||||
COPY . .
|
||||
RUN bun run build
|
||||
|
||||
FROM ${REGISTRY}/bun:1.3.11-alpine3.22 AS runtime
|
||||
WORKDIR /app
|
||||
COPY --from=prod-deps /app/node_modules /app/node_modules
|
||||
COPY --from=build /app/dist /app/dist
|
||||
|
||||
ARG APP_VERSION=latest
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=4321
|
||||
|
||||
LABEL version=$APP_VERSION
|
||||
LABEL description="Astro based personal website"
|
||||
|
||||
EXPOSE $PORT
|
||||
CMD ["bun", "run", "./dist/server/entry.mjs"]
|
||||
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 Alex Lebens
|
||||
Copyright (c) 2023 Tristen Tomczak
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
73
README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# 🌌 Milky-Way
|
||||
Milky Way brings forth a minimalist design ethos, allowing your work to shine like stars in the night sky. With clean lines and intuitive navigation, visitors are guided effortlessly through your portfolio, focusing solely on your creations.
|
||||
|
||||
Embrace the whimsical charm of Milky Way as it showcases your talents in a manner that's both elegant and endearing. Whether you're a designer, developer, artist, or creative professional of any kind, Milky Way provides the perfect canvas to showcase your endeavors.
|
||||
|
||||
With its responsive design, Milky Way ensures a seamless experience across devices, from desktops to smartphones, so your portfolio is accessible to all who wish to explore it. Let your work take center stage against the backdrop of this celestial template.
|
||||
|
||||
<p align="center">
|
||||
<img align="center" alt="Astro" src="https://portal.astro.build/_image?href=https%3A%2F%2Fstorage.googleapis.com%2Fdev-portal-bucket%2Fli8pypvydk9xnzzrtrgzfw9q11j836g6y9efm8.webp&w=1600&h=900&f=webp"/>
|
||||
</p>
|
||||
|
||||
[](https://astro.build) [](https://app.netlify.com/sites/astro-milky-way/deploys)
|
||||
|
||||
## 🔥 Features
|
||||
- [x] Simple and clean design, perfect for showcasing your work.
|
||||
- [x] Responsive layout for seamless viewing across different devices.
|
||||
- [x] Fast and efficient, thanks to the Astro static site generator.
|
||||
- [x] Easy to customize with CSS and straightforward HTML structure.
|
||||
|
||||
## ⚓ Lighthouse Score
|
||||
<p align="center">
|
||||
<img width="600" alt="Lighthouse Score" src="https://raw.githubusercontent.com/ttomczak3/Milky-Way/6e386e2f920c993c33d348a9c1271a1cec6c6d2b/milkyway-lighthouse-score.svg"/>
|
||||
</p>
|
||||
|
||||
## 🚀 Getting Started
|
||||
Clone this repository to your local machine using Git.
|
||||
|
||||
```scheme
|
||||
git clone https://github.com/ttomczak3/Milky-Way.git
|
||||
cd Milky-Way
|
||||
```
|
||||
|
||||
| Command | Action |
|
||||
| :---------------- | :------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:4321` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
|
||||
Edit the HTML files in the `src/pages` directory to add your projects, experiences, and personal information. You can also modify the CSS styles in `src/styles` to match your preferences.
|
||||
|
||||
## 📂 Project Structure
|
||||
|
||||
```
|
||||
/
|
||||
├── public/
|
||||
│ └── GitHub.webp
|
||||
│ └── blog-post.webp
|
||||
│ └── blog.webp
|
||||
│ └── favicon.svg
|
||||
│ └── laptop.webp
|
||||
│ └── space.webp
|
||||
│ └── youtube.webp
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ ├── content/
|
||||
│ ├── layouts/
|
||||
│ ├── pages/
|
||||
│ ├── styles/
|
||||
│ └── env.d.ts
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## 💻 Contributing
|
||||
Contributions to this project are welcome. If you find a bug or have a suggestion for improvement, please open an issue or submit a pull request.
|
||||
|
||||
## 📃 License
|
||||
This project is licensed under the MIT License. See the `LICENSE` file for details.
|
||||
|
||||
## ☕ Support
|
||||
If you enjoy Milky-Way and would like to show your support and appreciation through a tip, I would gratefully accept it.
|
||||
|
||||
[](https://ko-fi.com/X8X0P7FGR)
|
||||
4
astro.config.mjs
Normal file
@@ -0,0 +1,4 @@
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({});
|
||||
41
package.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "milky-way",
|
||||
"type": "module",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "10.0.4",
|
||||
"@astrojs/sitemap": "3.7.2",
|
||||
"@tailwindcss/postcss": "4.2.2",
|
||||
"@tailwindcss/vite": "4.2.2",
|
||||
"astro": "6.1.5",
|
||||
"tailwindcss": "4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@saithodev/semantic-release-gitea": "2.1.0",
|
||||
"@semantic-release/changelog": "6.0.3",
|
||||
"@semantic-release/commit-analyzer": "13.0.1",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@semantic-release/npm": "13.1.5",
|
||||
"@semantic-release/release-notes-generator": "14.1.0",
|
||||
"@tailwindcss/forms": "0.5.11",
|
||||
"@tailwindcss/typography": "0.5.19",
|
||||
"@typescript-eslint/parser": "8.58.1",
|
||||
"eslint": "10.2.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"eslint-plugin-astro": "1.7.0",
|
||||
"prettier": "3.8.1",
|
||||
"prettier-plugin-astro": "0.14.1",
|
||||
"prettier-plugin-tailwindcss": "0.7.2",
|
||||
"semantic-release": "25.0.3",
|
||||
"semantic-release-export-data": "1.2.0",
|
||||
"typescript": "6.0.2",
|
||||
"typescript-eslint": "8.58.1"
|
||||
}
|
||||
}
|
||||
8
postcss.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
23
prettier.config.mjs
Normal file
@@ -0,0 +1,23 @@
|
||||
/** @type {import("prettier").Config} */
|
||||
const config = {
|
||||
printWidth: 100,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'es5',
|
||||
useTabs: false,
|
||||
plugins: [
|
||||
'prettier-plugin-astro',
|
||||
'prettier-plugin-tailwindcss',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: '*.astro',
|
||||
options: {
|
||||
parser: 'astro',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
2
public/_headers
Normal file
@@ -0,0 +1,2 @@
|
||||
/images/*
|
||||
Cache-Control: public, max-age=31536000, immutable
|
||||
BIN
public/images/404.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/images/GitHub.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/images/blog-post.webp
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
public/images/blog.webp
Normal file
|
After Width: | Height: | Size: 308 KiB |
9
public/images/favicon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
|
||||
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
|
||||
<style>
|
||||
path { fill: #000; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
path { fill: #FFF; }
|
||||
}
|
||||
</style>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
7
public/images/github-mark-white.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96" width="98" height="96">
|
||||
<title>github-mark-white-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #fdebf3 }
|
||||
</style>
|
||||
<path id="Layer" fill-rule="evenodd" class="s0" d="m48.9 0c-27.1 0-48.9 22-48.9 49.2 0 21.8 14 40.2 33.4 46.7 2.4 0.5 3.3-1.1 3.3-2.4 0-1.1-0.1-5-0.1-9.1-13.5 3-16.4-5.8-16.4-5.8-2.2-5.8-5.4-7.2-5.4-7.2-4.4-3 0.3-3 0.3-3 5 0.3 7.5 5 7.5 5 4.4 7.5 11.5 5.4 14.3 4.1 0.4-3.2 1.7-5.4 3.1-6.6-10.9-1.1-22.3-5.4-22.3-24.3 0-5.4 2-9.8 5-13.2-0.5-1.2-2.2-6.3 0.5-13 0 0 4.1-1.3 13.4 5q1.5-0.4 3-0.7 1.6-0.3 3.1-0.5 1.5-0.2 3.1-0.3 1.5-0.1 3.1-0.1c4.1 0 8.3 0.6 12.2 1.6 9.3-6.3 13.4-5 13.4-5 2.7 6.7 1 11.8 0.5 13 3.1 3.4 5 7.8 5 13.2 0 18.9-11.4 23.1-22.3 24.3 1.8 1.5 3.3 4.5 3.3 9.1 0 6.6-0.1 11.9-0.1 13.5 0 1.3 0.9 2.9 3.3 2.4 19.4-6.5 33.4-24.9 33.4-46.7 0.1-27.2-21.8-49.2-48.7-49.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 874 B |
7
public/images/github-mark.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 98 96" width="98" height="96">
|
||||
<title>github-mark-white-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #1e1e2e }
|
||||
</style>
|
||||
<path id="Layer" fill-rule="evenodd" class="s0" d="m48.9 0c-27.1 0-48.9 22-48.9 49.2 0 21.8 14 40.2 33.4 46.7 2.4 0.5 3.3-1.1 3.3-2.4 0-1.1-0.1-5-0.1-9.1-13.5 3-16.4-5.8-16.4-5.8-2.2-5.8-5.4-7.2-5.4-7.2-4.4-3 0.3-3 0.3-3 5 0.3 7.5 5 7.5 5 4.4 7.5 11.5 5.4 14.3 4.1 0.4-3.2 1.7-5.4 3.1-6.6-10.9-1.1-22.3-5.4-22.3-24.3 0-5.4 2-9.8 5-13.2-0.5-1.2-2.2-6.3 0.5-13 0 0 4.1-1.3 13.4 5q1.5-0.4 3-0.7 1.6-0.3 3.1-0.5 1.5-0.2 3.1-0.3 1.5-0.1 3.1-0.1c4.1 0 8.3 0.6 12.2 1.6 9.3-6.3 13.4-5 13.4-5 2.7 6.7 1 11.8 0.5 13 3.1 3.4 5 7.8 5 13.2 0 18.9-11.4 23.1-22.3 24.3 1.8 1.5 3.3 4.5 3.3 9.1 0 6.6-0.1 11.9-0.1 13.5 0 1.3 0.9 2.9 3.3 2.4 19.4-6.5 33.4-24.9 33.4-46.7 0.1-27.2-21.8-49.2-48.7-49.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 874 B |
BIN
public/images/image-1.webp
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
public/images/image-2.webp
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/images/laptop.webp
Normal file
|
After Width: | Height: | Size: 203 KiB |
BIN
public/images/li-in-white.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/images/li-in.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
8
public/images/mail-white.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20">
|
||||
<title>mail-white-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #fdebf3 }
|
||||
</style>
|
||||
<path id="path2" fill-rule="evenodd" class="s0" d="m19 4v12h-18v-12zm-1 1h-16v10h16z"/>
|
||||
<path id="path3" fill-rule="evenodd" class="s0" d="m1.1 4.1h17.8v11.8h-17.8zm16.9 7.1c0-3.5 0-3.8-0.1-3.7-0.1 0-1.9 0.9-4 2-2.1 1.1-3.9 2-3.9 2 0 0-1.8-0.9-3.9-2-2.1-1.1-3.9-2-4-2-0.1-0.1-0.1 0.2-0.1 3.7v3.9h16zm-2.5-3.6l2.5-1.3v-1.3h-16v1.3l4 2.1 4 2.1 1.5-0.8c0.8-0.4 2.6-1.4 4-2.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 557 B |
8
public/images/mail.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" width="20" height="20">
|
||||
<title>mail-white-svg</title>
|
||||
<style>
|
||||
.s0 { fill: #1e1e2e }
|
||||
</style>
|
||||
<path id="path2" fill-rule="evenodd" class="s0" d="m19 4v12h-18v-12zm-1 1h-16v10h16z"/>
|
||||
<path id="path3" fill-rule="evenodd" class="s0" d="m1.1 4.1h17.8v11.8h-17.8zm16.9 7.1c0-3.5 0-3.8-0.1-3.7-0.1 0-1.9 0.9-4 2-2.1 1.1-3.9 2-3.9 2 0 0-1.8-0.9-3.9-2-2.1-1.1-3.9-2-4-2-0.1-0.1-0.1 0.2-0.1 3.7v3.9h16zm-2.5-3.6l2.5-1.3v-1.3h-16v1.3l4 2.1 4 2.1 1.5-0.8c0.8-0.4 2.6-1.4 4-2.1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 557 B |
BIN
public/images/space.webp
Normal file
|
After Width: | Height: | Size: 513 KiB |
BIN
public/images/youtube.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
41
renovate.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended",
|
||||
"mergeConfidence:all-badges",
|
||||
":rebaseStalePrs"
|
||||
],
|
||||
"timezone": "US/Central",
|
||||
"labels": [],
|
||||
"prHourlyLimit": 0,
|
||||
"prConcurrentLimit": 0,
|
||||
"rangeStrategy": "pin",
|
||||
"packageRules": [
|
||||
{
|
||||
"description": "Label dependency",
|
||||
"matchDatasources": [
|
||||
"npm"
|
||||
],
|
||||
"addLabels": [
|
||||
"dependency"
|
||||
],
|
||||
"automerge": false,
|
||||
"minimumReleaseAge": "1 days"
|
||||
},
|
||||
{
|
||||
"description": "Automerge dependency patch",
|
||||
"matchDatasources": [
|
||||
"npm"
|
||||
],
|
||||
"matchUpdateTypes": [
|
||||
"patch"
|
||||
],
|
||||
"addLabels": [
|
||||
"dependency",
|
||||
"automerge"
|
||||
],
|
||||
"automerge": true,
|
||||
"minimumReleaseAge": "1 days"
|
||||
}
|
||||
]
|
||||
}
|
||||
32
src/components/Card.astro
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
interface Props {
|
||||
url: string;
|
||||
image: string;
|
||||
title: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
const { url, image, title, body } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="card">
|
||||
<a class="card__link" href={url}>
|
||||
<div class="center">
|
||||
<img
|
||||
class="card__img"
|
||||
src={image}
|
||||
role="presentation"
|
||||
width="226"
|
||||
height="127"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="card__title">
|
||||
{title}
|
||||
</h3>
|
||||
<p class="card__txt">
|
||||
{body}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
29
src/components/CardPost.astro
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
interface Props {
|
||||
url: string;
|
||||
image: string;
|
||||
title: string;
|
||||
date: string;
|
||||
}
|
||||
|
||||
const { url, image, title, date } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="card">
|
||||
<a class="card__link" href={url}>
|
||||
<div class="center">
|
||||
<img
|
||||
class="card__img"
|
||||
src={image}
|
||||
role="presentation"
|
||||
width="226"
|
||||
height="127"
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="card__title">
|
||||
{title}
|
||||
</h3>
|
||||
</a>
|
||||
</li>
|
||||
14
src/components/Footer.astro
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
const year = new Date().getFullYear();
|
||||
---
|
||||
<footer>
|
||||
<div class="center">
|
||||
<ul class="footer">
|
||||
<li class="icon__btn"><a class="icon__link" href="#" target="_blank"><span class="git-icon">GitHub</span></a></li>
|
||||
<li class="icon__btn"><a class="icon__link" href="mailto: #@gmail.com"><span class="mail-icon">Email</span></a></li>
|
||||
<li class="icon__btn"><a class="icon__link" href="#" target="_blank"><span class="linked-in">LinkedIn</span></a></li>
|
||||
</ul>
|
||||
<small>© {year} Milky-Way by <a class="footer__link" href="https://github.com/ttomczak3">ttomczak</a>. All Rights Reserved.</small>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
9
src/components/Header.astro
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
import Navigation from './Navigation.astro';
|
||||
---
|
||||
<header>
|
||||
<nav transition:persist >
|
||||
<Navigation />
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
15
src/components/Navigation.astro
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
import ThemeIcon from './ThemeIcon.astro';
|
||||
---
|
||||
<div class="navbar">
|
||||
<div class="navbar__title">
|
||||
<a href="/">Milky-Way</a>
|
||||
</div>
|
||||
<div class="navbar__menu">
|
||||
<a href="/works/">Works</a>
|
||||
<a href="/posts/">Posts</a>
|
||||
<a href="https://github.com/ttomczak3/Milky-Way" target="_blank">GitHub</a>
|
||||
<ThemeIcon />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
55
src/components/ThemeIcon.astro
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
---
|
||||
<button id="themeToggle" title="Theme Toggle">
|
||||
<svg width="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path class="sun" fill-rule="evenodd" d="M12 17.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zm0 1.5a7 7 0 1 0 0-14 7 7 0 0 0 0 14zm12-7a.8.8 0 0 1-.8.8h-2.4a.8.8 0 0 1 0-1.6h2.4a.8.8 0 0 1 .8.8zM4 12a.8.8 0 0 1-.8.8H.8a.8.8 0 0 1 0-1.6h2.5a.8.8 0 0 1 .8.8zm16.5-8.5a.8.8 0 0 1 0 1l-1.8 1.8a.8.8 0 0 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM6.3 17.7a.8.8 0 0 1 0 1l-1.7 1.8a.8.8 0 1 1-1-1l1.7-1.8a.8.8 0 0 1 1 0zM12 0a.8.8 0 0 1 .8.8v2.5a.8.8 0 0 1-1.6 0V.8A.8.8 0 0 1 12 0zm0 20a.8.8 0 0 1 .8.8v2.4a.8.8 0 0 1-1.6 0v-2.4a.8.8 0 0 1 .8-.8zM3.5 3.5a.8.8 0 0 1 1 0l1.8 1.8a.8.8 0 1 1-1 1L3.5 4.6a.8.8 0 0 1 0-1zm14.2 14.2a.8.8 0 0 1 1 0l1.8 1.7a.8.8 0 0 1-1 1l-1.8-1.7a.8.8 0 0 1 0-1z"/>
|
||||
<path class="moon" fill-rule="evenodd" d="M16.5 6A10.5 10.5 0 0 1 4.7 16.4 8.5 8.5 0 1 0 16.4 4.7l.1 1.3zm-1.7-2a9 9 0 0 1 .2 2 9 9 0 0 1-11 8.8 9.4 9.4 0 0 1-.8-.3c-.4 0-.8.3-.7.7a10 10 0 0 0 .3.8 10 10 0 0 0 9.2 6 10 10 0 0 0 4-19.2 9.7 9.7 0 0 0-.9-.3c-.3-.1-.7.3-.6.7a9 9 0 0 1 .3.8z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<style>
|
||||
#themeToggle {
|
||||
border: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#themeToggle:hover {
|
||||
cursor: pointer;
|
||||
rotate: 10deg;
|
||||
}
|
||||
|
||||
.moon { fill: #eff1f5; }
|
||||
.sun { fill: transparent; }
|
||||
|
||||
:global(.light) .moon { fill: transparent; }
|
||||
:global(.light) .sun { fill: #1e1e2e; }
|
||||
</style>
|
||||
<script is:inline >
|
||||
const theme = (() => {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
|
||||
return 'light';
|
||||
}
|
||||
return 'dark';
|
||||
})();
|
||||
|
||||
if (theme === 'dark') {
|
||||
document.documentElement.classList.remove('light');
|
||||
} else {
|
||||
document.documentElement.classList.add('light');
|
||||
}
|
||||
|
||||
window.localStorage.setItem('theme', theme);
|
||||
|
||||
const handleToggleClick = () => {
|
||||
const element = document.documentElement;
|
||||
element.classList.toggle("light");
|
||||
|
||||
const isLight = element.classList.contains("light");
|
||||
localStorage.setItem("theme", isLight ? "light" : "dark");
|
||||
}
|
||||
|
||||
document.getElementById("themeToggle").addEventListener("click", handleToggleClick);
|
||||
</script>
|
||||
|
||||
45
src/content.config.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { z, defineCollection } from "astro:content";
|
||||
import { glob } from 'astro/loaders';
|
||||
|
||||
const projectsCollection = defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/projects" }),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string(),
|
||||
image: z.object({
|
||||
url: z.string(),
|
||||
alt: z.string()
|
||||
}),
|
||||
worksImage1: z.object({
|
||||
url: z.string(),
|
||||
alt: z.string()
|
||||
}),
|
||||
worksImage2: z.object({
|
||||
url: z.string(),
|
||||
alt: z.string()
|
||||
}),
|
||||
platform: z.string(),
|
||||
stack: z.string(),
|
||||
website: z.string(),
|
||||
github: z.string(),
|
||||
})
|
||||
});
|
||||
|
||||
const postsCollection = defineCollection({
|
||||
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/posts" }),
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
author: z.string(),
|
||||
date: z.string(),
|
||||
image: z.object({
|
||||
url: z.string(),
|
||||
alt: z.string()
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
export const collections = {
|
||||
projects: projectsCollection,
|
||||
posts: postsCollection
|
||||
};
|
||||
|
||||
22
src/content/posts/post-1.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 'Post 1'
|
||||
author: Tristen Tomczak
|
||||
date: '03-23-2025'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/blog-post.webp'
|
||||
alt: 'Post Thumbnail'
|
||||
---
|
||||
|
||||
Aenean a ex et metus finibus malesuada commodo in magna. In ut libero urna. Aenean in quam in ipsum rutrum egestas. Donec semper dignissim ante. Sed efficitur mi et sapien ultrices malesuada. Aliquam fermentum aliquam ante, eu semper mi vestibulum quis. Sed et purus metus. Pellentesque vestibulum commodo euismod. Duis a mauris accumsan lorem laoreet tempor. Mauris accumsan varius metus, in rutrum magna accumsan eget. Pellentesque at leo at sem tempor hendrerit non sit amet ante. Cras commodo augue sed magna rutrum rutrum.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-1.webp" alt="First Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-2.webp" alt="Second Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
22
src/content/posts/post-2.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 'Post 2'
|
||||
author: Tristen Tomczak
|
||||
date: '03-23-2025'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/blog-post.webp'
|
||||
alt: 'Post Thumbnail'
|
||||
---
|
||||
|
||||
Aenean a ex et metus finibus malesuada commodo in magna. In ut libero urna. Aenean in quam in ipsum rutrum egestas. Donec semper dignissim ante. Sed efficitur mi et sapien ultrices malesuada. Aliquam fermentum aliquam ante, eu semper mi vestibulum quis. Sed et purus metus. Pellentesque vestibulum commodo euismod. Duis a mauris accumsan lorem laoreet tempor. Mauris accumsan varius metus, in rutrum magna accumsan eget. Pellentesque at leo at sem tempor hendrerit non sit amet ante. Cras commodo augue sed magna rutrum rutrum.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-1.webp" alt="First Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-2.webp" alt="Second Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
22
src/content/posts/post-3.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 'Post 3'
|
||||
author: Tristen Tomczak
|
||||
date: '03-23-2025'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/blog-post.webp'
|
||||
alt: 'Post Thumbnail'
|
||||
---
|
||||
|
||||
Aenean a ex et metus finibus malesuada commodo in magna. In ut libero urna. Aenean in quam in ipsum rutrum egestas. Donec semper dignissim ante. Sed efficitur mi et sapien ultrices malesuada. Aliquam fermentum aliquam ante, eu semper mi vestibulum quis. Sed et purus metus. Pellentesque vestibulum commodo euismod. Duis a mauris accumsan lorem laoreet tempor. Mauris accumsan varius metus, in rutrum magna accumsan eget. Pellentesque at leo at sem tempor hendrerit non sit amet ante. Cras commodo augue sed magna rutrum rutrum.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-1.webp" alt="First Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-2.webp" alt="Second Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
22
src/content/posts/post-4.md
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
title: 'Post 4'
|
||||
author: Tristen Tomczak
|
||||
date: '03-23-2025'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/blog-post.webp'
|
||||
alt: 'Post Thumbnail'
|
||||
---
|
||||
|
||||
Aenean a ex et metus finibus malesuada commodo in magna. In ut libero urna. Aenean in quam in ipsum rutrum egestas. Donec semper dignissim ante. Sed efficitur mi et sapien ultrices malesuada. Aliquam fermentum aliquam ante, eu semper mi vestibulum quis. Sed et purus metus. Pellentesque vestibulum commodo euismod. Duis a mauris accumsan lorem laoreet tempor. Mauris accumsan varius metus, in rutrum magna accumsan eget. Pellentesque at leo at sem tempor hendrerit non sit amet ante. Cras commodo augue sed magna rutrum rutrum.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-1.webp" alt="First Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
|
||||
<div class="center">
|
||||
<img class="pro-img" src="/.netlify/images?url=/images/image-2.webp" alt="Second Image" width="500px" height="281" loading="lazy" decoding="async">
|
||||
</div>
|
||||
|
||||
Vivamus sed faucibus lorem. Aenean a lorem convallis, ultrices nisl vitae, imperdiet elit. Duis in tristique lacus. Quisque sollicitudin dolor ac dui faucibus, ut tincidunt velit blandit. Donec tincidunt metus eros, at dignissim enim blandit ut. Cras varius tincidunt tortor ac tincidunt. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed a ipsum quis nulla commodo pretium. Ut sed sem bibendum, facilisis dolor sit amet, interdum nibh. Aliquam id auctor dolor. In nulla diam, mattis quis nisl et, aliquam interdum quam. Donec lobortis ex arcu, ac pharetra ante vehicula euismod. Donec finibus faucibus felis vitae facilisis.
|
||||
19
src/content/projects/project-1.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: 'Project 1'
|
||||
description: 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/GitHub.webp'
|
||||
alt: 'GitHub wallpaper'
|
||||
worksImage1:
|
||||
url: '/.netlify/images?url=/images/image-1.webp'
|
||||
alt: 'first image of your project.'
|
||||
worksImage2:
|
||||
url: '/.netlify/images?url=/images/image-2.webp'
|
||||
alt: 'second image of your project.'
|
||||
platform: Web
|
||||
stack: Astro, JavaScript
|
||||
website: https://astro-milky-way.netlify.app/
|
||||
github: https://github.com/ttomczak3/Milky-Way
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras faucibus a tortor at molestie. Sed pellentesque leo auctor, auctor lorem nec, venenatis risus. Vivamus commodo ipsum vitae orci finibus, vel porta nunc viverra. In hac habitasse platea dictumst. Nunc pretium, ligula ultricies consequat sollicitudin, enim ex ullamcorper nisl.
|
||||
19
src/content/projects/project-2.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
title: 'Project 2'
|
||||
description: 'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci'
|
||||
image:
|
||||
url: '/.netlify/images?url=/images/GitHub.webp'
|
||||
alt: 'GitHub wallpaper'
|
||||
worksImage1:
|
||||
url: '/.netlify/images?url=/images/image-1.webp'
|
||||
alt: 'first image of your project.'
|
||||
worksImage2:
|
||||
url: '/.netlify/images?url=/images/image-2.webp'
|
||||
alt: 'second image of your project.'
|
||||
platform: Web
|
||||
stack: Astro, JavaScript
|
||||
website: https://astro-milky-way.netlify.app/
|
||||
github: https://github.com/ttomczak3/Milky-Way
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras faucibus a tortor at molestie. Sed pellentesque leo auctor, auctor lorem nec, venenatis risus. Vivamus commodo ipsum vitae orci finibus, vel porta nunc viverra. In hac habitasse platea dictumst. Nunc pretium, ligula ultricies consequat sollicitudin, enim ex ullamcorper nisl.
|
||||
2
src/env.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
45
src/layouts/Layout.astro
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
import Header from '../components/Header.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import '../styles/global.css';
|
||||
const { pageTitle } = Astro.props;
|
||||
---
|
||||
<!DOCTYPE html>
|
||||
<html transition:animate="none" lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="description" content="Astro description">
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Josefin+Sans&family=Pacifico&display=swap" as="style">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Josefin+Sans&family=Pacifico&display=swap" rel="stylesheet">
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{pageTitle}</title>
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main>
|
||||
<slot />
|
||||
<Footer />
|
||||
</main>
|
||||
<script>
|
||||
function colorMode() {
|
||||
if (
|
||||
localStorage.theme === "light" ||
|
||||
(!("theme" in localStorage) &&
|
||||
window.matchMedia("(prefers-color-scheme: light)").matches)
|
||||
) {
|
||||
document.documentElement.classList.add("light");
|
||||
} else {
|
||||
document.documentElement.classList.remove("light");
|
||||
}
|
||||
}
|
||||
|
||||
colorMode();
|
||||
document.addEventListener('astro:after-swap', colorMode);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
20
src/layouts/MarkdownPostsLayout.astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import BaseLayout from "./Layout.astro";
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout pageTitle={frontmatter.title}>
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/blog.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h2 style="text-decoration:none;">{frontmatter.title}</h2>
|
||||
<ul class="badge__list">
|
||||
<li>
|
||||
<span class="badge badge--item">AUTHOR</span>{frontmatter.author}
|
||||
</li>
|
||||
<li>
|
||||
<span class="badge badge--item">PUBLISHED</span>{frontmatter.date}
|
||||
</li>
|
||||
</ul>
|
||||
<slot />
|
||||
</BaseLayout>
|
||||
22
src/layouts/MarkdownWorksLayout.astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import BaseLayout from './Layout.astro';
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
<BaseLayout pageTitle={frontmatter.title}>
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/laptop.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h2>{frontmatter.title}</h2>
|
||||
<slot />
|
||||
<ul class="badge__list">
|
||||
<li><span class="badge badge--item">PLATFORM</span>{frontmatter.platform}</li>
|
||||
<li><span class="badge badge--item">STACK</span>{frontmatter.stack}</li>
|
||||
<li><span class="badge badge--item">WEBSITE</span><a class="badge__link" href={frontmatter.website} target="_blank">{frontmatter.website}</a></li>
|
||||
<li><span class="badge badge--item">GITHUB</span><a class="badge__link" href={frontmatter.github} target="_blank">{frontmatter.github}</a></li>
|
||||
</ul>
|
||||
<div class="center">
|
||||
<img class="pro-img" width="500px" height="281" src={frontmatter.worksImage1.url} alt={frontmatter.worksImage1.alt} />
|
||||
<img class="pro-img" width="500px" height="281" src={frontmatter.worksImage2.url} alt={frontmatter.worksImage2.alt} />
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
17
src/pages/404.astro
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
const description = "404";
|
||||
import '../styles/global.css';
|
||||
---
|
||||
<layout>
|
||||
<div class="lost">
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/404.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h1 class="lost__header">{ description }</h1>
|
||||
<p class="lost__body">It seems like you've wandered into the wrong forest!</p>
|
||||
<p>
|
||||
<a class="lost__link" href="/">Return Home</a>
|
||||
</p>
|
||||
</div>
|
||||
</layout>
|
||||
|
||||
51
src/pages/index.astro
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
const pageTitle = "Milky-Way";
|
||||
---
|
||||
<Layout pageTitle={pageTitle}>
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/space.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h1>Milky-Way</h1>
|
||||
<span class="badge">Digital Craftsman (Developer)</span>
|
||||
<h2>About</h2>
|
||||
<p>
|
||||
Praesent malesuada mi nisi, quis tempus magna venenatis ut.
|
||||
Suspendisse tempor sem at scelerisque congue. Curabitur mollis,
|
||||
mi id molestie finibus, metus sem sagittis nunc, euismod feugiat orci neque non lorem.
|
||||
Aenean sollicitudin erat eu molestie tempus. Nunc ac eros elementum, fermentum lacus at,
|
||||
pellentesque mauris. Suspendisse eros neque, interdum euismod consectetur convallis,
|
||||
porttitor at massa. Phasellus a ultrices arcu. Donec viverra, lorem in pellentesque euismod,
|
||||
libero justo gravida nibh, non ultricies justo purus ultrices est.
|
||||
</p>
|
||||
<p>
|
||||
Ut at interdum dui. Donec ut ante ex. Maecenas id ex eget nibh consequat accumsan.
|
||||
Proin semper semper dui nec feugiat. Donec cursus neque id aliquet finibus. Morbi
|
||||
facilisis turpis nibh, nec dictum orci dictum nec. In hac habitasse platea dictumst.
|
||||
Maecenas mollis turpis vitae turpis bibendum, vitae tristique arcu eleifend.
|
||||
</p>
|
||||
<div class="center">
|
||||
<a href="/works/">
|
||||
<button class="btn">My portfolio</button>
|
||||
</a>
|
||||
</div>
|
||||
<h2>Skills</h2>
|
||||
<div class="skills center">
|
||||
<img class="skills__img" width="46" height="46" alt="Python" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="Golang" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original-wordmark.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="JavaScript" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="TypeScript" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="Docker" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/docker/docker-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="Linux" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/linux/linux-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="Kubernetes" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/kubernetes/kubernetes-original.svg"/>
|
||||
<img class="skills__img" width="46" height="46" alt="Git" src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/git/git-original.svg"/>
|
||||
</div>
|
||||
<h2>Interests</h2>
|
||||
<p>
|
||||
Ut at interdum dui. Donec ut ante ex. Maecenas id ex eget nibh consequat accumsan.
|
||||
Proin semper semper dui nec feugiat. Donec cursus neque id aliquet finibus. Morbi
|
||||
facilisis turpis nibh, nec dictum orci dictum nec. In hac habitasse platea dictumst.
|
||||
Maecenas mollis turpis vitae turpis bibendum, vitae tristique arcu eleifend.
|
||||
</p>
|
||||
</Layout>
|
||||
|
||||
26
src/pages/posts.astro
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import Card from "../components/CardPost.astro";
|
||||
const allPosts = await getCollection("posts");
|
||||
const pageTitle = "Posts";
|
||||
---
|
||||
|
||||
<Layout pageTitle={pageTitle}>
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/blog.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h2>Blog</h2>
|
||||
<ul role="list" class="link-card-grid">
|
||||
{
|
||||
allPosts.map((cardPost) => (
|
||||
<Card
|
||||
url={`/posts/${cardPost.id}/`}
|
||||
image={cardPost.data.image.url}
|
||||
title={cardPost.data.title}
|
||||
date={cardPost.data.date}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Layout>
|
||||
20
src/pages/posts/[...slug].astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import { getCollection, getEntry, render } from "astro:content";
|
||||
import MarkdownPostsLayout from "../../layouts/MarkdownPostsLayout.astro";
|
||||
|
||||
export const prerender = true;
|
||||
export async function getStaticPaths() {
|
||||
const postEntries = await getCollection("posts");
|
||||
return postEntries.map((entry) => ({
|
||||
params: { slug: entry.id },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content, headings } = await render(entry);
|
||||
---
|
||||
|
||||
<MarkdownPostsLayout frontmatter={entry.data}>
|
||||
<Content />
|
||||
</MarkdownPostsLayout>
|
||||
20
src/pages/projects/[...slug].astro
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
import { getCollection, getEntry, render } from "astro:content";
|
||||
import MarkdownWorksLayout from "../../layouts/MarkdownWorksLayout.astro";
|
||||
|
||||
export const prerender = true;
|
||||
export async function getStaticPaths() {
|
||||
const projectEntries = await getCollection("projects");
|
||||
return projectEntries.map((entry) => ({
|
||||
params: { slug: entry.id },
|
||||
props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content, headings } = await render(entry);
|
||||
---
|
||||
|
||||
<MarkdownWorksLayout frontmatter={entry.data}>
|
||||
<Content />
|
||||
</MarkdownWorksLayout>
|
||||
22
src/pages/works.astro
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
import { getCollection } from "astro:content";
|
||||
import Layout from '../layouts/Layout.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
const allProjects = await getCollection("projects");
|
||||
const pageTitle = "Works";
|
||||
---
|
||||
<Layout pageTitle={pageTitle}>
|
||||
<div class="gif">
|
||||
<img src="/.netlify/images?url=/images/laptop.webp" role="presentation" width="300" height="259" decoding="async">
|
||||
</div>
|
||||
<h2>Projects</h2>
|
||||
<ul role="list" class="link-card-grid">
|
||||
{allProjects.map((card) => <Card
|
||||
url={`/projects/${card.id}/`}
|
||||
image={card.data.image.url}
|
||||
title={card.data.title}
|
||||
body={card.data.description}
|
||||
/>)}
|
||||
</ul>
|
||||
</Layout>
|
||||
|
||||
390
src/styles/global.css
Normal file
@@ -0,0 +1,390 @@
|
||||
/* Global styles */
|
||||
:root {
|
||||
--background: #1e1e2e;
|
||||
--background-light: #fcebf3;
|
||||
--button: #78c2ad;
|
||||
--text: #eff1f5;
|
||||
--text-light: #1e1e2e;
|
||||
--text-link: #78c2ad;
|
||||
--text-link-light: #375a7f;
|
||||
--underline: #375a7f;
|
||||
--header: 'Pacifico', cursive;
|
||||
--body: 'Josefin Sans', sans-serif;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: var(--background);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 540px;
|
||||
margin: 0 auto;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-family: var(--header);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2.6rem;
|
||||
margin: 0;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-decoration: underline var(--underline);
|
||||
text-decoration-thickness: 4px;
|
||||
text-underline-offset: 6px;
|
||||
font-size: 2.1rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: var(--body);
|
||||
font-size: 1.1rem;
|
||||
hyphens: auto;
|
||||
line-height: 1.5;
|
||||
text-indent: 1rem;
|
||||
}
|
||||
|
||||
small,
|
||||
li {
|
||||
font-family: var(--body);
|
||||
}
|
||||
|
||||
/* Navigation Bar */
|
||||
.navbar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
|
||||
.navbar__title {
|
||||
font-family: var(--header);
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.navbar__menu {
|
||||
font-family: var(--body);
|
||||
font-size: 1.1rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.navbar__title>a,
|
||||
.navbar__menu>a {
|
||||
color: var(--text);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar__menu>a {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.navbar__title>a:hover,
|
||||
.navbar__menu>a:hover,
|
||||
.navbar__menu>a:focus {
|
||||
text-decoration: underline var(--underline);
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 6px;
|
||||
}
|
||||
|
||||
/* Gifs */
|
||||
.gif {
|
||||
margin: 20px 0 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Badge */
|
||||
.badge {
|
||||
background-color: #584966;
|
||||
color: var(--text);
|
||||
border-radius: 6px;
|
||||
font-family: var(--body);
|
||||
font-weight: 600;
|
||||
font-size: .85rem;
|
||||
padding: 0.3em 0.6em 0.2em;
|
||||
}
|
||||
|
||||
.badge__list {
|
||||
text-indent: 1rem;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.badge__list>li {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.badge--item {
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
margin-right: 5px;
|
||||
padding: 0.5em 0.3em 0.3em 0.3em;
|
||||
}
|
||||
|
||||
.badge__link {
|
||||
color: var(--text-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.badge__link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
background-size: 400%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.card__link {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.card__img {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.card__title {
|
||||
margin: 0;
|
||||
font-family: var(--body);
|
||||
font-size: 1.5rem;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card__txt {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
text-indent: 0;
|
||||
text-align: center;
|
||||
margin: 0.5rem 0 0;
|
||||
}
|
||||
|
||||
.card__link:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.btn {
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
border: 2px solid var(--button);
|
||||
border-radius: 8px;
|
||||
color: var(--text);
|
||||
font: 600 16px var(--body);
|
||||
padding: 8px 16px;
|
||||
transition: background-color 0.4s, color 0.4s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: var(--button);
|
||||
color: var(--text-light);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Skills */
|
||||
.skills {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.skills__img {
|
||||
margin: 8px 4px;
|
||||
}
|
||||
|
||||
/* Items */
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pro-img {
|
||||
border-radius: 16px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
footer {
|
||||
margin: 4rem 0;
|
||||
}
|
||||
|
||||
.footer__link {
|
||||
color: var(--text-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
cursor: default;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.icon__btn {
|
||||
display: inline-block;
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.icon__link {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fdebf3;
|
||||
}
|
||||
|
||||
.icon__link:hover {
|
||||
border-color: var(--text-link);
|
||||
}
|
||||
|
||||
.git-icon {
|
||||
content: url("/.netlify/images?url=/images/github-mark-white.svg");
|
||||
}
|
||||
|
||||
.mail-icon {
|
||||
content: url("/.netlify/images?url=/images/mail-white.svg");
|
||||
}
|
||||
|
||||
.linked-in {
|
||||
content: url("/.netlify/images?url=/images/li-in-white.png");
|
||||
}
|
||||
|
||||
/* 404 */
|
||||
.lost {
|
||||
margin-top: 30%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lost__header {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
border: 0;
|
||||
font-size: 7.5rem;
|
||||
letter-spacing: 10px;
|
||||
}
|
||||
|
||||
.lost__body {
|
||||
color: #5C5B77;
|
||||
}
|
||||
|
||||
.lost__link {
|
||||
color: var(--text-link);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Theme Icon */
|
||||
html.light {
|
||||
background-color: var(--background-light);
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .navbar__title>a {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .navbar__menu>a {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .card__title {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .card__txt {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .btn {
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
.light .btn:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.light .icon__link {
|
||||
border: 2px solid var(--text-light);
|
||||
}
|
||||
|
||||
.light .icon__link:hover {
|
||||
border-color: var(--text-link);
|
||||
}
|
||||
|
||||
.light .git-icon {
|
||||
content: url("/.netlify/images?url=/images/github-mark.svg");
|
||||
}
|
||||
|
||||
.light .mail-icon {
|
||||
content: url("/.netlify/images?url=/images/mail.svg");
|
||||
}
|
||||
|
||||
.light .linked-in {
|
||||
content: url("/.netlify/images?url=/images/li-in.png");
|
||||
}
|
||||
|
||||
.light .badge__link {
|
||||
color: var(--text-link-light);
|
||||
}
|
||||
|
||||
.light .footer__link {
|
||||
color: var(--text-link-light);
|
||||
}
|
||||
|
||||
.light .icon__link:hover {
|
||||
border-color: var(--text-link-light);
|
||||
}
|
||||
|
||||
/* Media Query */
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.navbar__title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navbar__menu {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.navbar__menu>a {
|
||||
margin: 0 11px;
|
||||
}
|
||||
|
||||
.skills__img {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
margin: 8px 1px;
|
||||
}
|
||||
|
||||
.badge__list {
|
||||
font-size: 0.9rem;
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
.pro-img {
|
||||
height: auto;
|
||||
width: 350px;
|
||||
}
|
||||
}
|
||||
9
tailwind.config.cjs
Normal file
@@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', '*.{js,ts,jsx,tsx,mdx}'],
|
||||
darkMode: ['class', '[data-theme="dark"]'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [require('@tailwindcss/typography')],
|
||||
};
|
||||
28
tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"target": "ES6",
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
357
workflows/release-image.yaml
Normal file
@@ -0,0 +1,357 @@
|
||||
name: release-image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
directus-release:
|
||||
description: 'A parameter passed via API'
|
||||
required: true
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-js
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: release
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.14.1
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Install Dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Cache Astro Build
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
.astro
|
||||
node_modules/.vite
|
||||
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || 'static' }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}-
|
||||
${{ runner.os }}-astro-
|
||||
|
||||
- name: Lint Code
|
||||
run: bun run lint
|
||||
|
||||
- name: Build Project
|
||||
run: bun run build
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Test Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'During release tests failed for building Site Profile'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
|
||||
image: true
|
||||
|
||||
guarddog:
|
||||
runs-on: ubuntu-js
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: release
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12.13'
|
||||
|
||||
- name: Install GuardDog
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install guarddog
|
||||
|
||||
- name: Run GuardDog
|
||||
run: |
|
||||
guarddog npm scan ./
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Security Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'During release guarddog scan failed for Site Profile'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
|
||||
image: true
|
||||
|
||||
semantic-release:
|
||||
needs: [ build, guarddog ]
|
||||
runs-on: ubuntu-js
|
||||
if: |
|
||||
github.event_name != 'workflow_dispatch' ||
|
||||
inputs['directus-release'] == 'true'
|
||||
outputs:
|
||||
new-release-published: ${{ steps.semantic.outputs.new-release-published }}
|
||||
new-release-version: ${{ steps.semantic.outputs.new-release-version }}
|
||||
new-release-git-tag: ${{ steps.semantic.outputs.new-release-git-tag }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.BOT_TOKEN }}
|
||||
|
||||
- name: Prepare Content Patch
|
||||
if: inputs['directus-release'] == 'true'
|
||||
run: |
|
||||
git config user.name "gitea-bot"
|
||||
git config user.email "gitea-bot@alexlebens.net"
|
||||
git commit --allow-empty -m "fix(content): directus published update [skip ci]"
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.14.1
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Install Dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Run Semantic Release
|
||||
id: semantic
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||
NODE_PATH: ${{ github.workspace }}/node_modules
|
||||
run: |
|
||||
bun run semantic-release
|
||||
|
||||
release-harbor:
|
||||
runs-on: ubuntu-js
|
||||
needs: semantic-release
|
||||
if: ${{ needs.semantic-release.outputs.new-release-published == 'true' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: release
|
||||
|
||||
- name: Login to Harbor Registry
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ vars.REGISTRY_HOST }}
|
||||
username: ${{ vars.REGISTRY_USER }}
|
||||
password: ${{ secrets.REGISTRY_SECRET }}
|
||||
|
||||
- name: Login to Docker
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ vars.DH_REGISTRY }}
|
||||
username: ${{ secrets.DH_USERNAME }}
|
||||
password: ${{ secrets.DH_TOKEN }}
|
||||
|
||||
- name: Create Kubeconfig
|
||||
run: |
|
||||
mkdir $HOME/.kube
|
||||
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
driver: kubernetes
|
||||
driver-opts: |
|
||||
namespace=gitea
|
||||
qemu.install=true
|
||||
buildkitd-config-inline: |
|
||||
[registry."docker.io"]
|
||||
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
|
||||
|
||||
- name: Available Platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||
|
||||
- name: Extract Metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.REGISTRY_HOST }}/images/site-profile
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=sha,format=long
|
||||
type=raw,value=latest,enable=${{ needs.semantic-release.outputs.new-release-published == 'true' }}
|
||||
type=semver,pattern={{version}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
type=semver,pattern={{major}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
|
||||
- name: Build and Push Image
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
APP_VERSION=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
IS_RELEASE=true
|
||||
file: ./Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: ntfy Success
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: success()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Release Success - Site Profile'
|
||||
priority: 3
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,successfully,completed
|
||||
details: 'Harbor Image for Site Profile has been released!'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Release Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'Harbor Image for Site Profile has failed to be released.'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
|
||||
image: true
|
||||
|
||||
release-gitea:
|
||||
runs-on: ubuntu-js
|
||||
needs: [ semantic-release, release-harbor ]
|
||||
if: |
|
||||
always() &&
|
||||
needs.semantic-release.outputs.new-release-published == 'true'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: release
|
||||
|
||||
- name: Login to Gitea Registry
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ vars.REPOSITORY_HOST }}
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.REPOSITORY_TOKEN }}
|
||||
|
||||
- name: Login to Docker
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ vars.DH_REGISTRY }}
|
||||
username: ${{ secrets.DH_USERNAME }}
|
||||
password: ${{ secrets.DH_TOKEN }}
|
||||
|
||||
- name: Create Kubeconfig
|
||||
run: |
|
||||
mkdir $HOME/.kube
|
||||
echo "${{ secrets.KUBECONFIG_BUILDX }}" > $HOME/.kube/config
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
driver: kubernetes
|
||||
driver-opts: |
|
||||
namespace=gitea
|
||||
qemu.install=true
|
||||
buildkitd-config-inline: |
|
||||
[registry."docker.io"]
|
||||
mirrors = ["harbor.alexlebens.net/proxy-hub.docker/"]
|
||||
|
||||
- name: Available Platforms
|
||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
||||
|
||||
- name: Extract Metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@v6
|
||||
with:
|
||||
images: |
|
||||
${{ vars.REPOSITORY_HOST }}/${{ gitea.repository }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=sha,format=long
|
||||
type=raw,value=latest,enable=${{ needs.semantic-release.outputs.new-release-published == 'true' }}
|
||||
type=semver,pattern={{version}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
type=semver,pattern={{major}},value=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
|
||||
- name: Build and Push Image
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
platforms: linux/amd64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: |
|
||||
APP_VERSION=${{ needs.semantic-release.outputs.new-release-version }}
|
||||
COMMIT_SHA=${{ github.sha }}
|
||||
IS_RELEASE=true
|
||||
file: ./Dockerfile
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: ntfy Success
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: success()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Release Success - Site Profile'
|
||||
priority: 3
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,successfully,completed
|
||||
details: 'Gitea Image for Site Profile has been released!'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Release Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'Gitea Image for Site Profile has failed to be released.'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=release-image.yaml", "clear": true}]'
|
||||
image: true
|
||||
30
workflows/renovate.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
name: renovate
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 */6 * * *'
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
renovate:
|
||||
runs-on: ubuntu-latest
|
||||
container: ghcr.io/renovatebot/renovate:43
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Renovate
|
||||
run: renovate
|
||||
env:
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_ENDPOINT: ${{ vars.INSTANCE_URL }}
|
||||
RENOVATE_REPOSITORIES: alexlebens/site-profile
|
||||
RENOVATE_GIT_AUTHOR: Renovate Bot <renovate-bot@alexlebens.net>
|
||||
RENOVATE_REDIS_URL: ${{ vars.RENOVATE_REDIS_URL }}
|
||||
LOG_LEVEL: debug
|
||||
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
|
||||
RENOVATE_GIT_PRIVATE_KEY: ${{ secrets.RENOVATE_GIT_PRIVATE_KEY }}
|
||||
RENOVATE_GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }}
|
||||
RENOVATE_REGISTRY_ALIASES: '{"dhi.io": "dhi.io"}'
|
||||
RENOVATE_HOST_RULES: '[{"matchHost":"dhi.io","hostType":"docker","username":"${{ secrets.RENOVATE_DHI_USER }}","password":"${{ secrets.RENOVATE_DHI_TOKEN }}"}]'
|
||||
99
workflows/test-build.yaml
Normal file
@@ -0,0 +1,99 @@
|
||||
name: test-build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.gitea/workflows/**'
|
||||
- '**.md'
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-js
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24.14.1
|
||||
|
||||
- name: Set up Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.11
|
||||
|
||||
- name: Install Dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
- name: Cache Astro Build Cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
.astro
|
||||
node_modules/.vite
|
||||
key: ${{ runner.os }}-astro-${{ hashFiles('**/*.astro', 'astro.config.mjs') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-astro-
|
||||
|
||||
- name: Lint Code
|
||||
run: bun run lint
|
||||
|
||||
- name: Build Project
|
||||
run: bun run build
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Test Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'Tests have failed for building Site Profile'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=test-build.yaml", "clear": true}]'
|
||||
image: true
|
||||
|
||||
guarddog:
|
||||
runs-on: ubuntu-js
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.12.13'
|
||||
|
||||
- name: Install GuardDog
|
||||
run: |
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install guarddog
|
||||
|
||||
- name: Run GuardDog
|
||||
run: |
|
||||
guarddog npm scan ./
|
||||
|
||||
- name: ntfy Failed
|
||||
uses: niniyas/ntfy-action@master
|
||||
if: failure()
|
||||
with:
|
||||
url: '${{ secrets.NTFY_URL }}'
|
||||
topic: '${{ secrets.NTFY_TOPIC }}'
|
||||
title: 'Security Failure - Site Profile'
|
||||
priority: 4
|
||||
headers: '{"Authorization": "Bearer ${{ secrets.NTFY_CRED }}"}'
|
||||
tags: action,failed
|
||||
details: 'Guarddog scan failed for Site Profile'
|
||||
icon: 'https://cdn.jsdelivr.net/gh/selfhst/icons/png/gitea.png'
|
||||
actions: '[{"action": "view", "label": "Open Gitea", "url": "https://gitea.alexlebens.dev/alexlebens/site-profile/actions?workflow=test-build.yaml", "clear": true}]'
|
||||
image: true
|
||||