Compare commits
31 Commits
v1.0.0
...
a95f13943f
Author | SHA1 | Date | |
---|---|---|---|
a95f13943f | |||
7f23f234cd | |||
b227a41a43 | |||
bf5f265116 | |||
1cf12f91f0 | |||
00eec8be70 | |||
542e432596 | |||
0791f417f3 | |||
2439dfa78d | |||
e0248abb46 | |||
cc4a827331 | |||
e8ce3f9b75 | |||
b022a4ae37 | |||
736f17c442 | |||
90ee057637 | |||
77bc97087b | |||
9c4bfc84ff | |||
2e25a00637 | |||
63d4122322 | |||
5090214128 | |||
1e9475e806 | |||
47f26c96f6 | |||
f137aceb6e | |||
d3c49bcc78 | |||
dc68ded731 | |||
9d0a4d77b3 | |||
7c0a463917 | |||
35d081abc4 | |||
93895a5967
|
|||
03d46cb73a
|
|||
5c1910292e |
21
.gitea/workflows/build.yaml
Normal file
21
.gitea/workflows/build.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Build Zola Website
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Zola
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: zola@0.17.2
|
||||
|
||||
- name: Build Zola Website
|
||||
run: |
|
||||
zola build
|
||||
|
48
.gitea/workflows/build_and_deploy.yaml
Normal file
48
.gitea/workflows/build_and_deploy.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
name: Build and Deploy Zola Website
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
|
||||
env:
|
||||
ZOLA_VERSION: "0.19.2"
|
||||
HOST: ${{ secrets.SERVER_IP }}
|
||||
SSH_USERNAME: ${{ secrets.USERNAME }}
|
||||
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_KEY }}
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Zola
|
||||
run: |
|
||||
wget https://github.com/getzola/zola/releases/download/v${ZOLA_VERSION}/zola-v${ZOLA_VERSION}-x86_64-unknown-linux-gnu.tar.gz
|
||||
tar -xvzf *.tar.gz
|
||||
|
||||
- name: Build Zola Website
|
||||
run: |
|
||||
./zola build
|
||||
|
||||
- name: Set Destination Folder if MASTER
|
||||
if: ${{ github.ref == 'refs/heads/master' }}
|
||||
run: echo "DEST_FOLDER=/srv/www/cz/filiprojek/www" >> $GITHUB_ENV
|
||||
|
||||
- name: Set Destination Folder if DEV
|
||||
if: ${{ github.ref == 'refs/heads/dev' }}
|
||||
run: echo "DEST_FOLDER=/srv/www/cz/filiprojek/dev" >> $GITHUB_ENV
|
||||
|
||||
- name: Deploy
|
||||
run: |
|
||||
apt update -y && apt-get install -y --no-install-recommends rsync
|
||||
eval "$(ssh-agent -s)"
|
||||
ssh-add - <<< "${SSH_PRIVATE_KEY}"
|
||||
mkdir -p ~/.ssh/
|
||||
ssh-keyscan -H ${HOST} >> ~/.ssh/known_hosts
|
||||
rsync -r --delete-after public/* "${SSH_USERNAME}@${HOST}:${{ env.DEST_FOLDER }}"
|
||||
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
public/
|
10
Makefile
Normal file
10
Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
all: clean format build
|
||||
|
||||
format:
|
||||
biome format --write .
|
||||
|
||||
build:
|
||||
zola build
|
||||
|
||||
clean:
|
||||
rm -rf public/
|
33
README.md
33
README.md
@@ -1,17 +1,28 @@
|
||||
# Website
|
||||
- My personal website written in php, html, css and js
|
||||
- My personal website https://www.filiprojek.cz
|
||||
- Written in [Zola](https://getzola.org)
|
||||
|
||||
## To run development server
|
||||
- `php -S localhost:8000`
|
||||
- Then it can be access at http://localhost:8000/
|
||||
|
||||
## To run using Docker
|
||||
- ToDo
|
||||
- `zola serve`
|
||||
- Then it can be access at http://127.0.0.1:1111/
|
||||
|
||||
## To Do
|
||||
- [ ] Docker
|
||||
- [ ] kopirovani PGP na click opravit
|
||||
- [ ] projekty page css
|
||||
- [ ] contact page odesilani mailu
|
||||
- [ ] contact page pridat odkazy na dalsi soc site (github atd)
|
||||
- [ ] Docker?
|
||||
- [ ] redesign contact
|
||||
- [ ] redesign about
|
||||
- [ ] check about text content
|
||||
- [ ] responsibility
|
||||
|
||||
# Projects
|
||||
- nork
|
||||
- website
|
||||
- debrepo
|
||||
- auto irrigation
|
||||
- auto awning close
|
||||
- FofrTasks
|
||||
- FofrMess
|
||||
- pkmples.cz
|
||||
- deguapp?
|
||||
- wpa_tui?
|
||||
- dotfiles
|
||||
|
||||
|
15
biome.json
Normal file
15
biome.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.7.3/schema.json",
|
||||
"files": {
|
||||
"ignore": [".vscode/", "node_modules/", "public/"]
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": false,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,2 +0,0 @@
|
||||
<p>(c) filiprojek.cz 2023<p>
|
||||
|
@@ -1,11 +0,0 @@
|
||||
<section class="nav">
|
||||
<a class="logo" href="/">
|
||||
<img src="img/fr_logo.webp" alt="logo" />
|
||||
</a>
|
||||
<div class="links">
|
||||
<a href="/home">Úvod</a>
|
||||
<a href="/projects">Projekty</a>
|
||||
<a href="/contact">Kontakt</a>
|
||||
</div>
|
||||
</section>
|
||||
|
24
config.toml
Normal file
24
config.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
# The URL the site will be built for
|
||||
base_url = "https://dev.filiprojek.cz"
|
||||
title = "Filip Rojek"
|
||||
|
||||
compile_sass = true
|
||||
minify_html = false
|
||||
build_search_index = false
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
external_links_target_blank = true
|
||||
external_links_no_follow = true
|
||||
external_links_no_referrer = true
|
||||
smart_punctuation = true
|
||||
|
||||
[extra]
|
||||
git = "https://git.filiprojek.cz/fr/website"
|
||||
nav_items = [
|
||||
{name="Home", path="/"},
|
||||
{name="About", path="/about"},
|
||||
{name="Projects", path="/projects/"},
|
||||
{name="Posts", path="/posts"}
|
||||
]
|
||||
|
27
content/about.md
Normal file
27
content/about.md
Normal file
@@ -0,0 +1,27 @@
|
||||
+++
|
||||
title = "About"
|
||||
template = "about.html"
|
||||
+++
|
||||
|
||||
## About
|
||||
I am student of IT at Charles University in Prague (Faculty of Education).
|
||||
|
||||
My programming journey began in high school, where I developed a passion for `Linux` and co-founded [Fofrweb](https://fofrweb.com). Together with a classmate, I created web applications using `Node.js` and `Vue.js`, all hosted on my own Linux server.
|
||||
|
||||
As a member of [Microlab](https://microlab.space), the university's hacker space, I engage with fellow tech enthusiasts. Additionally, I contribute to the open-source community as a package maintainer for [Void Linux](https://voidlinux.org).
|
||||
|
||||
## Work
|
||||
I currently work as a Linux engineer. Most of my work involves Debian based systems - desktops (Raspberry Pi, Intel NUC) and servers.
|
||||
|
||||
I also create custom websites from time to time.
|
||||
|
||||
## Projects
|
||||
Most of my projects are hosted on Gitea and GitHub:
|
||||
- Gitea: [git.filiprojek.cz](https://git.filiprojek.cz/fr)
|
||||
- GitHub: [github.com/filiprojek](https://github.com/filiprojek)
|
||||
|
||||
## Contact
|
||||
- <a href="mailto:filip@filiprojek.cz">filip@filiprojek.cz</a>
|
||||
- [@filiprojek](https://t.me/filiprojek) on Telegram
|
||||
- PGP: [0x7E65EA58C6075F09](https://keys.openpgp.org/vks/v1/by-fingerprint/CA3D9BE28315B49164130CD97E65EA58C6075F09)
|
||||
|
7
content/posts/_index.md
Normal file
7
content/posts/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = "Posts"
|
||||
template = "post_list.html"
|
||||
page_template = "post.html"
|
||||
sort_by = "date"
|
||||
+++
|
||||
|
193
content/posts/jellyfin.md
Normal file
193
content/posts/jellyfin.md
Normal file
@@ -0,0 +1,193 @@
|
||||
+++
|
||||
title = "Jellyfin"
|
||||
date = 2024-10-03
|
||||
description = "Hosting a Jellyfin Instance with Docker and Docker Compose"
|
||||
+++
|
||||
|
||||
# Hosting a Jellyfin Instance with Docker and Docker Compose
|
||||
Running your own media server is a great way to have complete control over your media library, and Jellyfin is one of the best open-source media server solutions available today. With Docker and Docker Compose, you can efficiently manage and scale your Jellyfin instance, along with useful companion services like Jellyseerr and Jellystat for enhanced functionality.
|
||||
|
||||
In this article, I’ll walk through my setup of a Jellyfin media server, hosted in Docker using Docker Compose. This assumes you already have Docker and Docker Compose installed and are comfortable with Linux environments. I’ll cover the configuration of Jellyfin, hardware transcoding, and the integration of supporting services like Jellyseerr and Jellystat to manage requests and track server statistics.
|
||||
|
||||
## Docker Compose Configuration Breakdown
|
||||
Here’s the Docker Compose file I use to deploy Jellyfin and its associated services.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
jellyfin:
|
||||
image: jellyfin/jellyfin:latest
|
||||
container_name: jellyfin
|
||||
user: 1000:1000
|
||||
restart: 'unless-stopped'
|
||||
devices:
|
||||
- /dev/dri:/dev/dri # for hardware transcoding
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128
|
||||
#- /dev/kfd:/dev/kfd # Remove this device if you don't use the OpenCL tone-mapping
|
||||
group_add:
|
||||
- "103" # render gid
|
||||
- "27" # video gid
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- 5100:8096
|
||||
volumes:
|
||||
- ./config:/config
|
||||
- ./cache:/cache
|
||||
- /mnt/media/torrent:/media:ro
|
||||
- /dev/shm:/data/transcode # Offload transcoding to RAM if you have enough RAM
|
||||
environment:
|
||||
- JELLYFIN_PublishedServerUrl=https://your.host.com
|
||||
|
||||
jellyseerr:
|
||||
image: fallenbagel/jellyseerr:latest
|
||||
container_name: jellyseerr
|
||||
environment:
|
||||
- LOG_LEVEL=debug
|
||||
- TZ=Europe/Prague
|
||||
ports:
|
||||
- 5280:5055
|
||||
volumes:
|
||||
- ./jellyseerr-config:/app/config
|
||||
restart: unless-stopped
|
||||
|
||||
jellystat-db:
|
||||
image: postgres:15.2
|
||||
container_name: jellystat-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: <password>
|
||||
volumes:
|
||||
- ./jellystat-db:/var/lib/postgresql/data
|
||||
|
||||
jellystat:
|
||||
image: cyfershepard/jellystat:latest
|
||||
container_name: jellystat
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: <password>
|
||||
POSTGRES_IP: jellystat-db
|
||||
POSTGRES_PORT: 5432
|
||||
JWT_SECRET: <jwt_secret>
|
||||
TZ: Europe/Prague
|
||||
volumes:
|
||||
- ./jellystat-data:/app/backend/backup-data
|
||||
ports:
|
||||
- "5110:3000"
|
||||
depends_on:
|
||||
- jellystat-db
|
||||
```
|
||||
|
||||
## Jellyfin Service
|
||||
The core of this setup is the jellyfin service. This container runs the Jellyfin server itself, and the configuration is designed to optimize performance and security.
|
||||
|
||||
### Hardware Transcoding
|
||||
For efficient media transcoding, I’ve configured Jellyfin to leverage the hardware capabilities of the host machine. Specifically, I’ve mounted the /dev/dri device to the container for Intel or AMD GPU transcoding:
|
||||
|
||||
```yaml
|
||||
devices:
|
||||
- /dev/dri:/dev/dri
|
||||
- /dev/dri/renderD128:/dev/dri/renderD128
|
||||
```
|
||||
|
||||
If you don’t need OpenCL tone-mapping, you can skip the /dev/kfd device. By adding the necessary GIDs (103 for render and 27 for video), the container has access to GPU resources.
|
||||
|
||||
Additionally, I’ve offloaded transcoding operations to the system’s RAM by mounting /dev/shm as the transcode directory:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /dev/shm:/data/transcode
|
||||
```
|
||||
|
||||
This is particularly useful if you have sufficient RAM, as it improves transcoding performance by avoiding disk I/O overhead.
|
||||
|
||||
### Storage
|
||||
In the `volumes` section, I’ve mounted the following directories:
|
||||
|
||||
- `./config:/config`: Stores Jellyfin’s configuration data.
|
||||
- `./cache:/cache`: Holds cache data to improve performance.
|
||||
- `/mnt/media/torrent:/media:ro`: This is the read-only mount where Jellyfin accesses my media library. It’s pointed to my torrent directory, ensuring that Jellyfin can index and serve files from there but not modify them.
|
||||
|
||||
### Network and Ports
|
||||
|
||||
The Jellyfin server is exposed on port `5100` (mapped to Jellyfin’s default 8096 internal port). I also set the JELLYFIN_PublishedServerUrl environment variable to make sure the correct public URL is used for generating media links and external access.
|
||||
|
||||
## Jellyseerr for Media Requests
|
||||
|
||||
Jellyseerr is a companion service to Jellyfin, allowing users to request new content directly from the web interface. Here’s how I’ve integrated it:
|
||||
|
||||
```yaml
|
||||
jellyseerr:
|
||||
image: fallenbagel/jellyseerr:latest
|
||||
container_name: jellyseerr
|
||||
environment:
|
||||
- LOG_LEVEL=debug
|
||||
- TZ=Europe/Prague
|
||||
ports:
|
||||
- 5280:5055
|
||||
volumes:
|
||||
- ./jellyseerr-config:/app/config
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
I’ve exposed Jellyseerr on port `5280` and linked it to the default timezone (`Europe/Prague` in my case). It’s important to store the Jellyseerr configuration separately in the `./jellyseerr-config` directory to persist user settings and requests.
|
||||
|
||||
## Jellystat for Tracking Server Stats
|
||||
|
||||
Jellystat adds another layer of utility, giving you the ability to track detailed statistics about your Jellyfin server usage, including playback metrics, user activity, and media insights.
|
||||
|
||||
It relies on a PostgreSQL database (`jellystat-db` service) to store all the data. I’ve configured it like this:
|
||||
|
||||
```yaml
|
||||
jellystat-db:
|
||||
image: postgres:15.2
|
||||
container_name: jellystat-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: <password>
|
||||
volumes:
|
||||
- ./jellystat-db:/var/lib/postgresql/data
|
||||
|
||||
```
|
||||
|
||||
Jellystat then communicates with the PostgreSQL instance:
|
||||
|
||||
```yaml
|
||||
jellystat:
|
||||
image: cyfershepard/jellystat:latest
|
||||
container_name: jellystat
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: <password>
|
||||
POSTGRES_IP: jellystat-db
|
||||
POSTGRES_PORT: 5432
|
||||
JWT_SECRET: <jwt_secret>
|
||||
TZ: Europe/Prague
|
||||
volumes:
|
||||
- ./jellystat-data:/app/backend/backup-data
|
||||
ports:
|
||||
- "5110:3000"
|
||||
depends_on:
|
||||
- jellystat-db
|
||||
```
|
||||
|
||||
In this configuration, Jellystat is exposed on port `5110`, and I’ve configured the necessary environment variables to link it to the `jellystat-db` container. The JWT secret is used to authenticate requests between Jellystat and Jellyfin.
|
||||
|
||||
# Optimizing and Securing the Setup
|
||||
## 1. Automatic Restart
|
||||
Each service is configured with `restart: unless-stopped`, ensuring that the containers are restarted automatically if they crash or the host reboots. This adds reliability to the deployment, minimizing downtime.
|
||||
|
||||
## 2. Using RAM for Transcoding
|
||||
Offloading transcoding operations to RAM using `/dev/shm` significantly boosts the performance of Jellyfin, especially when dealing with multiple simultaneous streams or high-bitrate media.
|
||||
|
||||
## 3. Resource Isolation and Security
|
||||
By specifying the `user: 1000:1000` directive in the Jellyfin container, I’ve ensured that the Jellyfin service runs as a non-root user on the host. This enhances security by limiting the container's privileges. Also, mounting `/media` in read-only (`ro`) mode ensures that the Jellyfin service cannot modify the media files directly, reducing the risk of accidental data corruption.
|
||||
|
||||
# Conclusion
|
||||
This Docker Compose setup offers a robust Jellyfin media server that takes full advantage of hardware transcoding, secure media access, and useful companion services like Jellyseerr for content requests and Jellystat for usage tracking. Docker makes it easy to maintain, update, and scale this setup with minimal hassle, ensuring you have full control over your media experience.
|
||||
|
||||
With this configuration, you can efficiently manage and monitor your media server, adding a layer of automation and insight to your Jellyfin instance.
|
||||
|
9
content/posts/nextcloud.md
Normal file
9
content/posts/nextcloud.md
Normal file
@@ -0,0 +1,9 @@
|
||||
+++
|
||||
title = "Nextcloud"
|
||||
date = 2024-10-03
|
||||
description = "My Nextcloud docker based instance"
|
||||
+++
|
||||
|
||||
This is an post about my docker based Nextcloud instance
|
||||
|
||||
|
7
content/projects/_index.md
Normal file
7
content/projects/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = "Projects"
|
||||
template = "project_list.html"
|
||||
page_template = "project.html"
|
||||
sort_by = "date"
|
||||
+++
|
||||
|
18
content/projects/debrepo.md
Normal file
18
content/projects/debrepo.md
Normal file
@@ -0,0 +1,18 @@
|
||||
+++
|
||||
title = "Debrepo"
|
||||
date = 2023-05-01
|
||||
description = "A Debian repository management tool"
|
||||
|
||||
[extra]
|
||||
state = "stable"
|
||||
+++
|
||||
|
||||
# Debrepo
|
||||
A Debian repository management tool.
|
||||
|
||||
## About
|
||||
`Debrepo` is a software tool designed for creating and managing Debian repositories for `*.deb` packages, providing a lightweight and user-friendly alternative to more complex tools like `reprepo` or `aptly`. While these alternatives may offer more advanced features, Debrepo focuses on providing essential functionality and ease of use for repository management, allowing users to easily add, remove, and update packages within their repositories. With Debrepo, users can efficiently manage their Debian repositories without the unnecessary complexity of more advanced tools.
|
||||
|
||||
## Links
|
||||
- [Git](https://git.filiprojek.cz/fr/debrepo)
|
||||
|
16
content/projects/dotfiles.md
Normal file
16
content/projects/dotfiles.md
Normal file
@@ -0,0 +1,16 @@
|
||||
+++
|
||||
title = "Dotfiles"
|
||||
date = 2019-01-01
|
||||
description = "Collection of my configuration dotfiles"
|
||||
|
||||
[extra]
|
||||
state = "active"
|
||||
+++
|
||||
|
||||
# Dotfiles
|
||||
|
||||
## About
|
||||
- Collection of my configuration dotfiles.
|
||||
## Links
|
||||
- [Git](https://git.filiprojek.cz/fr/dotfiles)
|
||||
|
34
content/projects/fofrweb.md
Normal file
34
content/projects/fofrweb.md
Normal file
@@ -0,0 +1,34 @@
|
||||
+++
|
||||
title = "Fofrweb"
|
||||
date = 2019-11-27
|
||||
description = "Custom websites and student projects"
|
||||
|
||||
[extra]
|
||||
state = "active"
|
||||
+++
|
||||
|
||||
# About
|
||||
- Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.
|
||||
|
||||
# Projects
|
||||
|
||||
## Websites
|
||||
### date
|
||||
### links
|
||||
|
||||
## FofrMess
|
||||
### date
|
||||
|
||||
## FofrTasks
|
||||
### date
|
||||
|
||||
## Fofrbazar
|
||||
- Internetová bazarová platforma
|
||||
### 04/2021
|
||||
- Technologie
|
||||
- PHP
|
||||
- Laravel
|
||||
- MySQL
|
||||
- Linux
|
||||
- Apache
|
||||
|
15
content/projects/nork.md
Normal file
15
content/projects/nork.md
Normal file
@@ -0,0 +1,15 @@
|
||||
+++
|
||||
title = "Nork"
|
||||
date = 2021-08-13
|
||||
description = "Simple node.js tool that extends express projects"
|
||||
|
||||
[extra]
|
||||
state = "done"
|
||||
+++
|
||||
|
||||
# Nork
|
||||
## About
|
||||
- Simple node.js tool that extends express projects.
|
||||
|
||||
## Links
|
||||
- [Git](https://github.com/filiprojek/nork)
|
17
content/projects/pkmples.md
Normal file
17
content/projects/pkmples.md
Normal file
@@ -0,0 +1,17 @@
|
||||
+++
|
||||
title = "pkmples.cz website"
|
||||
date = 2023-12-18
|
||||
description = "Website for PKM Ples written in Zola"
|
||||
|
||||
[extra]
|
||||
state = "done"
|
||||
+++
|
||||
|
||||
Website for PKM Ples written in [Zola](https://getzola.org).
|
||||
|
||||
I am using [Gitea Actions](https://docs.gitea.com/usage/actions/overview) for CI/CD.
|
||||
|
||||
It is available at [pkmples.cz](https://pkmples.cz).
|
||||
|
||||
Source is available on my [Gitea](https://git.filiprojek.cz/fofrweb/com_pkmples.cz).
|
||||
|
21
content/projects/selfhosting.md
Normal file
21
content/projects/selfhosting.md
Normal file
@@ -0,0 +1,21 @@
|
||||
+++
|
||||
title = "Self Hosting"
|
||||
date = 2024-10-14
|
||||
description = "My selfhosting services"
|
||||
|
||||
[extra]
|
||||
state = "pending"
|
||||
+++
|
||||
|
||||
# Next Cloud
|
||||
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.
|
||||
|
||||
# Jellyfin
|
||||
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.
|
||||
|
||||
# Uptime Kuma
|
||||
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.
|
||||
|
||||
# Gitea
|
||||
Lorem ipsum dolor sit amet, officia excepteur ex fugiat reprehenderit enim labore culpa sint ad nisi Lorem pariatur mollit ex esse exercitation amet. Nisi anim cupidatat excepteur officia. Reprehenderit nostrud nostrud ipsum Lorem est aliquip amet voluptate voluptate dolor minim nulla est proident. Nostrud officia pariatur ut officia. Sit irure elit esse ea nulla sunt ex occaecat reprehenderit commodo officia dolor Lorem duis laboris cupidatat officia voluptate. Culpa proident adipisicing id nulla nisi laboris ex in Lorem sunt duis officia eiusmod. Aliqua reprehenderit commodo ex non excepteur duis sunt velit enim. Voluptate laboris sint cupidatat ullamco ut ea consectetur et est culpa et culpa duis.
|
||||
|
16
content/projects/website.md
Normal file
16
content/projects/website.md
Normal file
@@ -0,0 +1,16 @@
|
||||
+++
|
||||
title = "Website"
|
||||
date = 2023-08-29
|
||||
description = "My personal website"
|
||||
|
||||
[extra]
|
||||
state = "in development"
|
||||
+++
|
||||
|
||||
# Website
|
||||
## About
|
||||
- This website is built using the Zola static site generator.
|
||||
|
||||
## Links
|
||||
- [Git](https://git.filiprojek.cz/fr/website)
|
||||
|
@@ -1,37 +0,0 @@
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form_contact {
|
||||
--base-padding: .5rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 15rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.form_contact input {
|
||||
padding: var(--base-padding);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form_contact button {
|
||||
align-self: center;
|
||||
padding: var(--base-padding);
|
||||
margin-top: 1.5rem;
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.form_contact textarea {
|
||||
height: 8rem;
|
||||
resize: none;
|
||||
padding: var(--base-padding);
|
||||
}
|
||||
|
||||
.form_contact input, textarea, button {
|
||||
border-radius: 5px;
|
||||
}
|
28
css/home.css
28
css/home.css
@@ -1,28 +0,0 @@
|
||||
.content {
|
||||
margin-block: auto;
|
||||
}
|
||||
.home {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.home section {
|
||||
text-align: center;
|
||||
}
|
||||
.home a {
|
||||
color: lightblue;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
#pgp {
|
||||
cursor: pointer;
|
||||
}
|
||||
#pgpmobile {
|
||||
display: none;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#pgp {
|
||||
display: none;
|
||||
}
|
||||
#pgpmobile {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
.content h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.projects {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.projects img {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: flex;
|
||||
border: 1px solid red;
|
||||
border-radius: 15px;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
|
||||
width: min(40rem, calc(100% - 1rem));
|
||||
}
|
||||
|
||||
.card .card_text > * {
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.card > * {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.card .card_left {
|
||||
border: 1px solid green;
|
||||
}
|
||||
|
||||
.card .card_right {
|
||||
border: 1px solid yellow;
|
||||
}
|
||||
|
||||
.technologies {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.technologies span {
|
||||
border: 1px solid white;
|
||||
padding: .5rem;
|
||||
border-radius: 5px;
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
body {
|
||||
--base-padding: .5rem 1rem;
|
||||
--base-margin: 2rem;
|
||||
--background: #111111;
|
||||
--color: #ffffff;
|
||||
}
|
88
index.php
88
index.php
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
$prod = false;
|
||||
|
||||
require_once("./libraries/router.php");
|
||||
require_once("./libraries/console.php");
|
||||
|
||||
// Display Errors
|
||||
ini_set('display_startup_errors', 1);
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(-1);
|
||||
|
||||
// Display var_dump
|
||||
#var_dump($_SESSION);
|
||||
|
||||
// Date Time Zone
|
||||
date_default_timezone_set('Europe/Prague');
|
||||
|
||||
// site info
|
||||
$keywords = "Filip Rojek, rojek, vyvojar, developer, backend vyvojar, nodejs vyvojar, php vyvojar, web vyvojar, programator, linux administrator";
|
||||
$description = "Linux user, backend web developer and student.";
|
||||
$locale = "cs_CZ";
|
||||
$title = "Filip Rojek | Developer";
|
||||
$site_name = "Filip Rojek | Developer";
|
||||
$author = "Fofrweb | https://fofrweb.com";
|
||||
$email = "webmaster(@)fofrweb.com";
|
||||
$canonical = "https://www.filiprojek.cz/";
|
||||
?>
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=1.0" />
|
||||
<meta id="contentLanguage" http-equiv="Content-Language" content="<?= $locale ?>" />
|
||||
<meta name="keywords" content="<?= $keywords ?>" />
|
||||
<meta name="description" content="<?= $description ?>" />
|
||||
<meta property="og:locale" content="<?= $locale ?>" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="<?= $title ?>" />
|
||||
<meta property="og:description" content="<?= $description ?>" />
|
||||
<meta property="og:site_name" content="<?= $site_name ?>" />
|
||||
<meta name="author" content="<?= $author ?>" />
|
||||
<meta name="email" content="<?= $email ?>" />
|
||||
<meta name="copyright" content="<?= $copyright ?>" />
|
||||
<meta name="expires" content="never" />
|
||||
|
||||
<?php
|
||||
if($prod) { ?>
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="Seznambot" content="index,follow" />
|
||||
<meta name="Googlebot" content="index,follow" />
|
||||
<?php } else { ?>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<meta name="Seznambot" content="noindex, nofollow" />
|
||||
<meta name="Googlebot" content="noindex, nofollow" />
|
||||
<?php } ?>
|
||||
|
||||
<title><?= $title ?></title>
|
||||
<link rel="icon" type="image/x-icon" href="/img/fr.ico" />
|
||||
<link rel="canonical" href="<?= $canonical ?>" />
|
||||
<link rel="stylesheet" href="/css/style.css">
|
||||
<link rel="stylesheet" href="/css/variables.css">
|
||||
</head>
|
||||
<body>
|
||||
<?php
|
||||
require_once("./components/header.php");
|
||||
?>
|
||||
<section class="content">
|
||||
<?php
|
||||
$R = new Router();
|
||||
$R->route('GET', '/', 'home');
|
||||
$R->route('GET', '/home', 'home');
|
||||
$R->route('GET', '/domu', 'home');
|
||||
|
||||
$R->route('POST', '/contact/send', 'contact');
|
||||
|
||||
$R = null;
|
||||
?>
|
||||
</section>
|
||||
<footer>
|
||||
<?php
|
||||
require_once("./components/footer.php");
|
||||
?>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
|
||||
function console_log($data) {
|
||||
$output = $data;
|
||||
if (is_array($output))
|
||||
$output = implode(',', $output);
|
||||
|
||||
echo "<script>console.log('Debug Objects: " . $output . "' );</script>";
|
||||
}
|
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
class Mail {
|
||||
public $from;
|
||||
public $to;
|
||||
public $subject;
|
||||
public $message;
|
||||
public $headers;
|
||||
|
||||
function __construct($from, $to, $subject, $message, $headers=null) {
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
$this->subject = $subject;
|
||||
$this->headers = $headers;
|
||||
}
|
||||
|
||||
public function send() {
|
||||
// odeslání e-mailu
|
||||
$headers = 'From: noreply@fofrweb.com' . "\r\n" .
|
||||
'Reply-To: support@fofrweb.com' . "\r\n" .
|
||||
'X-Mailer: PHP/' . phpversion();
|
||||
$this->headers = $headers;
|
||||
|
||||
if (gettype($this->message) == 'string') {
|
||||
if(mail($this->to, $this->subject, $this->message, $this->headers)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
class Router {
|
||||
public $returned = false;
|
||||
|
||||
function route($method, $url, $filename) {
|
||||
$methods = ['GET', 'POST'];
|
||||
if(in_array($method, $methods)) {
|
||||
if($_SERVER['REQUEST_METHOD'] == $method) {
|
||||
if ($_SERVER['REQUEST_URI'] == $url) {
|
||||
require_once("./pages/$filename/$filename.php");
|
||||
$this->returned = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
if($_SERVER['REQUEST_METHOD'] == 'GET') {
|
||||
if(!$this->returned){
|
||||
$url = explode("/", $_SERVER['REQUEST_URI']);
|
||||
$url = $url[count($url)-1];
|
||||
|
||||
if (file_exists("./pages/$url/$url.php")) {
|
||||
require_once("./pages/$url/$url.php");
|
||||
} else {
|
||||
require_once("./pages/errors/404.php");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,61 +0,0 @@
|
||||
<link rel="stylesheet" href="/css/contact.css">
|
||||
<h1>Kontaktujte mě</h1>
|
||||
|
||||
<form class="form_contact" action="/contact/send" method="post" name="contact">
|
||||
<label for="name">Vaše jméno:</label>
|
||||
<input id="name" type="text" placeholder="Petr Novák" required autocomplete="on" />
|
||||
|
||||
<label for="email">Váš e-mail:</label>
|
||||
<input id="email" type="email" placeholder="petr@novak.cz" required autocomplete="on" />
|
||||
|
||||
<label for="message">Zpráva:</label>
|
||||
<textarea id="message" placeholder="Napište zprávu..." required></textarea>
|
||||
|
||||
<button type="button" id="submit">Odeslat zprávu</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
const submit = document.querySelector("#submit")
|
||||
submit.addEventListener("click", async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('name', document.querySelector("#name").value);
|
||||
formData.append('email', document.querySelector("#email").value);
|
||||
formData.append('message', document.querySelector("#message").value);
|
||||
|
||||
try {
|
||||
await fetch('/contact/send', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
console.log('Message sent successfully.')
|
||||
} catch (err) {
|
||||
console.error('Error:', error)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<?php
|
||||
ini_set('log_errors', 1);
|
||||
|
||||
if(isset($_POST['email']) && isset($_POST['message'])) {
|
||||
$email = $_POST['email'];
|
||||
$message = $_POST['message'];
|
||||
$name = $_POST['name'];
|
||||
|
||||
require_once(__DIR__ . "/../../libraries/mail.php");
|
||||
$message = "Nová zpráva z webu filiprojek.cz\n\nOdesílatel: $email\nZpráva:\n$message";
|
||||
$m = new Mail($_POST['email'], 'webmaster@filiprojek.cz', 'Nová zpráva z webu filiprojek.cz', $message);
|
||||
|
||||
if($m->send()) {
|
||||
console_log("zprava odeslana");
|
||||
header('Location: /?sent=true');
|
||||
} else {
|
||||
error_log("Message wasn't sent");
|
||||
error_log(var_dump($m));
|
||||
echo "<p>Něco se nepovedlo, zkuste to znovu... :(</p>";
|
||||
echo "<a href='/'>Zpět</a>";
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +0,0 @@
|
||||
<h1>Error 404 - not found</h1>
|
||||
|
@@ -1,33 +0,0 @@
|
||||
<link rel="stylesheet" href="./css/home.css" >
|
||||
<section class="home">
|
||||
<section>
|
||||
<p>Filip Rojek <filip(at)filiprojek.cz></p>
|
||||
<br>
|
||||
<p>Backend node.js & php developer <a href="https://www.fofrweb.com/" target="_blank">@fofrweb</a>,<br> Linux Gentoo user, student and coffee enthusiast<span class="underscore">_</span><p>
|
||||
<br>
|
||||
<p id="pgp" title="copy pgp to clipboard">PGP: fc37b989787acf8cbce7c0c2a56a345efe321161</p>
|
||||
<br>
|
||||
<p><a href="https://git.filiprojek.cz/fr" target="_blank">Git</a> <a href="https://t.me/filiprojek" target="_blank">Telegram</a> <a href="#" id="pgpmobile" title="copy pgp to clipboard">PGP</a></p>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const _ = document.querySelector(".underscore")
|
||||
let b = false
|
||||
setInterval(() => {
|
||||
b ? _.style.visibility = "visible" : _.style.visibility = "hidden"
|
||||
b = !b
|
||||
}, 500)
|
||||
|
||||
const pgp = document.querySelector("#pgp")
|
||||
const pgpmobile = document.querySelector("#pgpmobile")
|
||||
const pgpcp = (e) => {
|
||||
console.log(e)
|
||||
navigator.clipboard.writeText("fc37b989787acf8cbce7c0c2a56a345efe321161")
|
||||
alert("pgp has been copied to the clipboard")
|
||||
}
|
||||
|
||||
pgp.addEventListener("click", pgpcp)
|
||||
pgpmobile.addEventListener("click", pgpcp)
|
||||
</script>
|
||||
</section>
|
||||
|
@@ -1,107 +0,0 @@
|
||||
<link rel="stylesheet" href="/css/projects.css">
|
||||
<h1>Projekty</h1>
|
||||
|
||||
<section class="projects">
|
||||
<div class="card">
|
||||
<div class="card_left card_text">
|
||||
<p>pkmples.cz</p>
|
||||
<p>Komerční webová stránka pro PKM PARTY PLES.</p>
|
||||
<div class="technologies">
|
||||
<span>PHP</span>
|
||||
<span>Linux</span>
|
||||
<span>Nginx</span>
|
||||
</div>
|
||||
<p>01/2023</p>
|
||||
</div>
|
||||
<div class="card_right">
|
||||
<img src="https://www.fofrweb.com/images/pkm_two.png" alt="project screenshot">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card_left">
|
||||
<img src="https://www.fofrweb.com/images/rapidd.png" alt="project screenshot">
|
||||
</div>
|
||||
<div class="card_right card_text">
|
||||
<p>rapiddglass.cz</p>
|
||||
<p>Komerční webová stránka pro přípravek na ochranu a čištění krbového skla Rapidd Glass.</p>
|
||||
<p>10/2022</p>
|
||||
<div class="technologies">
|
||||
<span>PHP</span>
|
||||
<span>Linux</span>
|
||||
<span>Nginx</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card_left card_text">
|
||||
<p>danielryslavy.cz</p>
|
||||
<p>Komerční webová stránka osobního trenéra Daniela Ryšlavého.</p>
|
||||
<p>06/2022</p>
|
||||
<div class="technologies">
|
||||
<span>PHP</span>
|
||||
<span>Linux</span>
|
||||
<span>Nginx</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card_right">
|
||||
<img src="https://www.fofrweb.com/images/fitness.png" alt="project screenshot">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card_left">
|
||||
<img src="#" alt="project screenshot">
|
||||
</div>
|
||||
<div class="card_right card_text">
|
||||
<p>Fofrmess</p>
|
||||
<p>Chatovací realtime PWA aplikace.</p>
|
||||
<p>02/2022</p>
|
||||
<div class="technologies">
|
||||
<span>Node.js</span>
|
||||
<span>Express.js</span>
|
||||
<span>PostgreSQL</span>
|
||||
<span>Linux</span>
|
||||
<span>Nginx</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card_left card_text">
|
||||
<p>Fofrtasks</p>
|
||||
<p>Aplikace pro vytváření a správu poznámek a úkolů.</p>
|
||||
<p>10/2021</p>
|
||||
<div class="technologies">
|
||||
<span>Node.js</span>
|
||||
<span>Express.js</span>
|
||||
<span>MongoDB</span>
|
||||
<span>Linux</span>
|
||||
<span>Nginx</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card_right">
|
||||
<img src="#" alt="project screenshot">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card_left">
|
||||
<img src="#" alt="project screenshot">
|
||||
</div>
|
||||
<div class="card_right card_text">
|
||||
<p>Fofrbazar</p>
|
||||
<p>Internetová bazarová platforma.</p>
|
||||
<p>04/2021</p>
|
||||
<div class="technologies">
|
||||
<span>PHP</span>
|
||||
<span>Laravel</span>
|
||||
<span>MySQL</span>
|
||||
<span>Linux</span>
|
||||
<span>Apache</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
25
sass/_fonts.scss
Normal file
25
sass/_fonts.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
@font-face {
|
||||
font-family: open-sans;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
src: url('fonts/open-sans-regular.woff2') format('woff2'),
|
||||
url('fonts/open-sans-regular.woff') format('woff'),
|
||||
url('fonts/open-sans-regular.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: open-sans;
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
src: url('fonts/open-sans-italic.woff2') format('woff2'),
|
||||
url('fonts/open-sans-italic.woff') format('woff'),
|
||||
url('fonts/open-sans-italic.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: open-sans;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
src: url('fonts/open-sans-bold.woff2') format('woff2'),
|
||||
url('fonts/open-sans-bold.woff') format('woff'),
|
||||
url('fonts/open-sans-bold.ttf') format('truetype');
|
||||
}
|
||||
|
15
sass/about.scss
Normal file
15
sass/about.scss
Normal file
@@ -0,0 +1,15 @@
|
||||
.about {
|
||||
h2 {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
h3, h4, h5, h6, p {
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
|
42
sass/content.scss
Normal file
42
sass/content.scss
Normal file
@@ -0,0 +1,42 @@
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 2rem 30%;
|
||||
}
|
||||
|
||||
.content {
|
||||
@media (max-width: 690px) {
|
||||
margin: 2rem 2rem;
|
||||
}
|
||||
.pgpwrap {
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.contact {
|
||||
h1 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
li::before {
|
||||
content: '•';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color:white;
|
||||
}
|
||||
}
|
||||
|
14
sass/general.scss
Normal file
14
sass/general.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
.flex-col {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
35
sass/home.scss
Normal file
35
sass/home.scss
Normal file
@@ -0,0 +1,35 @@
|
||||
.content {
|
||||
margin-block: auto;
|
||||
}
|
||||
.home {
|
||||
margin-top: 5rem;
|
||||
font-size: 1.2rem;
|
||||
padding: 0 5%;
|
||||
|
||||
section {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
color: lightblue;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#pgp {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#pgpmobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
#pgp {
|
||||
display: none;
|
||||
}
|
||||
#pgpmobile {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
51
sass/project.scss
Normal file
51
sass/project.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
.language-yaml {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-x: auto;
|
||||
padding: .5rem;
|
||||
margin: .5rem 0;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.project-wrapper {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
|
||||
h1 {
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3, h4, h5, h6, p {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
|
||||
.link-back {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.project-wrapper {
|
||||
max-width: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.project-wrapper {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
|
29
sass/project_list.scss
Normal file
29
sass/project_list.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
.project-list {
|
||||
h1 {
|
||||
margin-bottom: 2rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
.project {
|
||||
margin-bottom: 3rem;
|
||||
|
||||
.title {
|
||||
text-decoration: none;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-style: italic;
|
||||
margin-bottom: .5rem;
|
||||
margin-top: .5rem;
|
||||
}
|
||||
hr {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
|
@@ -30,48 +30,55 @@ html {
|
||||
background: #000000;
|
||||
}
|
||||
|
||||
.nav {
|
||||
nav {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 1rem;
|
||||
padding: 1rem 2rem;
|
||||
justify-content: space-between;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.nav .logo img {
|
||||
.logo img {
|
||||
height: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.nav .links {
|
||||
.links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
z-index: -1;
|
||||
color: var(--color);
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--color);
|
||||
}
|
||||
}
|
||||
|
||||
a { color: var(--color); }
|
||||
a:visited { color: var(--color); }
|
||||
a:hover { color: red; }
|
||||
a {
|
||||
color: lightblue;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:hover { color: var(--a-hover); }
|
||||
|
||||
@media (min-width: 400px) {
|
||||
.nav .links {
|
||||
nav .links {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
display: flex;
|
||||
width: 100vw;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-self: flex-end;
|
||||
margin-top: auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
15
sass/vars.scss
Normal file
15
sass/vars.scss
Normal file
@@ -0,0 +1,15 @@
|
||||
* {
|
||||
--base-padding: .5rem 1rem;
|
||||
--base-margin: 2rem;
|
||||
|
||||
--c-white: #ffffff;
|
||||
--c-gray: #111111;
|
||||
--c-red: #FF3A20;
|
||||
--c-darkblue: #02081e;
|
||||
--c-blue: #5B85AA;
|
||||
|
||||
--a-hover: var(--c-red);
|
||||
--color: var(--c-white);
|
||||
--background: var(--c-gray);
|
||||
}
|
||||
|
1
static/img/at.svg
Normal file
1
static/img/at.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#ffffffff" d="M480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480v58q0 26 17 44t43 18q26 0 43-18t17-44v-58q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93h200v80H480Zm0-280q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35Z"/></svg>
|
After Width: | Height: | Size: 605 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 526 B After Width: | Height: | Size: 526 B |
107
static/js/console.js
Normal file
107
static/js/console.js
Normal file
@@ -0,0 +1,107 @@
|
||||
const c = document.querySelector(".console")
|
||||
const ps1 = "[fr@website ~]$ "
|
||||
const motd = "Welcome to my website!<br>You can use `help` for more informations :)"
|
||||
let line = "";
|
||||
|
||||
function exec(command) {
|
||||
switch (command[0]) {
|
||||
case "help":
|
||||
line = "help<br>author<br>contact<br>clear<br>echo"
|
||||
break;
|
||||
case "author":
|
||||
line = "Filip Rojek, 2023"
|
||||
break
|
||||
case "contact":
|
||||
line = "Filip Rojek <<a href='mailto: filip@filiprojek.cz'>filip@filiprojek.cz</a>><br>web: <a href='https://filiprojek.cz' target='_blank'>www.filiprojek.cz</a><br>telegram: <a href='https://t.me/filiprojek' target='_blank'>@filiprojek</a>"
|
||||
break
|
||||
case "clear":
|
||||
write("000ctrll")
|
||||
return false
|
||||
case "echo":
|
||||
line = ""
|
||||
command.forEach((cmd, i) => {
|
||||
if (i === 0) return
|
||||
line += cmd + " "
|
||||
});
|
||||
line = line.substring(0, line.length - 1) // remove last space
|
||||
break
|
||||
default:
|
||||
line = "frsh: " + command[0] + ": command not found"
|
||||
break;
|
||||
}
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
function write(key, payload) {
|
||||
switch(key) {
|
||||
case "Enter":
|
||||
command = c.lastChild.textContent.replace(ps1, "")
|
||||
command = command.split(" ");
|
||||
let out = exec(command)
|
||||
if(out !== false) {
|
||||
line = document.createElement("p")
|
||||
line.innerHTML += out
|
||||
line.innerHTML += "<br>"
|
||||
c.appendChild(line)
|
||||
line = document.createElement("p")
|
||||
line.innerHTML += ps1
|
||||
c.appendChild(line)
|
||||
}
|
||||
command = ""
|
||||
break
|
||||
case "000ctrll":
|
||||
c.innerHTML = ""
|
||||
if(payload == motd) c.innerHTML = motd
|
||||
line = document.createElement("p")
|
||||
line.innerHTML += ps1
|
||||
c.appendChild(line)
|
||||
break
|
||||
case "000backspace":
|
||||
if(c.lastChild.textContent.slice(0, -1) !== ps1.slice(0, -1)) {
|
||||
c.lastChild.innerHTML = c.lastChild.textContent.slice(0, -1)
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
c.lastChild.innerHTML += key
|
||||
}
|
||||
}
|
||||
|
||||
function customCtrlShortcuts(plusKey) {
|
||||
document.addEventListener("keydown", e => {
|
||||
if(e.ctrlKey && e.key == plusKey) {
|
||||
e.preventDefault()
|
||||
write("000ctrl"+plusKey)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// On load init the terminal
|
||||
window.addEventListener("load", () => {
|
||||
write("000ctrll", motd)
|
||||
if (navigator.userAgent.toLowerCase().includes("mobile")) {
|
||||
const mi = document.querySelector(".mobile-input")
|
||||
mi.style="opacity: 0; width: 0; height: 0"
|
||||
c.addEventListener("click", e => {
|
||||
mi.focus()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Capture the keypress
|
||||
window.addEventListener("keypress", e => {
|
||||
e.preventDefault()
|
||||
write(e.key)
|
||||
})
|
||||
|
||||
window.addEventListener("keydown", e => {
|
||||
if(e.key == "Backspace") {
|
||||
e.preventDefault()
|
||||
write("000backspace")
|
||||
}
|
||||
})
|
||||
|
||||
// Register custom ctrl shortcuts
|
||||
customCtrlShortcuts("l") // ctrl + l
|
||||
customCtrlShortcuts("c") // ctrl + c
|
23
templates/404.html
Normal file
23
templates/404.html
Normal file
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Filip Rojek | 404 Not found</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="/img/fr.ico">
|
||||
<link rel="stylesheet" href="/general.css">
|
||||
<link rel="stylesheet" href="/vars.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<style>
|
||||
.flex-col {
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content flex-col">
|
||||
<h2>404 - Not found</h2>
|
||||
<a href="/">Home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
11
templates/about.html
Normal file
11
templates/about.html
Normal file
@@ -0,0 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="/about.css">
|
||||
{% endblock styles %}
|
||||
|
||||
{% block content %}
|
||||
<section class="about content">
|
||||
{{ page.content | safe }}
|
||||
</section>
|
||||
{% endblock content %}
|
72
templates/base.html
Normal file
72
templates/base.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>
|
||||
{% block title %}
|
||||
{% if current_path != "/" %}
|
||||
{{ config.title }} –
|
||||
{% if section.title %}
|
||||
{{ section.title }}
|
||||
{% elif page.title %}
|
||||
{{ page.title }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ config.title }}
|
||||
{% endif %}
|
||||
{% endblock title %}
|
||||
</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Filip Rojek - Personal website">
|
||||
<link rel="icon" type="image/x-icon" href="/img/fr.ico">
|
||||
<link rel="stylesheet" href="/general.css">
|
||||
<link rel="stylesheet" href="/vars.css">
|
||||
<link rel="stylesheet" href="/content.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="stylesheet" href="/home.css">
|
||||
{% block styles %}
|
||||
{% endblock styles %}
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<img src="/img/fr_logo.webp" alt="logo">
|
||||
</a>
|
||||
<div class="links">
|
||||
{% for item in config.extra.nav_items %}
|
||||
<a href="{{ item.path }}"
|
||||
{% if item.path == current_path and item.path != "/" %}
|
||||
class="active"
|
||||
{% endif %}
|
||||
>{{ item.name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{% block content %}
|
||||
<section>
|
||||
{% if section %}
|
||||
{{ section.content | safe }}
|
||||
{% elif page %}
|
||||
{{ page.content | safe }}
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock content %}
|
||||
</main>
|
||||
<footer>
|
||||
<p>Build time: {{ now() | date(format="%Y-%m-%d %H:%M") }},
|
||||
{% if config.extra.git %}
|
||||
<a href="{{ config.extra.git }}" target="_blank">Source</a>
|
||||
{% endif %}</p>
|
||||
<p>© filiprojek.cz 2022 - {{ now() | date(format="%Y")}}</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
console.log("I heard that a cool frontend developer works for https://fofrweb.com")
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
26
templates/index.html
Normal file
26
templates/index.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<section class="home">
|
||||
<section>
|
||||
<p>Filip Rojek <filip@filiprojek.cz></p>
|
||||
<br>
|
||||
<p>Linux sysadmin, backend node.js & php developer <a href="https://www.fofrweb.com/" target="_blank">@fofrweb</a>,<br> GNU/Linux <a href="http://voidlinux.org" target="_blank">Void</a> user, student and coffee enthusiast<span class="underscore">_</span><p>
|
||||
<br>
|
||||
<p>PGP: <a href="https://keys.openpgp.org/vks/v1/by-fingerprint/CA3D9BE28315B49164130CD97E65EA58C6075F09" target="_blank">0x7E65EA58C6075F09</a></p>
|
||||
<br>
|
||||
<p><a href="https://git.filiprojek.cz/fr" target="_blank">Git</a> <a href="https://t.me/filiprojek" target="_blank">Telegram</a> <a href="#" id="pgpmobile" title="copy pgp to clipboard">PGP</a></p>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const _ = document.querySelector(".underscore")
|
||||
let b = false
|
||||
setInterval(() => {
|
||||
b ? _.style.visibility = "visible" : _.style.visibility = "hidden"
|
||||
b = !b
|
||||
}, 500)
|
||||
</script>
|
||||
</section>
|
||||
|
||||
{% endblock content %}
|
||||
|
26
templates/post.html
Normal file
26
templates/post.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="/project.css">
|
||||
{% endblock styles %}
|
||||
|
||||
{% block content %}
|
||||
<section class="project-wrapper flex-col">
|
||||
<!--
|
||||
<section class="left-bar flex-col">
|
||||
<h2>Projects</h2>
|
||||
{% set section = get_section(path=page.ancestors | last) %}
|
||||
{% for project in section.pages %}
|
||||
<a href="{{ project.permalink }}">{{project.title}}</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
-->
|
||||
|
||||
<!-- <h2>{{ page.title }}</h2> -->
|
||||
<div>
|
||||
{{ page.content | safe }}
|
||||
</div>
|
||||
<a href="/posts" class="link-back">Back to list of posts</a>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
28
templates/post_list.html
Normal file
28
templates/post_list.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="/project_list.css">
|
||||
{% endblock styles %}
|
||||
|
||||
{% block content %}
|
||||
<section class="project-list content">
|
||||
<h1>My Posts</h1>
|
||||
{% for post in section.pages %}
|
||||
<div class="project">
|
||||
<a class="title" href="{{ post.permalink }}">
|
||||
{{ post.title }}
|
||||
</a>
|
||||
<p class="description">
|
||||
{% if post.description %}
|
||||
{{ post.description }}
|
||||
{% else %}
|
||||
…
|
||||
{% endif %}
|
||||
</p>
|
||||
<hr>
|
||||
<p>{{ post.date }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
26
templates/project.html
Normal file
26
templates/project.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="/project.css">
|
||||
{% endblock styles %}
|
||||
|
||||
{% block content %}
|
||||
<section class="project-wrapper flex-col">
|
||||
<!--
|
||||
<section class="left-bar flex-col">
|
||||
<h2>Projects</h2>
|
||||
{% set section = get_section(path=page.ancestors | last) %}
|
||||
{% for project in section.pages %}
|
||||
<a href="{{ project.permalink }}">{{project.title}}</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
-->
|
||||
|
||||
<h2>{{ page.title }}</h2>
|
||||
<div>
|
||||
{{ page.content | safe }}
|
||||
</div>
|
||||
<a href="/projects" class="link-back">Back to list of projects</a>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
28
templates/project_list.html
Normal file
28
templates/project_list.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block styles %}
|
||||
<link rel="stylesheet" href="/project_list.css">
|
||||
{% endblock styles %}
|
||||
|
||||
{% block content %}
|
||||
<section class="project-list content">
|
||||
<h1>My Projects</h1>
|
||||
{% for project in section.pages %}
|
||||
<div class="project">
|
||||
<a class="title" href="{{ project.permalink }}">
|
||||
{{ project.title }}
|
||||
</a>
|
||||
<p class="description">
|
||||
{% if project.description %}
|
||||
{{ project.description }}
|
||||
{% else %}
|
||||
…
|
||||
{% endif %}
|
||||
</p>
|
||||
<hr>
|
||||
<p>{{ project.date }} | {{ project.extra.state}}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
Reference in New Issue
Block a user