From 90314a5851fa4c31f1d8b92119d66fbf30c787bd Mon Sep 17 00:00:00 2001 From: rail Date: Sat, 25 Oct 2025 21:04:29 +0300 Subject: [PATCH] Initial commit: railwayka.ru landing page as dcape app - Dcape app structure with Makefile, docker-compose.yml - Woodpecker CI/CD pipeline configuration - Custom Dockerfile with nginx alpine - Static website with dark theme and animations - Service cards, tech stack, GitOps workflow visualization - Konami code easter egg --- .gitignore | 2 + .woodpecker.yml | 20 +++ Dockerfile | 6 + Makefile | 46 +++++++ README.md | 75 +++++++++++ docker-compose.yml | 13 ++ html/index.html | 131 ++++++++++++++++++ html/script.js | 75 +++++++++++ html/style.css | 324 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 692 insertions(+) create mode 100644 .gitignore create mode 100644 .woodpecker.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 html/index.html create mode 100644 html/script.js create mode 100644 html/style.css diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87bf182 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.env.bak diff --git a/.woodpecker.yml b/.woodpecker.yml new file mode 100644 index 0000000..16157a6 --- /dev/null +++ b/.woodpecker.yml @@ -0,0 +1,20 @@ + +# Woodpecker CI pipeline for railwayka landing page + +variables: + - &dcape_img 'dcape-compose' + +clone: + git: + image: woodpeckerci/plugin-git + settings: + lfs: false + tags: false + +steps: + deploy: + image: *dcape_img + commands: + - make .default-deploy + volumes: + - /var/run/docker.sock:/var/run/docker.sock diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7696acc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +ARG IMAGE=nginx +ARG IMAGE_VER=1.27-alpine + +FROM --platform=$BUILDPLATFORM ${IMAGE}:${IMAGE_VER} + +COPY html /usr/share/nginx/html diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..044c443 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +## dcape-app-railwayka-landing Makefile +## This file extends Makefile.app from dcape +#: + +SHELL = /bin/bash +CFG ?= .env +CFG_BAK ?= $(CFG).bak + +#- Docker repo & image name without version +IMAGE ?= nginx + +#- ver +IMAGE_VER ?= 1.27-alpine + +# ------------------------------------------------------------------------------ +# app custom config + +# Overwrite for setup +APP_SITE ?= railwayka.ru + +#- domain (no www for this site) +APP_ACME_DOMAIN ?= $(APP_SITE) + +PERSIST_FILES = + +# ------------------------------------------------------------------------------ + +# if exists - load old values +-include $(CFG_BAK) +export + +-include $(CFG) +export + +# ------------------------------------------------------------------------------ +# Find and include DCAPE_ROOT/Makefile +DCAPE_COMPOSE ?= dcape-compose +DCAPE_ROOT ?= $(shell docker inspect -f "{{.Config.Labels.dcape_root}}" $(DCAPE_COMPOSE)) + +ifeq ($(shell test -e $(DCAPE_ROOT)/Makefile.app && echo -n yes),yes) + include $(DCAPE_ROOT)/Makefile.app +else + include /opt/dcape/Makefile.app +endif + +.default-deploy: docker-build diff --git a/README.md b/README.md new file mode 100644 index 0000000..c38465b --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# railwayka.ru Landing Page + +Business card website for railwayka.ru infrastructure, deployed via [dcape](https://github.com/dopos/dcape) GitOps platform. + +## Features + +- Modern dark theme with animated stars background +- Service cards showcasing current and planned services +- Technology stack descriptions +- GitOps workflow visualization +- Konami code easter egg +- Responsive design + +## Technology Stack + +- **Nginx**: Alpine-based web server +- **Docker**: Containerized deployment +- **Traefik**: Automatic HTTPS via Let's Encrypt +- **Dcape**: GitOps-based deployment + +## Deployment + +This application uses GitOps workflow: + +1. Push code to Git repository +2. Woodpecker CI automatically triggers +3. Application builds and deploys +4. Service goes live at https://railwayka.ru + +### Manual Deployment + +```bash +# Clone repository +git clone https://git.dc.railwayka.ru/howl/railwayka-landing.git +cd railwayka-landing + +# Configure environment +make config +# Edit .env.sample and rename to .env +mv .env.sample .env + +# Build and deploy +make docker-build +make up +``` + +## Local Development + +To run locally: + +```bash +# Serve files with any web server +cd html +python3 -m http.server 8000 +# Visit http://localhost:8000 +``` + +## Structure + +``` +. +├── Makefile # Dcape app Makefile +├── docker-compose.yml # Service definition +├── Dockerfile # Custom nginx image +├── .woodpecker.yml # CI/CD pipeline +├── html/ # Static website files +│ ├── index.html +│ ├── style.css +│ └── script.js +└── README.md +``` + +## License + +MIT diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..17ad2cd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +# Railwayka Landing Page - dcape app config + +services: + app: + labels: + - traefik.http.routers.${APP_TAG}.rule=Host(`${APP_SITE:?Must be set}`) + # TLS support + - traefik.http.routers.${APP_TAG}.tls.domains[0].main=${APP_SITE} + build: + context: . + args: + IMAGE: "${IMAGE}" + IMAGE_VER: "${IMAGE_VER}" diff --git a/html/index.html b/html/index.html new file mode 100644 index 0000000..baa3b33 --- /dev/null +++ b/html/index.html @@ -0,0 +1,131 @@ + + + + + + railwayka.ru - Infrastructure & Services + + + +
+
+
+

railwayka.ru

+

Self-Hosted Infrastructure & Services

+
+ +
+

Welcome to my personal infrastructure platform, powered by modern DevOps technologies and GitOps workflows.

+
+ +
+

Available Services

+
+
+
🔧
+

Git Repository

+

Self-hosted Git server with web interface

+ Visit Gitea → +
+ +
+
💬
+

Matrix Server

+

Decentralized, secure messaging

+ Coming Soon +
+ +
+
📝
+

Blog

+

Technical writing and documentation

+ Coming Soon +
+ +
+
🔐
+

Password Manager

+

Self-hosted credential storage

+ Coming Soon +
+ +
+
☁️
+

Cloud Storage

+

Private file storage and sync

+ Coming Soon +
+ +
+
📊
+

Monitoring

+

System metrics and dashboards

+ Coming Soon +
+
+
+ +
+

Technology Stack

+
+
+

🚀 Dcape

+

GitOps-based infrastructure management platform. Deploy applications with simple git push commands.

+
+
+

🐳 Docker

+

All services run in isolated containers with automatic orchestration and health monitoring.

+
+
+

🔒 Let's Encrypt

+

Automatic SSL/TLS certificates with wildcard domain support for secure HTTPS connections.

+
+
+

🌐 Traefik

+

Modern reverse proxy with automatic service discovery and HTTPS routing.

+
+
+

🗄️ PostgreSQL

+

Shared production-grade database for application data persistence.

+
+
+

📡 PowerDNS

+

Self-hosted authoritative DNS server with API-driven zone management.

+
+
+
+ +
+

What is GitOps?

+

GitOps is a modern infrastructure management approach where Git serves as the single source of truth. All infrastructure changes and application deployments are made through Git commits and pull requests.

+
+
+ 1 +

Push code to Git repository

+
+
+
+ 2 +

CI/CD pipeline builds

+
+
+
+ 3 +

Automatic deployment

+
+
+
+ 4 +

Service goes live

+
+
+
+ +
+

Powered by open-source technologies and self-hosted infrastructure

+ +
+
+ + + diff --git a/html/script.js b/html/script.js new file mode 100644 index 0000000..cbd39f9 --- /dev/null +++ b/html/script.js @@ -0,0 +1,75 @@ +// Smooth scroll for any future internal links +document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + const target = document.querySelector(this.getAttribute('href')); + if (target) { + target.scrollIntoView({ + behavior: 'smooth', + block: 'start' + }); + } + }); +}); + +// Add fade-in animation to cards on scroll +const observerOptions = { + threshold: 0.1, + rootMargin: '0px 0px -50px 0px' +}; + +const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.style.opacity = '0'; + entry.target.style.transform = 'translateY(20px)'; + entry.target.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + + setTimeout(() => { + entry.target.style.opacity = '1'; + entry.target.style.transform = 'translateY(0)'; + }, 100); + + observer.unobserve(entry.target); + } + }); +}, observerOptions); + +// Observe all service cards and tech items +document.querySelectorAll('.service-card, .tech-item, .flow-step').forEach(el => { + observer.observe(el); +}); + +// Easter egg: Konami code detector +let konamiCode = ['ArrowUp', 'ArrowUp', 'ArrowDown', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowLeft', 'ArrowRight', 'b', 'a']; +let konamiIndex = 0; + +document.addEventListener('keydown', (e) => { + if (e.key === konamiCode[konamiIndex]) { + konamiIndex++; + if (konamiIndex === konamiCode.length) { + document.body.style.animation = 'rainbow 2s ease infinite'; + setTimeout(() => { + document.body.style.animation = ''; + konamiIndex = 0; + }, 5000); + } + } else { + konamiIndex = 0; + } +}); + +// Add CSS for rainbow animation +const style = document.createElement('style'); +style.textContent = ` + @keyframes rainbow { + 0% { filter: hue-rotate(0deg); } + 100% { filter: hue-rotate(360deg); } + } +`; +document.head.appendChild(style); + +// Console message for developers +console.log('%c👋 Hello, developer!', 'font-size: 20px; color: #6366f1; font-weight: bold;'); +console.log('%cInterested in the infrastructure? Check out https://git.dc.railwayka.ru', 'font-size: 14px; color: #a1a1aa;'); +console.log('%cPowered by Dcape - GitOps infrastructure management', 'font-size: 12px; color: #6366f1;'); diff --git a/html/style.css b/html/style.css new file mode 100644 index 0000000..f874746 --- /dev/null +++ b/html/style.css @@ -0,0 +1,324 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +:root { + --bg-primary: #0a0e27; + --bg-secondary: #141b3d; + --text-primary: #e4e4e7; + --text-secondary: #a1a1aa; + --accent: #6366f1; + --accent-hover: #4f46e5; + --card-bg: rgba(20, 27, 61, 0.6); + --border: rgba(99, 102, 241, 0.2); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; + overflow-x: hidden; +} + +.stars { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + background-image: + radial-gradient(2px 2px at 20px 30px, white, transparent), + radial-gradient(2px 2px at 60px 70px, white, transparent), + radial-gradient(1px 1px at 50px 50px, white, transparent), + radial-gradient(1px 1px at 130px 80px, white, transparent), + radial-gradient(2px 2px at 90px 10px, white, transparent); + background-size: 200px 200px; + animation: twinkle 5s ease-in-out infinite; + opacity: 0.4; +} + +@keyframes twinkle { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 0.7; } +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 2rem; + position: relative; + z-index: 1; +} + +header { + text-align: center; + padding: 4rem 0 2rem; +} + +h1 { + font-size: 4rem; + font-weight: 800; + background: linear-gradient(135deg, var(--accent) 0%, #ec4899 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + margin-bottom: 0.5rem; + position: relative; +} + +.glitch { + animation: glitch 3s infinite; +} + +@keyframes glitch { + 0%, 90%, 100% { + text-shadow: none; + } + 92% { + text-shadow: 2px 2px #ff00de, -2px -2px #00fff9; + } + 94% { + text-shadow: -2px 2px #ff00de, 2px -2px #00fff9; + } +} + +.tagline { + font-size: 1.25rem; + color: var(--text-secondary); + font-weight: 300; +} + +section { + margin: 4rem 0; +} + +.intro { + text-align: center; + font-size: 1.1rem; + color: var(--text-secondary); + max-width: 600px; + margin: 2rem auto; +} + +h2 { + font-size: 2rem; + margin-bottom: 2rem; + text-align: center; + color: var(--text-primary); +} + +.service-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.5rem; + margin-top: 2rem; +} + +.service-card { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 2rem; + transition: all 0.3s ease; + backdrop-filter: blur(10px); + position: relative; + overflow: hidden; +} + +.service-card::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1), transparent); + transition: left 0.5s ease; +} + +.service-card:hover::before { + left: 100%; +} + +.service-card:hover { + transform: translateY(-5px); + border-color: var(--accent); + box-shadow: 0 10px 30px rgba(99, 102, 241, 0.3); +} + +.service-icon { + font-size: 3rem; + margin-bottom: 1rem; +} + +.service-card h3 { + font-size: 1.5rem; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +.service-card p { + color: var(--text-secondary); + margin-bottom: 1rem; +} + +.service-link { + display: inline-block; + color: var(--accent); + text-decoration: none; + font-weight: 500; + transition: color 0.3s ease; +} + +.service-link:hover { + color: var(--accent-hover); + text-decoration: underline; +} + +.service-card.coming-soon { + opacity: 0.7; +} + +.badge { + display: inline-block; + padding: 0.25rem 0.75rem; + background: rgba(99, 102, 241, 0.2); + border: 1px solid var(--accent); + border-radius: 20px; + font-size: 0.875rem; + color: var(--accent); +} + +.tech-info { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 2rem; + margin-top: 2rem; +} + +.tech-item { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 1.5rem; + backdrop-filter: blur(10px); +} + +.tech-item h3 { + font-size: 1.25rem; + margin-bottom: 0.75rem; + color: var(--text-primary); +} + +.tech-item p { + color: var(--text-secondary); + font-size: 0.95rem; +} + +code { + background: rgba(99, 102, 241, 0.2); + padding: 0.2rem 0.5rem; + border-radius: 4px; + font-family: 'Courier New', monospace; + color: var(--accent); +} + +.about-gitops { + background: var(--card-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 3rem; + backdrop-filter: blur(10px); +} + +.about-gitops p { + text-align: center; + max-width: 800px; + margin: 0 auto 2rem; + color: var(--text-secondary); +} + +.gitops-flow { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + gap: 1rem; + margin-top: 2rem; +} + +.flow-step { + background: rgba(99, 102, 241, 0.1); + border: 2px solid var(--accent); + border-radius: 12px; + padding: 1.5rem; + text-align: center; + min-width: 150px; + transition: all 0.3s ease; +} + +.flow-step:hover { + transform: scale(1.05); + background: rgba(99, 102, 241, 0.2); +} + +.step-number { + display: block; + width: 40px; + height: 40px; + margin: 0 auto 0.5rem; + background: var(--accent); + color: white; + border-radius: 50%; + line-height: 40px; + font-weight: bold; +} + +.flow-arrow { + color: var(--accent); + font-size: 2rem; + font-weight: bold; +} + +footer { + text-align: center; + padding: 3rem 0 2rem; + color: var(--text-secondary); + border-top: 1px solid var(--border); + margin-top: 4rem; +} + +.footer-note { + margin-top: 0.5rem; + font-size: 0.9rem; +} + +footer strong { + color: var(--accent); +} + +@media (max-width: 768px) { + h1 { + font-size: 2.5rem; + } + + .service-grid { + grid-template-columns: 1fr; + } + + .gitops-flow { + flex-direction: column; + } + + .flow-arrow { + transform: rotate(90deg); + } + + .container { + padding: 1rem; + } +}